@ui5/webcomponents-tools 0.0.0-38861c872 → 0.0.0-38f83ffef
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 +1121 -0
- package/README.md +2 -5
- package/assets-meta.js +3 -11
- package/components-package/eslint.js +59 -31
- package/components-package/nps.js +48 -26
- package/components-package/vite.config.js +7 -11
- package/components-package/wdio.js +415 -405
- package/icons-collection/nps.js +2 -2
- package/lib/amd-to-es6/index.js +102 -0
- package/lib/amd-to-es6/no-remaining-require.js +33 -0
- package/lib/cem/custom-elements-manifest.config.mjs +129 -55
- package/lib/cem/event.mjs +75 -21
- package/lib/cem/schema-internal.json +71 -0
- package/lib/cem/types-internal.d.ts +564 -785
- package/lib/cem/types.d.ts +520 -655
- package/lib/cem/utils.mjs +115 -47
- package/lib/cem/validate.js +41 -37
- package/lib/create-icons/index.js +13 -10
- package/lib/create-illustrations/index.js +44 -9
- package/lib/create-new-component/{tsFileContentTemplate.js → Component.js} +13 -10
- package/lib/create-new-component/ComponentTemplate.js +12 -0
- package/lib/create-new-component/index.js +14 -22
- package/lib/css-processors/css-processor-components.mjs +3 -2
- package/lib/css-processors/css-processor-themes.mjs +2 -7
- package/lib/css-processors/scope-variables.mjs +3 -0
- package/lib/css-processors/shared.mjs +2 -22
- package/lib/dev-server/{dev-server.js → dev-server.mjs} +4 -4
- package/lib/dev-server/virtual-index-html-plugin.js +24 -20
- package/lib/generate-json-imports/i18n.js +46 -62
- package/lib/generate-json-imports/themes.js +13 -32
- package/lib/hbs2ui5/RenderTemplates/LitRenderer.js +12 -7
- package/lib/hbs2ui5/index.js +3 -3
- package/lib/i18n/defaults.js +3 -2
- package/lib/remove-dev-mode/remove-dev-mode.mjs +37 -0
- package/lib/scoping/get-all-tags.js +9 -2
- package/lib/scoping/lint-src.js +8 -7
- package/package.json +11 -8
- package/tsconfig.json +18 -0
- package/components-package/wdio.sync.js +0 -368
- package/lib/create-new-component/jsFileContentTemplate.js +0 -73
- package/lib/esm-abs-to-rel/index.js +0 -61
- package/lib/generate-custom-elements-manifest/index.js +0 -327
- package/lib/jsdoc/config.json +0 -29
- package/lib/jsdoc/configTypescript.json +0 -29
- package/lib/jsdoc/plugin.js +0 -2468
- package/lib/jsdoc/preprocess.js +0 -146
- package/lib/jsdoc/template/publish.js +0 -4120
- package/lib/replace-global-core/index.js +0 -25
package/icons-collection/nps.js
CHANGED
@@ -41,8 +41,8 @@ const copyIconAssetsCommand = (options) => {
|
|
41
41
|
const getScripts = (options) => {
|
42
42
|
const createJSImportsCmd = createIconImportsCommand(options);
|
43
43
|
const copyAssetsCmd = copyIconAssetsCommand(options);
|
44
|
-
const tsCommand = options.
|
45
|
-
const tsCrossEnv = options.
|
44
|
+
const tsCommand = !options.legacy ? "tsc --build" : "";
|
45
|
+
const tsCrossEnv = !options.legacy ? "cross-env UI5_TS=true" : "";
|
46
46
|
|
47
47
|
const scripts = {
|
48
48
|
clean: "rimraf dist && rimraf src/generated",
|
@@ -0,0 +1,102 @@
|
|
1
|
+
const fs = require("fs").promises;
|
2
|
+
const path = require("path");
|
3
|
+
const basePath = process.argv[2];
|
4
|
+
const babelCore = require("@babel/core");
|
5
|
+
const babelParser = require("@babel/parser");
|
6
|
+
const babelGenerator = require("@babel/generator").default;
|
7
|
+
const replaceAsync = require('replace-in-file');
|
8
|
+
|
9
|
+
const convertSAPUIDefineToDefine = async (filePath) => {
|
10
|
+
return replaceAsync({
|
11
|
+
files: filePath,
|
12
|
+
processor: (input) => {
|
13
|
+
return input.replace("sap.ui.define", "define").replace(", /* bExport= */ false", "").replace(", /* bExport= */ true", "");
|
14
|
+
}
|
15
|
+
})
|
16
|
+
}
|
17
|
+
|
18
|
+
const convertAmdToEs6 = async (code) => {
|
19
|
+
return (await babelCore.transformAsync(code, {
|
20
|
+
plugins: [['babel-plugin-amd-to-esm', {}]]
|
21
|
+
})).code;
|
22
|
+
}
|
23
|
+
|
24
|
+
const convertAbsImportsToRelative = (filePath, code) => {
|
25
|
+
let changed = false;
|
26
|
+
// console.log("File processing started: ", srcPath);
|
27
|
+
|
28
|
+
if (code.includes("import(")) {
|
29
|
+
// esprima can't parse this, but it's from the project files
|
30
|
+
return;
|
31
|
+
}
|
32
|
+
|
33
|
+
const tree = babelParser.parse(code, { sourceType: "module" });
|
34
|
+
const importer = filePath.replace(basePath, "");
|
35
|
+
const importerDir = path.dirname(importer);
|
36
|
+
// console.log("Importer -> ", importer);
|
37
|
+
|
38
|
+
tree?.program?.body?.forEach(node => {
|
39
|
+
if (node.type === "ImportDeclaration") {
|
40
|
+
let importee = node.source.value;
|
41
|
+
// console.log(importee);
|
42
|
+
if (importee.startsWith(".")) {
|
43
|
+
// add .js extension if missing
|
44
|
+
if (!importee.endsWith(".js")) {
|
45
|
+
node.source.value += ".js"
|
46
|
+
changed = true;
|
47
|
+
}
|
48
|
+
return;
|
49
|
+
}
|
50
|
+
let importeeDir = path.dirname(importee);
|
51
|
+
let importeeFile = path.basename(importee);
|
52
|
+
let relativePath = path.relative(importerDir, importeeDir);
|
53
|
+
if (relativePath.length === 0) {
|
54
|
+
relativePath = "."
|
55
|
+
}
|
56
|
+
if (!relativePath.startsWith(".")) {
|
57
|
+
relativePath = "./" + relativePath;
|
58
|
+
}
|
59
|
+
|
60
|
+
relativePath = relativePath.replace(/\\/g, "/"); // the browser expects unix paths
|
61
|
+
let relativeImport = `${relativePath}/${importeeFile}.js`;
|
62
|
+
// console.log(importee + " --> " + relativeImport);
|
63
|
+
node.source.value = relativeImport;
|
64
|
+
changed = true;
|
65
|
+
}
|
66
|
+
});
|
67
|
+
|
68
|
+
return changed ? babelGenerator(tree).code : code;
|
69
|
+
}
|
70
|
+
|
71
|
+
const replaceGlobalCoreUsage = (filePath, code) => {
|
72
|
+
if (!filePath.includes("Configuration")) {
|
73
|
+
const replaced = code.replace(/sap\.ui\.getCore\(\)/g, `Core`);
|
74
|
+
return code !== replaced ? `import Core from 'sap/ui/core/Core';${replaced}` : code;
|
75
|
+
}
|
76
|
+
|
77
|
+
return code;
|
78
|
+
};
|
79
|
+
|
80
|
+
const transformAmdToES6Module = async (filePath) => {
|
81
|
+
await convertSAPUIDefineToDefine(filePath);
|
82
|
+
|
83
|
+
let code = (await fs.readFile(filePath)).toString();
|
84
|
+
|
85
|
+
code = await convertAmdToEs6(code);
|
86
|
+
|
87
|
+
code = replaceGlobalCoreUsage(filePath, code);
|
88
|
+
|
89
|
+
code = convertAbsImportsToRelative(filePath, code);
|
90
|
+
|
91
|
+
return fs.writeFile(filePath, code);
|
92
|
+
}
|
93
|
+
|
94
|
+
const transformAmdToES6Modules = async () => {
|
95
|
+
const { globby } = await import("globby");
|
96
|
+
const fileNames = await globby(basePath.replace(/\\/g, "/") + "**/*.js");
|
97
|
+
return Promise.all(fileNames.map(transformAmdToES6Module).filter(x => !!x));
|
98
|
+
};
|
99
|
+
|
100
|
+
transformAmdToES6Modules().then(() => {
|
101
|
+
console.log("Success: all amd modules are transformed to es6!");
|
102
|
+
});
|
@@ -0,0 +1,33 @@
|
|
1
|
+
const fs = require("fs").promises;
|
2
|
+
const path = require("path");
|
3
|
+
const basePath = process.argv[2];
|
4
|
+
const babelCore = require("@babel/core");
|
5
|
+
const babelParser = require("@babel/parser");
|
6
|
+
const babelGenerator = require("@babel/generator").default;
|
7
|
+
const walk = require("estree-walk");
|
8
|
+
|
9
|
+
const checkHasRequire = (filePath, code) => {
|
10
|
+
code = code.replace(/sap\.ui\.require/g, "unhandledRequire");
|
11
|
+
|
12
|
+
const tree = babelParser.parse(code, { sourceType: "module" });
|
13
|
+
walk(tree, {
|
14
|
+
CallExpression: function (node) {
|
15
|
+
if (node.type === "CallExpression" && node?.callee?.name === "unhandledRequire") {
|
16
|
+
throw new Error(`sap.ui.require found in ${filePath}`);
|
17
|
+
}
|
18
|
+
}
|
19
|
+
});
|
20
|
+
}
|
21
|
+
|
22
|
+
const checkFile = async (filePath) => {
|
23
|
+
let code = (await fs.readFile(filePath)).toString();
|
24
|
+
checkHasRequire(filePath, code);
|
25
|
+
}
|
26
|
+
|
27
|
+
const checkAll = async () => {
|
28
|
+
const { globby } = await import("globby");
|
29
|
+
const fileNames = await globby(basePath.replace(/\\/g, "/") + "**/*.js");
|
30
|
+
return Promise.all(fileNames.map(checkFile).filter(x => !!x));
|
31
|
+
};
|
32
|
+
|
33
|
+
checkAll();
|
@@ -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,
|
@@ -13,13 +14,20 @@ import {
|
|
13
14
|
hasTag,
|
14
15
|
findTag,
|
15
16
|
findAllTags,
|
16
|
-
getJSDocErrors,
|
17
17
|
getTypeRefs,
|
18
18
|
normalizeDescription,
|
19
19
|
formatArrays,
|
20
20
|
isClass,
|
21
|
-
normalizeTagType
|
21
|
+
normalizeTagType,
|
22
|
+
logDocumentationError,
|
23
|
+
displayDocumentationErrors,
|
24
|
+
toKebabCase
|
22
25
|
} from "./utils.mjs";
|
26
|
+
import { generateCustomData } from "cem-plugin-vs-code-custom-data-generator";
|
27
|
+
import { customElementJetBrainsPlugin } from "custom-element-jet-brains-integration";
|
28
|
+
|
29
|
+
const packageJSON = JSON.parse(fs.readFileSync("./package.json"));
|
30
|
+
const devMode = process.env.UI5_CEM_MODE === "dev";
|
23
31
|
|
24
32
|
const extractClassNodeJSDoc = node => {
|
25
33
|
const fileContent = node.getFullText();
|
@@ -58,21 +66,43 @@ function processClass(ts, classNode, moduleDoc) {
|
|
58
66
|
currClass.customElement = !!customElementDecorator || className === "UI5Element" || undefined;
|
59
67
|
currClass.kind = "class";
|
60
68
|
currClass.deprecated = getDeprecatedStatus(classParsedJsDoc);
|
69
|
+
currClass._ui5experimental = getExperimentalStatus(classParsedJsDoc);
|
61
70
|
currClass._ui5since = getSinceStatus(classParsedJsDoc);
|
62
71
|
currClass._ui5privacy = getPrivacyStatus(classParsedJsDoc);
|
63
72
|
currClass._ui5abstract = hasTag(classParsedJsDoc, "abstract") ? true : undefined;
|
64
73
|
currClass.description = normalizeDescription(classParsedJsDoc.description || findTag(classParsedJsDoc, "class")?.description);
|
65
74
|
currClass._ui5implements = findAllTags(classParsedJsDoc, "implements")
|
66
|
-
.map(tag =>
|
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
|
+
})
|
67
86
|
.filter(Boolean);
|
68
87
|
|
69
88
|
|
70
89
|
if (hasTag(classParsedJsDoc, "extends")) {
|
71
90
|
const superclassTag = findTag(classParsedJsDoc, "extends");
|
72
|
-
|
91
|
+
const customElement = superclassTag.name === "HTMLElement";
|
73
92
|
|
74
|
-
if (
|
93
|
+
if (customElement) {
|
94
|
+
currClass.superclass = { name: "HTMLElement" }
|
75
95
|
currClass.customElement = true;
|
96
|
+
} else {
|
97
|
+
currClass.superclass = getReference(ts, superclassTag.name, classNode, moduleDoc.path);
|
98
|
+
|
99
|
+
if (classNode?.heritageClauses?.[0]?.types?.[0]?.expression?.text !== superclassTag.name) {
|
100
|
+
logDocumentationError(moduleDoc.path, `@extends ${superclassTag.name} is used, but the class doesn't extend the corresponding superclass`)
|
101
|
+
}
|
102
|
+
|
103
|
+
if (currClass.superclass?.name === "UI5Element") {
|
104
|
+
currClass.customElement = true;
|
105
|
+
}
|
76
106
|
}
|
77
107
|
}
|
78
108
|
|
@@ -105,9 +135,13 @@ function processClass(ts, classNode, moduleDoc) {
|
|
105
135
|
}
|
106
136
|
|
107
137
|
// Events
|
108
|
-
currClass.events = findAllDecorators(classNode, "event")
|
138
|
+
currClass.events = findAllDecorators(classNode, ["event", "eventStrict"])
|
109
139
|
?.map(event => processEvent(ts, event, classNode, moduleDoc));
|
110
140
|
|
141
|
+
const filename = classNode.getSourceFile().fileName;
|
142
|
+
const sourceFile = typeProgram.getSourceFile(filename);
|
143
|
+
const tsProgramClassNode = sourceFile.statements.find(statement => ts.isClassDeclaration(statement) && statement.name?.text === classNode.name?.text);
|
144
|
+
|
111
145
|
// Slots (with accessor), methods and fields
|
112
146
|
for (let i = 0; i < (currClass.members?.length || 0); i++) {
|
113
147
|
const member = currClass.members[i];
|
@@ -162,7 +196,29 @@ function processClass(ts, classNode, moduleDoc) {
|
|
162
196
|
const propertyDecorator = findDecorator(classNodeMember, "property");
|
163
197
|
|
164
198
|
if (propertyDecorator) {
|
165
|
-
member.
|
199
|
+
member._ui5noAttribute = propertyDecorator?.expression?.arguments[0]?.properties?.find(property => property.name.text === "noAttribute")?.initializer?.kind === ts.SyntaxKind.TrueKeyword || undefined;
|
200
|
+
}
|
201
|
+
|
202
|
+
if (currClass.customElement && member.privacy === "public") {
|
203
|
+
const tsProgramMember = tsProgramClassNode.members.find(m => ts.isPropertyDeclaration(m) && m.name?.text === member.name);
|
204
|
+
const attributeValue = typeChecker.typeToString(typeChecker.getTypeAtLocation(tsProgramMember), tsProgramMember);
|
205
|
+
|
206
|
+
if (attributeValue === "boolean" && member.default === "true") {
|
207
|
+
logDocumentationError(moduleDoc.path, `Boolean properties must be initialzed to false. [${member.name}] property of class [${className}] is intialized to \`true\``)
|
208
|
+
}
|
209
|
+
|
210
|
+
if (!member.type) {
|
211
|
+
logDocumentationError(moduleDoc.path, `Public properties must have type. The type of [${member.name}] property is not determinated automatically. Please check it.`)
|
212
|
+
}
|
213
|
+
|
214
|
+
currClass.attributes.push({
|
215
|
+
description: member.description,
|
216
|
+
name: toKebabCase(member.name),
|
217
|
+
default: member.default,
|
218
|
+
fieldName: member.name,
|
219
|
+
type: { text: attributeValue },
|
220
|
+
deprecated: member.deprecated
|
221
|
+
})
|
166
222
|
}
|
167
223
|
|
168
224
|
if (hasTag(memberParsedJsDoc, "formProperty")) {
|
@@ -181,24 +237,14 @@ function processClass(ts, classNode, moduleDoc) {
|
|
181
237
|
member.default = tagValue;
|
182
238
|
}
|
183
239
|
|
184
|
-
if (member.privacy === "public") {
|
185
|
-
|
186
|
-
|
187
|
-
if (!member.default) {
|
188
|
-
JSDocErrors.push(
|
189
|
-
`=== ERROR: Problem found with ${member.name}'s JSDoc comment in ${moduleDoc.path}: Default value is missing`
|
190
|
-
);
|
191
|
-
}
|
240
|
+
if (member.privacy === "public" && !member.default) {
|
241
|
+
logDocumentationError(moduleDoc.path, `Missing default value for '${member.name}'.`)
|
192
242
|
}
|
193
243
|
|
194
244
|
// Getters are treated as fields so they should not have return, instead of return they should have default value defined with @default
|
195
245
|
if (member.readonly) {
|
196
246
|
if (member.privacy === "public" && !member.type) {
|
197
|
-
|
198
|
-
|
199
|
-
JSDocErrors.push(
|
200
|
-
`=== ERROR: Problem found with ${member.name}'s JSDoc comment in ${moduleDoc.path}: Missing return type`
|
201
|
-
);
|
247
|
+
logDocumentationError(moduleDoc.path, `Missing return type for read-only field '${member.name}'.`)
|
202
248
|
}
|
203
249
|
|
204
250
|
delete member.return;
|
@@ -247,21 +293,19 @@ function processClass(ts, classNode, moduleDoc) {
|
|
247
293
|
if (member.return) {
|
248
294
|
const returnTag = findTag(memberParsedJsDoc, "returns");
|
249
295
|
member.return.description = returnTag?.description ? `${returnTag.name} ${returnTag.description}` : returnTag?.name;
|
250
|
-
member.return.type.text = classNodeMember?.type?.getFullText?.()?.trim();
|
296
|
+
member.return.type.text = formatArrays(classNodeMember?.type?.getFullText?.()?.trim());
|
251
297
|
const typeRefs = (getTypeRefs(ts, classNodeMember, member.return)
|
252
298
|
?.map(typeRef => getReference(ts, typeRef, classNodeMember, moduleDoc.path)).filter(Boolean)) || [];
|
253
299
|
|
254
300
|
if (typeRefs.length) {
|
255
301
|
member.return.type.references = typeRefs;
|
256
302
|
}
|
257
|
-
}
|
258
|
-
|
259
|
-
if (member.privacy === "public" && !member.return) {
|
260
|
-
const JSDocErrors = getJSDocErrors();
|
261
303
|
|
262
|
-
|
263
|
-
|
264
|
-
|
304
|
+
if (member.privacy === "public" && !member.return.type.text) {
|
305
|
+
logDocumentationError(moduleDoc.path, `Missing return type for function '${member.name}'.`)
|
306
|
+
}
|
307
|
+
} else if (member.privacy === "public") {
|
308
|
+
logDocumentationError(moduleDoc.path, `Missing return type for function '${member.name}'.`)
|
265
309
|
}
|
266
310
|
}
|
267
311
|
}
|
@@ -281,6 +325,7 @@ function processInterface(ts, interfaceNode, moduleDoc) {
|
|
281
325
|
kind: "interface",
|
282
326
|
name: interfaceName,
|
283
327
|
description: normalizeDescription(interfaceParsedJsDoc?.description),
|
328
|
+
_ui5experimental: getExperimentalStatus(interfaceParsedJsDoc),
|
284
329
|
_ui5privacy: getPrivacyStatus(interfaceParsedJsDoc),
|
285
330
|
_ui5since: getSinceStatus(interfaceParsedJsDoc),
|
286
331
|
deprecated: getDeprecatedStatus(interfaceParsedJsDoc),
|
@@ -301,6 +346,7 @@ function processEnum(ts, enumNode, moduleDoc) {
|
|
301
346
|
kind: "enum",
|
302
347
|
name: enumName,
|
303
348
|
description: normalizeDescription(enumJSdoc?.comment),
|
349
|
+
_ui5experimental: getExperimentalStatus(enumParsedJsDoc),
|
304
350
|
_ui5privacy: getPrivacyStatus(enumParsedJsDoc),
|
305
351
|
_ui5since: getSinceStatus(enumParsedJsDoc),
|
306
352
|
deprecated: getDeprecatedStatus(enumParsedJsDoc) || undefined,
|
@@ -342,7 +388,7 @@ const processPublicAPI = object => {
|
|
342
388
|
if ((key === "privacy" && object[key] !== "public") || (key === "_ui5privacy" && object[key] !== "public")) {
|
343
389
|
return true;
|
344
390
|
} else if (typeof object[key] === "object") {
|
345
|
-
if (key === "cssParts" || key === "_ui5implements") {
|
391
|
+
if (key === "cssParts" || key === "attributes" || key === "_ui5implements") {
|
346
392
|
continue;
|
347
393
|
}
|
348
394
|
|
@@ -413,21 +459,18 @@ export default {
|
|
413
459
|
}
|
414
460
|
},
|
415
461
|
moduleLinkPhase({ moduleDoc }) {
|
416
|
-
|
417
|
-
const shouldRemove = processPublicAPI(moduleDoc.declarations[i]) || ["function", "variable"].includes(moduleDoc.declarations[i].kind)
|
418
|
-
if (shouldRemove) {
|
419
|
-
moduleDoc.declarations.splice(i, 1);
|
420
|
-
i--;
|
421
|
-
}
|
422
|
-
}
|
462
|
+
moduleDoc.path = moduleDoc.path?.replace(/^src/, "dist").replace(/\.ts$/, ".js");
|
423
463
|
|
424
|
-
|
425
|
-
|
426
|
-
}
|
464
|
+
moduleDoc.exports = moduleDoc.exports.
|
465
|
+
filter(e => !(e.kind === "custom-element-definition" && !moduleDoc.declarations?.find(d => d.name === e.name)?.tagName))
|
427
466
|
|
428
|
-
moduleDoc.exports
|
467
|
+
moduleDoc.exports.forEach(e => {
|
429
468
|
const classNode = moduleDoc.declarations.find(c => c.name === e.declaration.name);
|
430
469
|
|
470
|
+
if (e.declaration && e.declaration.module) {
|
471
|
+
e.declaration.module = e.declaration.module.replace(/^src/, "dist").replace(/\.ts$/, ".js");
|
472
|
+
}
|
473
|
+
|
431
474
|
if (classNode?.customElement && classNode.tagName && e.kind !== "custom-element-definition") {
|
432
475
|
moduleDoc.exports.push({
|
433
476
|
kind: "custom-element-definition",
|
@@ -441,23 +484,54 @@ export default {
|
|
441
484
|
})
|
442
485
|
},
|
443
486
|
packageLinkPhase({ customElementsManifest }) {
|
444
|
-
|
445
|
-
|
446
|
-
|
447
|
-
|
448
|
-
|
449
|
-
|
450
|
-
|
451
|
-
|
452
|
-
|
453
|
-
|
454
|
-
|
455
|
-
|
456
|
-
|
457
|
-
|
487
|
+
customElementsManifest.modules.forEach(moduleDoc => {
|
488
|
+
for (let i = 0; i < moduleDoc.declarations.length; i++) {
|
489
|
+
const shouldRemove = processPublicAPI(moduleDoc.declarations[i]) || ["function", "variable"].includes(moduleDoc.declarations[i].kind)
|
490
|
+
if (shouldRemove) {
|
491
|
+
moduleDoc.declarations.splice(i, 1);
|
492
|
+
i--;
|
493
|
+
}
|
494
|
+
}
|
495
|
+
|
496
|
+
const typeReferences = new Set();
|
497
|
+
const registerTypeReference = reference => typeReferences.add(JSON.stringify(reference))
|
498
|
+
|
499
|
+
moduleDoc.declarations.forEach(declaration => {
|
500
|
+
["events", "slots", "members"].forEach(memberType => {
|
501
|
+
declaration[memberType]?.forEach(member => {
|
502
|
+
if (member.type?.references) {
|
503
|
+
member.type.references.forEach(registerTypeReference)
|
504
|
+
} else if (member._ui5type?.references) {
|
505
|
+
member._ui5type.references.forEach(registerTypeReference)
|
506
|
+
} else if (member.kind === "method") {
|
507
|
+
member.return?.type?.references?.forEach(registerTypeReference)
|
508
|
+
|
509
|
+
member.parameters?.forEach(parameter => {
|
510
|
+
parameter.type?.references?.forEach(registerTypeReference)
|
511
|
+
})
|
512
|
+
}
|
513
|
+
})
|
514
|
+
})
|
458
515
|
});
|
459
|
-
|
516
|
+
|
517
|
+
typeReferences.forEach(reference => {
|
518
|
+
reference = JSON.parse(reference);
|
519
|
+
if (reference.package === packageJSON?.name && reference.module === moduleDoc.path) {
|
520
|
+
const hasExport = moduleDoc.exports.some(e => e.declaration?.name === reference.name && e.declaration?.module === reference.module)
|
521
|
+
|
522
|
+
if (!hasExport) {
|
523
|
+
logDocumentationError(moduleDoc.path?.replace(/^dist/, "src").replace(/\.js$/, ".ts"), `Type '${reference.name}' is used to describe a public API but is not exported.`,)
|
524
|
+
}
|
525
|
+
}
|
526
|
+
})
|
527
|
+
});
|
528
|
+
|
529
|
+
if (devMode) {
|
530
|
+
displayDocumentationErrors();
|
531
|
+
}
|
460
532
|
}
|
461
533
|
},
|
534
|
+
generateCustomData({ outdir: "dist", cssFileName: null, cssPropertiesDocs: false }),
|
535
|
+
customElementJetBrainsPlugin({ outdir: "dist", cssFileName: null, cssPropertiesDocs: false })
|
462
536
|
],
|
463
537
|
};
|
package/lib/cem/event.mjs
CHANGED
@@ -4,29 +4,22 @@ import {
|
|
4
4
|
getDeprecatedStatus,
|
5
5
|
getSinceStatus,
|
6
6
|
getType,
|
7
|
+
getTypeRefs,
|
7
8
|
validateJSDocComment,
|
8
9
|
hasTag,
|
9
10
|
findTag,
|
10
11
|
findAllTags,
|
11
12
|
getReference,
|
12
13
|
normalizeDescription,
|
13
|
-
normalizeTagType
|
14
|
+
normalizeTagType,
|
15
|
+
logDocumentationError
|
14
16
|
} from "./utils.mjs";
|
15
17
|
|
16
18
|
const jsDocRegExp = /\/\*\*(.|\n)+?\s+\*\//;
|
19
|
+
const ASTFalseKeywordCode = 94;
|
17
20
|
|
18
21
|
const getParams = (ts, eventDetails, commentParams, classNode, moduleDoc) => {
|
19
22
|
return commentParams?.map(commentParam => {
|
20
|
-
const decoratorParam = eventDetails?.find(prop => prop?.name?.text === commentParam?.name);
|
21
|
-
|
22
|
-
if (!decoratorParam || !decoratorParam?.jsDoc?.[0]) {
|
23
|
-
return;
|
24
|
-
}
|
25
|
-
|
26
|
-
const decoratorParamParsedComment = parse(decoratorParam?.jsDoc?.[0]?.getText?.(), { spacing: 'preserve' })[0];
|
27
|
-
|
28
|
-
validateJSDocComment("eventParam", decoratorParamParsedComment, decoratorParam.name?.text, moduleDoc);
|
29
|
-
|
30
23
|
const { typeName, name } = getType(normalizeTagType(commentParam?.type));
|
31
24
|
let type;
|
32
25
|
|
@@ -45,17 +38,16 @@ const getParams = (ts, eventDetails, commentParams, classNode, moduleDoc) => {
|
|
45
38
|
return {
|
46
39
|
type,
|
47
40
|
name: commentParam?.name,
|
48
|
-
_ui5privacy:
|
41
|
+
_ui5privacy: "public",
|
49
42
|
description: normalizeDescription(commentParam?.description),
|
50
|
-
_ui5since: getSinceStatus(decoratorParamParsedComment),
|
51
|
-
deprecated: getDeprecatedStatus(decoratorParamParsedComment),
|
52
43
|
};
|
53
|
-
})
|
44
|
+
});
|
54
45
|
};
|
55
46
|
|
56
47
|
function processEvent(ts, event, classNode, moduleDoc) {
|
48
|
+
const name = event?.expression?.arguments?.[0]?.text;
|
57
49
|
const result = {
|
58
|
-
name
|
50
|
+
name,
|
59
51
|
_ui5privacy: "private",
|
60
52
|
type: { text: "CustomEvent" }
|
61
53
|
};
|
@@ -68,19 +60,35 @@ function processEvent(ts, event, classNode, moduleDoc) {
|
|
68
60
|
|
69
61
|
const eventParsedComment = parse(comment, { spacing: 'preserve' })[0];
|
70
62
|
|
71
|
-
validateJSDocComment("event", eventParsedComment,
|
63
|
+
validateJSDocComment("event", eventParsedComment, name, moduleDoc);
|
72
64
|
|
73
65
|
const deprecatedTag = findTag(eventParsedComment, "deprecated");
|
74
66
|
const privacy = findTag(eventParsedComment, ["public", "private", "protected"])?.tag || "private";
|
75
67
|
const sinceTag = findTag(eventParsedComment, "since");
|
76
68
|
const commentParams = findAllTags(eventParsedComment, "param");
|
77
|
-
const allowPreventDefault = hasTag(eventParsedComment, "allowPreventDefault") || undefined;
|
78
69
|
const description = normalizeDescription(eventParsedComment?.description);
|
79
70
|
const native = hasTag(eventParsedComment, "native");
|
80
|
-
const
|
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
|
+
});
|
81
87
|
|
82
88
|
result.description = description;
|
83
|
-
result.
|
89
|
+
result._ui5Cancelable = eventCancelable !== undefined ? eventCancelable : false;
|
90
|
+
result._ui5allowPreventDefault = result._ui5Cancelable;
|
91
|
+
result._ui5Bubbles = eventBubbles !== undefined ? eventBubbles : false;
|
84
92
|
|
85
93
|
if (native) {
|
86
94
|
result.type = { text: "Event" };
|
@@ -104,7 +112,53 @@ function processEvent(ts, event, classNode, moduleDoc) {
|
|
104
112
|
: sinceTag.name;
|
105
113
|
}
|
106
114
|
|
107
|
-
|
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
|
+
|
108
162
|
result._ui5parameters = getParams(ts, eventDetails, commentParams, classNode, moduleDoc);
|
109
163
|
}
|
110
164
|
|