Skip to content

Commit 6cb9e67

Browse files
committed
add support for ParentComponent type annotation
1 parent 0be795f commit 6cb9e67

File tree

4 files changed

+66
-24
lines changed

4 files changed

+66
-24
lines changed

readme.md

Lines changed: 23 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,25 @@ Usage with examples:
1111

1212
```jsx
1313
// Use the `Component` type to mark components that will be transformed by the plugin
14-
import { Component } from 'solid-js'
14+
import type { Component } from 'solid-js'
1515
const MyComp: Component<...> = ({ a, b, c }) => {a; b; c;};
1616

1717
// ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ The above code will compile to
18-
import { Component } from 'solid-js'
18+
import type { Component } from 'solid-js'
1919
const MyComp: Component<...> = props => {props.a; props.b; props.c;}
2020

2121

2222

23+
// Also works with the `ParentComponent` type
24+
import type { ParentComponent } from 'solid-js'
25+
const MyComp: ParentComponent<...> = ({ a, b, c }) => {a; b; c;};
26+
27+
// ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓
28+
import type { ParentComponent } from 'solid-js'
29+
const MyComp: ParentComponent<...> = props => {props.a; props.b; props.c;}
30+
31+
32+
2333
// You can use a compile time function instead of using the `Component` type (needed for vanilla JS)
2434
import { component } from 'undestructure-macros'
2535
const MyComp = component(({ a, b, c }) => {a; b; c;})
@@ -30,13 +40,13 @@ const MyComp = props => {props.a; props.b; props.c;}
3040

3141

3242
// Default props using `mergeProps`
33-
import { Component } from 'solid-js'
43+
import type { Component } from 'solid-js'
3444
const MyComp: Component<...> = (
3545
{ a = 1, b = 2, c = 3 } = defaultProps
3646
) => {a; b; c;}
3747

3848
// ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓
39-
import { Component, mergeProps } from 'solid-js'
49+
import { type Component, mergeProps } from 'solid-js'
4050
const MyComp: Component<...> = props => {
4151
props = mergeProps(defaultProps, { a: 1, b: 2, c: 3 }, props);
4252
props.a; props.b; props.c;
@@ -45,21 +55,21 @@ const MyComp: Component<...> = props => {
4555

4656

4757
// Rename props
48-
import { Component } from 'solid-js'
58+
import type { Component } from 'solid-js'
4959
const MyComp: Component<...> = ({ a: d, b: e, c: f }) => {d; e; f;}
5060

5161
// ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓
52-
import { Component, mergeProps } from 'solid-js'
62+
import { type Component, mergeProps } from 'solid-js'
5363
const MyComp: Component<...> = props => {props.a; props.b; props.c;}
5464

5565

5666

5767
// Rest element destructuring using `splitProps`
58-
import { Component } from 'solid-js'
68+
import type { Component } from 'solid-js'
5969
const MyComp: Component<...> = ({ a, b, c, ...other }) => {a; b; c; other;}
6070

6171
// ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓
62-
import { Component, splitProps } from 'solid-js'
72+
import { type Component, splitProps } from 'solid-js'
6373
const MyComp: Component<...> = props => {
6474
let other;
6575
[props, other] = splitProps(props, ["a", "b", "c"]);
@@ -69,15 +79,15 @@ const MyComp: Component<...> = props => {
6979

7080

7181
// You can nest components
72-
import { Component } from 'solid-js'
82+
import type { Component } from 'solid-js'
7383
const Parent: Component<...> = ({ a, b }) => {
7484
const Child: Component<...> = ({ c, d }) => {
7585
a; b; c; d;
7686
}
7787
}
7888

7989
// ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓
80-
import { Component } from 'solid-js'
90+
import type { Component } from 'solid-js'
8191
const Parent: Component<...> = props1 => {
8292
const Child: Component<...> = props2 => {
8393
props1.a; props1.b; props2.c; props2.d;
@@ -99,7 +109,7 @@ In both cases you can use the 'import as' syntax.
99109
Examples:
100110

101111
```tsx
102-
import { Component } from 'solid-js'
112+
import type { Component } from 'solid-js'
103113

104114
const MyComponent: Component = // ...
105115
```
@@ -111,15 +121,15 @@ const MyComponent: Solid.Component = // ...
111121
```
112122

113123
```tsx
114-
import { Component as ComponentAlias } from 'solid-js'
124+
import type { Component as ComponentAlias } from 'solid-js'
115125

116126
const MyComponent: ComponentAlias = // ...
117127
```
118128

119129
This example won't work:
120130

