@next-core/build-next-bricks 1.10.0 → 1.11.0

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.
@@ -1,35 +1,67 @@
1
+ // @ts-check
1
2
  import { parse } from "doctrine";
2
- import { getTypeAnnotation } from "./utils.js";
3
+ import { getTypeAnnotation } from "./getTypeDeclaration.js";
3
4
 
4
5
  /**
5
6
  * @typedef {import("@next-core/brick-manifest").BrickManifest} BrickManifest
6
7
  * @typedef {import("@next-core/brick-manifest").PropertyManifest} PropertyManifest
7
8
  * @typedef {import("@next-core/brick-manifest").EventManifest} EventManifest
8
9
  * @typedef {import("@next-core/brick-manifest").MethodManifest} MethodManifest
10
+ * @typedef {import("@next-core/brick-manifest").MethodParamManifest} MethodParamManifest
9
11
  * @typedef {import("@next-core/brick-manifest").ProviderManifest} ProviderManifest
12
+ * @typedef {import("@next-core/brick-manifest").Annotation} Annotation
10
13
  * @typedef {import("@babel/types").Node} Node
11
14
  * @typedef {import("@babel/traverse").NodePath} NodePath
12
15
  * @typedef {import("@babel/types").ClassDeclaration} ClassDeclaration
16
+ * @typedef {import("@babel/types").Identifier} Identifier
13
17
  * @typedef {import("doctrine").Tag} Tag
18
+ * @typedef {BrickManifest & { types: BrickTypes; }} BrickManifestAndTypes
19
+ * @typedef {{ name: string; annotation?: Annotation }} BrickPropertyWithAnnotation
20
+ * @typedef {{ name: string; detail: { annotation: Annotation } }} BrickEventWithAnnotation
21
+ * @typedef {{ name: string; params: BrickMethodParamWithAnnotation[]; returns: { annotation?: Annotation } }} BrickMethodWithAnnotation
22
+ * @typedef {{ name: string; annotation?: Annotation }} BrickMethodParamWithAnnotation
23
+ * @typedef {{
24
+ * properties: BrickPropertyWithAnnotation[];
25
+ * events: BrickEventWithAnnotation[];
26
+ * methods: BrickMethodWithAnnotation[];
27
+ * usedReferences?: Set<string>;
28
+ * }} BrickTypes
29
+ * @typedef {ProviderManifest & {
30
+ * params: (MethodParamManifest & {
31
+ * annotation?: Annotation;
32
+ * })[];
33
+ * returns?: {
34
+ * description?: string;
35
+ * annotation?: Annotation;
36
+ * };
37
+ * typeParameters?: Annotation;
38
+ * usedReferences?: Set<string>;
39
+ * }} ProviderManifestAndTypes
14
40
  */
15
41
 
16
42
  /**
17
43
  * @param {string} name
18
44
  * @param {NodePath} nodePath
19
- * @param {string} source'
20
- * @param {Set<string>} referenceSet
21
- * @returns {BrickManifest}
45
+ * @param {string} source
46
+ * @returns {BrickManifestAndTypes}
22
47
  */
23
48
  export default function makeBrickManifest(name, nodePath, source) {
24
- /** @type {import("@babel/traverse").NodePath<ClassDeclaration>} */
25
- const classPath = nodePath.parentPath;
26
- /** @type {BrickManifest} */
49
+ const classPath =
50
+ /** @type {import("@babel/traverse").NodePath<ClassDeclaration>} */ (
51
+ nodePath.parentPath
52
+ );
53
+ /** @type {BrickManifestAndTypes} */
27
54
  const manifest = {
28
55
  name,
29
56
  properties: [],
30
57
  events: [],
31
58
  slots: [],
32
59
  methods: [],
60
+ types: {
61
+ properties: [],
62
+ events: [],
63
+ methods: [],
64
+ },
33
65
  };
34
66
 
35
67
  const docComment = findDocComment(nodePath, source);
@@ -52,7 +84,11 @@ export default function makeBrickManifest(name, nodePath, source) {
52
84
  }
53
85
  }
54
86
 
55
- scanFields(manifest, classPath.node.body.body, source);
87
+ manifest.types.usedReferences = scanFields(
88
+ manifest,
89
+ classPath.node.body.body,
90
+ source
91
+ );
56
92
 
