@ui5/webcomponents-tools 0.0.0-bf8366eb6 → 0.0.0-c143e338b

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 (100) hide show
  1. package/CHANGELOG.md +909 -0
  2. package/LICENSE.txt +201 -0
  3. package/README.md +7 -10
  4. package/assets-meta.js +1 -5
  5. package/bin/dev.js +3 -2
  6. package/bin/ui5nps.js +265 -0
  7. package/components-package/eslint.js +59 -31
  8. package/components-package/nps.js +96 -65
  9. package/components-package/vite.config.js +7 -11
  10. package/components-package/wdio.js +12 -5
  11. package/icons-collection/nps.js +30 -21
  12. package/lib/amd-to-es6/index.js +15 -10
  13. package/lib/cem/cem.js +12 -0
  14. package/lib/cem/custom-elements-manifest.config.mjs +74 -45
  15. package/lib/cem/event.mjs +69 -32
  16. package/lib/cem/patch/@custom-elements-manifest/analyzer/cli.js +128 -0
  17. package/lib/cem/patch/@custom-elements-manifest/analyzer/package.json +59 -0
  18. package/lib/cem/patch/@custom-elements-manifest/analyzer/src/browser-entrypoint.js +23 -0
  19. package/lib/cem/patch/@custom-elements-manifest/analyzer/src/create.js +117 -0
  20. package/lib/cem/patch/@custom-elements-manifest/analyzer/src/features/analyse-phase/arrow-function.js +26 -0
  21. package/lib/cem/patch/@custom-elements-manifest/analyzer/src/features/analyse-phase/class-jsdoc.js +157 -0
  22. package/lib/cem/patch/@custom-elements-manifest/analyzer/src/features/analyse-phase/classes.js +20 -0
  23. package/lib/cem/patch/@custom-elements-manifest/analyzer/src/features/analyse-phase/creators/createArrowFunction.js +17 -0
  24. package/lib/cem/patch/@custom-elements-manifest/analyzer/src/features/analyse-phase/creators/createAttribute.js +24 -0
  25. package/lib/cem/patch/@custom-elements-manifest/analyzer/src/features/analyse-phase/creators/createClass.js +301 -0
  26. package/lib/cem/patch/@custom-elements-manifest/analyzer/src/features/analyse-phase/creators/createClassField.js +26 -0
  27. package/lib/cem/patch/@custom-elements-manifest/analyzer/src/features/analyse-phase/creators/createFunctionLike.js +73 -0
  28. package/lib/cem/patch/@custom-elements-manifest/analyzer/src/features/analyse-phase/creators/createMixin.js +33 -0
  29. package/lib/cem/patch/@custom-elements-manifest/analyzer/src/features/analyse-phase/creators/createVariable.js +22 -0
  30. package/lib/cem/patch/@custom-elements-manifest/analyzer/src/features/analyse-phase/creators/handlers.js +338 -0
  31. package/lib/cem/patch/@custom-elements-manifest/analyzer/src/features/analyse-phase/custom-elements-define-calls.js +90 -0
  32. package/lib/cem/patch/@custom-elements-manifest/analyzer/src/features/analyse-phase/exports.js +156 -0
  33. package/lib/cem/patch/@custom-elements-manifest/analyzer/src/features/analyse-phase/function-like.js +24 -0
  34. package/lib/cem/patch/@custom-elements-manifest/analyzer/src/features/analyse-phase/mixins.js +29 -0
  35. package/lib/cem/patch/@custom-elements-manifest/analyzer/src/features/analyse-phase/reexported-wrapped-mixin-exports.js +84 -0
  36. package/lib/cem/patch/@custom-elements-manifest/analyzer/src/features/analyse-phase/variables.js +34 -0
  37. package/lib/cem/patch/@custom-elements-manifest/analyzer/src/features/collect-phase/collect-imports.js +101 -0
  38. package/lib/cem/patch/@custom-elements-manifest/analyzer/src/features/framework-plugins/catalyst/catalyst.js +11 -0
  39. package/lib/cem/patch/@custom-elements-manifest/analyzer/src/features/framework-plugins/catalyst/controller.js +34 -0
  40. package/lib/cem/patch/@custom-elements-manifest/analyzer/src/features/framework-plugins/catalyst-major-2/catalyst.js +11 -0
  41. package/lib/cem/patch/@custom-elements-manifest/analyzer/src/features/framework-plugins/catalyst-major-2/controller.js +34 -0
  42. package/lib/cem/patch/@custom-elements-manifest/analyzer/src/features/framework-plugins/decorators/attr.js +53 -0
  43. package/lib/cem/patch/@custom-elements-manifest/analyzer/src/features/framework-plugins/decorators/custom-element-decorator.js +36 -0
  44. package/lib/cem/patch/@custom-elements-manifest/analyzer/src/features/framework-plugins/fast/fast.js +7 -0
  45. package/lib/cem/patch/@custom-elements-manifest/analyzer/src/features/framework-plugins/lit/lit.js +13 -0
  46. package/lib/cem/patch/@custom-elements-manifest/analyzer/src/features/framework-plugins/lit/member-denylist.js +21 -0
  47. package/lib/cem/patch/@custom-elements-manifest/analyzer/src/features/framework-plugins/lit/method-denylist.js +20 -0
  48. package/lib/cem/patch/@custom-elements-manifest/analyzer/src/features/framework-plugins/lit/property-decorator.js +94 -0
  49. package/lib/cem/patch/@custom-elements-manifest/analyzer/src/features/framework-plugins/lit/static-properties.js +121 -0
  50. package/lib/cem/patch/@custom-elements-manifest/analyzer/src/features/framework-plugins/lit/utils.js +66 -0
  51. package/lib/cem/patch/@custom-elements-manifest/analyzer/src/features/framework-plugins/stencil/stencil.js +129 -0
  52. package/lib/cem/patch/@custom-elements-manifest/analyzer/src/features/index.js +80 -0
  53. package/lib/cem/patch/@custom-elements-manifest/analyzer/src/features/link-phase/cleanup-classes.js +25 -0
  54. package/lib/cem/patch/@custom-elements-manifest/analyzer/src/features/link-phase/field-denylist.js +22 -0
  55. package/lib/cem/patch/@custom-elements-manifest/analyzer/src/features/link-phase/method-denylist.js +25 -0
  56. package/lib/cem/patch/@custom-elements-manifest/analyzer/src/features/post-processing/apply-inheritance.js +78 -0
  57. package/lib/cem/patch/@custom-elements-manifest/analyzer/src/features/post-processing/is-custom-element.js +34 -0
  58. package/lib/cem/patch/@custom-elements-manifest/analyzer/src/features/post-processing/link-class-to-tagname.js +27 -0
  59. package/lib/cem/patch/@custom-elements-manifest/analyzer/src/features/post-processing/remove-unexported-declarations.js +23 -0
  60. package/lib/cem/patch/@custom-elements-manifest/analyzer/src/features/post-processing/resolve-initializers.js +52 -0
  61. package/lib/cem/patch/@custom-elements-manifest/analyzer/src/utils/ast-helpers.js +186 -0
  62. package/lib/cem/patch/@custom-elements-manifest/analyzer/src/utils/cli-helpers.js +164 -0
  63. package/lib/cem/patch/@custom-elements-manifest/analyzer/src/utils/exports.js +44 -0
  64. package/lib/cem/patch/@custom-elements-manifest/analyzer/src/utils/find-external-manifests.js +67 -0
  65. package/lib/cem/patch/@custom-elements-manifest/analyzer/src/utils/imports.js +25 -0
  66. package/lib/cem/patch/@custom-elements-manifest/analyzer/src/utils/index.js +71 -0
  67. package/lib/cem/patch/@custom-elements-manifest/analyzer/src/utils/jsdoc.js +19 -0
  68. package/lib/cem/patch/@custom-elements-manifest/analyzer/src/utils/manifest-helpers.js +194 -0
  69. package/lib/cem/patch/@custom-elements-manifest/analyzer/src/utils/mixins.js +112 -0
  70. package/lib/cem/schema-internal.json +65 -0
  71. package/lib/cem/types-internal.d.ts +14 -2
  72. package/lib/cem/utils.mjs +69 -30
  73. package/lib/cem/validate.js +61 -55
  74. package/lib/copy-and-watch/index.js +105 -97
  75. package/lib/copy-list/index.js +16 -10
  76. package/lib/create-icons/index.js +24 -19
  77. package/lib/create-illustrations/index.js +49 -27
  78. package/lib/create-new-component/{tsFileContentTemplate.js → Component.js} +12 -9
  79. package/lib/create-new-component/ComponentTemplate.js +12 -0
  80. package/lib/create-new-component/index.js +13 -12
  81. package/lib/css-processors/css-processor-components.mjs +74 -59
  82. package/lib/css-processors/css-processor-themes.mjs +85 -62
  83. package/lib/css-processors/shared.mjs +5 -35
  84. package/lib/dev-server/{dev-server.js → dev-server.mjs} +26 -14
  85. package/lib/dev-server/virtual-index-html-plugin.js +24 -20
  86. package/lib/generate-js-imports/illustrations.js +53 -54
  87. package/lib/generate-json-imports/i18n.js +56 -36
  88. package/lib/generate-json-imports/themes.js +27 -14
  89. package/lib/hbs2ui5/RenderTemplates/LitRenderer.js +12 -7
  90. package/lib/hbs2ui5/index.js +3 -3
  91. package/lib/i18n/defaults.js +15 -9
  92. package/lib/i18n/toJSON.js +14 -10
  93. package/lib/icons-hash/icons-hash.mjs +149 -0
  94. package/lib/remove-dev-mode/remove-dev-mode.mjs +38 -24
  95. package/lib/rimraf/rimraf.js +31 -0
  96. package/lib/scoping/get-all-tags.js +9 -2
  97. package/package.json +22 -17
  98. package/tsconfig.json +18 -0
  99. package/lib/css-processors/css-processor-component-styles.mjs +0 -48
  100. package/lib/dev-server/ssr-dom-shim-loader.js +0 -26
