@ui5/webcomponents-tools 0.0.0-07d38e78e → 0.0.0-093de5dd1
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.
- package/CHANGELOG.md +1030 -0
- package/LICENSE.txt +201 -0
- package/README.md +7 -10
- package/assets-meta.js +1 -5
- package/bin/dev.js +3 -2
- package/bin/ui5nps.js +274 -0
- package/components-package/eslint.js +59 -31
- package/components-package/nps.js +98 -75
- package/components-package/vite.config.js +7 -11
- package/components-package/wdio.js +12 -5
- package/icons-collection/nps.js +30 -21
- package/lib/amd-to-es6/index.js +15 -10
- package/lib/cem/cem.js +12 -0
- package/lib/cem/custom-elements-manifest.config.mjs +90 -45
- package/lib/cem/event.mjs +69 -32
- package/lib/cem/merge.mjs +220 -0
- package/lib/cem/patch/@custom-elements-manifest/analyzer/cli.js +128 -0
- package/lib/cem/patch/@custom-elements-manifest/analyzer/package.json +59 -0
- package/lib/cem/patch/@custom-elements-manifest/analyzer/src/browser-entrypoint.js +23 -0
- package/lib/cem/patch/@custom-elements-manifest/analyzer/src/create.js +117 -0
- package/lib/cem/patch/@custom-elements-manifest/analyzer/src/features/analyse-phase/arrow-function.js +26 -0
- package/lib/cem/patch/@custom-elements-manifest/analyzer/src/features/analyse-phase/class-jsdoc.js +157 -0
- package/lib/cem/patch/@custom-elements-manifest/analyzer/src/features/analyse-phase/classes.js +20 -0
- package/lib/cem/patch/@custom-elements-manifest/analyzer/src/features/analyse-phase/creators/createArrowFunction.js +17 -0
- package/lib/cem/patch/@custom-elements-manifest/analyzer/src/features/analyse-phase/creators/createAttribute.js +24 -0
- package/lib/cem/patch/@custom-elements-manifest/analyzer/src/features/analyse-phase/creators/createClass.js +301 -0
- package/lib/cem/patch/@custom-elements-manifest/analyzer/src/features/analyse-phase/creators/createClassField.js +26 -0
- package/lib/cem/patch/@custom-elements-manifest/analyzer/src/features/analyse-phase/creators/createFunctionLike.js +73 -0
- package/lib/cem/patch/@custom-elements-manifest/analyzer/src/features/analyse-phase/creators/createMixin.js +33 -0
- package/lib/cem/patch/@custom-elements-manifest/analyzer/src/features/analyse-phase/creators/createVariable.js +22 -0
- package/lib/cem/patch/@custom-elements-manifest/analyzer/src/features/analyse-phase/creators/handlers.js +338 -0
- package/lib/cem/patch/@custom-elements-manifest/analyzer/src/features/analyse-phase/custom-elements-define-calls.js +90 -0
- package/lib/cem/patch/@custom-elements-manifest/analyzer/src/features/analyse-phase/exports.js +156 -0
- package/lib/cem/patch/@custom-elements-manifest/analyzer/src/features/analyse-phase/function-like.js +24 -0
- package/lib/cem/patch/@custom-elements-manifest/analyzer/src/features/analyse-phase/mixins.js +29 -0
- package/lib/cem/patch/@custom-elements-manifest/analyzer/src/features/analyse-phase/reexported-wrapped-mixin-exports.js +84 -0
- package/lib/cem/patch/@custom-elements-manifest/analyzer/src/features/analyse-phase/variables.js +34 -0
- package/lib/cem/patch/@custom-elements-manifest/analyzer/src/features/collect-phase/collect-imports.js +101 -0
- package/lib/cem/patch/@custom-elements-manifest/analyzer/src/features/framework-plugins/catalyst/catalyst.js +11 -0
- package/lib/cem/patch/@custom-elements-manifest/analyzer/src/features/framework-plugins/catalyst/controller.js +34 -0
- package/lib/cem/patch/@custom-elements-manifest/analyzer/src/features/framework-plugins/catalyst-major-2/catalyst.js +11 -0
- package/lib/cem/patch/@custom-elements-manifest/analyzer/src/features/framework-plugins/catalyst-major-2/controller.js +34 -0
- package/lib/cem/patch/@custom-elements-manifest/analyzer/src/features/framework-plugins/decorators/attr.js +53 -0
- package/lib/cem/patch/@custom-elements-manifest/analyzer/src/features/framework-plugins/decorators/custom-element-decorator.js +36 -0
- package/lib/cem/patch/@custom-elements-manifest/analyzer/src/features/framework-plugins/fast/fast.js +7 -0
- package/lib/cem/patch/@custom-elements-manifest/analyzer/src/features/framework-plugins/lit/lit.js +13 -0
- package/lib/cem/patch/@custom-elements-manifest/analyzer/src/features/framework-plugins/lit/member-denylist.js +21 -0
- package/lib/cem/patch/@custom-elements-manifest/analyzer/src/features/framework-plugins/lit/method-denylist.js +20 -0
- package/lib/cem/patch/@custom-elements-manifest/analyzer/src/features/framework-plugins/lit/property-decorator.js +94 -0
- package/lib/cem/patch/@custom-elements-manifest/analyzer/src/features/framework-plugins/lit/static-properties.js +121 -0
- package/lib/cem/patch/@custom-elements-manifest/analyzer/src/features/framework-plugins/lit/utils.js +66 -0
- package/lib/cem/patch/@custom-elements-manifest/analyzer/src/features/framework-plugins/stencil/stencil.js +129 -0
- package/lib/cem/patch/@custom-elements-manifest/analyzer/src/features/index.js +80 -0
- package/lib/cem/patch/@custom-elements-manifest/analyzer/src/features/link-phase/cleanup-classes.js +25 -0
- package/lib/cem/patch/@custom-elements-manifest/analyzer/src/features/link-phase/field-denylist.js +22 -0
- package/lib/cem/patch/@custom-elements-manifest/analyzer/src/features/link-phase/method-denylist.js +25 -0
- package/lib/cem/patch/@custom-elements-manifest/analyzer/src/features/post-processing/apply-inheritance.js +78 -0
- package/lib/cem/patch/@custom-elements-manifest/analyzer/src/features/post-processing/is-custom-element.js +34 -0
- package/lib/cem/patch/@custom-elements-manifest/analyzer/src/features/post-processing/link-class-to-tagname.js +27 -0
- package/lib/cem/patch/@custom-elements-manifest/analyzer/src/features/post-processing/remove-unexported-declarations.js +23 -0
- package/lib/cem/patch/@custom-elements-manifest/analyzer/src/features/post-processing/resolve-initializers.js +52 -0
- package/lib/cem/patch/@custom-elements-manifest/analyzer/src/utils/ast-helpers.js +186 -0
- package/lib/cem/patch/@custom-elements-manifest/analyzer/src/utils/cli-helpers.js +164 -0
- package/lib/cem/patch/@custom-elements-manifest/analyzer/src/utils/exports.js +44 -0
- package/lib/cem/patch/@custom-elements-manifest/analyzer/src/utils/find-external-manifests.js +67 -0
- package/lib/cem/patch/@custom-elements-manifest/analyzer/src/utils/imports.js +25 -0
- package/lib/cem/patch/@custom-elements-manifest/analyzer/src/utils/index.js +71 -0
- package/lib/cem/patch/@custom-elements-manifest/analyzer/src/utils/jsdoc.js +19 -0
- package/lib/cem/patch/@custom-elements-manifest/analyzer/src/utils/manifest-helpers.js +194 -0
- package/lib/cem/patch/@custom-elements-manifest/analyzer/src/utils/mixins.js +112 -0
- package/lib/cem/schema-internal.json +65 -0
- package/lib/cem/types-internal.d.ts +14 -2
- package/lib/cem/utils.mjs +69 -30
- package/lib/cem/validate.js +61 -55
- package/lib/chokidar/chokidar.js +28 -0
- package/lib/copy-and-watch/index.js +105 -97
- package/lib/copy-list/index.js +16 -10
- package/lib/create-icons/index.js +24 -19
- package/lib/create-illustrations/index.js +49 -27
- package/lib/create-new-component/{tsFileContentTemplate.js → Component.js} +12 -9
- package/lib/create-new-component/ComponentTemplate.js +12 -0
- package/lib/create-new-component/index.js +13 -12
- package/lib/css-processors/css-processor-components.mjs +74 -59
- package/lib/css-processors/css-processor-themes.mjs +85 -62
- package/lib/css-processors/shared.mjs +5 -35
- package/lib/dev-server/{dev-server.js → dev-server.mjs} +26 -14
- package/lib/dev-server/virtual-index-html-plugin.js +24 -20
- package/lib/eslint/eslint.js +44 -0
- package/lib/generate-js-imports/illustrations.js +53 -54
- package/lib/generate-json-imports/i18n.js +56 -36
- package/lib/generate-json-imports/themes.js +27 -14
- package/lib/hbs2ui5/RenderTemplates/LitRenderer.js +12 -7
- package/lib/hbs2ui5/index.js +3 -3
- package/lib/i18n/defaults.js +15 -9
- package/lib/i18n/toJSON.js +38 -12
- package/lib/icons-hash/icons-hash.mjs +149 -0
- package/lib/remove-dev-mode/remove-dev-mode.mjs +38 -24
- package/lib/rimraf/rimraf.js +31 -0
- package/lib/scoping/get-all-tags.js +9 -2
- package/lib/test-runner/test-runner.js +56 -48
- package/lib/vite-bundler/vite-bundler.mjs +35 -0
- package/package.json +22 -19
- package/tsconfig.json +18 -0
- package/lib/css-processors/css-processor-component-styles.mjs +0 -48
- 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,17 @@ 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
|
+
let aliasMap = {};
|
|
31
|
+
|
|
32
|
+
const devMode = process.env.UI5_CEM_MODE === "dev";
|
|
33
|
+
try {
|
|
34
|
+
aliasMap = JSON.parse(fs.readFileSync("./.ui5-cem-aliases.json"));
|
|
35
|
+
} catch (e) {
|
|
36
|
+
if (devMode) {
|
|
37
|
+
console.warn("No .ui5-cem-aliases.json file found. Continuing without aliases.");
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
29
41
|
|
|
30
42
|
const extractClassNodeJSDoc = node => {
|
|
31
43
|
const fileContent = node.getFullText();
|
|
@@ -64,12 +76,23 @@ function processClass(ts, classNode, moduleDoc) {
|
|
|
64
76
|
currClass.customElement = !!customElementDecorator || className === "UI5Element" || undefined;
|
|
65
77
|
currClass.kind = "class";
|
|
66
78
|
currClass.deprecated = getDeprecatedStatus(classParsedJsDoc);
|
|
79
|
+
currClass._ui5experimental = getExperimentalStatus(classParsedJsDoc);
|
|
67
80
|
currClass._ui5since = getSinceStatus(classParsedJsDoc);
|
|
68
81
|
currClass._ui5privacy = getPrivacyStatus(classParsedJsDoc);
|
|
69
82
|
currClass._ui5abstract = hasTag(classParsedJsDoc, "abstract") ? true : undefined;
|
|
70
83
|
currClass.description = normalizeDescription(classParsedJsDoc.description || findTag(classParsedJsDoc, "class")?.description);
|
|
71
84
|
currClass._ui5implements = findAllTags(classParsedJsDoc, "implements")
|
|
72
|
-
.map(tag =>
|
|
85
|
+
.map(tag => {
|
|
86
|
+
const correctInterfaceDescription = classNode?.heritageClauses?.some(heritageClause => {
|
|
87
|
+
return heritageClause?.types?.some(type => type.expression?.text === normalizeTagType(tag.type));
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
if (!correctInterfaceDescription) {
|
|
91
|
+
logDocumentationError(moduleDoc.path, `@interface {${tag.type}} tag is used, but the class doesn't implement the corresponding interface`)
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return getReference(ts, normalizeTagType(tag.type), classNode, moduleDoc.path)
|
|
95
|
+
})
|
|
73
96
|
.filter(Boolean);
|
|
74
97
|
|
|
75
98
|
|
|
@@ -77,6 +100,10 @@ function processClass(ts, classNode, moduleDoc) {
|
|
|
77
100
|
const superclassTag = findTag(classParsedJsDoc, "extends");
|
|
78
101
|
currClass.superclass = getReference(ts, superclassTag.name, classNode, moduleDoc.path);
|
|
79
102
|
|
|
103
|
+
if (classNode?.heritageClauses?.[0]?.types?.[0]?.expression?.text !== superclassTag.name) {
|
|
104
|
+
logDocumentationError(moduleDoc.path, `@extends ${superclassTag.name} is used, but the class doesn't extend the corresponding superclass`)
|
|
105
|
+
}
|
|
106
|
+
|
|
80
107
|
if (currClass.superclass?.name === "UI5Element") {
|
|
81
108
|
currClass.customElement = true;
|
|
82
109
|
}
|
|
@@ -111,9 +138,13 @@ function processClass(ts, classNode, moduleDoc) {
|
|
|
111
138
|
}
|
|
112
139
|
|
|
113
140
|
// Events
|
|
114
|
-
currClass.events = findAllDecorators(classNode, "event")
|
|
141
|
+
currClass.events = findAllDecorators(classNode, ["event", "eventStrict"])
|
|
115
142
|
?.map(event => processEvent(ts, event, classNode, moduleDoc));
|
|
116
143
|
|
|
144
|
+
const filename = classNode.getSourceFile().fileName;
|
|
145
|
+
const sourceFile = typeProgram.getSourceFile(filename);
|
|
146
|
+
const tsProgramClassNode = sourceFile.statements.find(statement => ts.isClassDeclaration(statement) && statement.name?.text === classNode.name?.text);
|
|
147
|
+
|
|
117
148
|
// Slots (with accessor), methods and fields
|
|
118
149
|
for (let i = 0; i < (currClass.members?.length || 0); i++) {
|
|
119
150
|
const member = currClass.members[i];
|
|
@@ -168,17 +199,21 @@ function processClass(ts, classNode, moduleDoc) {
|
|
|
168
199
|
const propertyDecorator = findDecorator(classNodeMember, "property");
|
|
169
200
|
|
|
170
201
|
if (propertyDecorator) {
|
|
171
|
-
member._ui5validator = propertyDecorator?.expression?.arguments[0]?.properties?.find(property => ["validator", "type"].includes(property.name.text))?.initializer?.text || "String";
|
|
172
202
|
member._ui5noAttribute = propertyDecorator?.expression?.arguments[0]?.properties?.find(property => property.name.text === "noAttribute")?.initializer?.kind === ts.SyntaxKind.TrueKeyword || undefined;
|
|
173
203
|
}
|
|
174
204
|
|
|
175
|
-
if (currClass.customElement && member.privacy === "public"
|
|
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);
|
|
205
|
+
if (currClass.customElement && member.privacy === "public") {
|
|
179
206
|
const tsProgramMember = tsProgramClassNode.members.find(m => ts.isPropertyDeclaration(m) && m.name?.text === member.name);
|
|
180
207
|
const attributeValue = typeChecker.typeToString(typeChecker.getTypeAtLocation(tsProgramMember), tsProgramMember);
|
|
181
208
|
|
|
209
|
+
if (attributeValue === "boolean" && member.default === "true") {
|
|
210
|
+
logDocumentationError(moduleDoc.path, `Boolean properties must be initialzed to false. [${member.name}] property of class [${className}] is intialized to \`true\``)
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
if (!member.type) {
|
|
214
|
+
logDocumentationError(moduleDoc.path, `Public properties must have type. The type of [${member.name}] property is not determinated automatically. Please check it.`)
|
|
215
|
+
}
|
|
216
|
+
|
|
182
217
|
currClass.attributes.push({
|
|
183
218
|
description: member.description,
|
|
184
219
|
name: toKebabCase(member.name),
|
|
@@ -293,6 +328,7 @@ function processInterface(ts, interfaceNode, moduleDoc) {
|
|
|
293
328
|
kind: "interface",
|
|
294
329
|
name: interfaceName,
|
|
295
330
|
description: normalizeDescription(interfaceParsedJsDoc?.description),
|
|
331
|
+
_ui5experimental: getExperimentalStatus(interfaceParsedJsDoc),
|
|
296
332
|
_ui5privacy: getPrivacyStatus(interfaceParsedJsDoc),
|
|
297
333
|
_ui5since: getSinceStatus(interfaceParsedJsDoc),
|
|
298
334
|
deprecated: getDeprecatedStatus(interfaceParsedJsDoc),
|
|
@@ -313,6 +349,7 @@ function processEnum(ts, enumNode, moduleDoc) {
|
|
|
313
349
|
kind: "enum",
|
|
314
350
|
name: enumName,
|
|
315
351
|
description: normalizeDescription(enumJSdoc?.comment),
|
|
352
|
+
_ui5experimental: getExperimentalStatus(enumParsedJsDoc),
|
|
316
353
|
_ui5privacy: getPrivacyStatus(enumParsedJsDoc),
|
|
317
354
|
_ui5since: getSinceStatus(enumParsedJsDoc),
|
|
318
355
|
deprecated: getDeprecatedStatus(enumParsedJsDoc) || undefined,
|
|
@@ -425,14 +462,6 @@ export default {
|
|
|
425
462
|
}
|
|
426
463
|
},
|
|
427
464
|
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
465
|
moduleDoc.path = moduleDoc.path?.replace(/^src/, "dist").replace(/\.ts$/, ".js");
|
|
437
466
|
|
|
438
467
|
moduleDoc.exports = moduleDoc.exports.
|
|
@@ -456,41 +485,57 @@ export default {
|
|
|
456
485
|
})
|
|
457
486
|
}
|
|
458
487
|
})
|
|
488
|
+
},
|
|
489
|
+
packageLinkPhase({ customElementsManifest }) {
|
|
490
|
+
customElementsManifest.modules.forEach(moduleDoc => {
|
|
491
|
+
for (let i = 0; i < moduleDoc.declarations.length; i++) {
|
|
492
|
+
const shouldRemove = processPublicAPI(moduleDoc.declarations[i]) || ["function", "variable"].includes(moduleDoc.declarations[i].kind)
|
|
493
|
+
if (shouldRemove) {
|
|
494
|
+
moduleDoc.declarations.splice(i, 1);
|
|
495
|
+
i--;
|
|
496
|
+
}
|
|
497
|
+
}
|
|
459
498
|
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
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
|
-
}
|
|
477
|
-
})
|
|
499
|
+
moduleDoc.declarations.forEach(declaration => {
|
|
500
|
+
if (declaration.superclass?.name && aliasMap[declaration.superclass.name]) {
|
|
501
|
+
declaration.superclass.name = aliasMap[declaration.superclass.name];
|
|
502
|
+
}
|
|
478
503
|
})
|
|
479
|
-
});
|
|
480
504
|
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
505
|
+
const typeReferences = new Set();
|
|
506
|
+
const registerTypeReference = reference => typeReferences.add(JSON.stringify(reference))
|
|
507
|
+
|
|
508
|
+
moduleDoc.declarations.forEach(declaration => {
|
|
509
|
+
["events", "slots", "members"].forEach(memberType => {
|
|
510
|
+
declaration[memberType]?.forEach(member => {
|
|
511
|
+
if (member.type?.references) {
|
|
512
|
+
member.type.references.forEach(registerTypeReference)
|
|
513
|
+
} else if (member._ui5type?.references) {
|
|
514
|
+
member._ui5type.references.forEach(registerTypeReference)
|
|
515
|
+
} else if (member.kind === "method") {
|
|
516
|
+
member.return?.type?.references?.forEach(registerTypeReference)
|
|
517
|
+
|
|
518
|
+
member.parameters?.forEach(parameter => {
|
|
519
|
+
parameter.type?.references?.forEach(registerTypeReference)
|
|
520
|
+
})
|
|
521
|
+
}
|
|
522
|
+
})
|
|
523
|
+
})
|
|
524
|
+
});
|
|
525
|
+
|
|
526
|
+
typeReferences.forEach(reference => {
|
|
527
|
+
reference = JSON.parse(reference);
|
|
528
|
+
if (reference.package === packageJSON?.name && reference.module === moduleDoc.path) {
|
|
529
|
+
const hasExport = moduleDoc.exports.some(e => e.declaration?.name === reference.name && e.declaration?.module === reference.module)
|
|
485
530
|
|
|
486
|
-
|
|
487
|
-
|
|
531
|
+
if (!hasExport) {
|
|
532
|
+
logDocumentationError(moduleDoc.path?.replace(/^dist/, "src").replace(/\.js$/, ".ts"), `Type '${reference.name}' is used to describe a public API but is not exported.`,)
|
|
533
|
+
}
|
|
488
534
|
}
|
|
489
|
-
}
|
|
490
|
-
})
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
if (context.dev) {
|
|
535
|
+
})
|
|
536
|
+
});
|
|
537
|
+
|
|
538
|
+
if (devMode) {
|
|
494
539
|
displayDocumentationErrors();
|
|
495
540
|
}
|
|
496
541
|
}
|
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:
|
|
41
|
+
_ui5privacy: "public",
|
|
51
42
|
description: normalizeDescription(commentParam?.description),
|
|
52
|
-
_ui5since: getSinceStatus(decoratorParamParsedComment),
|
|
53
|
-
deprecated: getDeprecatedStatus(decoratorParamParsedComment),
|
|
54
43
|
};
|
|
55
|
-
})
|
|
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
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
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.
|
|
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
|
-
|
|
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,220 @@
|
|
|
1
|
+
import { pathToFileURL } from "url";
|
|
2
|
+
import path from "path";
|
|
3
|
+
import { createRequire } from 'module';
|
|
4
|
+
import { readFile, writeFile } from "fs/promises";
|
|
5
|
+
|
|
6
|
+
const require = createRequire(import.meta.url);
|
|
7
|
+
|
|
8
|
+
const UI5_BASE_CLASS = "UI5Element";
|
|
9
|
+
|
|
10
|
+
const main = async (argv) => {
|
|
11
|
+
let customElementsPath = null;
|
|
12
|
+
const CACHED_CEMS = new Map();
|
|
13
|
+
const DECLARATION_PACKAGE = new WeakMap();
|
|
14
|
+
const DECLARATION_MODULE = new WeakMap();
|
|
15
|
+
|
|
16
|
+
function removeInheritedFrom(obj) {
|
|
17
|
+
if (obj === null || typeof obj !== 'object') {
|
|
18
|
+
return obj;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
if (Array.isArray(obj)) {
|
|
22
|
+
return obj.map(item => removeInheritedFrom(item));
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const result = {};
|
|
26
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
27
|
+
if (key === 'inheritedFrom') {
|
|
28
|
+
continue;
|
|
29
|
+
}
|
|
30
|
+
result[key] = removeInheritedFrom(value);
|
|
31
|
+
}
|
|
32
|
+
return result;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
async function readPackageJson(filePath) {
|
|
36
|
+
try {
|
|
37
|
+
return JSON.parse(await readFile(filePath, "utf-8"));
|
|
38
|
+
} catch (error) {
|
|
39
|
+
throw new Error(`Failed to read package.json at ${filePath}: ${error.message}`);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
async function loadPackageJson(depName) {
|
|
44
|
+
try {
|
|
45
|
+
// First try the standard require method (works when exports includes package.json)
|
|
46
|
+
const pkg = require(`${depName}/package.json`);
|
|
47
|
+
const pkgPath = require.resolve(`${depName}/package.json`);
|
|
48
|
+
return { path: path.dirname(pkgPath), pkg };
|
|
49
|
+
} catch (e) {
|
|
50
|
+
// If that fails, resolve the package path and read package.json directly
|
|
51
|
+
try {
|
|
52
|
+
const packagePath = require.resolve(depName);
|
|
53
|
+
let currentDir = path.dirname(packagePath);
|
|
54
|
+
|
|
55
|
+
// Navigate up to find package.json (the resolved path might be deep in dist/ or similar)
|
|
56
|
+
while (currentDir !== path.parse(currentDir).root) {
|
|
57
|
+
try {
|
|
58
|
+
const pkgPath = path.join(currentDir, 'package.json');
|
|
59
|
+
const content = await readFile(pkgPath, 'utf-8');
|
|
60
|
+
const pkg = JSON.parse(content);
|
|
61
|
+
|
|
62
|
+
// Verify this is the correct package.json by checking the name
|
|
63
|
+
if (pkg.name === depName) {
|
|
64
|
+
return { path: currentDir, pkg };
|
|
65
|
+
}
|
|
66
|
+
} catch {
|
|
67
|
+
// Continue searching up the directory tree
|
|
68
|
+
}
|
|
69
|
+
currentDir = path.dirname(currentDir);
|
|
70
|
+
}
|
|
71
|
+
} catch (resolveError) {
|
|
72
|
+
// console.warn(`Could not resolve ${depName}:`, resolveError.message);
|
|
73
|
+
}
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
async function collectThirdPartyCem() {
|
|
79
|
+
const packageJSONPath = path.resolve(process.cwd(), "package.json");
|
|
80
|
+
const packageJSON = await readPackageJson(packageJSONPath);
|
|
81
|
+
|
|
82
|
+
const dependencyKeys = Object.keys(packageJSON).filter(key => key.toLowerCase().includes("dependencies"));
|
|
83
|
+
const dependencies = dependencyKeys.flatMap(key => Object.keys(packageJSON[key]));
|
|
84
|
+
|
|
85
|
+
const thirdPartCEM = (await Promise.all(dependencies.map(async dep => {
|
|
86
|
+
const result = await loadPackageJson(dep);
|
|
87
|
+
if (!result?.pkg?.customElements) return null;
|
|
88
|
+
|
|
89
|
+
return {
|
|
90
|
+
path: result.path,
|
|
91
|
+
name: dep,
|
|
92
|
+
cem: result.pkg.customElements
|
|
93
|
+
};
|
|
94
|
+
}))).filter(Boolean);
|
|
95
|
+
|
|
96
|
+
await Promise.all(thirdPartCEM.map(async dep => {
|
|
97
|
+
const cemPath = path.resolve(dep.path, dep.cem);
|
|
98
|
+
try {
|
|
99
|
+
const cemContent = JSON.parse(await readFile(cemPath, "utf-8"));
|
|
100
|
+
CACHED_CEMS.set(dep.name, cemContent);
|
|
101
|
+
} catch (error) {
|
|
102
|
+
console.warn(`Failed to read CEM for ${dep.name} from ${cemPath}: ${error.message}`);
|
|
103
|
+
}
|
|
104
|
+
}));
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
async function readCurrentCEM() {
|
|
108
|
+
const packageJSONPath = path.resolve(process.cwd(), "package.json");
|
|
109
|
+
const packageJSON = await readPackageJson(packageJSONPath);
|
|
110
|
+
|
|
111
|
+
if (!packageJSON?.customElements) {
|
|
112
|
+
return null;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
customElementsPath = packageJSON.customElements;
|
|
116
|
+
const cemPath = path.resolve(process.cwd(), customElementsPath);
|
|
117
|
+
|
|
118
|
+
try {
|
|
119
|
+
const cemContent = JSON.parse(await readFile(cemPath, "utf-8"));
|
|
120
|
+
CACHED_CEMS.set(packageJSON.name, cemContent);
|
|
121
|
+
return cemContent;
|
|
122
|
+
} catch (error) {
|
|
123
|
+
throw new Error(`Failed to read CEM from ${cemPath}: ${error.message}`);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
async function resolveReference(ref) {
|
|
128
|
+
const pkg = CACHED_CEMS.get(ref.package);
|
|
129
|
+
|
|
130
|
+
if (!pkg) {
|
|
131
|
+
return null;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
const mod = (pkg.modules || []).find(m => m.path === ref.module);
|
|
135
|
+
|
|
136
|
+
if (!mod) {
|
|
137
|
+
return null;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const declaration = (mod.declarations || []).find(d => d.name === ref.name);
|
|
141
|
+
|
|
142
|
+
if (!declaration) {
|
|
143
|
+
return null;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
DECLARATION_PACKAGE.set(declaration, ref.package);
|
|
147
|
+
DECLARATION_MODULE.set(declaration, ref.module);
|
|
148
|
+
|
|
149
|
+
return resolveDeclaration(declaration);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
async function resolveDeclaration(declaration) {
|
|
153
|
+
if (!declaration.superclass || declaration.superclass.name === UI5_BASE_CLASS) {
|
|
154
|
+
return [declaration];
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
const superclassDeclarations = await resolveReference(declaration.superclass);
|
|
158
|
+
return [declaration, superclassDeclarations].flat().filter(Boolean);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
const merge = async () => {
|
|
162
|
+
const currentCEM = await readCurrentCEM();
|
|
163
|
+
if (!currentCEM) {
|
|
164
|
+
throw new Error("No custom elements manifest found in current project");
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
await collectThirdPartyCem();
|
|
168
|
+
|
|
169
|
+
const modules = currentCEM.modules || [];
|
|
170
|
+
|
|
171
|
+
for (const mod of modules) {
|
|
172
|
+
const declarations = (mod.declarations || []).filter(d => d.kind === "class");
|
|
173
|
+
|
|
174
|
+
for (const declaration of declarations) {
|
|
175
|
+
const declarationHierarchy = await resolveDeclaration(declaration);
|
|
176
|
+
const allKeys = declarationHierarchy.flatMap(dec => Object.keys(dec));
|
|
177
|
+
const uniqueKeys = [...new Set(allKeys)];
|
|
178
|
+
const arrayKeys = uniqueKeys
|
|
179
|
+
.filter(key => !key.startsWith("_ui5"))
|
|
180
|
+
.filter(key => declarationHierarchy.some(dec => Array.isArray(dec[key])));
|
|
181
|
+
|
|
182
|
+
for (const key of arrayKeys) {
|
|
183
|
+
const allItems = declarationHierarchy.flatMap(dec => dec[key] || []);
|
|
184
|
+
|
|
185
|
+
// Remove duplicates based on name property
|
|
186
|
+
const seen = new Set();
|
|
187
|
+
declaration[key] = allItems.filter(item => {
|
|
188
|
+
if (!item.name) return true;
|
|
189
|
+
if (seen.has(item.name)) return false;
|
|
190
|
+
seen.add(item.name);
|
|
191
|
+
return true;
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
const cleanedCEM = removeInheritedFrom(currentCEM);
|
|
198
|
+
const outputPath = path.resolve(process.cwd(), customElementsPath);
|
|
199
|
+
|
|
200
|
+
try {
|
|
201
|
+
await writeFile(outputPath, JSON.stringify(cleanedCEM, null, 2), "utf-8");
|
|
202
|
+
console.log(`Successfully merged CEM to ${outputPath}`);
|
|
203
|
+
} catch (error) {
|
|
204
|
+
throw new Error(`Failed to write merged CEM to ${outputPath}: ${error.message}`);
|
|
205
|
+
}
|
|
206
|
+
};
|
|
207
|
+
|
|
208
|
+
await merge();
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
const filePath = process.argv[1];
|
|
212
|
+
const fileUrl = pathToFileURL(filePath).href;
|
|
213
|
+
|
|
214
|
+
if (import.meta.url === fileUrl) {
|
|
215
|
+
main(process.argv)
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
export default {
|
|
219
|
+
_ui5mainFn: main
|
|
220
|
+
}
|