57
93
  return manifest;
58
94
  }
@@ -61,18 +97,74 @@ export default function makeBrickManifest(name, nodePath, source) {
61
97
  * @param {string} name
62
98
  * @param {NodePath} nodePath
63
99
  * @param {string} source
64
- * @returns {ProviderManifest}
100
+ * @returns {ProviderManifestAndTypes}
65
101
  */
66
102
  export function makeProviderManifest(name, nodePath, source) {
67
- /** @type {ProviderManifest} */
103
+ /**
104
+ * @type {ProviderManifestAndTypes}
105
+ */
68
106
  const manifest = {
69
107
  name,
108
+ type: "provider",
109
+ params: [],
110
+ usedReferences: new Set(),
70
111
  };
112
+
71
113
  const docComment = findDocComment(nodePath, source);
72
114
  if (docComment) {
73
115
  manifest.description = docComment.description;
74
116
  manifest.deprecated = getDeprecatedInfo(docComment.tags);
75
117
  }
118
+
119
+ const fn = /** @type {import("@babel/types").FunctionDeclaration} */ (
120
+ nodePath.node
121
+ );
122
+ let index = 0;
123
+ for (const param of fn.params) {
124
+ const annotation = getTypeAnnotation(
125
+ param.typeAnnotation,
126
+ source,
127
+ manifest.usedReferences
128
+ );
129
+ if (param.type === "Identifier") {
130
+ manifest.params.push({
131
+ name: param.name,
132
+ description: docComment?.tags.find(
133
+ (tag) => tag.title === "param" && tag.name === param.name
134
+ )?.description,
135
+ annotation,
136
+ });
137
+ } else {
138
+ const paramTag = docComment?.tags.filter(
139
+ (tag) => tag.title === "param"
140
+ )?.[index];
141
+ manifest.params.push({
142
+ name: paramTag?.name ?? `param_${index + 1}`,
143
+ description: paramTag?.description,
144
+ isRestElement: param.type === "RestElement",
145
+ annotation,
146
+ });
147
+ }
148
+ index++;
149
+ }
150
+ const returnAnnotation = getTypeAnnotation(
151
+ fn.returnType,
152
+ source,
153
+ manifest.usedReferences
154
+ );
155
+
156
+ manifest.returns = {
157
+ description: docComment?.tags.find((tag) => tag.title === "returns")
158
+ ?.description,
159
+ annotation: returnAnnotation,
160
+ };
161
+
162
+ manifest.typeParameters = getTypeAnnotation(
163
+ fn.typeParameters,
164
+ source,
165
+ manifest.usedReferences
166
+ );
167
+
76
168
  return manifest;
77
169
  }
78
170
 
@@ -93,12 +185,14 @@ function findDocComment({ node, parentPath }, source) {
93
185
  }
94
186
 
95
187
  /**
96
- * @param {BrickManifest} manifest
188
+ * @param {BrickManifestAndTypes} manifest
97
189
  * @param {Node[]} nodes
98
190
  * @param {string} source
99
- * @param {Set<string>} referenceSet
191
+ * @returns {Set<string>}
100
192
  */
101
- function scanFields(manifest, nodes, source, referenceSet = new Set()) {
193
+ function scanFields(manifest, nodes, source) {
194
+ /** @type {Set<string>} */
195
+ const usedReferences = new Set();
102
196
  for (const node of nodes) {
103
197
  if (node.type === "ClassAccessorProperty" && node.decorators?.length) {
104
198
  for (const { expression } of node.decorators) {
@@ -110,7 +204,7 @@ function scanFields(manifest, nodes, source, referenceSet = new Set()) {
110
204
  case "property": {
111
205
  /** @type {PropertyManifest} */
112
206
  const prop = {
113
- name: node.key.name,
207
+ name: /** @type {Identifier} */ (node.key).name,
114
208
  };
115
209
  const docComment = parseDocComment(node, source);
116
210
  if (docComment) {
@@ -148,12 +242,18 @@ function scanFields(manifest, nodes, source, referenceSet = new Set()) {
148
242
  ) {
149
243
  const { typeAnnotation } = node.typeAnnotation;
150
244
  prop.type = getTypeWithoutUndefined(typeAnnotation, source);
151
- prop.types = getTypesWithoutUndefined(
152
- typeAnnotation,
245
+
246
+ const annotation = getTypeAnnotation(
247
+ getNodeWithoutUndefined(typeAnnotation),
153
248
  source,
154
- referenceSet
249
+ usedReferences
155
250
  );
156
- prop.reference = [...referenceSet];
251
+ if (annotation) {
252
+ manifest.types.properties.push({
253
+ name: prop.name,
254
+ annotation,
255
+ });
256
+ }
157
257
  }
158
258
  if (node.value && !prop.default) {
159
259
  prop.default = source.substring(
@@ -167,7 +267,7 @@ function scanFields(manifest, nodes, source, referenceSet = new Set()) {
167
267
 
168
268
  case "event": {
169
269
  /** @type {EventManifest} */
170
- const event = {};
270
+ const event = { name: undefined };
171
271
 
172
272
  // Find out the `type` option for the event.
173
273
  if (expression.arguments.length > 0) {
@@ -190,7 +290,9 @@ function scanFields(manifest, nodes, source, referenceSet = new Set()) {
190
290
  }
191
291
  if (event.name === undefined) {
192
292
  throw new Error(
193
- `Invalid @event() call: no literal type option in event '${node.key.name}'`
293
+ `Invalid @event() call: no literal type option in event '${
294
+ /** @type {Identifier} */ (node.key).name
295
+ }'`
194
296
  );
195
297
  }
196
298
  const docComment = parseDocComment(node, source);
@@ -218,12 +320,21 @@ function scanFields(manifest, nodes, source, referenceSet = new Set()) {
218
320
  const param = typeAnnotation.typeParameters.params[0];
219
321
  event.detail ??= {};
220
322
  event.detail.type = source.substring(param.start, param.end);
221
- event.detail.types = getTypeAnnotation(
323
+
324
+ const annotation = getTypeAnnotation(
222
325
  param,
223
326
  source,
224
- referenceSet
327
+ usedReferences
225
328
  );
226
- event.detail.reference = [...referenceSet];
329
+
330
+ if (annotation) {
331
+ manifest.types.events.push({
332
+ name: event.name,
333
+ detail: {
334
+ annotation,
335
+ },
336
+ });
337
+ }
227
338
  }
228
339
  }
229
340
  manifest.events.push(event);
@@ -241,40 +352,99 @@ function scanFields(manifest, nodes, source, referenceSet = new Set()) {
241
352
  ) {
242
353
  /** @type {MethodManifest} */
243
354
  const method = {
244
- name: node.key.name,
355
+ name: /** @type {Identifier} */ (node.key).name,
245
356
  params: [],
246
357
  };
247
358
  const docComment = parseDocComment(node, source);
248
359
  if (docComment) {
249
360
  method.description = docComment.description;
250
361
  method.deprecated = getDeprecatedInfo(docComment.tags);
362
+ method.returns = {
363
+ description: docComment.tags.find(
364
+ (tag) => tag.title === "returns"
365
+ )?.description,
366
+ };
251
367
  }
368
+
369
+ let index = 0;
370
+ /** @type {BrickMethodParamWithAnnotation[]} */
371
+ const typedParams = [];
252
372
  for (const param of node.params) {
253
- method.params.push(source.substring(param.start, param.end));
373
+ const typeAnnotation =
374
+ /** @type {Identifier} */
375
+ (param).typeAnnotation;
376
+ const annotation = getTypeAnnotation(
377
+ typeAnnotation,
378
+ source,
379
+ usedReferences
380
+ );
381
+ const paramType =
382
+ typeAnnotation.type === "TSTypeAnnotation"
383
+ ? source.substring(
384
+ typeAnnotation.typeAnnotation.start,
385
+ typeAnnotation.typeAnnotation.end
386
+ )
387
+ : undefined;
388
+ /** @type {string} */
389
+ let paramName;
390
+ if (param.type === "Identifier") {
391
+ paramName = param.name;
392
+ method.params.push({
393
+ name: paramName,
394
+ description: docComment?.tags.find(
395
+ (tag) => tag.title === "param" && tag.name === param.name
396
+ )?.description,
397
+ type: paramType,
398
+ });
399
+ } else {
400
+ const paramTag = docComment?.tags.filter(
401
+ (tag) => tag.title === "param"
402
+ )?.[index];
403
+ paramName = paramTag?.name ?? `param_${index + 1}`;
404
+ method.params.push({
405
+ name: paramName,
406
+ description: paramTag?.description,
407
+ type: paramType,
408
+ });
409
+ }
410
+ typedParams.push({
411
+ name: paramName,
412
+ annotation,
413
+ });
414
+ index++;
254
415
  }
416
+
417
+ /** @type {{ annotation?: Annotation }} */
418
+ const typedReturns = {};
255
419
  if (node.returnType && node.returnType.type === "TSTypeAnnotation") {
256
420
  const { typeAnnotation } = node.returnType;
257
- method.return ??= {};
258
- method.return.type = source.substring(
259
- typeAnnotation.start,
260
- typeAnnotation.end
261
- );
262
- method.return.types = getTypeAnnotation(
421
+ method.returns = {
422
+ ...method.returns,
423
+ type: source.substring(typeAnnotation.start, typeAnnotation.end),
424
+ };
425
+
426
+ typedReturns.annotation = getTypeAnnotation(
263
427
  typeAnnotation,
264
428
  source,
265
- referenceSet
429
+ usedReferences
266
430
  );
267
- method.return.reference = [...referenceSet];
268
431
  }
432
+ manifest.types.methods.push({
433
+ name: method.name,
434
+ params: typedParams,
435
+ returns: typedReturns,
436
+ });
269
437
  manifest.methods.push(method);
270
438
  }
271
439
  }
272
440
  }
273
441
  }
442
+
443
+ return usedReferences;
274
444
  }
275
445
 
276
446
  /**
277
- * @param {Node[]} nodes
447
+ * @param {Node} node
278
448
  * @param {string} source
279
449
  */
280
450
  export function parseDocComment(node, source) {
@@ -290,6 +460,21 @@ export function parseDocComment(node, source) {
290
460
  }
291
461
  }
292
462
 
463
+ /**
464
+ * @param {Node} node
465
+ * @param {string} source
466
+ * @returns {undefined | { description?: string; deprecated?: boolean | string; }}
467
+ */
468
+ export function parseTypeComment(node, source) {
469
+ const docComment = parseDocComment(node, source);
470
+ if (docComment) {
471
+ return {
472
+ description: docComment.description,
473
+ deprecated: getDeprecatedInfo(docComment.tags),
474
+ };
475
+ }
476
+ }
477
+
293
478
  /**
294
479
  * @param {Node} node
295
480
  * @param {string} source
@@ -309,20 +494,25 @@ function getTypeWithoutUndefined(node, source) {
309
494
  }
310
495
 
311
496
  /**
312
- * @param {import("@babel/types")/.typeAnnotation} typeAnnotation
313
- * @param {string} source
314
- * @param {Set<string} set
497
+ * @param {Node} node
498
+ * @returns {Node}
315
499
  */
316
- function getTypesWithoutUndefined(typeAnnotation, source, set) {
317
- if (typeAnnotation.type === "TSUnionType" && typeAnnotation.types) {
318
- return {
319
- type: "union",
320
- types: typeAnnotation.types
321
- .filter((type) => type.type !== "TSUndefinedKeyword")
322
- .map((item) => getTypeAnnotation(item, source, set)),
323
- };
500
+ function getNodeWithoutUndefined(node) {
501
+ if (node.type === "TSUnionType") {
502
+ const filteredTypes = node.types.filter(
503
+ (type) => type.type !== "TSUndefinedKeyword"
504
+ );
505
+ if (filteredTypes.length < node.types.length) {
506
+ if (filteredTypes.length === 1) {
507
+ return filteredTypes[0];
508
+ }
509
+ return {
510
+ ...node,
511
+ types: filteredTypes,
512
+ };
513
+ }
324
514
  }
325
- return getTypeAnnotation(typeAnnotation, source, set);
515
+ return node;
326
516
  }
327
517
 
328
518
  /**