@@ -4,6 +4,7 @@ import path from "path";
4
4
  import fs from 'fs';
5
5
  import {
6
6
  getDeprecatedStatus,
7
+ getExperimentalStatus,
7
8
  getSinceStatus,
8
9
  getPrivacyStatus,
9
10
  getReference,
@@ -26,6 +27,7 @@ import { generateCustomData } from "cem-plugin-vs-code-custom-data-generator";
26
27
  import { customElementJetBrainsPlugin } from "custom-element-jet-brains-integration";
27
28
 
28
29
  const packageJSON = JSON.parse(fs.readFileSync("./package.json"));
30
+ const devMode = process.env.UI5_CEM_MODE === "dev";
29
31
 
30
32
  const extractClassNodeJSDoc = node => {
31
33
  const fileContent = node.getFullText();
@@ -64,12 +66,23 @@ function processClass(ts, classNode, moduleDoc) {
64
66
  currClass.customElement = !!customElementDecorator || className === "UI5Element" || undefined;
65
67
  currClass.kind = "class";
66
68
  currClass.deprecated = getDeprecatedStatus(classParsedJsDoc);
69
+ currClass._ui5experimental = getExperimentalStatus(classParsedJsDoc);
67
70
  currClass._ui5since = getSinceStatus(classParsedJsDoc);
68
71
  currClass._ui5privacy = getPrivacyStatus(classParsedJsDoc);
69
72
  currClass._ui5abstract = hasTag(classParsedJsDoc, "abstract") ? true : undefined;
70
73
  currClass.description = normalizeDescription(classParsedJsDoc.description || findTag(classParsedJsDoc, "class")?.description);
71
74
  currClass._ui5implements = findAllTags(classParsedJsDoc, "implements")
72
- .map(tag => getReference(ts, normalizeTagType(tag.type), classNode, moduleDoc.path))
75
+ .map(tag => {
76
+ const correctInterfaceDescription = classNode?.heritageClauses?.some(heritageClause => {
77
+ return heritageClause?.types?.some(type => type.expression?.text === normalizeTagType(tag.type));
78
+ });
79
+
80
+ if (!correctInterfaceDescription) {
81
+ logDocumentationError(moduleDoc.path, `@interface {${tag.type}} tag is used, but the class doesn't implement the corresponding interface`)
82
+ }
83
+
84
+ return getReference(ts, normalizeTagType(tag.type), classNode, moduleDoc.path)
85
+ })
73
86
  .filter(Boolean);
74
87
 
75
88
 
@@ -77,6 +90,10 @@ function processClass(ts, classNode, moduleDoc) {
77
90
  const superclassTag = findTag(classParsedJsDoc, "extends");
78
91
  currClass.superclass = getReference(ts, superclassTag.name, classNode, moduleDoc.path);
79
92
 
93
+ if (classNode?.heritageClauses?.[0]?.types?.[0]?.expression?.text !== superclassTag.name) {
94
+ logDocumentationError(moduleDoc.path, `@extends ${superclassTag.name} is used, but the class doesn't extend the corresponding superclass`)
95
+ }
96
+
80
97
  if (currClass.superclass?.name === "UI5Element") {
81
98
  currClass.customElement = true;
82
99
  }
@@ -111,9 +128,13 @@ function processClass(ts, classNode, moduleDoc) {
111
128
  }
112
129
 
113
130
  // Events
114
- currClass.events = findAllDecorators(classNode, "event")
131
+ currClass.events = findAllDecorators(classNode, ["event", "eventStrict"])
115
132
  ?.map(event => processEvent(ts, event, classNode, moduleDoc));
116
133
 
134
+ const filename = classNode.getSourceFile().fileName;
135
+ const sourceFile = typeProgram.getSourceFile(filename);
136
+ const tsProgramClassNode = sourceFile.statements.find(statement => ts.isClassDeclaration(statement) && statement.name?.text === classNode.name?.text);
137
+
117
138
  // Slots (with accessor), methods and fields
118
139
  for (let i = 0; i < (currClass.members?.length || 0); i++) {
119
140
  const member = currClass.members[i];
@@ -168,17 +189,21 @@ function processClass(ts, classNode, moduleDoc) {
168
189
  const propertyDecorator = findDecorator(classNodeMember, "property");
169
190
 
170
191
  if (propertyDecorator) {
171
- member._ui5validator = propertyDecorator?.expression?.arguments[0]?.properties?.find(property => ["validator", "type"].includes(property.name.text))?.initializer?.text || "String";
172
192
  member._ui5noAttribute = propertyDecorator?.expression?.arguments[0]?.properties?.find(property => property.name.text === "noAttribute")?.initializer?.kind === ts.SyntaxKind.TrueKeyword || undefined;
173
193
  }
174
194
 
175
- if (currClass.customElement && member.privacy === "public" && !propertyDecorator?.expression?.arguments[0]?.properties?.find(property => property.name.text === "multiple") && !["object"].includes(member._ui5validator?.toLowerCase())) {
176
- const filename = classNode.getSourceFile().fileName;
177
- const sourceFile = typeProgram.getSourceFile(filename);
178
- const tsProgramClassNode = sourceFile.statements.find(statement => ts.isClassDeclaration(statement) && statement.name?.text === classNode.name?.text);
195
+ if (currClass.customElement && member.privacy === "public") {
179
196
  const tsProgramMember = tsProgramClassNode.members.find(m => ts.isPropertyDeclaration(m) && m.name?.text === member.name);
180
197
  const attributeValue = typeChecker.typeToString(typeChecker.getTypeAtLocation(tsProgramMember), tsProgramMember);
181
198
 
199
+ if (attributeValue === "boolean" && member.default === "true") {
200
+ logDocumentationError(moduleDoc.path, `Boolean properties must be initialzed to false. [${member.name}] property of class [${className}] is intialized to \`true\``)
201
+ }
202
+
203
+ if (!member.type) {
204
+ logDocumentationError(moduleDoc.path, `Public properties must have type. The type of [${member.name}] property is not determinated automatically. Please check it.`)
205
+ }
206
+
182
207
  currClass.attributes.push({
183
208
  description: member.description,
184
209
  name: toKebabCase(member.name),
@@ -293,6 +318,7 @@ function processInterface(ts, interfaceNode, moduleDoc) {
293
318
  kind: "interface",
294
319
  name: interfaceName,
295
320
  description: normalizeDescription(interfaceParsedJsDoc?.description),
321
+ _ui5experimental: getExperimentalStatus(interfaceParsedJsDoc),
296
322
  _ui5privacy: getPrivacyStatus(interfaceParsedJsDoc),
297
323
  _ui5since: getSinceStatus(interfaceParsedJsDoc),
298
324
  deprecated: getDeprecatedStatus(interfaceParsedJsDoc),
@@ -313,6 +339,7 @@ function processEnum(ts, enumNode, moduleDoc) {
313
339
  kind: "enum",
314
340
  name: enumName,
315
341
  description: normalizeDescription(enumJSdoc?.comment),
342
+ _ui5experimental: getExperimentalStatus(enumParsedJsDoc),
316
343
  _ui5privacy: getPrivacyStatus(enumParsedJsDoc),
317
344
  _ui5since: getSinceStatus(enumParsedJsDoc),
318
345
  deprecated: getDeprecatedStatus(enumParsedJsDoc) || undefined,
@@ -425,14 +452,6 @@ export default {
425
452
  }
426
453
  },
427
454
  moduleLinkPhase({ moduleDoc }) {
428
- for (let i = 0; i < moduleDoc.declarations.length; i++) {
429
- const shouldRemove = processPublicAPI(moduleDoc.declarations[i]) || ["function", "variable"].includes(moduleDoc.declarations[i].kind)
430
- if (shouldRemove) {
431
- moduleDoc.declarations.splice(i, 1);
432
- i--;
433
- }
434
- }
435
-
436
455
  moduleDoc.path = moduleDoc.path?.replace(/^src/, "dist").replace(/\.ts$/, ".js");
437
456
 
438
457
  moduleDoc.exports = moduleDoc.exports.
@@ -456,41 +475,51 @@ export default {
456
475
  })
457
476
  }
458
477
  })
478
+ },
479
+ packageLinkPhase({ customElementsManifest }) {
480
+ customElementsManifest.modules.forEach(moduleDoc => {
481
+ for (let i = 0; i < moduleDoc.declarations.length; i++) {
482
+ const shouldRemove = processPublicAPI(moduleDoc.declarations[i]) || ["function", "variable"].includes(moduleDoc.declarations[i].kind)
483
+ if (shouldRemove) {
484
+ moduleDoc.declarations.splice(i, 1);
485
+ i--;
486
+ }
487
+ }
459
488
 
460
- const typeReferences = new Set();
461
- const registerTypeReference = reference => typeReferences.add(JSON.stringify(reference))
462
-
463
- moduleDoc.declarations.forEach(declaration => {
464
- ["events", "slots", "members"].forEach(memberType => {
465
- declaration[memberType]?.forEach(member => {
466
- if (member.type?.references) {
467
- member.type.references.forEach(registerTypeReference)
468
- } else if (member._ui5type?.references) {
469
- member._ui5type.references.forEach(registerTypeReference)
470
- } else if (member.kind === "method") {
471
- member.return?.type?.references?.forEach(registerTypeReference)
472
-
473
- member.parameters?.forEach(parameter => {
474
- parameter.type?.references?.forEach(registerTypeReference)
475
- })
476
- }
489
+ const typeReferences = new Set();
490
+ const registerTypeReference = reference => typeReferences.add(JSON.stringify(reference))
491
+
492
+ moduleDoc.declarations.forEach(declaration => {
493
+ ["events", "slots", "members"].forEach(memberType => {
494
+ declaration[memberType]?.forEach(member => {
495
+ if (member.type?.references) {
496
+ member.type.references.forEach(registerTypeReference)
497
+ } else if (member._ui5type?.references) {
498
+ member._ui5type.references.forEach(registerTypeReference)
499
+ } else if (member.kind === "method") {
500
+ member.return?.type?.references?.forEach(registerTypeReference)
501
+
502
+ member.parameters?.forEach(parameter => {
503
+ parameter.type?.references?.forEach(registerTypeReference)
504
+ })
505
+ }
506
+ })
477
507
  })
478
- })
479
- });
508
+ });
480
509
 
481
- typeReferences.forEach(reference => {
482
- reference = JSON.parse(reference);
483
- if (reference.package === packageJSON?.name && reference.module === moduleDoc.path) {
484
- const hasExport = moduleDoc.exports.some(e => e.declaration?.name === reference.name && e.declaration?.module === reference.module)
510
+ typeReferences.forEach(reference => {
511
+ reference = JSON.parse(reference);
512
+ if (reference.package === packageJSON?.name && reference.module === moduleDoc.path) {
513
+ const hasExport = moduleDoc.exports.some(e => e.declaration?.name === reference.name && e.declaration?.module === reference.module)
485
514
 
486
- if (!hasExport) {
487
- logDocumentationError(moduleDoc.path?.replace(/^dist/, "src").replace(/\.js$/, ".ts"), `Type '${reference.name}' is used to describe a public API but is not exported.`,)
515
+ if (!hasExport) {
516
+ logDocumentationError(moduleDoc.path?.replace(/^dist/, "src").replace(/\.js$/, ".ts"), `Type '${reference.name}' is used to describe a public API but is not exported.`,)
517
+ }
488
518
  }
489
- }
490
- })
491
- },
492
- packageLinkPhase({ context }) {
493
- if (context.dev) {
519
+ })
520
+ });
521
+
522
+ if (devMode) {
494
523
  displayDocumentationErrors();
495
524
  }
496
525
  }
package/lib/cem/event.mjs CHANGED
@@ -16,19 +16,10 @@ import {
16
16
  } from "./utils.mjs";
17
17
 
18
18
  const jsDocRegExp = /\/\*\*(.|\n)+?\s+\*\//;
19
+ const ASTFalseKeywordCode = 94;
19
20
 
20
21
  const getParams = (ts, eventDetails, commentParams, classNode, moduleDoc) => {
21
22
  return commentParams?.map(commentParam => {
22
- const decoratorParam = eventDetails?.find(prop => prop?.name?.text === commentParam?.name);
23
-
24
- if (!decoratorParam || !decoratorParam?.jsDoc?.[0]) {
25
- return;
26
- }
27
-
28
- const decoratorParamParsedComment = parse(decoratorParam?.jsDoc?.[0]?.getText?.(), { spacing: 'preserve' })[0];
29
-
30
- validateJSDocComment("eventParam", decoratorParamParsedComment, decoratorParam.name?.text, moduleDoc);
31
-
32
23
  const { typeName, name } = getType(normalizeTagType(commentParam?.type));
33
24
  let type;
34
25
 
@@ -47,12 +38,10 @@ const getParams = (ts, eventDetails, commentParams, classNode, moduleDoc) => {
47
38
  return {
48
39
  type,
49
40
  name: commentParam?.name,
50
- _ui5privacy: getPrivacyStatus(decoratorParamParsedComment),
41
+ _ui5privacy: "public",
51
42
  description: normalizeDescription(commentParam?.description),
52
- _ui5since: getSinceStatus(decoratorParamParsedComment),
53
- deprecated: getDeprecatedStatus(decoratorParamParsedComment),
54
43
  };
55
- }).filter(pair => !!pair);
44
+ });
56
45
  };
57
46
 
58
47
  function processEvent(ts, event, classNode, moduleDoc) {
@@ -77,30 +66,32 @@ function processEvent(ts, event, classNode, moduleDoc) {
77
66
  const privacy = findTag(eventParsedComment, ["public", "private", "protected"])?.tag || "private";
78
67
  const sinceTag = findTag(eventParsedComment, "since");
79
68
  const commentParams = findAllTags(eventParsedComment, "param");
80
- const allowPreventDefault = hasTag(eventParsedComment, "allowPreventDefault") || undefined;
81
69
  const description = normalizeDescription(eventParsedComment?.description);
82
70
  const native = hasTag(eventParsedComment, "native");
83
- const eventDetails = event?.expression?.arguments?.[1]?.properties?.find(prop => prop?.name?.text === "detail")?.initializer?.properties;
84
-
85
- if (event?.expression?.arguments?.[1] && !event?.expression?.typeArguments) {
86
- logDocumentationError(moduleDoc.path, `Event details for event '${name}' must be described using generics. Add type via generics to the decorator: @event<TypeForDetails>("${name}", {details}).`)
87
- }
71
+ const eventArgs = event?.expression?.arguments;
72
+ let eventBubbles;
73
+ let eventCancelable;
74
+ let eventDetails;
75
+
76
+ eventArgs && eventArgs.forEach(arg => {
77
+ arg.properties?.forEach(prop => {
78
+ if (prop.name?.text === "bubbles") {
79
+ eventBubbles = prop.initializer.kind === ASTFalseKeywordCode ? false : true;
80
+ } else if (prop.name?.text === "cancelable") {
81
+ eventCancelable = prop.initializer.kind === ASTFalseKeywordCode ? false : true;
82
+ } else if (prop.name?.text === "detail") {
83
+ eventDetails = prop.initializer?.properties;
84
+ }
85
+ });
86
+ });
88
87
 
89
88
  result.description = description;
90
- result._ui5allowPreventDefault = allowPreventDefault;
89
+ result._ui5Cancelable = eventCancelable !== undefined ? eventCancelable : false;
90
+ result._ui5allowPreventDefault = result._ui5Cancelable;
91
+ result._ui5Bubbles = eventBubbles !== undefined ? eventBubbles : false;
91
92
 
92
93
  if (native) {
93
94
  result.type = { text: "Event" };
94
- } else if (event?.expression?.typeArguments) {
95
- const typesText = event?.expression?.typeArguments.map(type => type.typeName?.text).filter(Boolean).join(" | ");
96
- const typeRefs = (getTypeRefs(ts, event.expression)
97
- ?.map(e => getReference(ts, e, event, moduleDoc.path)).filter(Boolean)) || [];
98
-
99
- result.type = { text: `CustomEvent<${typesText}>` };
100
-
101
- if (typeRefs.length) {
102
- result.type.references = typeRefs;
103
- }
104
95
  }
105
96
 
106
97
  if (privacy) {
@@ -121,7 +112,53 @@ function processEvent(ts, event, classNode, moduleDoc) {
121
112
  : sinceTag.name;
122
113
  }
123
114
 
124
- if (commentParams && eventDetails) {
115
+ const eventDetailType = classNode.members?.find(member => {
116
+ return ts.isPropertyDeclaration(member) && member.name.text === "eventDetails"
117
+ })?.type;
118
+ const eventDetailRef = eventDetailType?.members?.find(member => member.name.text === name) || eventDetailType?.types?.[eventDetailType?.types?.length - 1]?.members?.find(member => member.name.text === name);
119
+ const hasGeneric = !!event?.expression?.typeArguments
120
+
121
+ if (commentParams.length) {
122
+ if (eventDetailRef && hasGeneric) {
123
+ logDocumentationError(moduleDoc.path, `Event details for event '${name}' has to be defined either with generic or with eventDetails.`)
124
+ } else if (eventDetails) {
125
+ if (hasGeneric) {
126
+ const typesText = event?.expression?.typeArguments.map(type => type.typeName?.text).filter(Boolean).join(" | ");
127
+ const typeRefs = (getTypeRefs(ts, event.expression)
128
+ ?.map(e => getReference(ts, e, event, moduleDoc.path)).filter(Boolean)) || [];
129
+
130
+ result.type = { text: `CustomEvent<${typesText}>` };
131
+
132
+ if (typeRefs.length) {
133
+ result.type.references = typeRefs;
134
+ }
135
+ } else if (eventDetailRef) {
136
+ const typesText = eventDetailRef?.type?.typeName?.text;
137
+ const typeRefs = (getTypeRefs(ts, eventDetailRef)
138
+ ?.map(e => getReference(ts, e, event, moduleDoc.path)).filter(Boolean)) || [];
139
+
140
+ result.type = { text: `CustomEvent<${typesText}>` };
141
+
142
+ if (typeRefs.length) {
143
+ result.type.references = typeRefs;
144
+ }
145
+ } else {
146
+ logDocumentationError(moduleDoc.path, `Event details for event '${name}' must be described using generics. Add type via generics to the decorator: @event<TypeForDetails>("${name}", {details}).`)
147
+ }
148
+ } else if (eventDetailRef) {
149
+ const typesText = eventDetailRef?.type?.typeName?.text;
150
+ const typeRefs = (getTypeRefs(ts, eventDetailRef)
151
+ ?.map(e => getReference(ts, e, event, moduleDoc.path)).filter(Boolean)) || [];
152
+
153
+ result.type = { text: `CustomEvent<${typesText}>` };
154
+
155
+ if (typeRefs.length) {
156
+ result.type.references = typeRefs;
157
+ }
158
+ } else {
159
+ logDocumentationError(moduleDoc.path, `Event details for event '${name}' must be described.`)
160
+ }
161
+
125
162
  result._ui5parameters = getParams(ts, eventDetails, commentParams, classNode, moduleDoc);
126
163
  }
127
164
 
@@ -0,0 +1,128 @@
1
+ #!/usr/bin/env node
2
+
3
+ import ts from 'typescript';
4
+ import path from 'path';
5
+ // Patch: Custom patch to not merge child parent privacy/type
6
+ // https://github.com/open-wc/custom-elements-manifest/pull/300
7
+ import { globby } from 'globby';
8
+ import fs from 'fs';
9
+ import commandLineArgs from 'command-line-args';
10
+ import chokidar from 'chokidar';
11
+ import debounce from 'debounce';
12
+
13
+ import { create } from './src/create.js';
14
+ import {
15
+ getUserConfig,
16
+ getCliConfig,
17
+ addFrameworkPlugins,
18
+ addCustomElementsPropertyToPackageJson,
19
+ mergeGlobsAndExcludes,
20
+ timestamp,
21
+ DEFAULTS,
22
+ MENU,
23
+ } from './src/utils/cli-helpers.js';
24
+ import { findExternalManifests } from './src/utils/find-external-manifests.js';
25
+
26
+ /**
27
+ * @param {{argv:string[]; cwd: string; noWrite:boolean}} [opts]
28
+ */
29
+ export async function cli({ argv = process.argv, cwd = process.cwd(), noWrite } = {}) {
30
+ const mainDefinitions = [{ name: 'command', defaultOption: true }];
31
+ const mainOptions = commandLineArgs(mainDefinitions, { stopAtFirstUnknown: true, argv });
32
+ const cliArgs = mainOptions._unknown || [];
33
+
34
+ if (mainOptions.command === 'analyze') {
35
+ const { config: configPath, ...cliConfig } = getCliConfig(cliArgs);
36
+ const userConfig = await getUserConfig(configPath, cwd);
37
+
38
+ /**
39
+ * Merged config options
40
+ * Command line options override userConfig options
41
+ */
42
+ const mergedOptions = { ...DEFAULTS, ...userConfig, ...cliConfig };
43
+ const merged = mergeGlobsAndExcludes(DEFAULTS, userConfig, cliConfig);
44
+ async function run() {
45
+ const globs = await globby(merged, { cwd });
46
+ const modules = userConfig?.overrideModuleCreation
47
+ ? userConfig.overrideModuleCreation({ ts, globs })
48
+ : globs.map((glob) => {
49
+ const fullPath = path.resolve(cwd, glob);
50
+ const source = fs.readFileSync(fullPath).toString();
51
+
52
+ return ts.createSourceFile(glob, source, ts.ScriptTarget.ES2015, true);
53
+ });
54
+
55
+ let thirdPartyCEMs = [];
56
+ if (mergedOptions?.dependencies) {
57
+ try {
58
+ const fullPathGlobs = globs.map(glob => path.resolve(cwd, glob));
59
+ thirdPartyCEMs = await findExternalManifests(fullPathGlobs, {basePath: cwd});
60
+ } catch (e) {
61
+ if (mergedOptions.dev) console.log(`Failed to add third party CEMs. \n\n${e.stack}`);
62
+ }
63
+ }
64
+
65
+ let plugins = await addFrameworkPlugins(mergedOptions);
66
+ plugins = [...plugins, ...(userConfig?.plugins || [])];
67
+
68
+ const context = { dev: mergedOptions.dev, thirdPartyCEMs };
69
+
70
+ /**
71
+ * Create the manifest
72
+ */
73
+ const customElementsManifest = create({modules, plugins, context});
74
+
75
+ if (mergedOptions.dev) {
76
+ console.log(JSON.stringify(customElementsManifest, null, 2));
77
+ }
78
+
79
+ if(!noWrite) {
80
+ const outdir = path.join(cwd, mergedOptions.outdir);
81
+ if (!fs.existsSync(outdir)) {
82
+ fs.mkdirSync(outdir, { recursive: true });
83
+ }
84
+ fs.writeFileSync(
85
+ path.join(outdir, 'custom-elements.json'),
86
+ `${JSON.stringify(customElementsManifest, null, 2)}\n`,
87
+ );
88
+ }
89
+
90
+ if (!mergedOptions.quiet) {
91
+ console.log(`[${timestamp()}] @custom-elements-manifest/analyzer: Created new manifest.`);
92
+ }
93
+
94
+ return customElementsManifest;
95
+ }
96
+ /** The manifest that will be returned for programmatic calls of cli */
97
+ const manifest = await run();
98
+
99
+ /**
100
+ * Watch mode
101
+ */
102
+ if (mergedOptions.watch) {
103
+ const fileWatcher = chokidar.watch(merged);
104
+
105
+ const onChange = debounce(run, 100);
106
+
107
+ fileWatcher.addListener('add', onChange);
108
+ fileWatcher.addListener('change', onChange);
109
+ fileWatcher.addListener('unlink', onChange);
110
+ }
111
+
112
+ try {
113
+ if (mergedOptions.packagejson) {
114
+ addCustomElementsPropertyToPackageJson(mergedOptions.outdir);
115
+ }
116
+ } catch {
117
+ console.log(
118
+ `Could not add 'customElements' property to ${cwd}${
119
+ path.sep
120
+ }package.json. \nAdding this property helps tooling locate your Custom Elements Manifest. Please consider adding it yourself, or file an issue if you think this is a bug.\nhttps://www.github.com/open-wc/custom-elements-manifest`,
121
+ );
122
+ }
123
+
124
+ return manifest;
125
+ } else {
126
+ console.log(MENU);
127
+ }
128
+ }
@@ -0,0 +1,59 @@
1
+ {
2
+ "name": "@custom-elements-manifest/analyzer",
3
+ "version": "0.10.10",
4
+ "description": "",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "types": "index.d.ts",
8
+ "bin": {
9
+ "custom-elements-manifest": "./cem.js",
10
+ "cem": "./cem.js"
11
+ },
12
+ "repository": {
13
+ "type": "git",
14
+ "url": "https://github.com/open-wc/custom-elements-manifest.git",
15
+ "directory": "packages/analyzer"
16
+ },
17
+ "author": "open-wc",
18
+ "homepage": "https://github.com/open-wc/custom-elements-manifest",
19
+ "bugs": {
20
+ "url": "https://github.com/open-wc/custom-elements-manifest"
21
+ },
22
+ "main": "index.js",
23
+ "scripts": {
24
+ "prepublishOnly": "npm test && npm run build:browser",
25
+ "start": "nodemon --ignore './custom-elements.json' cem.js analyze --dev --fast",
26
+ "test": "asdgf",
27
+ "build:browser": "esbuild src/browser-entrypoint.js --bundle --format=esm --outfile=browser/index.js",
28
+ "test:watch": "watchexec -w src -w test npm test",
29
+ "update-fixtures": "node scripts/update-version.js --version 1.0.0"
30
+ },
31
+ "keywords": [
32
+ "custom-elements",
33
+ "custom-elements-json",
34
+ "custom-elements-manifest",
35
+ "customelements",
36
+ "webcomponents",
37
+ "customelementsjson",
38
+ "customelementsmanifest"
39
+ ],
40
+ "dependencies": {
41
+ "@custom-elements-manifest/find-dependencies": "^0.0.6",
42
+ "@github/catalyst": "^1.6.0",
43
+ "@web/config-loader": "0.1.3",
44
+ "chokidar": "3.5.2",
45
+ "command-line-args": "5.1.2",
46
+ "comment-parser": "1.2.4",
47
+ "custom-elements-manifest": "1.0.0",
48
+ "debounce": "1.2.1",
49
+ "globby": "11.0.4",
50
+ "typescript": "~5.4.2"
51
+ },
52
+ "devDependencies": {},
53
+ "contributors": [
54
+ "Pascal Schilp <pascalschilp@gmail.com>",
55
+ "Benny Powers <web@bennypowers.com>",
56
+ "Matias Huhta <huhta.matias@gmail.com>"
57
+ ],
58
+ "customElements": "custom-elements.json"
59
+ }
@@ -0,0 +1,23 @@
1
+ /**
2
+ * This file is the entrypoint for rollup to correctly bundle the analyzer for the browser.
3
+ * Do not use directly, but import from ./browser/index.js
4
+ */
5
+
6
+ import ts from 'typescript';
7
+
8
+ import { create } from './create.js';
9
+ import { catalystPlugin } from './features/framework-plugins/catalyst/catalyst.js';
10
+ import { catalystPlugin2 } from './features/framework-plugins/catalyst-major-2/catalyst.js';
11
+ import { stencilPlugin } from './features/framework-plugins/stencil/stencil.js';
12
+ import { litPlugin } from './features/framework-plugins/lit/lit.js';
13
+ import { fastPlugin } from './features/framework-plugins/fast/fast.js';
14
+
15
+ export {
16
+ ts,
17
+ create,
18
+ catalystPlugin,
19
+ catalystPlugin2,
20
+ stencilPlugin,
21
+ litPlugin,
22
+ fastPlugin
23
+ };
@@ -0,0 +1,117 @@
1
+ import ts from 'typescript';
2
+ import { FEATURES } from './features/index.js';
3
+ import { withErrorHandling } from './utils/index.js';
4
+
5
+ /**
6
+ * CORE
7
+ *
8
+ * This function is the core of the analyzer. It takes an array of ts sourceFiles, and creates a
9
+ * custom elements manifest.
10
+ */
11
+ export function create({modules, plugins = [], context = {dev:false}}) {
12
+ const customElementsManifest = {
13
+ schemaVersion: '1.0.0',
14
+ readme: '',
15
+ modules: [],
16
+ };
17
+
18
+ const { dev } = context;
19
+
20
+ const mergedPlugins = [
21
+ ...FEATURES,
22
+ ...plugins,
23
+ ];
24
+
25
+ if(dev) console.log('[INITIALIZE PLUGINS]');
26
+ mergedPlugins.forEach(({name, initialize}) => {
27
+ withErrorHandling(name, () => {
28
+ initialize?.({ts, customElementsManifest, context});
29
+ });
30
+ });
31
+
32
+ modules.forEach(currModule => {
33
+ if(dev) console.log('[COLLECT PHASE]: ', currModule.fileName);
34
+ /**
35
+ * COLLECT PHASE
36
+ * First pass through all modules. Can be used to gather imports, exports, types, default values,
37
+ * which you may need to know the existence of in a later phase.
38
+ */
39
+ collect(currModule, context, mergedPlugins);
40
+ });
41
+
42
+ modules.forEach(currModule => {
43
+ if(dev) console.log('[ANALYZE PHASE]: ', currModule.fileName);
44
+ const moduleDoc = {
45
+ kind: "javascript-module",
46
+ path: currModule.fileName,
47
+ declarations: [],
48
+ exports: []
49
+ };
50
+
51
+ /**
52
+ * ANALYZE PHASE
53
+ * Go through the AST of every separate module, and gather as much as information as we can
54
+ * This includes a modules imports, which are not specified in custom-elements.json, but are
55
+ * required for the LINK PHASE, and deleted when processed
56
+ */
57
+ analyze(currModule, moduleDoc, context, mergedPlugins);
58
+ customElementsManifest.modules.push(moduleDoc);
59
+
60
+ if(dev) console.log('[MODULE LINK PHASE]: ', currModule.fileName);
61
+ /**
62
+ * LINK PHASE
63
+ * All information for a module has been gathered, now we can link information together. Like:
64
+ * - Finding a CustomElement's tagname by finding its customElements.define() call (or 'export')
65
+ * - Applying inheritance to classes (adding `inheritedFrom` properties/attrs/events/methods)
66
+ */
67
+ mergedPlugins.forEach(({name, moduleLinkPhase}) => {
68
+ withErrorHandling(name, () => {
69
+ moduleLinkPhase?.({ts, moduleDoc, context});
70
+ });
71
+ });
72
+ });
73
+
74
+ if(dev) console.log('[PACKAGE LINK PHASE]');
75
+ /**
76
+ * PACKAGE LINK PHASE
77
+ * All modules have now been parsed, we can now link information from across modules together
78
+ * - Link classes to their definitions etc
79
+ * - Match tagNames for classDocs
80
+ * - Apply inheritance
81
+ */
82
+ mergedPlugins.forEach(({name, packageLinkPhase}) => {
83
+ withErrorHandling(name, () => {
84
+ packageLinkPhase?.({customElementsManifest, context});
85
+ });
86
+ });
87
+
88
+ return customElementsManifest;
89
+ }
90
+
91
+ function collect(source, context, mergedPlugins) {
92
+ visitNode(source);
93
+
94
+ function visitNode(node) {
95
+ mergedPlugins.forEach(({name, collectPhase}) => {
96
+ withErrorHandling(name, () => {
97
+ collectPhase?.({ts, node, context});
98
+ });
99
+ });
100
+
101
+ ts.forEachChild(node, visitNode);
102
+ }
103
+ }
104
+
105
+ function analyze(source, moduleDoc, context, mergedPlugins) {
106
+ visitNode(source);
107
+
108
+ function visitNode(node) {
109
+ mergedPlugins.forEach(({name, analyzePhase}) => {
110
+ withErrorHandling(name, () => {
111
+ analyzePhase?.({ts, node, moduleDoc, context});
112
+ });
113
+ });
114
+
115
+ ts.forEachChild(node, visitNode);
116
+ }
117
+ }