@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/lib/cem/utils.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  import fs from "fs";
2
2
  import path from "path";
3
3
 
4
- let JSDocErrors = [];
4
+ let documentationErrors = new Map();
5
5
 
6
6
  const getDeprecatedStatus = (jsdocComment) => {
7
7
  const deprecatedTag = findTag(jsdocComment, "deprecated");
@@ -18,18 +18,20 @@ const normalizeDescription = (description) => {
18
18
  return typeof description === 'string' ? description.replaceAll(/^-\s+|^(\n)+|(\n)+$/g, ""): description;
19
19
  }
20
20
 
21
- const getTypeRefs = (ts, classNodeMember, member) => {
21
+ const getTypeRefs = (ts, node, member) => {
22
22
  const extractTypeRefs = (type) => {
23
23
  if (type?.kind === ts.SyntaxKind.TypeReference) {
24
24
  return type.typeArguments?.length
25
25
  ? type.typeArguments.map((typeRef) => typeRef.typeName?.text)
26
26
  : [type.typeName?.text];
27
+ } else if (type?.kind === ts.SyntaxKind.ArrayType) {
28
+ return [type.elementType?.typeName?.text];
27
29
  } else if (type?.kind === ts.SyntaxKind.UnionType) {
28
30
  return type.types
29
31
  .map((type) => extractTypeRefs(type))
30
32
  .flat(1);
31
33
  } else if (type?.kind === ts.SyntaxKind.TemplateLiteralType) {
32
- if (member.type) {
34
+ if (member?.type) {
33
35
  member.type.text = member.type.text.replaceAll?.(/`|\${|}/g, "");
34
36
  }
35
37
 
@@ -39,7 +41,7 @@ const getTypeRefs = (ts, classNodeMember, member) => {
39
41
  }
40
42
  };
41
43
 
42
- let typeRefs = extractTypeRefs(classNodeMember.type);
44
+ let typeRefs = extractTypeRefs(node.type) || node?.typeArguments?.map(n => extractTypeRefs(n)).flat(2);
43
45
 
44
46
  if (typeRefs) {
45
47
  typeRefs = typeRefs.filter((e) => !!e);
@@ -62,7 +64,7 @@ const getPrivacyStatus = (jsdocComment) => {
62
64
  return privacyTag?.tag || "private";
63
65
  };
64
66
 
65
- const findPackageName = (ts, sourceFile, typeName, packageJSON) => {
67
+ const findPackageName = (ts, sourceFile, typeName) => {
66
68
  const localStatements = [
67
69
  ts.SyntaxKind.EnumDeclaration,
68
70
  ts.SyntaxKind.InterfaceDeclaration,
@@ -102,7 +104,7 @@ const findPackageName = (ts, sourceFile, typeName, packageJSON) => {
102
104
  }
103
105
  };
104
106
 
105
- const findImportPath = (ts, sourceFile, typeName, packageJSON, modulePath) => {
107
+ const findImportPath = (ts, sourceFile, typeName, modulePath) => {
106
108
  const localStatements = [
107
109
  ts.SyntaxKind.EnumDeclaration,
108
110
  ts.SyntaxKind.InterfaceDeclaration,
@@ -158,6 +160,8 @@ const normalizeTagType = (type) => {
158
160
  return type?.trim();
159
161
  }
160
162
 
163
+ const packageJSON = JSON.parse(fs.readFileSync("./package.json"));
164
+
161
165
  const getReference = (ts, type, classNode, modulePath) => {
162
166
  let sourceFile = classNode.parent;
163
167
 
@@ -165,22 +169,19 @@ const getReference = (ts, type, classNode, modulePath) => {
165
169
  sourceFile = sourceFile.parent;
166
170
  }
167
171
 
168
- const packageJSON = JSON.parse(fs.readFileSync("./package.json"));
169
-
170
172
  const typeName =
171
173
  typeof type === "string"
172
174
  ? normalizeTagType(type)
173
175
  : type.class?.expression?.text ||
174
176
  type.typeExpression?.type?.getText() ||
175
177
  type.typeExpression?.type?.elementType?.typeName?.text;
176
- const packageName = findPackageName(ts, sourceFile, typeName, packageJSON);
178
+ const packageName = findPackageName(ts, sourceFile, typeName);
177
179
  const importPath = findImportPath(
178
180
  ts,
179
181
  sourceFile,
180
182
  typeName,
181
- packageJSON,
182
183
  modulePath
183
- );
184
+ )?.replace(`${packageName}/`, "");
184
185
 
185
186
  return packageName && {
186
187
  name: typeName,
@@ -317,21 +318,43 @@ const validateJSDocComment = (fieldType, jsdocComment, node, moduleDoc) => {
317
318
  }
318
319
 
319
320
  if (!isValid) {
320
- JSDocErrors.push(
321
- `=== ERROR: Problem found with ${node}'s JSDoc comment in ${moduleDoc.path}: \n\t- @${tag.tag} tag is being used wrong or it's not part of ${fieldType} JSDoc tags`
322
- );
321
+ logDocumentationError(moduleDoc.path, `Incorrect use of @${tag.tag}. Ensure it is part of ${fieldType} JSDoc tags.`)
323
322
  }
324
323
 
325
324
  return !!isValid;
326
325
  });
327
326
  };
328
327
 
329
- const getJSDocErrors = () => {
330
- return JSDocErrors;
331
- };
328
+ const logDocumentationError = (modulePath, message) => {
329
+ let moduleErrors = documentationErrors.get(modulePath);
330
+
331
+ if (!moduleErrors) {
332
+ documentationErrors.set(modulePath, []);
333
+ moduleErrors = documentationErrors.get(modulePath);
334
+ }
335
+
336
+ moduleErrors.push(message);
337
+ }
338
+
339
+ const displayDocumentationErrors = () => {
340
+ let errorsCount = 0;
341
+ [...documentationErrors.keys()].forEach(modulePath => {
342
+ const moduleErrors = documentationErrors.get(modulePath);
343
+
344
+ console.log(`=== ERROR: ${moduleErrors.length > 1 ? `${moduleErrors.length} problems` : "Problem"} found in file: ${modulePath}:`)
345
+ moduleErrors.forEach(moduleError => {
346
+ errorsCount++;
347
+ console.log(`\t- ${moduleError}`)
348
+ })
349
+ })
350
+
351
+ if(errorsCount) {
352
+ throw new Error(`Found ${errorsCount} errors in the description of the public API.`);
353
+ }
354
+ }
332
355
 
333
356
  const formatArrays = (typeText) => {
334
- return typeText.replaceAll(/(\S+)\[\]/g, "Array<$1>")
357
+ return typeText?.replaceAll(/(\S+)\[\]/g, "Array<$1>")
335
358
  }
336
359
 
337
360
  export {
@@ -346,10 +369,11 @@ export {
346
369
  hasTag,
347
370
  findTag,
348
371
  findAllTags,
349
- getJSDocErrors,
350
372
  getTypeRefs,
351
373
  normalizeDescription,
352
374
  formatArrays,
353
375
  isClass,
354
- normalizeTagType
376
+ normalizeTagType,
377
+ displayDocumentationErrors,
378
+ logDocumentationError,
355
379
  };
@@ -1,6 +1,10 @@
1
1
  const fs = require('fs');
2
2
  const Ajv = require('ajv');
3
3
  const path = require('path');
4
+ const yargs = require('yargs/yargs')
5
+ const { hideBin } = require('yargs/helpers')
6
+ const argv = yargs(hideBin(process.argv))
7
+ .argv;
4
8
 
5
9
  // Load your JSON schema
6
10
  const extenalSchema = require('./schema.json');
@@ -36,28 +40,26 @@ const clearProps = (data) => {
36
40
  return data;
37
41
  }
38
42
 
39
- const inputDataExternal = clearProps(JSON.parse(JSON.stringify(inputDataInternal)));
40
-
41
43
  const ajv = new Ajv({ allowUnionTypes: true, allError: true })
42
-
43
44
  let validate = ajv.compile(internalSchema)
44
45
 
45
46
  // Validate the JSON data against the schema
46
- if (validate(inputDataInternal)) {
47
- console.log('Validation internal custom-elements successful');
48
- } else {
49
- console.error('Validation of internal custom-elements failed');
50
- // console.error('Validation of internal custom-elements failed:', validate.errors);
47
+ if (argv.dev) {
48
+ if (validate(inputDataInternal)) {
49
+ console.log('Internal custom element manifest is validated successfully');
50
+ } else {
51
+ throw new Error(`Validation of internal custom elements manifest failed: ${validate.errors}`);
52
+ }
51
53
  }
52
54
 
55
+ const inputDataExternal = clearProps(JSON.parse(JSON.stringify(inputDataInternal)));
53
56
  validate = ajv.compile(extenalSchema)
54
57
 
55
58
  // Validate the JSON data against the schema
56
59
  if (validate(inputDataExternal)) {
57
- console.log('Validation external custom-elements successful');
60
+ console.log('Custom element manifest is validated successfully');
58
61
  fs.writeFileSync(inputFilePath, JSON.stringify(inputDataExternal, null, 2), 'utf8');
59
62
  fs.writeFileSync(inputFilePath.replace("custom-elements", "custom-elements-internal"), JSON.stringify(inputDataInternal, null, 2), 'utf8');
60
- } else {
61
- console.error('Validation of external custom-elements failed:');
62
- // console.error('Validation of external custom-elements failed:', ajv.errorsText(validate.errors));
63
- }
63
+ } else if (argv.dev) {
64
+ throw new Error(`Validation of public custom elements manifest failed: ${validate.errors}`);
65
+ }
@@ -5,61 +5,40 @@ const path = require("path");
5
5
  const inputDir = process.argv[2];
6
6
  const outputDir = process.argv[3];
7
7
 
8
- const camelToKebabMap = new Map();
9
- const apiIndex = new Map();
10
- const forbiddenAttributeTypes = ["object", "array"];
11
-
12
- const camelToKebabCase = string => {
13
- if (!camelToKebabMap.has(string)) {
14
- const result = string.replace(/([a-z])([A-Z])/g, "$1-$2").toLowerCase();
15
- camelToKebabMap.set(string, result);
16
- }
17
- return camelToKebabMap.get(string);
18
- };
8
+ const moduleDeclarations = new Map();
19
9
 
20
10
  const generateJavaScriptExport = entity => {
21
11
  return {
22
- declaration: generateRefenrece(entity.name),
23
- deprecated: !!entity.deprecated,
12
+ declaration: {
13
+ name: entity.basename,
14
+ module: `dist/${entity.resource}`,
15
+ },
24
16
  kind: "js",
25
17
  name: "default",
26
18
  };
27
19
  };
28
20
 
29
21
  const generateCustomElementExport = entity => {
22
+ if (!entity.tagname) return;
23
+
30
24
  return {
31
25
  declaration: {
32
26
  name: entity.basename,
33
- module: `${entity.module}.js`,
27
+ module: `dist/${entity.resource}`,
34
28
  },
35
- deprecated: !!entity.deprecated,
36
29
  kind: "custom-element-definition",
37
- name: entity.tagname,
38
- };
39
- };
40
-
41
- const generateJavaScriptModule = entity => {
42
- return {
43
- kind: "javascript-module",
44
- path: `${entity.basename}.js`,
45
- declarations: [
46
- generateCustomElementDeclaration(entity),
47
- ],
48
- exports: [
49
- generateJavaScriptExport(entity),
50
- generateCustomElementExport(entity),
51
- ],
30
+ name: entity.basename,
52
31
  };
53
32
  };
54
33
 
55
34
  const generateSingleClassField = classField => {
56
35
  let generatedClassField = {
57
- deprecated: !!classField.deprecated,
58
36
  kind: "field",
59
37
  name: classField.name,
60
- privacy: classField.visibility,
61
- static: !!classField.static,
62
38
  type: generateType(classField.type),
39
+ privacy: classField.visibility,
40
+ deprecated: !!classField.deprecated || undefined,
41
+ static: !!classField.static || undefined,
63
42
  };
64
43
 
65
44
  if (classField.defaultValue) {
@@ -75,7 +54,7 @@ const generateSingleClassField = classField => {
75
54
 
76
55
  const generateSingleParameter = parameter => {
77
56
  let generatedParameter = {
78
- deprecated: !!parameter.deprecated,
57
+ deprecated: !!parameter.deprecated || undefined,
79
58
  name: parameter.name,
80
59
  type: generateType(parameter.type),
81
60
  };
@@ -86,6 +65,7 @@ const generateSingleParameter = parameter => {
86
65
 
87
66
  if (parameter.optional) {
88
67
  generatedParameter.optional = parameter.optional;
68
+ generatedParameter.default = parameter.defaultValue;
89
69
  }
90
70
 
91
71
  return generatedParameter;
@@ -101,7 +81,7 @@ const generateParameters = (parameters) => {
101
81
 
102
82
  const generateSingleClassMethod = classMethod => {
103
83
  let generatedClassMethod = {
104
- deprecated: !!classMethod.deprecated,
84
+ deprecated: !!classMethod.deprecated || undefined,
105
85
  kind: "method",
106
86
  name: classMethod.name,
107
87
  privacy: classMethod.visibility,
@@ -122,7 +102,7 @@ const generateSingleClassMethod = classMethod => {
122
102
  };
123
103
 
124
104
  if (classMethod.returnValue.description) {
125
- generatedClassMethod.return.description = classMethod.returnValue.type;
105
+ generatedClassMethod.return.description = classMethod.returnValue.description;
126
106
  }
127
107
  }
128
108
 
@@ -150,47 +130,16 @@ const generateMembers = (classFields, classMethods) => {
150
130
  };
151
131
 
152
132
  const generateType = type => {
153
- const dataType = apiIndex.get(type);
154
-
155
133
  return {
156
- text: dataType && dataType.name.includes(".types.") ?
157
- filterPublicApi(dataType.properties)
158
- .map(prop => `"${prop.name}"`)
159
- .join(" | ") : type,
160
- };
161
- };
162
-
163
- const generateSingleAttribute = attribute => {
164
- let generatedAttribute = {
165
- default: attribute.defaultValue,
166
- deprecated: !!attribute.deprecated,
167
- fieldName: attribute.name,
168
- name: camelToKebabCase(attribute.name),
169
- type: generateType(attribute.type),
134
+ text: type,
170
135
  };
171
-
172
- if (attribute.description) {
173
- generatedAttribute.description = attribute.description;
174
- }
175
-
176
- return generatedAttribute;
177
- };
178
-
179
- const generateAttributes = attributes => {
180
- attributes = attributes.reduce((newAttributesArray, attribute) => {
181
- newAttributesArray.push(generateSingleAttribute(attribute));
182
-
183
- return newAttributesArray;
184
- }, []);
185
-
186
- return attributes;
187
136
  };
188
137
 
189
138
  const generateSingleEvent = event => {
190
139
  let generatedEvent = {
191
- deprecated: !!event.deprecated,
140
+ deprecated: !!event.deprecated || undefined,
192
141
  name: event.name,
193
- type: event.native === "true" ? "NativeEvent" : "CustomEvent",
142
+ type: generateType(event.native === "true" ? "NativeEvent" : "CustomEvent")
194
143
  };
195
144
 
196
145
  if (event.description) {
@@ -212,7 +161,7 @@ const generateEvents = events => {
212
161
 
213
162
  const generateSingleSlot = slot => {
214
163
  return {
215
- deprecated: !!slot.deprecated,
164
+ deprecated: !!slot.deprecated || undefined,
216
165
  description: slot.description,
217
166
  name: slot.name,
218
167
  };
@@ -230,9 +179,9 @@ const generateSlots = slots => {
230
179
 
231
180
  const generateCustomElementDeclaration = entity => {
232
181
  let generatedCustomElementDeclaration = {
233
- deprecated: !!entity.deprecated,
182
+ deprecated: !!entity.deprecated || undefined,
234
183
  customElement: true,
235
- kind: entity.basename,
184
+ kind: entity.kind,
236
185
  name: entity.basename,
237
186
  tagName: entity.tagname,
238
187
  };
@@ -241,9 +190,6 @@ const generateCustomElementDeclaration = entity => {
241
190
  const events = filterPublicApi(entity.events);
242
191
  const classFields = filterPublicApi(entity.properties);
243
192
  const classMethods = filterPublicApi(entity.methods);
244
- const attributes = classFields.filter(property => {
245
- return property.noattribute !== "true" && property.readonly !== "true" && !forbiddenAttributeTypes.includes(property.type.toLowerCase());
246
- });
247
193
 
248
194
  if (slots.length) {
249
195
  generatedCustomElementDeclaration.slots = generateSlots(slots);
@@ -253,10 +199,6 @@ const generateCustomElementDeclaration = entity => {
253
199
  generatedCustomElementDeclaration.events = generateEvents(events);
254
200
  }
255
201
 
256
- if (attributes.length) {
257
- generatedCustomElementDeclaration.attributes = generateAttributes(attributes);
258
- }
259
-
260
202
  if (entity.description) {
261
203
  generatedCustomElementDeclaration.description = entity.description;
262
204
  }
@@ -273,31 +215,8 @@ const generateCustomElementDeclaration = entity => {
273
215
  };
274
216
 
275
217
  const generateRefenrece = (entityName) => {
276
- let packageName;
277
- let basename;
278
-
279
- if (!entityName) {
280
- throw new Error("JSDoc error: entity not found in api.json.");
281
- }
282
-
283
- if (entityName.includes(".")) {
284
- basename = entityName.split(".").pop();
285
- } else {
286
- basename = entityName
287
- }
288
-
289
- if (entityName.includes("sap.ui.webc.main")) {
290
- packageName = "@ui5/webcomponents";
291
- } else if (entityName.includes("sap.ui.webc.fiori")) {
292
- packageName = "@ui5/webcomponents-fiori";
293
- } else if (entityName.includes("sap.ui.webc.base")) {
294
- packageName = "@ui5/webcomponents-base";
295
- }
296
-
297
218
  return {
298
- module: `${basename}.js`,
299
- name: `${basename}`,
300
- package: packageName,
219
+ name: entityName,
301
220
  };
302
221
  };
303
222
 
@@ -313,12 +232,37 @@ const generate = async () => {
313
232
  modules: [],
314
233
  };
315
234
 
316
- file.symbols.forEach(entity => {
317
- if (entity.tagname || entity.kind === "class") {
318
- customElementsManifest.modules.push(generateJavaScriptModule(entity));
235
+ filterPublicApi(file.symbols).forEach(entity => {
236
+ let declaration = moduleDeclarations.get(entity.resource);
237
+
238
+ if (!declaration) {
239
+ moduleDeclarations.set(entity.resource, {
240
+ declarations: [],
241
+ exports: [],
242
+ });
243
+ declaration = moduleDeclarations.get(entity.resource);
244
+ }
245
+
246
+ if (entity.kind === "class" && entity.tagname) {
247
+ declaration.declarations.push(generateCustomElementDeclaration(entity));
248
+ declaration.exports.push(generateJavaScriptExport(entity));
249
+ declaration.exports.push(generateCustomElementExport(entity));
250
+ } else if (entity.kind === "class" && entity.static) {
251
+ declaration.exports.push(generateJavaScriptExport(entity));
319
252
  }
320
253
  });
321
254
 
255
+ [...moduleDeclarations.keys()].forEach(key => {
256
+ let declaration = moduleDeclarations.get(key);
257
+
258
+ customElementsManifest.modules.push({
259
+ kind: "javascript-module",
260
+ path: `dist/${key}`,
261
+ declarations: declaration.declarations,
262
+ exports: declaration.exports
263
+ })
264
+ })
265
+
322
266
  await fs.writeFile(path.join(outputDir, "custom-elements.json"), JSON.stringify(customElementsManifest));
323
267
  };
324
268
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ui5/webcomponents-tools",
3
- "version": "1.22.0-rc.1",
3
+ "version": "1.22.0-rc.3",
4
4
  "description": "UI5 Web Components: webcomponents.tools",
5
5
  "author": "SAP SE (https://www.sap.com)",
6
6
  "license": "Apache-2.0",
@@ -79,5 +79,5 @@
79
79
  "esbuild": "^0.19.9",
80
80
  "yargs": "^17.5.1"
81
81
  },
82
- "gitHead": "1a4ba450e7d222535ddaf4f1d3b0ff91b020bb2a"
82
+ "gitHead": "7ff123775e38dc646e3a1c70311e33b0c6d7105a"
83
83
  }
@@ -1,61 +0,0 @@
1
- const esprima = require("esprima");
2
- const escodegen = require("escodegen");
3
-
4
- const fs = require("fs").promises;
5
- const path = require("path");
6
- const basePath = process.argv[2];
7
-
8
- const convertImports = async (srcPath) => {
9
- let changed = false;
10
- let code = (await fs.readFile(srcPath)).toString();
11
- if (code.includes("import(")) {
12
- // esprima can't parse this, but it's from the project files
13
- return;
14
- }
15
- const tree = esprima.parseModule(code);
16
- const importer = srcPath.replace(basePath, "");
17
- const importerDir = path.dirname(importer);
18
- // console.log("-> ", importer);
19
- tree.body.forEach(node => {
20
- if (node.type === "ImportDeclaration") {
21
- let importee = node.source.value;
22
- if (importee.startsWith(".")) {
23
- // add .js extension if missing
24
- if (!importee.endsWith(".js")) {
25
- node.source.value += ".js"
26
- changed = true;
27
- }
28
- return;
29
- }
30
- let importeeDir = path.dirname(importee);
31
- let importeeFile = path.basename(importee);
32
- let relativePath = path.relative(importerDir, importeeDir);
33
- if (relativePath.length === 0) {
34
- relativePath = "."
35
- }
36
- if (!relativePath.startsWith(".")) {
37
- relativePath = "./" + relativePath;
38
- }
39
-
40
- relativePath = relativePath.replace(/\\/g, "/"); // the browser expects unix paths
41
- let relativeImport = `${relativePath}/${importeeFile}.js`;
42
- // console.log(importee + " --> " + relativeImport);
43
- node.source.value = relativeImport;
44
- changed = true;
45
- }
46
- });
47
-
48
- if (changed) {
49
- return fs.writeFile(srcPath, escodegen.generate(tree));
50
- }
51
- }
52
-
53
- const generate = async () => {
54
- const { globby } = await import("globby");
55
- const fileNames = await globby(basePath.replace(/\\/g, "/") + "**/*.js");
56
- return Promise.all(fileNames.map(convertImports).filter(x => !!x));
57
- };
58
-
59
- generate().then(() => {
60
- console.log("Success: Converted absolute imports to relative for files in:", basePath);
61
- });
@@ -1,25 +0,0 @@
1
- const fs = require("fs").promises;
2
-
3
- const basePath = process.argv[2];
4
-
5
- const replaceGlobalCoreUsage = async (srcPath) => {
6
-
7
- const original = (await fs.readFile(srcPath)).toString();
8
- let replaced = original.replace(/sap\.ui\.getCore\(\)/g, `Core`);
9
-
10
- if (original !== replaced) {
11
- replaced = `import Core from 'sap/ui/core/Core';
12
- ${replaced}`;
13
- return fs.writeFile(srcPath, replaced);
14
- }
15
- };
16
-
17
- const generate = async () => {
18
- const { globby } = await import("globby");
19
- const fileNames = await globby(basePath.replace(/\\/g, "/") + "**/*.js");
20
- return Promise.all(fileNames.map(replaceGlobalCoreUsage).filter(x => !!x));
21
- };
22
-
23
- generate().then(() => {
24
- console.log("Success: Replaced global core usage in:", basePath);
25
- });