@ui5/webcomponents-tools 1.22.0-rc.1 → 1.22.0-rc.3
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 +16 -0
- package/components-package/nps.js +4 -4
- package/lib/amd-to-es6/index.js +104 -0
- package/lib/cem/custom-elements-manifest.config.mjs +63 -44
- package/lib/cem/event.mjs +20 -3
- package/lib/cem/schema-internal.json +3 -0
- package/lib/cem/types-internal.d.ts +555 -785
- package/lib/cem/types.d.ts +520 -655
- package/lib/cem/utils.mjs +44 -20
- package/lib/cem/validate.js +15 -13
- package/lib/generate-custom-elements-manifest/index.js +51 -107
- package/package.json +2 -2
- package/lib/esm-abs-to-rel/index.js +0 -61
- package/lib/replace-global-core/index.js +0 -25
package/CHANGELOG.md
CHANGED
@@ -3,6 +3,22 @@
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
5
5
|
|
6
|
+
# [1.22.0-rc.3](https://github.com/SAP/ui5-webcomponents/compare/v1.22.0-rc.2...v1.22.0-rc.3) (2024-02-01)
|
7
|
+
|
8
|
+
**Note:** Version bump only for package @ui5/webcomponents-tools
|
9
|
+
|
10
|
+
|
11
|
+
|
12
|
+
|
13
|
+
|
14
|
+
# [1.22.0-rc.2](https://github.com/SAP/ui5-webcomponents/compare/v1.22.0-rc.1...v1.22.0-rc.2) (2024-01-25)
|
15
|
+
|
16
|
+
**Note:** Version bump only for package @ui5/webcomponents-tools
|
17
|
+
|
18
|
+
|
19
|
+
|
20
|
+
|
21
|
+
|
6
22
|
# [1.22.0-rc.1](https://github.com/SAP/ui5-webcomponents/compare/v1.22.0-rc.0...v1.22.0-rc.1) (2024-01-18)
|
7
23
|
|
8
24
|
|
@@ -135,13 +135,13 @@ const getScripts = (options) => {
|
|
135
135
|
bundle: `node ${LIB}/dev-server/dev-server.js ${viteConfig}`,
|
136
136
|
},
|
137
137
|
generateAPI: {
|
138
|
-
default: `nps ${ tsOption ? "generateAPI.generateCEM generateAPI.validateCEM" : "generateAPI.prepare generateAPI.preprocess generateAPI.jsdoc generateAPI.cleanup generateAPI.prepareManifest"}`,
|
139
|
-
generateCEM: `cem analyze --config
|
140
|
-
validateCEM: `node "${LIB}/cem/validate.js"`,
|
138
|
+
default: `nps ${ tsOption ? "generateAPI.generateCEM generateAPI.validateCEM" : "generateAPI.prepare generateAPI.preprocess generateAPI.jsdoc generateAPI.cleanup generateAPI.prepareManifest generateAPI.validateCEM"}`,
|
139
|
+
generateCEM: `cem analyze --config "${LIB}/cem/custom-elements-manifest.config.mjs" ${ options.dev ? "--dev" : "" }`,
|
140
|
+
validateCEM: `node "${LIB}/cem/validate.js" ${ options.dev ? "--dev" : "" }`,
|
141
141
|
prepare: `node "${LIB}/copy-and-watch/index.js" --silent "dist/**/*.js" jsdoc-dist/`,
|
142
142
|
prepareManifest: `node "${LIB}/generate-custom-elements-manifest/index.js" dist dist`,
|
143
143
|
preprocess: `node "${preprocessJSDocScript}" jsdoc-dist/ src`,
|
144
|
-
jsdoc: `jsdoc -c "${LIB}/jsdoc/
|
144
|
+
jsdoc: `jsdoc -c "${LIB}/jsdoc/config.json"`,
|
145
145
|
cleanup: "rimraf jsdoc-dist/"
|
146
146
|
},
|
147
147
|
};
|
@@ -0,0 +1,104 @@
|
|
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
|
+
code = code.replace(/sap\.ui\.require/g, "require");
|
20
|
+
|
21
|
+
return (await babelCore.transformAsync(code, {
|
22
|
+
plugins: [['babel-plugin-amd-to-esm', {}]]
|
23
|
+
})).code;
|
24
|
+
}
|
25
|
+
|
26
|
+
const convertAbsImportsToRelative = (filePath, code) => {
|
27
|
+
let changed = false;
|
28
|
+
// console.log("File processing started: ", srcPath);
|
29
|
+
|
30
|
+
if (code.includes("import(")) {
|
31
|
+
// esprima can't parse this, but it's from the project files
|
32
|
+
return;
|
33
|
+
}
|
34
|
+
|
35
|
+
const tree = babelParser.parse(code, { sourceType: "module" });
|
36
|
+
const importer = filePath.replace(basePath, "");
|
37
|
+
const importerDir = path.dirname(importer);
|
38
|
+
// console.log("Importer -> ", importer);
|
39
|
+
|
40
|
+
tree?.program?.body?.forEach(node => {
|
41
|
+
if (node.type === "ImportDeclaration") {
|
42
|
+
let importee = node.source.value;
|
43
|
+
// console.log(importee);
|
44
|
+
if (importee.startsWith(".")) {
|
45
|
+
// add .js extension if missing
|
46
|
+
if (!importee.endsWith(".js")) {
|
47
|
+
node.source.value += ".js"
|
48
|
+
changed = true;
|
49
|
+
}
|
50
|
+
return;
|
51
|
+
}
|
52
|
+
let importeeDir = path.dirname(importee);
|
53
|
+
let importeeFile = path.basename(importee);
|
54
|
+
let relativePath = path.relative(importerDir, importeeDir);
|
55
|
+
if (relativePath.length === 0) {
|
56
|
+
relativePath = "."
|
57
|
+
}
|
58
|
+
if (!relativePath.startsWith(".")) {
|
59
|
+
relativePath = "./" + relativePath;
|
60
|
+
}
|
61
|
+
|
62
|
+
relativePath = relativePath.replace(/\\/g, "/"); // the browser expects unix paths
|
63
|
+
let relativeImport = `${relativePath}/${importeeFile}.js`;
|
64
|
+
// console.log(importee + " --> " + relativeImport);
|
65
|
+
node.source.value = relativeImport;
|
66
|
+
changed = true;
|
67
|
+
}
|
68
|
+
});
|
69
|
+
|
70
|
+
return changed ? babelGenerator(tree).code : code;
|
71
|
+
}
|
72
|
+
|
73
|
+
const replaceGlobalCoreUsage = (filePath, code) => {
|
74
|
+
if (!filePath.includes("Configuration")) {
|
75
|
+
const replaced = code.replace(/sap\.ui\.getCore\(\)/g, `Core`);
|
76
|
+
return code !== replaced ? `import Core from 'sap/ui/core/Core';${replaced}` : code;
|
77
|
+
}
|
78
|
+
|
79
|
+
return code;
|
80
|
+
};
|
81
|
+
|
82
|
+
const transformAmdToES6Module = async (filePath) => {
|
83
|
+
await convertSAPUIDefineToDefine(filePath);
|
84
|
+
|
85
|
+
let code = (await fs.readFile(filePath)).toString();
|
86
|
+
|
87
|
+
code = await convertAmdToEs6(code);
|
88
|
+
|
89
|
+
code = replaceGlobalCoreUsage(filePath, code);
|
90
|
+
|
91
|
+
code = convertAbsImportsToRelative(filePath, code);
|
92
|
+
|
93
|
+
return fs.writeFile(filePath, code);
|
94
|
+
}
|
95
|
+
|
96
|
+
const transformAmdToES6Modules = async () => {
|
97
|
+
const { globby } = await import("globby");
|
98
|
+
const fileNames = await globby(basePath.replace(/\\/g, "/") + "**/*.js");
|
99
|
+
return Promise.all(fileNames.map(transformAmdToES6Module).filter(x => !!x));
|
100
|
+
};
|
101
|
+
|
102
|
+
transformAmdToES6Modules().then(() => {
|
103
|
+
console.log("Success: all amd modules are transformed to es6!");
|
104
|
+
});
|
@@ -13,14 +13,17 @@ import {
|
|
13
13
|
hasTag,
|
14
14
|
findTag,
|
15
15
|
findAllTags,
|
16
|
-
getJSDocErrors,
|
17
16
|
getTypeRefs,
|
18
17
|
normalizeDescription,
|
19
18
|
formatArrays,
|
20
19
|
isClass,
|
21
|
-
normalizeTagType
|
20
|
+
normalizeTagType,
|
21
|
+
logDocumentationError,
|
22
|
+
displayDocumentationErrors
|
22
23
|
} from "./utils.mjs";
|
23
24
|
|
25
|
+
const packageJSON = JSON.parse(fs.readFileSync("./package.json"));
|
26
|
+
|
24
27
|
const extractClassNodeJSDoc = node => {
|
25
28
|
const fileContent = node.getFullText();
|
26
29
|
const allJSDocsRegExp = new RegExp(`\\/\\*\\*(.|\\n)+?\\s+\\*\\/`, "gm");
|
@@ -163,6 +166,7 @@ function processClass(ts, classNode, moduleDoc) {
|
|
163
166
|
|
164
167
|
if (propertyDecorator) {
|
165
168
|
member._ui5validator = propertyDecorator?.expression?.arguments[0]?.properties?.find(property => ["validator", "type"].includes(property.name.text))?.initializer?.text || "String";
|
169
|
+
member._ui5noAttribute = propertyDecorator?.expression?.arguments[0]?.properties?.find(property => property.name.text === "noAttribute")?.initializer?.kind === ts.SyntaxKind.TrueKeyword || undefined;
|
166
170
|
}
|
167
171
|
|
168
172
|
if (hasTag(memberParsedJsDoc, "formProperty")) {
|
@@ -181,24 +185,14 @@ function processClass(ts, classNode, moduleDoc) {
|
|
181
185
|
member.default = tagValue;
|
182
186
|
}
|
183
187
|
|
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
|
-
}
|
188
|
+
if (member.privacy === "public" && !member.default) {
|
189
|
+
logDocumentationError(moduleDoc.path, `Missing default value for '${member.name}'.`)
|
192
190
|
}
|
193
191
|
|
194
192
|
// Getters are treated as fields so they should not have return, instead of return they should have default value defined with @default
|
195
193
|
if (member.readonly) {
|
196
194
|
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
|
-
);
|
195
|
+
logDocumentationError(moduleDoc.path, `Missing return type for read-only field '${member.name}'.`)
|
202
196
|
}
|
203
197
|
|
204
198
|
delete member.return;
|
@@ -247,21 +241,19 @@ function processClass(ts, classNode, moduleDoc) {
|
|
247
241
|
if (member.return) {
|
248
242
|
const returnTag = findTag(memberParsedJsDoc, "returns");
|
249
243
|
member.return.description = returnTag?.description ? `${returnTag.name} ${returnTag.description}` : returnTag?.name;
|
250
|
-
member.return.type.text = classNodeMember?.type?.getFullText?.()?.trim();
|
244
|
+
member.return.type.text = formatArrays(classNodeMember?.type?.getFullText?.()?.trim());
|
251
245
|
const typeRefs = (getTypeRefs(ts, classNodeMember, member.return)
|
252
246
|
?.map(typeRef => getReference(ts, typeRef, classNodeMember, moduleDoc.path)).filter(Boolean)) || [];
|
253
247
|
|
254
248
|
if (typeRefs.length) {
|
255
249
|
member.return.type.references = typeRefs;
|
256
250
|
}
|
257
|
-
}
|
258
|
-
|
259
|
-
if (member.privacy === "public" && !member.return) {
|
260
|
-
const JSDocErrors = getJSDocErrors();
|
261
251
|
|
262
|
-
|
263
|
-
|
264
|
-
|
252
|
+
if (member.privacy === "public" && !member.return.type.text) {
|
253
|
+
logDocumentationError(moduleDoc.path, `Missing return type for function '${member.name}'.`)
|
254
|
+
}
|
255
|
+
} else if (member.privacy === "public") {
|
256
|
+
logDocumentationError(moduleDoc.path, `Missing return type for function '${member.name}'.`)
|
265
257
|
}
|
266
258
|
}
|
267
259
|
}
|
@@ -421,13 +413,18 @@ export default {
|
|
421
413
|
}
|
422
414
|
}
|
423
415
|
|
424
|
-
|
425
|
-
|
426
|
-
|
416
|
+
moduleDoc.path = moduleDoc.path?.replace(/^src/, "dist").replace(/\.ts$/, ".js");
|
417
|
+
|
418
|
+
moduleDoc.exports = moduleDoc.exports.
|
419
|
+
filter(e => !(e.kind === "custom-element-definition" && !moduleDoc.declarations?.find(d => d.name === e.name)?.tagName))
|
427
420
|
|
428
|
-
moduleDoc.exports
|
421
|
+
moduleDoc.exports.forEach(e => {
|
429
422
|
const classNode = moduleDoc.declarations.find(c => c.name === e.declaration.name);
|
430
423
|
|
424
|
+
if (e.declaration && e.declaration.module) {
|
425
|
+
e.declaration.module = e.declaration.module.replace(/^src/, "dist").replace(/\.ts$/, ".js");
|
426
|
+
}
|
427
|
+
|
431
428
|
if (classNode?.customElement && classNode.tagName && e.kind !== "custom-element-definition") {
|
432
429
|
moduleDoc.exports.push({
|
433
430
|
kind: "custom-element-definition",
|
@@ -439,24 +436,46 @@ export default {
|
|
439
436
|
})
|
440
437
|
}
|
441
438
|
})
|
442
|
-
|
443
|
-
|
444
|
-
|
445
|
-
|
446
|
-
|
447
|
-
|
448
|
-
|
449
|
-
|
450
|
-
|
451
|
-
|
452
|
-
|
453
|
-
|
454
|
-
|
455
|
-
|
456
|
-
|
457
|
-
|
458
|
-
|
439
|
+
|
440
|
+
const typeReferences = new Set();
|
441
|
+
const registerTypeReference = reference => typeReferences.add(JSON.stringify(reference))
|
442
|
+
|
443
|
+
moduleDoc.declarations.forEach(declaration => {
|
444
|
+
["events", "slots", "members"].forEach(memberType => {
|
445
|
+
declaration[memberType]?.forEach(member => {
|
446
|
+
if (member.type?.references) {
|
447
|
+
member.type.references.forEach(registerTypeReference)
|
448
|
+
} else if (member._ui5type?.references) {
|
449
|
+
member._ui5type.references.forEach(registerTypeReference)
|
450
|
+
} else if (member.kind === "method") {
|
451
|
+
member.return?.type?.references?.forEach(registerTypeReference)
|
452
|
+
|
453
|
+
member.parameters?.forEach(parameter => {
|
454
|
+
parameter.type?.references?.forEach(registerTypeReference)
|
455
|
+
})
|
456
|
+
}
|
457
|
+
})
|
458
|
+
})
|
459
|
+
});
|
460
|
+
|
461
|
+
typeReferences.forEach(reference => {
|
462
|
+
reference = JSON.parse(reference);
|
463
|
+
if (reference.package === packageJSON?.name && reference.module === moduleDoc.path) {
|
464
|
+
const hasExport = moduleDoc.exports.some(e => e.declaration?.name === reference.name && e.declaration?.module === reference.module)
|
465
|
+
|
466
|
+
if (!hasExport) {
|
467
|
+
logDocumentationError(moduleDoc.path?.replace(/^dist/, "src").replace(/\.js$/, ".ts"), `Type '${reference.name}' is used to describe a public API but is not exported.`,)
|
468
|
+
}
|
469
|
+
}
|
459
470
|
})
|
471
|
+
|
472
|
+
moduleDoc.exports = moduleDoc.exports.
|
473
|
+
filter(e => moduleDoc.declarations.find(d => d.name === e.declaration.name && ["class", "function", "variable", "enum"].includes(d.kind)) || e.name === "default");
|
474
|
+
},
|
475
|
+
packageLinkPhase({ context }) {
|
476
|
+
if (context.dev) {
|
477
|
+
displayDocumentationErrors();
|
478
|
+
}
|
460
479
|
}
|
461
480
|
},
|
462
481
|
],
|
package/lib/cem/event.mjs
CHANGED
@@ -4,13 +4,15 @@ 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+\*\//;
|
@@ -54,8 +56,9 @@ const getParams = (ts, eventDetails, commentParams, classNode, moduleDoc) => {
|
|
54
56
|
};
|
55
57
|
|
56
58
|
function processEvent(ts, event, classNode, moduleDoc) {
|
59
|
+
const name = event?.expression?.arguments?.[0]?.text;
|
57
60
|
const result = {
|
58
|
-
name
|
61
|
+
name,
|
59
62
|
_ui5privacy: "private",
|
60
63
|
type: { text: "CustomEvent" }
|
61
64
|
};
|
@@ -68,7 +71,7 @@ function processEvent(ts, event, classNode, moduleDoc) {
|
|
68
71
|
|
69
72
|
const eventParsedComment = parse(comment, { spacing: 'preserve' })[0];
|
70
73
|
|
71
|
-
validateJSDocComment("event", eventParsedComment,
|
74
|
+
validateJSDocComment("event", eventParsedComment, name, moduleDoc);
|
72
75
|
|
73
76
|
const deprecatedTag = findTag(eventParsedComment, "deprecated");
|
74
77
|
const privacy = findTag(eventParsedComment, ["public", "private", "protected"])?.tag || "private";
|
@@ -79,11 +82,25 @@ function processEvent(ts, event, classNode, moduleDoc) {
|
|
79
82
|
const native = hasTag(eventParsedComment, "native");
|
80
83
|
const eventDetails = event?.expression?.arguments?.[1]?.properties?.find(prop => prop?.name?.text === "detail")?.initializer?.properties;
|
81
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
|
+
}
|
88
|
+
|
82
89
|
result.description = description;
|
83
90
|
result._ui5allowPreventDefault = allowPreventDefault;
|
84
91
|
|
85
92
|
if (native) {
|
86
93
|
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
|
+
}
|
87
104
|
}
|
88
105
|
|
89
106
|
if (privacy) {
|