Skip to content

Commit b41801b

Browse files
authored
feat(component-meta): collect destructured props defaults (#5101)
1 parent 8dfbbc0 commit b41801b

File tree

8 files changed

+54
-14
lines changed

8 files changed

+54
-14
lines changed

packages/component-meta/lib/base.ts

+8
Original file line numberDiff line numberDiff line change
@@ -754,6 +754,14 @@ function readVueComponentDefaultProps(
754754
...resolvePropsOption(ast, obj, printer, ts),
755755
};
756756
}
757+
} else if (descriptor.scriptSetup && scriptSetupRanges?.defineProps?.destructured) {
758+
const ast = descriptor.scriptSetup.ast;
759+
for (const [prop, initializer] of scriptSetupRanges.defineProps.destructured) {
760+
if (initializer) {
761+
const expText = printer?.printNode(ts.EmitHint.Expression, initializer, ast) ?? initializer.getText(ast);
762+
result[prop] = { default: expText };
763+
}
764+
}
757765
}
758766

759767
function findObjectLiteralExpression(node: ts.Node) {

packages/component-meta/tests/index.spec.ts

+11
Original file line numberDiff line numberDiff line change
@@ -365,6 +365,17 @@ const worker = (checker: ComponentMetaChecker, withTsconfig: boolean) => describ
365365
});
366366
});
367367

