@ui5/webcomponents-tools 1.22.0-rc.1 → 1.22.0-rc.2

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 CHANGED
@@ -3,6 +3,14 @@
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.2](https://github.com/SAP/ui5-webcomponents/compare/v1.22.0-rc.1...v1.22.0-rc.2) (2024-01-25)
7
+
8
+ **Note:** Version bump only for package @ui5/webcomponents-tools
9
+
10
+
11
+
12
+
13
+
6
14
  # [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
15
 
8
16
 
@@ -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 "${LIB}/cem/custom-elements-manifest.config.mjs"`,
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/configTypescript.json"`,
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
+ });
@@ -21,6 +21,8 @@ import {
21
21
  normalizeTagType
22
22
  } from "./utils.mjs";
23
23
 
24
+ const packageJSON = JSON.parse(fs.readFileSync("./package.json"));
25
+
24
26
  const extractClassNodeJSDoc = node => {
25
27
  const fileContent = node.getFullText();
26
28
  const allJSDocsRegExp = new RegExp(`\\/\\*\\*(.|\\n)+?\\s+\\*\\/`, "gm");
@@ -421,13 +423,18 @@ export default {
421
423
  }
422
424
  }
423
425
 
424
- if (moduleDoc.exports) {
425
- moduleDoc.exports = moduleDoc.exports.filter(e => !(e.kind === "custom-element-definition" && !moduleDoc.declarations?.find(d => d.name === e.name)?.tagName))
426
- }
426
+ moduleDoc.path = moduleDoc.path?.replace(/^src/, "dist").replace(/\.ts$/, ".js");
427
427
 
428
- moduleDoc.exports?.forEach(e => {
428
+ moduleDoc.exports = moduleDoc.exports.
429
+ filter(e => !(e.kind === "custom-element-definition" && !moduleDoc.declarations?.find(d => d.name === e.name)?.tagName))
430
+
431
+ moduleDoc.exports.forEach(e => {
429
432
  const classNode = moduleDoc.declarations.find(c => c.name === e.declaration.name);
430
433
 
434
+ if (e.declaration && e.declaration.module) {
435
+ e.declaration.module = e.declaration.module.replace(/^src/, "dist").replace(/\.ts$/, ".js");
436
+ }
437
+
431
438
  if (classNode?.customElement && classNode.tagName && e.kind !== "custom-element-definition") {
432
439
  moduleDoc.exports.push({
433
440
  kind: "custom-element-definition",
@@ -439,24 +446,53 @@ export default {
439
446
  })
440
447
  }
441
448
  })
442
- },
443
- packageLinkPhase({ customElementsManifest }) {
444
- // Uncomment and handle errors appropriately
445
- // const JSDocErrors = getJSDocErrors();
446
- // if (JSDocErrors.length > 0) {
447
- // console.log(JSDocErrors.join("\n"));
448
- // console.log(`Invalid JSDoc. ${JSDocErrors.length} were found.`);
449
- // throw new Error(`Invalid JSDoc.`);
450
- // }
451
-
452
- customElementsManifest.modules?.forEach(m => {
453
- m.path = m.path?.replace(/^src/, "dist").replace(/\.ts$/, ".js");
454
-
455
- m.exports?.forEach(e => {
456
- if (e.declaration && e.declaration.module)
457
- e.declaration.module = e.declaration?.module?.replace(/^src/, "dist").replace(/\.ts$/, ".js");
458
- });
449
+
450
+ const typeReferences = new Set();
451
+ const registerTypeReference = reference => typeReferences.add(JSON.stringify(reference))
452
+
453
+ moduleDoc.declarations.forEach(declaration => {
454
+ ["events", "slots", "members"].forEach(memberType => {
455
+ declaration[memberType]?.forEach(member => {
456
+ if (member.type?.references) {
457
+ member.type.references.forEach(registerTypeReference)
458
+ } else if (member._ui5type?.references) {
459
+ member._ui5type.references.forEach(registerTypeReference)
460
+ } else if (member.kind === "method") {
461
+ member.return?.type?.references?.forEach(registerTypeReference)
462
+
463
+ member.parameters?.forEach(parameter => {
464
+ parameter.type?.references?.forEach(registerTypeReference)
465
+ })
466
+ }
467
+ })
468
+ })
469
+ });
470
+
471
+ typeReferences.forEach(reference => {
472
+ reference = JSON.parse(reference);
473
+ if (reference.package === packageJSON?.name && reference.module === moduleDoc.path) {
474
+ const hasExport = moduleDoc.exports.some(e => e.declaration?.name === reference.name && e.declaration?.module === reference.module)
475
+
476
+ if (!hasExport) {
477
+ const JSDocErrors = getJSDocErrors();
478
+ JSDocErrors.push(
479
+ `=== ERROR: Problem found with ${reference.name} type reference in ${moduleDoc.path?.replace(/^dist/, "src").replace(/\.js$/, ".ts")}: \n\t- ${reference.name} is used as type of public API, but it's not exported`)
480
+ }
481
+ }
459
482
  })
483
+
484
+ moduleDoc.exports = moduleDoc.exports.
485
+ filter(e => moduleDoc.declarations.find(d => d.name === e.declaration.name && ["class", "function", "variable", "enum"].includes(d.kind)) || e.name === "default");
486
+ },
487
+ packageLinkPhase({ context }) {
488
+ if (context.dev) {
489
+ const JSDocErrors = getJSDocErrors();
490
+ if (JSDocErrors.length > 0) {
491
+ console.log(JSDocErrors.join("\n"));
492
+ console.log(`Invalid JSDoc. ${JSDocErrors.length} were found.`);
493
+ throw new Error(`Invalid JSDoc.`);
494
+ }
495
+ }
460
496
  }
461
497
  },
462
498
  ],
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
+ getJSDocErrors
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: event?.expression?.arguments?.[0]?.text,
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, event?.expression?.arguments?.[0]?.text, moduleDoc);
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,29 @@ 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
+ const JSDocErrors = getJSDocErrors();
87
+
88
+ JSDocErrors.push(
89
+ `=== ERROR: Problem found with ${name}'s description in ${moduleDoc.path}: \n\t- Event details have to be described with type via generics type passed to the decorator ( @event<TypeForDetails>("example-name", {details}) ) `
90
+ );
91
+ }
92
+
82
93
  result.description = description;
83
94
  result._ui5allowPreventDefault = allowPreventDefault;
84
95
 
85
96
  if (native) {
86
97
  result.type = { text: "Event" };
98
+ } else if (event?.expression?.typeArguments) {
99
+ const typesText = event?.expression?.typeArguments.map(type => type.typeName?.text).filter(Boolean).join(" | ");
100
+ const typeRefs = (getTypeRefs(ts, event.expression)
101
+ ?.map(e => getReference(ts, e, event, moduleDoc.path)).filter(Boolean)) || [];
102
+
103
+ result.type = { text: `CustomEvent<${typesText}>` };
104
+
105
+ if (typeRefs.length) {
106
+ result.type.references = typeRefs;
107
+ }
87
108
  }
88
109
 
89
110
  if (privacy) {