@ui5/webcomponents-tools 0.0.0-0a35d6b3d → 0.0.0-0dd36ca4b
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 +514 -0
- package/assets-meta.js +2 -6
- package/components-package/nps.js +12 -18
- package/components-package/wdio.js +404 -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 +83 -45
- package/lib/cem/event.mjs +20 -3
- package/lib/cem/schema-internal.json +3 -0
- package/lib/cem/types-internal.d.ts +550 -786
- package/lib/cem/types.d.ts +520 -655
- package/lib/cem/utils.mjs +49 -20
- package/lib/cem/validate.js +20 -13
- package/lib/create-icons/index.js +8 -6
- package/lib/create-illustrations/index.js +40 -33
- package/lib/create-new-component/index.js +4 -11
- package/lib/create-new-component/tsFileContentTemplate.js +3 -12
- package/lib/css-processors/css-processor-component-styles.mjs +47 -0
- package/lib/css-processors/scope-variables.mjs +3 -0
- package/lib/dev-server/ssr-dom-shim-loader.js +26 -0
- package/lib/generate-js-imports/illustrations.js +9 -9
- package/lib/generate-json-imports/i18n.js +3 -35
- package/lib/generate-json-imports/themes.js +2 -29
- package/lib/i18n/defaults.js +1 -1
- package/lib/scoping/lint-src.js +8 -7
- package/package.json +3 -2
- 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();
|
@@ -13,13 +13,19 @@ 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,
|
23
|
+
toKebabCase
|
22
24
|
} from "./utils.mjs";
|
25
|
+
import { generateCustomData } from "cem-plugin-vs-code-custom-data-generator";
|
26
|
+
import { customElementJetBrainsPlugin } from "custom-element-jet-brains-integration";
|
27
|
+
|
28
|
+
const packageJSON = JSON.parse(fs.readFileSync("./package.json"));
|
23
29
|
|
24
30
|
const extractClassNodeJSDoc = node => {
|
25
31
|
const fileContent = node.getFullText();
|
@@ -163,6 +169,24 @@ function processClass(ts, classNode, moduleDoc) {
|
|
163
169
|
|
164
170
|
if (propertyDecorator) {
|
165
171
|
member._ui5validator = propertyDecorator?.expression?.arguments[0]?.properties?.find(property => ["validator", "type"].includes(property.name.text))?.initializer?.text || "String";
|
172
|
+
member._ui5noAttribute = propertyDecorator?.expression?.arguments[0]?.properties?.find(property => property.name.text === "noAttribute")?.initializer?.kind === ts.SyntaxKind.TrueKeyword || undefined;
|
173
|
+
}
|
174
|
+
|
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);
|
179
|
+
const tsProgramMember = tsProgramClassNode.members.find(m => ts.isPropertyDeclaration(m) && m.name?.text === member.name);
|
180
|
+
const attributeValue = typeChecker.typeToString(typeChecker.getTypeAtLocation(tsProgramMember), tsProgramMember);
|
181
|
+
|
182
|
+
currClass.attributes.push({
|
183
|
+
description: member.description,
|
184
|
+
name: toKebabCase(member.name),
|
185
|
+
default: member.default,
|
186
|
+
fieldName: member.name,
|
187
|
+
type: { text: attributeValue },
|
188
|
+
deprecated: member.deprecated
|
189
|
+
})
|
166
190
|
}
|
167
191
|
|
168
192
|
if (hasTag(memberParsedJsDoc, "formProperty")) {
|
@@ -181,24 +205,14 @@ function processClass(ts, classNode, moduleDoc) {
|
|
181
205
|
member.default = tagValue;
|
182
206
|
}
|
183
207
|
|
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
|
-
}
|
208
|
+
if (member.privacy === "public" && !member.default) {
|
209
|
+
logDocumentationError(moduleDoc.path, `Missing default value for '${member.name}'.`)
|
192
210
|
}
|
193
211
|
|
194
212
|
// Getters are treated as fields so they should not have return, instead of return they should have default value defined with @default
|
195
213
|
if (member.readonly) {
|
196
214
|
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
|
-
);
|
215
|
+
logDocumentationError(moduleDoc.path, `Missing return type for read-only field '${member.name}'.`)
|
202
216
|
}
|
203
217
|
|
204
218
|
delete member.return;
|
@@ -247,21 +261,19 @@ function processClass(ts, classNode, moduleDoc) {
|
|
247
261
|
if (member.return) {
|
248
262
|
const returnTag = findTag(memberParsedJsDoc, "returns");
|
249
263
|
member.return.description = returnTag?.description ? `${returnTag.name} ${returnTag.description}` : returnTag?.name;
|
250
|
-
member.return.type.text = classNodeMember?.type?.getFullText?.()?.trim();
|
264
|
+
member.return.type.text = formatArrays(classNodeMember?.type?.getFullText?.()?.trim());
|
251
265
|
const typeRefs = (getTypeRefs(ts, classNodeMember, member.return)
|
252
266
|
?.map(typeRef => getReference(ts, typeRef, classNodeMember, moduleDoc.path)).filter(Boolean)) || [];
|
253
267
|
|
254
268
|
if (typeRefs.length) {
|
255
269
|
member.return.type.references = typeRefs;
|
256
270
|
}
|
257
|
-
}
|
258
|
-
|
259
|
-
if (member.privacy === "public" && !member.return) {
|
260
|
-
const JSDocErrors = getJSDocErrors();
|
261
271
|
|
262
|
-
|
263
|
-
|
264
|
-
|
272
|
+
if (member.privacy === "public" && !member.return.type.text) {
|
273
|
+
logDocumentationError(moduleDoc.path, `Missing return type for function '${member.name}'.`)
|
274
|
+
}
|
275
|
+
} else if (member.privacy === "public") {
|
276
|
+
logDocumentationError(moduleDoc.path, `Missing return type for function '${member.name}'.`)
|
265
277
|
}
|
266
278
|
}
|
267
279
|
}
|
@@ -342,7 +354,7 @@ const processPublicAPI = object => {
|
|
342
354
|
if ((key === "privacy" && object[key] !== "public") || (key === "_ui5privacy" && object[key] !== "public")) {
|
343
355
|
return true;
|
344
356
|
} else if (typeof object[key] === "object") {
|
345
|
-
if (key === "cssParts" || key === "_ui5implements") {
|
357
|
+
if (key === "cssParts" || key === "attributes" || key === "_ui5implements") {
|
346
358
|
continue;
|
347
359
|
}
|
348
360
|
|
@@ -421,13 +433,18 @@ export default {
|
|
421
433
|
}
|
422
434
|
}
|
423
435
|
|
424
|
-
|
425
|
-
|
426
|
-
|
436
|
+
moduleDoc.path = moduleDoc.path?.replace(/^src/, "dist").replace(/\.ts$/, ".js");
|
437
|
+
|
438
|
+
moduleDoc.exports = moduleDoc.exports.
|
439
|
+
filter(e => !(e.kind === "custom-element-definition" && !moduleDoc.declarations?.find(d => d.name === e.name)?.tagName))
|
427
440
|
|
428
|
-
moduleDoc.exports
|
441
|
+
moduleDoc.exports.forEach(e => {
|
429
442
|
const classNode = moduleDoc.declarations.find(c => c.name === e.declaration.name);
|
430
443
|
|
444
|
+
if (e.declaration && e.declaration.module) {
|
445
|
+
e.declaration.module = e.declaration.module.replace(/^src/, "dist").replace(/\.ts$/, ".js");
|
446
|
+
}
|
447
|
+
|
431
448
|
if (classNode?.customElement && classNode.tagName && e.kind !== "custom-element-definition") {
|
432
449
|
moduleDoc.exports.push({
|
433
450
|
kind: "custom-element-definition",
|
@@ -439,25 +456,46 @@ export default {
|
|
439
456
|
})
|
440
457
|
}
|
441
458
|
})
|
442
|
-
|
443
|
-
|
444
|
-
|
445
|
-
|
446
|
-
|
447
|
-
|
448
|
-
|
449
|
-
|
450
|
-
|
451
|
-
|
452
|
-
|
453
|
-
|
454
|
-
|
455
|
-
|
456
|
-
|
457
|
-
|
458
|
-
|
459
|
+
|
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
|
+
}
|
477
|
+
})
|
478
|
+
})
|
479
|
+
});
|
480
|
+
|
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)
|
485
|
+
|
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.`,)
|
488
|
+
}
|
489
|
+
}
|
459
490
|
})
|
491
|
+
},
|
492
|
+
packageLinkPhase({ context }) {
|
493
|
+
if (context.dev) {
|
494
|
+
displayDocumentationErrors();
|
495
|
+
}
|
460
496
|
}
|
461
497
|
},
|
498
|
+
generateCustomData({ outdir: "dist", cssFileName: null, cssPropertiesDocs: false }),
|
499
|
+
customElementJetBrainsPlugin({ outdir: "dist", cssFileName: null, cssPropertiesDocs: false })
|
462
500
|
],
|
463
501
|
};
|
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) {
|