Skip to content
This repository was archived by the owner on Dec 25, 2024. It is now read-only.

Commit 486f157

Browse files
committed
fix: improve type props handling
1 parent 5648b44 commit 486f157

File tree

10 files changed

+168
-55
lines changed

10 files changed

+168
-55
lines changed

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@
6666
"devDependencies": {
6767
"@antfu/eslint-config": "^0.7.0",
6868
"@antfu/ni": "^0.7.0",
69+
"@antfu/utils": "^0.2.4",
6970
"@types/jest": "^27.0.1",
7071
"@types/node": "^16.7.1",
7172
"@vue/composition-api": "^1.1.1",

pnpm-lock.yaml

Lines changed: 12 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/core/identifiers.ts

Lines changed: 5 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -34,11 +34,7 @@ export function getIdentifierDeclarations(nodes: Statement[], identifiers = new
3434
for (const declarator of node.declarations)
3535
handleVariableId(declarator.id)
3636
}
37-
else if (node.type === 'FunctionDeclaration') {
38-
if (node.id)
39-
identifiers.add(node.id.name)
40-
}
41-
else if (node.type === 'ClassDeclaration') {
37+
else if (node.type === 'FunctionDeclaration' || node.type === 'ClassDeclaration') {
4238
if (node.id)
4339
identifiers.add(node.id.name)
4440
}
@@ -67,9 +63,7 @@ export function getIdentifierUsages(node?: Expression | SpreadElement | PrivateN
6763
}
6864
else if (node.type === 'CallExpression') {
6965
getIdentifierUsages(node.callee as Expression, identifiers)
70-
node.arguments.forEach((arg) => {
71-
getIdentifierUsages(arg as Expression, identifiers)
72-
})
66+
node.arguments.forEach(arg => getIdentifierUsages(arg as Expression, identifiers))
7367
}
7468
else if (node.type === 'BinaryExpression' || node.type === 'LogicalExpression') {
7569
getIdentifierUsages(node.left, identifiers)
@@ -99,25 +93,18 @@ export function getIdentifierUsages(node?: Expression | SpreadElement | PrivateN
9993
})
10094
}
10195
else if (node.type === 'ArrayExpression') {
102-
node.elements.forEach((element) => {
103-
getIdentifierUsages(element, identifiers)
104-
})
96+
node.elements.forEach(element => getIdentifierUsages(element, identifiers))
10597
}
106-
else if (node.type === 'SpreadElement') {
98+
else if (node.type === 'SpreadElement' || node.type === 'ReturnStatement') {
10799
getIdentifierUsages(node.argument, identifiers)
108100
}
109101
else if (node.type === 'NewExpression') {
110102
getIdentifierUsages(node.callee as Expression, identifiers)
111-
node.arguments.forEach((arg) => {
112-
getIdentifierUsages(arg as Expression, identifiers)
113-
})
103+
node.arguments.forEach(arg => getIdentifierUsages(arg as Expression, identifiers))
114104
}
115105
else if (node.type === 'ArrowFunctionExpression' || node.type === 'FunctionDeclaration' || node.type === 'FunctionExpression') {
116106
getIdentifierUsages(node.body, identifiers)
117107
}
118-
else if (node.type === 'ReturnStatement') {
119-
getIdentifierUsages(node.argument, identifiers)
120-
}
121108
// else {
122109
// console.log(node)
123110
// }

src/core/transformScriptSetup.ts

Lines changed: 29 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,28 @@
11
import { types as t } from '@babel/core'
22
import { camelize, capitalize } from '@vue/shared'
3-
import traverse from '@babel/traverse'
3+
import { Node, Statement } from '@babel/types'
44
import generate from '@babel/generator'
5+
import { partition } from '@antfu/utils'
56
import { ParsedSFC, ScriptSetupTransformOptions } from '../types'
67
import { applyMacros } from './macros'
78
import { getIdentifierDeclarations } from './identifiers'
89

910
export function transformScriptSetup(sfc: ParsedSFC, options?: ScriptSetupTransformOptions) {
1011
const { scriptSetup, script, template } = sfc
1112

12-
const imports = scriptSetup.ast.body.filter(n => n.type === 'ImportDeclaration')
13-
const body = scriptSetup.ast.body.filter(n => n.type !== 'ImportDeclaration')
13+
const { nodes: body, props } = applyMacros(scriptSetup.ast.body)
1414

15-
const { nodes: scriptSetupBody, props } = applyMacros(body)
15+
const [hoisted, setupBody] = partition(
16+
body,
17+
n => n.type === 'ImportDeclaration'
18+
|| n.type === 'ExportNamedDeclaration'
19+
|| n.type.startsWith('TS'),
20+
)
1621

1722
// get all identifiers in `<script setup>`
1823
const declarations = new Set<string>()
19-
getIdentifierDeclarations(imports, declarations)
20-
getIdentifierDeclarations(body, declarations)
24+
getIdentifierDeclarations(hoisted, declarations)
25+
getIdentifierDeclarations(setupBody, declarations)
2126

2227
// filter out identifiers that are used in `<template>`
2328
const returns = Array.from(declarations)
@@ -31,31 +36,28 @@ export function transformScriptSetup(sfc: ParsedSFC, options?: ScriptSetupTransf
3136
)
3237

3338
// append `<script setup>` imports to `<script>`
34-
let ast = t.program([
35-
...imports,
36-
...script.ast.body,
37-
])
3839

3940
const __sfc = t.identifier('__sfc_main')
4041

4142
let hasBody = false
4243

43-
// replace `export default` with a temproray variable
44-
// `const __sfc_main = { ... }`
45-
traverse(ast, {
46-
ExportDefaultDeclaration(path) {
47-
hasBody = true
48-
const decl = path.node.declaration
49-
path.replaceWith(
50-
t.variableDeclaration('const', [
51-
t.variableDeclarator(
52-
__sfc,
53-
decl as any,
54-
),
55-
]),
56-
)
57-
},
58-
})
44+
let ast = t.program(
45+
[...hoisted, ...script.ast.body]
46+
.map((node: Node) => {
47+
// replace `export default` with a temproray variable
48+
// `const __sfc_main = { ... }`
49+
if (node.type === 'ExportDefaultDeclaration') {
50+
hasBody = true
51+
return t.variableDeclaration('const', [
52+
t.variableDeclarator(
53+
__sfc,
54+
node.declaration as any,
55+
),
56+
])
57+
}
58+
return node
59+
}) as Statement[],
60+
)
5961

6062
// inject `const __sfc_main = {}` if `<script>` has default export
6163
if (!hasBody) {
@@ -104,7 +106,7 @@ export function transformScriptSetup(sfc: ParsedSFC, options?: ScriptSetupTransf
104106
t.identifier('__props'),
105107
t.identifier('__ctx'),
106108
], t.blockStatement([
107-
...scriptSetupBody,
109+
...setupBody,
108110
returnStatement as any,
109111
])),
110112
),

test/__snapshots__/transform.test.ts.snap

Lines changed: 82 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ exports[`transform fixture Macros.vue 1`] = `
2727
<div @click=\\"emit(props.msg)\\">{{ msg }}</div>
2828
</template>
2929
30-
<script >
30+
<script lang=\\"js\\">
3131
const __sfc_main = {};
3232
__sfc_main.props = {
3333
msg: String
@@ -57,22 +57,70 @@ const __sfc_main = {};
5757
__sfc_main.props = {
5858
msg: String
5959
};
60+
export default __sfc_main;
61+
</script>
62+
"
63+
`;
64+
65+
exports[`transform fixture MacrosType.vue 1`] = `
66+
"<template>
67+
<div @click=\\"emit(props.msg)\\">
68+
{{ msg }}
69+
</div>
70+
</template>
71+
72+
<script lang=\\"ts\\">
73+
const __sfc_main = {};
74+
__sfc_main.props = {
75+
msg: {
76+
key: \\"msg\\",
77+
required: false,
78+
type: [String],
79+
default: 'Hello'
80+
},
81+
value: {
82+
key: \\"value\\",
83+
required: true,
84+
type: [Number, String]
85+
},
86+
data: {
87+
key: \\"data\\",
88+
required: false,
89+
type: [Object]
90+
}
91+
};
6092
6193
__sfc_main.setup = (__props, __ctx) => {
62-
return {};
94+
const props = __props;
95+
const emit = __ctx.emit;
96+
return {
97+
props,
98+
emit
99+
};
63100
};
64101
65102
export default __sfc_main;
66103
</script>
67104
"
68105
`;
69106
70-
exports[`transform fixture MacrosType.vue 1`] = `
107+
exports[`transform fixture MacrosType2.vue 1`] = `
71108
"<template>
72-
<div @click=\\"emit(props.msg)\\">{{ msg }}</div>
109+
<div @click=\\"emit(props.msg)\\">
110+
{{ msg }}
111+
</div>
73112
</template>
74113
75114
<script lang=\\"ts\\">
115+
export interface Props {
116+
msg: string;
117+
value: number | string;
118+
data?: {
119+
value: boolean;
120+
};
121+
arr?: [number, string, {}];
122+
any: any;
123+
}
76124
const __sfc_main = {};
77125
__sfc_main.props = {
78126
msg: {
@@ -84,7 +132,22 @@ __sfc_main.props = {
84132
value: {
85133
key: \\"value\\",
86134
required: true,
87-
type: [Number]
135+
type: [Number, String]
136+
},
137+
data: {
138+
key: \\"data\\",
139+
required: false,
140+
type: [Object]
141+
},
142+
arr: {
143+
key: \\"arr\\",
144+
required: false,
145+
type: [Array]
146+
},
147+
any: {
148+
key: \\"any\\",
149+
required: true,
150+
type: [null]
88151
}
89152
};
90153
@@ -150,6 +213,20 @@ export default __sfc_main;
150213
"
151214
`;
152215
216+
exports[`transform fixture ScriptOnly.vue 1`] = `
217+
"<script lang=\\"ts\\">
218+
import { defineComponent } from '@vue/composition-api';
219+
220+
const __sfc_main = defineComponent({
221+
name: 'Hi'
222+
});
223+
224+
export default __sfc_main;
225+
</script>
226+
227+
"
228+
`;
229+
153230
exports[`transform fixture TemplateOnly.vue 1`] = `
154231
"<template>
155232
<div>

test/fixtures/Macros.vue

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@
22
<div @click="emit(props.msg)">{{ msg }}</div>
33
</template>
44

5-
<script setup>
5+
<script setup lang="js">
66
const props = defineProps({
77
msg: String,
88
})
99
10-
const emit = defineEmits()
10+
const emit = defineEmits(['msg', 'update'])
1111
</script>

test/fixtures/MacrosType.vue

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
<template>
2-
<div @click="emit(props.msg)">{{ msg }}</div>
2+
<div @click="emit(props.msg)">
3+
{{ msg }}
4+
</div>
35
</template>
46

57
<script setup lang="ts">
68
const props = withDefaults(
7-
defineProps<{ msg: string; value: number }>(),
9+
defineProps<{ msg: string; value: number | string; data?: { value: boolean } }>(),
810
{ msg: 'Hello' },
911
)
1012

test/fixtures/MacrosType2.vue

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<template>
2+
<div @click="emit(props.msg)">
3+
{{ msg }}
4+
</div>
5+
</template>
6+
7+
<script setup lang="ts">
8+
export interface Props {
9+
msg: string
10+
value: number | string
11+
data?: { value: boolean }
12+
arr?: [number, string, {}]
13+
any: any
14+
}
15+
16+
const props = withDefaults(
17+
defineProps<Props>(),
18+
{ msg: 'Hello' },
19+
)
20+
21+
const emit = defineEmits<{
22+
(msg: string): void
23+
(msg: number): number
24+
}>()
25+
</script>

test/fixtures/ScriptOnly.vue

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<script lang="ts">
2+
import { defineComponent } from '@vue/composition-api'
3+
4+
export default defineComponent({
5+
name: 'Hi',
6+
})
7+
</script>

test/identifiers.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ describe('identifiers', () => {
4545
['!a', ['a']],
4646
['!!c', ['c']],
4747
['[a,b,[c,{d}],...args]', ['a', 'b', 'c', 'd', 'args']],
48-
['new Foo(a,[b])', ['Foo', 'a', 'b']],
48+
['new Foo(a,[b,,c])', ['Foo', 'a', 'b', 'c']],
4949
['new RC.Foo()', ['RC']],
5050
['() => foo(bar)', ['foo', 'bar']],
5151
['() => { foo() + bar; a }', ['foo', 'bar', 'a']],

0 commit comments

Comments
 (0)