vue-component-meta 0.39.0 → 0.39.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/out/index.d.ts +44 -16
  2. package/out/index.js +231 -57
  3. package/package.json +4 -3
package/out/index.d.ts CHANGED
@@ -1,25 +1,53 @@
1
+ export declare type PropertyMeta = {
2
+ name: string;
3
+ default?: string;
4
+ description: string;
5
+ required: boolean;
6
+ type: string;
7
+ tags: {
8
+ name: string;
9
+ text?: string;
10
+ }[];
11
+ schema: PropertyMetaSchema;
12
+ };
13
+ export declare type PropertyMetaSchema = string | {
14
+ kind: 'enum';
15
+ type: string;
16
+ schema: PropertyMetaSchema[];
17
+ } | {
18
+ kind: 'array';
19
+ type: string;
20
+ schema: PropertyMetaSchema[];
21
+ } | {
22
+ kind: 'event';
23
+ type: string;
24
+ schema: PropertyMetaSchema[];
25
+ } | {
26
+ kind: 'object';
27
+ type: string;
28
+ schema: Record<string, PropertyMeta>;
29
+ };
1
30
  export declare function createComponentMetaChecker(tsconfigPath: string): {
2
- getComponentMeta: (componentPath: string) => {
3
- props: {
31
+ getGlobalPropNames: () => string[];
32
+ getExportNames: (componentPath: string) => string[];
33
+ getComponentMeta: (componentPath: string, exportName?: string) => {
34
+ props: PropertyMeta[];
35
+ events: {
4
36
  name: string;
5
- isOptional: boolean;
6
37
  type: string;
7
- documentationComment: string;
8
- }[];
9
- events: {
10
- name: any;
11
- parametersType: string;
12
- parameters: {
13
- name: string;
14
- type: string;
15
- isOptional: string;
16
- }[];
17
- documentationComment: string;
38
+ signature: string;
39
+ schema: PropertyMetaSchema[];
18
40
  }[];
19
41
  slots: {
20
42
  name: string;
21
- propsType: string;
22
- documentationComment: string;
43
+ type: string;
44
+ description: string;
45
+ }[];
46
+ exposed: {
47
+ name: string;
48
+ type: string;
49
+ description: string;
23
50
  }[];
24
51
  };
25
52
  };
53
+ export declare function findCmponentDefaultProps(componentPath: string): Record<string, string>;
package/out/index.js CHANGED
@@ -1,8 +1,82 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.createComponentMetaChecker = void 0;
3
+ exports.findCmponentDefaultProps = exports.createComponentMetaChecker = void 0;
4
4
  const vue = require("@volar/vue-language-core");
5
5
  const ts = require("typescript/lib/tsserverlibrary");
6
+ function createSchemaResolvers(typeChecker, symbolNode) {
7
+ function reducer(acc, cur) {
8
+ acc[cur.name] = cur;
9
+ return acc;
10
+ }
11
+ function resolveSymbolSchema(prop) {
12
+ var _a, _b, _c;
13
+ const subtype = typeChecker.getTypeOfSymbolAtLocation(prop, symbolNode);
14
+ typeChecker.getDefaultFromTypeParameter(subtype);
15
+ return {
16
+ name: prop.getEscapedName().toString(),
17
+ description: ts.displayPartsToString(prop.getDocumentationComment(typeChecker)),
18
+ tags: prop.getJsDocTags(typeChecker).map(tag => {
19
+ var _a;
20
+ return ({
21
+ name: tag.name,
22
+ text: (_a = tag.text) === null || _a === void 0 ? void 0 : _a.map(part => part.text).join(''),
23
+ });
24
+ }),
25
+ required: !Boolean((_c = (_b = (_a = prop.declarations) === null || _a === void 0 ? void 0 : _a[0]) === null || _b === void 0 ? void 0 : _b.questionToken) !== null && _c !== void 0 ? _c : false),
26
+ type: typeChecker.typeToString(subtype),
27
+ schema: resolveSchema(subtype),
28
+ };
29
+ }
30
+ function resolveCallbackSchema(signature) {
31
+ return {
32
+ kind: 'event',
33
+ type: typeChecker.signatureToString(signature),
34
+ schema: typeChecker.getTypeArguments(typeChecker.getTypeOfSymbolAtLocation(signature.parameters[0], symbolNode)).map(resolveSchema)
35
+ };
36
+ }
37
+ function resolveEventSchema(subtype) {
38
+ return (subtype.getCallSignatures().length === 1)
39
+ ? resolveCallbackSchema(subtype.getCallSignatures()[0])
40
+ : typeChecker.typeToString(subtype);
41
+ }
42
+ function resolveNestedSchema(subtype) {
43
+ // !!(subtype.flags & ts.TypeFlags.Object)
44
+ return (subtype.isClassOrInterface() || subtype.isIntersection())
45
+ ? {
46
+ kind: 'object',
47
+ type: typeChecker.typeToString(subtype),
48
+ schema: subtype.getProperties().map(resolveSymbolSchema).reduce(reducer, {})
49
+ }
50
+ : resolveEventSchema(subtype);
51
+ }
52
+ function resolveArraySchema(subtype) {
53
+ // @ts-ignore - typescript internal, isArrayLikeType exists
54
+ return typeChecker.isArrayLikeType(subtype)
55
+ ? {
56
+ kind: 'array',
57
+ type: typeChecker.typeToString(subtype),
58
+ schema: typeChecker.getTypeArguments(subtype).map(resolveSchema)
59
+ }
60
+ : resolveNestedSchema(subtype);
61
+ }
62
+ function resolveSchema(subtype) {
63
+ return subtype.isUnion()
64
+ ? {
65
+ kind: 'enum',
66
+ type: typeChecker.typeToString(subtype),
67
+ schema: subtype.types.map(resolveArraySchema)
68
+ }
69
+ : resolveArraySchema(subtype);
70
+ }
71
+ return {
72
+ resolveSymbolSchema,
73
+ resolveCallbackSchema,
74
+ resolveEventSchema,
75
+ resolveNestedSchema,
76
+ resolveArraySchema,
77
+ resolveSchema,
78
+ };
79
+ }
6
80
  function createComponentMetaChecker(tsconfigPath) {
7
81
  const parsedCommandLine = vue.tsShared.createParsedCommandLine(ts, {
8
82
  useCaseSensitiveFileNames: ts.sys.useCaseSensitiveFileNames,
@@ -13,17 +87,29 @@ function createComponentMetaChecker(tsconfigPath) {
13
87
  readFile: ts.sys.readFile,
14
88
  }, tsconfigPath);
15
89
  const scriptSnapshot = {};
90
+ const globalComponentName = tsconfigPath.replace(/\\/g, '/') + '.global.ts';
16
91
  const core = vue.createLanguageContext(Object.assign(Object.assign({}, ts.sys), { getDefaultLibFileName: (options) => ts.getDefaultLibFilePath(options), useCaseSensitiveFileNames: () => ts.sys.useCaseSensitiveFileNames, getCompilationSettings: () => parsedCommandLine.options, getScriptFileNames: () => {
17
- const result = [...parsedCommandLine.fileNames];
18
- for (const fileName of parsedCommandLine.fileNames) {
19
- if (fileName.endsWith('.vue')) {
20
- result.push(fileName + '.meta.ts');
21
- }
22
- }
23
- return result;
92
+ return [
93
+ ...parsedCommandLine.fileNames,
94
+ ...parsedCommandLine.fileNames.map(getMetaFileName),
95
+ globalComponentName,
96
+ getMetaFileName(globalComponentName),
97
+ ];
24
98
  }, getProjectReferences: () => parsedCommandLine.projectReferences, getScriptVersion: (fileName) => '0', getScriptSnapshot: (fileName) => {
25
99
  if (!scriptSnapshot[fileName]) {
26
- const fileText = fileName.endsWith('.meta.ts') ? getMetaScriptContent(fileName) : ts.sys.readFile(fileName);
100
+ let fileText;
101
+ if (fileName.endsWith('.meta.ts')) {
102
+ fileText = getMetaScriptContent(fileName);
103
+ }
104
+ else if (fileName === globalComponentName) {
105
+ fileText = `
106
+ import { defineComponent } from 'vue';
107
+ export default defineComponent({});
108
+ `;
109
+ }
110
+ else {
111
+ fileText = ts.sys.readFile(fileName);
112
+ }
27
113
  if (fileText !== undefined) {
28
114
  scriptSnapshot[fileName] = ts.ScriptSnapshot.fromString(fileText);
29
115
  }
@@ -34,76 +120,86 @@ function createComponentMetaChecker(tsconfigPath) {
34
120
  const program = tsLs.getProgram();
35
121
  const typeChecker = program.getTypeChecker();
36
122
  return {
123
+ getGlobalPropNames,
124
+ getExportNames,
37
125
  getComponentMeta,
38
126
  };
127
+ /**
128
+ * Get helper array to map internal properties added by vue to any components
129
+ *
130
+ * @example
131
+ * ```ts
132
+ * import { createComponentMetaChecker } from 'vue-component-meta'
133
+ *
134
+ * const checker = createComponentMetaChecker('path/to/tsconfig.json')
135
+ * const meta = checker.getComponentMeta('path/to/component.vue')
136
+ * const globalPropNames = checker.getGlobalPropNames();
137
+ * const props = meta.props.filter(prop => !globalPropNames.includes(prop.name))
138
+ * ```
139
+ */
140
+ function getGlobalPropNames() {
141
+ const meta = getComponentMeta(globalComponentName);
142
+ return meta.props.map(prop => prop.name);
143
+ }
144
+ function getMetaFileName(fileName) {
145
+ return (fileName.endsWith('.vue') ? fileName : fileName.substring(0, fileName.lastIndexOf('.'))) + '.meta.ts';
146
+ }
39
147
  function getMetaScriptContent(fileName) {
40
148
  return `
41
- import Component from '${fileName.substring(0, fileName.length - '.meta.ts'.length)}';
42
- export default new Component();
149
+ import * as Components from '${fileName.substring(0, fileName.length - '.meta.ts'.length)}';
150
+ export default {} as { [K in keyof typeof Components]: InstanceType<typeof Components[K]>; };;
43
151
  `;
44
152
  }
45
- function getComponentMeta(componentPath) {
153
+ function getExportNames(componentPath) {
154
+ return _getExports(componentPath).exports.map(e => e.getName());
155
+ }
156
+ function getComponentMeta(componentPath, exportName = 'default') {
46
157
  var _a;
47
- const sourceFile = program === null || program === void 0 ? void 0 : program.getSourceFile(componentPath + '.meta.ts');
48
- if (!sourceFile) {
49
- throw 'Could not find main source file';
50
- }
51
- const moduleSymbol = typeChecker.getSymbolAtLocation(sourceFile);
52
- if (!moduleSymbol) {
53
- throw 'Could not find module symbol';
54
- }
55
- const exportedSymbols = typeChecker.getExportsOfModule(moduleSymbol);
56
- let symbolNode;
57
- for (const symbol of exportedSymbols) {
58
- const [declaration] = (_a = symbol.getDeclarations()) !== null && _a !== void 0 ? _a : [];
59
- if (ts.isExportAssignment(declaration)) {
60
- symbolNode = declaration.expression;
61
- }
62
- }
63
- if (!symbolNode) {
64
- throw 'Could not find symbol node';
158
+ const { symbolNode, exports } = _getExports(componentPath);
159
+ const _export = exports.find((property) => property.getName() === exportName);
160
+ if (!_export) {
161
+ throw `Could not find export ${exportName}`;
65
162
  }
66
- const symbolType = typeChecker.getTypeAtLocation(symbolNode);
67
- const symbolProperties = symbolType.getProperties();
163
+ const componentType = typeChecker.getTypeOfSymbolAtLocation(_export, symbolNode);
164
+ const symbolProperties = (_a = componentType.getProperties()) !== null && _a !== void 0 ? _a : [];
68
165
  return {
69
166
  props: getProps(),
70
167
  events: getEvents(),
71
168
  slots: getSlots(),
169
+ exposed: getExposed(),
72
170
  };
73
171
  function getProps() {
74
172
  const $props = symbolProperties.find(prop => prop.escapedName === '$props');
173
+ let result = [];
75
174
  if ($props) {
76
175
  const type = typeChecker.getTypeOfSymbolAtLocation($props, symbolNode);
77
- const properties = type.getProperties();
78
- return properties.map(prop => {
79
- var _a, _b;
80
- return ({
81
- name: prop.escapedName,
82
- // @ts-ignore
83
- isOptional: !!((_b = (_a = prop.declarations) === null || _a === void 0 ? void 0 : _a[0]) === null || _b === void 0 ? void 0 : _b.questionToken),
84
- type: typeChecker.typeToString(typeChecker.getTypeOfSymbolAtLocation(prop, symbolNode)),
85
- documentationComment: ts.displayPartsToString(prop.getDocumentationComment(typeChecker)),
86
- });
87
- });
176
+ const properties = type.getApparentProperties();
177
+ const { resolveSymbolSchema } = createSchemaResolvers(typeChecker, symbolNode);
178
+ result = properties.map(resolveSymbolSchema);
88
179
  }
89
- return [];
180
+ // fill defaults
181
+ if (componentPath.endsWith('.vue') && exportName === 'default') {
182
+ const defaults = findCmponentDefaultProps(componentPath);
183
+ for (const propName in defaults) {
184
+ const prop = result.find(p => p.name === propName);
185
+ if (prop) {
186
+ prop.default = defaults[propName];
187
+ }
188
+ }
189
+ }
190
+ return result;
90
191
  }
91
192
  function getEvents() {
92
193
  const $emit = symbolProperties.find(prop => prop.escapedName === '$emit');
93
194
  if ($emit) {
94
195
  const type = typeChecker.getTypeOfSymbolAtLocation($emit, symbolNode);
95
196
  const calls = type.getCallSignatures();
197
+ const { resolveSchema } = createSchemaResolvers(typeChecker, symbolNode);
96
198
  return calls.map(call => ({
97
- // @ts-ignore
98
199
  name: typeChecker.getTypeOfSymbolAtLocation(call.parameters[0], symbolNode).value,
99
- parametersType: typeChecker.typeToString(typeChecker.getTypeOfSymbolAtLocation(call.parameters[1], symbolNode)),
100
- // @ts-ignore
101
- parameters: typeChecker.getTypeArguments(typeChecker.getTypeOfSymbolAtLocation(call.parameters[1], symbolNode)).map(arg => ({
102
- name: 'TODO',
103
- type: typeChecker.typeToString(arg),
104
- isOptional: 'TODO',
105
- })),
106
- documentationComment: ts.displayPartsToString(call.getDocumentationComment(typeChecker)),
200
+ type: typeChecker.typeToString(typeChecker.getTypeOfSymbolAtLocation(call.parameters[1], symbolNode)),
201
+ signature: typeChecker.signatureToString(call),
202
+ schema: typeChecker.getTypeArguments(typeChecker.getTypeOfSymbolAtLocation(call.parameters[1], symbolNode)).map(resolveSchema),
107
203
  }));
108
204
  }
109
205
  return [];
@@ -116,15 +212,93 @@ function createComponentMetaChecker(tsconfigPath) {
116
212
  const type = typeChecker.getTypeOfSymbolAtLocation($slots, symbolNode);
117
213
  const properties = type.getProperties();
118
214
  return properties.map(prop => ({
119
- name: prop.escapedName,
120
- propsType: typeChecker.typeToString(typeChecker.getTypeOfSymbolAtLocation(typeChecker.getTypeOfSymbolAtLocation(prop, symbolNode).getCallSignatures()[0].parameters[0], symbolNode)),
121
- // props: {}, // TODO
122
- documentationComment: ts.displayPartsToString(prop.getDocumentationComment(typeChecker)),
215
+ name: prop.getName(),
216
+ type: typeChecker.typeToString(typeChecker.getTypeOfSymbolAtLocation(typeChecker.getTypeOfSymbolAtLocation(prop, symbolNode).getCallSignatures()[0].parameters[0], symbolNode)),
217
+ description: ts.displayPartsToString(prop.getDocumentationComment(typeChecker)),
218
+ }));
219
+ }
220
+ return [];
221
+ }
222
+ function getExposed() {
223
+ const exposed = symbolProperties.filter(prop =>
224
+ // only exposed props will have a syntheticOrigin
225
+ Boolean(prop.syntheticOrigin));
226
+ if (exposed.length) {
227
+ return exposed.map(expose => ({
228
+ name: expose.getName(),
229
+ type: typeChecker.typeToString(typeChecker.getTypeOfSymbolAtLocation(expose, symbolNode)),
230
+ description: ts.displayPartsToString(expose.getDocumentationComment(typeChecker)),
123
231
  }));
124
232
  }
125
233
  return [];
126
234
  }
127
235
  }
236
+ function _getExports(componentPath) {
237
+ var _a;
238
+ const sourceFile = program === null || program === void 0 ? void 0 : program.getSourceFile(getMetaFileName(componentPath));
239
+ if (!sourceFile) {
240
+ throw 'Could not find main source file';
241
+ }
242
+ const moduleSymbol = typeChecker.getSymbolAtLocation(sourceFile);
243
+ if (!moduleSymbol) {
244
+ throw 'Could not find module symbol';
245
+ }
246
+ const exportedSymbols = typeChecker.getExportsOfModule(moduleSymbol);
247
+ let symbolNode;
248
+ for (const symbol of exportedSymbols) {
249
+ const [declaration] = (_a = symbol.getDeclarations()) !== null && _a !== void 0 ? _a : [];
250
+ if (ts.isExportAssignment(declaration)) {
251
+ symbolNode = declaration.expression;
252
+ }
253
+ }
254
+ if (!symbolNode) {
255
+ throw 'Could not find symbol node';
256
+ }
257
+ const exportDefaultType = typeChecker.getTypeAtLocation(symbolNode);
258
+ const exports = exportDefaultType.getProperties();
259
+ return {
260
+ symbolNode,
261
+ exports,
262
+ };
263
+ }
128
264
  }
129
265
  exports.createComponentMetaChecker = createComponentMetaChecker;
266
+ function findCmponentDefaultProps(componentPath) {
267
+ const fileText = ts.sys.readFile(componentPath);
268
+ if (fileText === undefined) {
269
+ throw new Error(`${componentPath} not found`);
270
+ }
271
+ const vueSourceFile = vue.createSourceFile(componentPath, fileText, {}, {}, ts);
272
+ const descriptor = vueSourceFile.getDescriptor();
273
+ const scriptSetupRanges = vueSourceFile.getScriptSetupRanges();
274
+ const result = {};
275
+ if (descriptor.scriptSetup && (scriptSetupRanges === null || scriptSetupRanges === void 0 ? void 0 : scriptSetupRanges.withDefaultsArg)) {
276
+ const defaultsText = descriptor.scriptSetup.content.substring(scriptSetupRanges.withDefaultsArg.start, scriptSetupRanges.withDefaultsArg.end);
277
+ const ast = ts.createSourceFile('/tmp.' + descriptor.scriptSetup.lang, '(' + defaultsText + ')', ts.ScriptTarget.Latest);
278
+ const obj = findObjectLiteralExpression(ast);
279
+ if (obj) {
280
+ for (const prop of obj.properties) {
281
+ if (ts.isPropertyAssignment(prop)) {
282
+ const name = prop.name.getText(ast);
283
+ const exp = prop.initializer.getText(ast);
284
+ result[name] = exp;
285
+ }
286
+ }
287
+ }
288
+ function findObjectLiteralExpression(node) {
289
+ if (ts.isObjectLiteralExpression(node)) {
290
+ return node;
291
+ }
292
+ let result;
293
+ node.forEachChild(child => {
294
+ if (!result) {
295
+ result = findObjectLiteralExpression(child);
296
+ }
297
+ });
298
+ return result;
299
+ }
300
+ }
301
+ return result;
302
+ }
303
+ exports.findCmponentDefaultProps = findCmponentDefaultProps;
130
304
  //# sourceMappingURL=index.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vue-component-meta",
3
- "version": "0.39.0",
3
+ "version": "0.39.2",
4
4
  "main": "out/index.js",
5
5
  "license": "MIT",
6
6
  "files": [
@@ -13,9 +13,10 @@
13
13
  "directory": "packages/vue-component-meta"
14
14
  },
15
15
  "dependencies": {
16
- "@volar/vue-language-core": "0.39.0"
16
+ "@volar/vue-language-core": "0.39.2"
17
17
  },
18
18
  "peerDependencies": {
19
19
  "typescript": "*"
20
- }
20
+ },
21
+ "gitHead": "eac2dca4206dfa3f92ad50dd53650f924a9e6f94"
21
22
  }