368+
test('reference-type-props-destructured', () => {
369+
const componentPath = path.resolve(__dirname, '../../../test-workspace/component-meta/reference-type-props/component-destructure.vue');
370+
const meta = checker.getComponentMeta(componentPath);
371+
372+
expect(meta.type).toEqual(TypeMeta.Class);
373+
374+
const text = meta.props.find(prop => prop.name === 'text');
375+
376+
expect(text?.default).toEqual('"foobar"');
377+
})
378+
368379
test('reference-type-props-js', () => {
369380
const componentPath = path.resolve(__dirname, '../../../test-workspace/component-meta/reference-type-props/component-js.vue');
370381
const meta = checker.getComponentMeta(componentPath);

packages/language-core/lib/codegen/utils/index.ts

+6-5
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ export function collectVars(
3232
results: string[] = []
3333
) {
3434
const identifiers = collectIdentifiers(ts, node, []);
35-
for (const [id] of identifiers) {
35+
for (const { id } of identifiers) {
3636
results.push(getNodeText(ts, id, ast));
3737
}
3838
return results;
@@ -41,15 +41,16 @@ export function collectVars(
4141
export function collectIdentifiers(
4242
ts: typeof import('typescript'),
4343
node: ts.Node,
44-
results: [id: ts.Identifier, isRest: boolean][] = [],
45-
isRest = false
44+
results: Array<{ id: ts.Identifier, isRest: boolean, initializer: ts.Expression | undefined }> = [],
45+
isRest = false,
46+
initializer: ts.Expression | undefined = undefined
4647
) {
4748
if (ts.isIdentifier(node)) {
48-
results.push([node, isRest]);
49+
results.push({ id: node, isRest, initializer });
4950
}
5051
else if (ts.isObjectBindingPattern(node)) {
5152
for (const el of node.elements) {
52-
collectIdentifiers(ts, el.name, results, !!el.dotDotDotToken);
53+
collectIdentifiers(ts, el.name, results, !!el.dotDotDotToken, el.initializer);
5354
}
5455
}
5556
else if (ts.isArrayBindingPattern(node)) {

packages/language-core/lib/parsers/scriptSetupRanges.ts

+4-4
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ type DefineProp = {
2424

2525
type DefineProps = CallExpressionRange & {
2626
name?: string;
27-
destructured?: Set<string>;
27+
destructured?: Map<string, ts.Expression | undefined>;
2828
destructuredRest?: string;
2929
statement: TextRange;
3030
};
@@ -285,15 +285,15 @@ export function parseScriptSetupRanges(
285285
};
286286
if (ts.isVariableDeclaration(parent)) {
287287
if (ts.isObjectBindingPattern(parent.name)) {
288-
defineProps.destructured = new Set();
288+
defineProps.destructured = new Map();
289289
const identifiers = collectIdentifiers(ts, parent.name, []);
290-
for (const [id, isRest] of identifiers) {
290+
for (const { id, isRest, initializer } of identifiers) {
291291
const name = _getNodeText(id);
292292
if (isRest) {
293293
defineProps.destructuredRest = name;
294294
}
295295
else {
296-
defineProps.destructured.add(name);
296+
defineProps.destructured.set(name, initializer);
297297
}
298298
}
299299
}

packages/language-core/lib/plugins/vue-tsx.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ function createTsx(
133133
);
134134
const destructuredPropNames = unstable.computedSet(
135135
computed(() => {
136-
const newNames = new Set(scriptSetupRanges.get()?.defineProps?.destructured);
136+
const newNames = new Set(scriptSetupRanges.get()?.defineProps?.destructured?.keys());
137137
const rest = scriptSetupRanges.get()?.defineProps?.destructuredRest;
138138
if (rest) {
139139
newNames.add(rest);

packages/language-service/lib/plugins/vue-inlayhints.ts

+4-4
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ export function create(ts: typeof import('typescript')): LanguageServicePlugin {
4545
for (const [prop, isShorthand] of findDestructuredProps(
4646
ts,
4747
virtualCode._sfc.scriptSetup.ast,
48-
scriptSetupRanges.defineProps.destructured
48+
scriptSetupRanges.defineProps.destructured.keys()
4949
)) {
5050
const name = prop.text;
5151
const end = prop.getEnd();
@@ -117,7 +117,7 @@ type Scope = Record<string, boolean>;
117117
export function findDestructuredProps(
118118
ts: typeof import('typescript'),
119119
ast: ts.SourceFile,
120-
props: Set<string>
120+
props: MapIterator<string>
121121
) {
122122
const rootScope: Scope = Object.create(null);
123123
const scopeStack: Scope[] = [rootScope];
@@ -192,7 +192,7 @@ export function findDestructuredProps(
192192
&& ts.isCallExpression(initializer)
193193
&& initializer.expression.getText(ast) === 'defineProps';
194194

195-
for (const [id] of collectIdentifiers(ts, name)) {
195+
for (const { id } of collectIdentifiers(ts, name)) {
196196
if (isDefineProps) {
197197
excludedIds.add(id);
198198
} else {
@@ -208,7 +208,7 @@ export function findDestructuredProps(
208208
}
209209

210210
for (const p of parameters) {
211-
for (const [id] of collectIdentifiers(ts, p)) {
211+
for (const { id } of collectIdentifiers(ts, p)) {
212212
registerLocalBinding(id);
213213
}
214214
}

packages/tsc/tests/__snapshots__/dts.spec.ts.snap

+9
Original file line numberDiff line numberDiff line change
@@ -335,6 +335,15 @@ export default _default;
335335
"
336336
`;
337337
338+
exports[`vue-tsc-dts > Input: reference-type-props/component-destructure.vue, Output: reference-type-props/component-destructure.vue.d.ts 1`] = `
339+
"type __VLS_Props = {
340+
text: string;
341+
};
342+
declare const _default: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, HTMLDivElement>;
343+
export default _default;
344+
"
345+
`;
346+
338347
exports[`vue-tsc-dts > Input: reference-type-props/component-js.vue, Output: reference-type-props/component-js.vue.d.ts 1`] = `
339348
"declare const _default: import("vue").DefineComponent<import("vue").ExtractPropTypes<{
340349
foo: {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<script lang="ts" setup>
2+
const {
3+
text = 'foobar'
4+
} = defineProps<{
5+
text: string;
6+
}>();
7+
</script>
8+
9+
<template>
10+
<div>{{ text }}</div>
11+
</template>

0 commit comments

Comments
 (0)