diff --git a/packages/component-meta/lib/base.ts b/packages/component-meta/lib/base.ts index d0517916a0..c4477d7006 100644 --- a/packages/component-meta/lib/base.ts +++ b/packages/component-meta/lib/base.ts @@ -266,8 +266,9 @@ ${commandLine.vueOptions.target < 3 ? vue2TypeHelpersCode : typeHelpersCode} let _events: ReturnType | undefined; let _slots: ReturnType | undefined; let _exposed: ReturnType | undefined; + let _extended_meta: ReturnType | undefined; - return { + const meta = { get type() { return _type ?? (_type = getType()); }, @@ -282,8 +283,16 @@ ${commandLine.vueOptions.target < 3 ? vue2TypeHelpersCode : typeHelpersCode} }, get exposed() { return _exposed ?? (_exposed = getExposed()); - }, - }; + } + } + + return new Proxy(meta, { + get(target, prop) { + if (prop in target) return target[prop as keyof typeof target] + const extendedMeta = _extended_meta ?? (_extended_meta = getExtendedMeta()) + return extendedMeta?.[prop as string] + } + }) function getType() { @@ -297,6 +306,36 @@ ${commandLine.vueOptions.target < 3 ? vue2TypeHelpersCode : typeHelpersCode} return 0; } + + function getExtendedMeta() { + let meta: Record = {}; + const snapshot = language.scripts.get(componentPath)?.snapshot!; + const fileText = snapshot.getText(0, snapshot.getLength()); + const ast = ts.createSourceFile('/temp', fileText, ts.ScriptTarget.Latest, true); + const identifier = 'defineComponentMeta' + const printer = ts.createPrinter(checkerOptions.printer); + + if (!ast.identifiers.get(identifier)) return + + function traverse(node: ts.Node): void { + if (ts.isIdentifier(node) && node.text === identifier) { + const argument = node.parent.arguments[0]; + if (ts.isObjectLiteralExpression(argument)) { + argument.properties.forEach(property => { + if (!ts.isPropertyAssignment(property)) return + const key = property.name.getText(); + const valueNode = resolveDefaultOptionExpression(property.initializer, ts); + meta[key] = printer?.printNode(ts.EmitHint.Expression, valueNode, ast) ?? valueNode.getText(ast) + }); + } + } + return ts.forEachChild(node, traverse) + } + + traverse(ast); + return meta; + } + function getProps() { const $props = symbolProperties.find(prop => prop.escapedName === 'props'); diff --git a/packages/component-meta/lib/types.ts b/packages/component-meta/lib/types.ts index 961addaf08..543c67be9d 100644 --- a/packages/component-meta/lib/types.ts +++ b/packages/component-meta/lib/types.ts @@ -13,6 +13,7 @@ export interface ComponentMeta { events: EventMeta[]; slots: SlotMeta[]; exposed: ExposeMeta[]; + [key: string]: any } export enum TypeMeta { diff --git a/packages/component-meta/tests/index.spec.ts b/packages/component-meta/tests/index.spec.ts index 2c08b08a74..511255ad75 100644 --- a/packages/component-meta/tests/index.spec.ts +++ b/packages/component-meta/tests/index.spec.ts @@ -859,6 +859,27 @@ const worker = (checker: ComponentMetaChecker, withTsconfig: boolean) => describ expect(a).toBeDefined(); expect(b).toBeDefined(); }); + + test('component-define-meta', () => { + const componentPath = path.resolve(__dirname, '../../../test-workspace/component-meta/define-meta/component-define-meta.vue'); + const meta = checker.getComponentMeta(componentPath); + expect(meta.foo).toMatch('bar') + expect(meta.nested).toMatch(`{ foo: 'baz', arr: [1, 2] }`) + }); + + test('component-define-meta.ts', () => { + const componentPath = path.resolve(__dirname, '../../../test-workspace/component-meta/define-meta/component-define-meta.ts'); + const meta = checker.getComponentMeta(componentPath); + expect(meta.foo).toMatch('bar') + expect(meta.nested).toMatch(`{ foo: 'baz', arr: [1, 2] }`) + }); + + test('component-define-meta.tsx', () => { + const componentPath = path.resolve(__dirname, '../../../test-workspace/component-meta/define-meta/component-define-meta.tsx'); + const meta = checker.getComponentMeta(componentPath); + expect(meta.foo).toMatch('bar') + expect(meta.nested).toMatch(`{ foo: 'baz', arr: [1, 2] }`) + }); }); const checkerOptions: MetaCheckerOptions = { diff --git a/test-workspace/component-meta/define-meta/component-define-meta.ts b/test-workspace/component-meta/define-meta/component-define-meta.ts new file mode 100644 index 0000000000..7729d88d86 --- /dev/null +++ b/test-workspace/component-meta/define-meta/component-define-meta.ts @@ -0,0 +1,6 @@ +import { h, defineComponent } from 'vue'; + +export default defineComponent(() => { + defineComponentMeta({ foo: 'bar', nested: { foo: 'baz', arr: [1, 2] } }) + return () => h('span'); +}); diff --git a/test-workspace/component-meta/define-meta/component-define-meta.tsx b/test-workspace/component-meta/define-meta/component-define-meta.tsx new file mode 100644 index 0000000000..fb82a04213 --- /dev/null +++ b/test-workspace/component-meta/define-meta/component-define-meta.tsx @@ -0,0 +1,6 @@ +import { defineComponent } from 'vue'; + +export default defineComponent(() => { + defineComponentMeta({ foo: 'bar', nested: { foo: 'baz', arr: [1, 2] } }) + return () => +}); diff --git a/test-workspace/component-meta/define-meta/component-define-meta.vue b/test-workspace/component-meta/define-meta/component-define-meta.vue new file mode 100644 index 0000000000..e445dbab40 --- /dev/null +++ b/test-workspace/component-meta/define-meta/component-define-meta.vue @@ -0,0 +1,7 @@ + + +