121131
```tsx
122-
import { Component } from 'solid-js'
132+
import type { Component } from 'solid-js'
123133

124134
type ComponentAlias = Component
125135

src/check-func/check-func-annotation.cjs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ const possibleAnnotationKinds = ['type', 'ctf', 'pragma']
66

77

88
/**
9-
* Checks if the function is annotated with the `Component` type or the `component` CTF.
9+
* Checks if the function is annotated with the `Component` or `ParentComponent` types or the `component` CTF.
1010
* @todo Add support for pragma annotations.
1111
*/
1212
function checkFuncAnnotation(opts, path, state) {

src/check-func/check-type-annotation.cjs

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11

22
/**
3-
* Checks if the function is annotated with the `Component` type.
3+
* Checks if the function is annotated with the `Component` or `ParentComponent` types.
44
*/
55
function checkTypeAnnotation(path) {
66
// Check that the func is an arrow function which is assigned to a variable.
@@ -19,11 +19,17 @@ function checkTypeAnnotation(path) {
1919

2020
const importSpecifier = typeBinding.path.node
2121
if (importSpecifier.type !== "ImportSpecifier") return false
22-
if (importSpecifier.imported.name !== "Component") return false
22+
if (
23+
importSpecifier.imported.name !== "Component"
24+
&& importSpecifier.imported.name !== "ParentComponent"
25+
) return false
2326
if (typeBinding.path.parent.source.value !== "solid-js") return false
2427
}
2528
else if (typeAnnotation.typeName.type === "TSQualifiedName") {
26-
if (typeAnnotation.typeName.right.name !== "Component") return false
29+
if (
30+
typeAnnotation.typeName.right.name !== "Component"
31+
&& typeAnnotation.typeName.right.name !== "ParentComponent"
32+
) return false
2733
const typeQualification = typeAnnotation.typeName.left
2834
if (typeQualification.type !== "Identifier") return false
2935
const typeQualificationName = typeQualification.name

tests/index.test.js

Lines changed: 33 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import * as assert from 'uvu/assert'
66
test('index', async () => {
77
await testBasicCase()
88
await testAliasedCtf()
9-
await testTypeAnnotation()
9+
await testComponentTypeAnnotation()
1010
await testAliasedTypeAnnotation()
1111
await testDefaultProps()
1212
await testFallbackProps()
@@ -78,14 +78,14 @@ _props2 => {
7878
}
7979

8080

81-
async function testTypeAnnotation() {
81+
async function testComponentTypeAnnotation() {
8282
const src =
83-
/*javascript*/`import { Component } from 'solid-js';
83+
/*javascript*/`import type { Component } from 'solid-js';
8484
const comp: Component = ({ a, b }) => {a; b;};
8585
const comp2: Component<T> = ({ a, b }) => {a; b;};`
8686

8787
const expectedOutput =
88-
/*javascript*/`import { Component } from 'solid-js';
88+
/*javascript*/`import type { Component } from 'solid-js';
8989
const comp: Component = _props => {
9090
_props.a;
9191
_props.b;
@@ -104,14 +104,40 @@ const comp2: Component<T> = _props2 => {
104104
}
105105

106106

107+
async function testParentComponentTypeAnnotation() {
108+
const src =
109+
/*javascript*/`import type { ParentComponent } from 'solid-js';
110+
const comp: ParentComponent = ({ a, b }) => {a; b;};
111+
const comp2: ParentComponent<T> = ({ a, b }) => {a; b;};`
112+
113+
const expectedOutput =
114+
/*javascript*/`import type { ParentComponent } from 'solid-js';
115+
const comp: ParentComponent = _props => {
116+
_props.a;
117+
_props.b;
118+
};
119+
const comp2: ParentComponent<T> = _props2 => {
120+
_props2.a;
121+
_props2.b;
122+
};`
123+
124+
const res = await transformAsync(
125+
src,
126+
{ plugins: ["@babel/plugin-syntax-typescript", "./src/index.cjs"] }
127+
)
128+
129+
assert.snapshot(res.code, expectedOutput, 'TS annotation.')
130+
}
131+
132+
107133
async function testAliasedTypeAnnotation() {
108134
const src =
109-
/*javascript*/`import { Component, Component as Comp } from 'solid-js';
135+
/*javascript*/`import type { Component, Component as Comp } from 'solid-js';
110136
const comp: Component = ({ a, b }) => {a; b;};
111137
const comp2: Comp<T> = ({ a, b }) => {a; b;};`
112138

113139
const expectedOutput =
114-
/*javascript*/`import { Component, Component as Comp } from 'solid-js';
140+
/*javascript*/`import type { Component, Component as Comp } from 'solid-js';
115141
const comp: Component = _props => {
116142
_props.a;
117143
_props.b;
@@ -694,7 +720,7 @@ export const Parent: Component = _props => {
694720
)
695721
}
696722
catch (e) {
697-
assert.unreachable(e.message);
723+
assert.unreachable(e.message)
698724
}
699725
assert.snapshot(res.code, expectedOutput, 'Nested components with type annotation')
700726
}

0 commit comments

Comments
 (0)