@ui5/webcomponents-tools 0.0.0-35e2c9666 → 0.0.0-38861c872

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.
Files changed (78) hide show
  1. package/CHANGELOG.md +715 -0
  2. package/README.md +6 -6
  3. package/assets-meta.js +16 -7
  4. package/bin/dev.js +1 -5
  5. package/components-package/eslint.js +36 -0
  6. package/components-package/nps.js +105 -69
  7. package/components-package/postcss.components.js +1 -21
  8. package/components-package/postcss.themes.js +1 -26
  9. package/components-package/vite.config.js +13 -0
  10. package/components-package/wdio.js +405 -369
  11. package/components-package/wdio.sync.js +10 -2
  12. package/icons-collection/nps.js +13 -14
  13. package/lib/cem/custom-elements-manifest.config.mjs +463 -0
  14. package/lib/cem/event.mjs +114 -0
  15. package/lib/cem/schema-internal.json +1354 -0
  16. package/lib/cem/schema.json +1098 -0
  17. package/lib/cem/types-internal.d.ts +1032 -0
  18. package/lib/cem/types.d.ts +871 -0
  19. package/lib/cem/utils.mjs +355 -0
  20. package/lib/cem/validate.js +63 -0
  21. package/lib/copy-and-watch/index.js +24 -1
  22. package/lib/copy-list/index.js +17 -17
  23. package/lib/create-icons/index.js +124 -72
  24. package/lib/create-illustrations/index.js +128 -113
  25. package/lib/create-new-component/index.js +71 -104
  26. package/lib/create-new-component/jsFileContentTemplate.js +73 -0
  27. package/lib/create-new-component/tsFileContentTemplate.js +71 -0
  28. package/lib/css-processors/css-processor-components.mjs +77 -0
  29. package/lib/css-processors/css-processor-themes.mjs +79 -0
  30. package/lib/css-processors/scope-variables.mjs +46 -0
  31. package/lib/css-processors/shared.mjs +76 -0
  32. package/lib/dev-server/custom-hot-update-plugin.js +39 -0
  33. package/lib/dev-server/dev-server.js +66 -0
  34. package/lib/dev-server/virtual-index-html-plugin.js +52 -0
  35. package/lib/esm-abs-to-rel/index.js +17 -10
  36. package/lib/generate-custom-elements-manifest/index.js +327 -0
  37. package/lib/generate-js-imports/illustrations.js +86 -0
  38. package/lib/generate-json-imports/i18n.js +43 -31
  39. package/lib/generate-json-imports/themes.js +36 -24
  40. package/lib/hbs2lit/src/compiler.js +24 -4
  41. package/lib/hbs2lit/src/includesReplacer.js +5 -5
  42. package/lib/hbs2lit/src/litVisitor2.js +85 -22
  43. package/lib/hbs2lit/src/svgProcessor.js +12 -5
  44. package/lib/hbs2ui5/RenderTemplates/LitRenderer.js +32 -4
  45. package/lib/hbs2ui5/index.js +56 -23
  46. package/lib/i18n/defaults.js +65 -57
  47. package/lib/i18n/toJSON.js +12 -11
  48. package/lib/jsdoc/configTypescript.json +29 -0
  49. package/lib/jsdoc/plugin.js +32 -0
  50. package/lib/jsdoc/preprocess.js +146 -0
  51. package/lib/jsdoc/template/publish.js +9 -1
  52. package/lib/postcss-combine-duplicated-selectors/index.js +185 -0
  53. package/lib/replace-global-core/index.js +13 -8
  54. package/lib/scoping/get-all-tags.js +2 -9
  55. package/lib/scoping/scope-test-pages.js +2 -1
  56. package/lib/test-runner/test-runner.js +71 -0
  57. package/package.json +28 -29
  58. package/components-package/rollup-plugins/empty-module.js +0 -15
  59. package/components-package/rollup.js +0 -239
  60. package/lib/documentation/index.js +0 -165
  61. package/lib/documentation/templates/api-component-since.js +0 -3
  62. package/lib/documentation/templates/api-css-variables-section.js +0 -24
  63. package/lib/documentation/templates/api-events-section.js +0 -35
  64. package/lib/documentation/templates/api-methods-section.js +0 -26
  65. package/lib/documentation/templates/api-properties-section.js +0 -42
  66. package/lib/documentation/templates/api-slots-section.js +0 -28
  67. package/lib/documentation/templates/template.js +0 -39
  68. package/lib/hash/config.js +0 -10
  69. package/lib/hash/generate.js +0 -19
  70. package/lib/hash/upToDate.js +0 -31
  71. package/lib/polyfill-placeholder/index.js +0 -5
  72. package/lib/postcss-css-to-esm/index.js +0 -45
  73. package/lib/postcss-css-to-json/index.js +0 -36
  74. package/lib/postcss-new-files/index.js +0 -36
  75. package/lib/postcss-p/postcss-p.mjs +0 -11
  76. package/lib/serve/index.js +0 -46
  77. package/lib/serve/serve.json +0 -3
  78. package/package-lock.json +0 -48
@@ -1,32 +1,36 @@
1
- const fs = require("fs");
1
+ const fs = require("fs").promises;
2
2
  const path = require('path');
3
- const mkdirp = require("mkdirp");
4
3
  const assets = require("../../assets-meta.js");
5
4
 
6
- const inputFolder = path.normalize(process.argv[2]);
7
- const outputFile = path.normalize(`${process.argv[3]}/Themes-static.js`);
8
- const outputFileDynamic = path.normalize(`${process.argv[3]}/Themes.js`);
5
+ const isTypeScript = process.env.UI5_TS;
6
+ const ext = isTypeScript ? 'ts' : 'js';
7
+
8
+ const generate = async () => {
9
+ const inputFolder = path.normalize(process.argv[2]);
10
+ const outputFile = path.normalize(`${process.argv[3]}/Themes-static.${ext}`);
11
+ const outputFileDynamic = path.normalize(`${process.argv[3]}/Themes.${ext}`);
9
12
 
10
13
  // All supported optional themes
11
- const allThemes = assets.themes.all;
14
+ const allThemes = assets.themes.all;
12
15
 
13
16
  // All themes present in the file system
14
- const dirs = fs.readdirSync(inputFolder);
15
- const themesOnFileSystem = dirs.map(dir => {
16
- const matches = dir.match(/sap_.*$/);
17
- return matches ? dir : undefined;
18
- }).filter(key => !!key && allThemes.includes(key));
17
+ const dirs = await fs.readdir(inputFolder);
18
+ const themesOnFileSystem = dirs.map(dir => {
19
+ const matches = dir.match(/sap_.*$/);
20
+ return matches ? dir : undefined;
21
+ }).filter(key => !!key && allThemes.includes(key));
19
22
 
20
- const packageName = JSON.parse(fs.readFileSync("package.json")).name;
23
+ const packageName = JSON.parse(await fs.readFile("package.json")).name;
21
24
 
22
- const importLines = themesOnFileSystem.map(theme => `import ${theme} from "../assets/themes/${theme}/parameters-bundle.css.json";`).join("\n");
23
- const themeUrlsByName = "{\n" + themesOnFileSystem.join(",\n") + "\n}";
24
- const availableThemesArray = `[${themesOnFileSystem.map(theme => `"${theme}"`).join(", ")}]`;
25
- const dynamicImportLines = themesOnFileSystem.map(theme => `\t\tcase "${theme}": return (await import("../assets/themes/${theme}/parameters-bundle.css.json")).default;`).join("\n");
25
+ const importLines = themesOnFileSystem.map(theme => `import ${theme} from "../assets/themes/${theme}/parameters-bundle.css.json";`).join("\n");
26
+ const themeUrlsByName = "{\n" + themesOnFileSystem.join(",\n") + "\n}";
27
+ const availableThemesArray = `[${themesOnFileSystem.map(theme => `"${theme}"`).join(", ")}]`;
28
+ const dynamicImportLines = themesOnFileSystem.map(theme => `\t\tcase "${theme}": return (await import(/* webpackChunkName: "${packageName.replace("@", "").replace("/", "-")}-${theme.replace("_", "-")}-parameters-bundle" */"../assets/themes/${theme}/parameters-bundle.css.json")).default;`).join("\n");
26
29
 
27
30
 
28
31
  // static imports file content
29
- const contentStatic = `import { registerThemePropertiesLoader } from "@ui5/webcomponents-base/dist/asset-registries/Themes.js";
32
+ const contentStatic = `// @ts-nocheck
33
+ import { registerThemePropertiesLoader } from "@ui5/webcomponents-base/dist/asset-registries/Themes.js";
30
34
 
31
35
  ${importLines}
32
36
 
@@ -39,7 +43,7 @@ const loadThemeProperties = async (themeName) => {
39
43
  throw new Error("[themes] Inlined JSON not supported with static imports of assets. Use dynamic imports of assets or configure JSON imports as URLs");
40
44
  }
41
45
  return (await fetch(themeUrlsByName[themeName])).json();
42
- }
46
+ };
43
47
 
44
48
  ${availableThemesArray}
45
49
  .forEach(themeName => registerThemePropertiesLoader("${packageName}", themeName, loadThemeProperties));
@@ -47,14 +51,15 @@ ${availableThemesArray}
47
51
 
48
52
 
49
53
  // dynamic imports file content
50
- const contentDynamic = `import { registerThemePropertiesLoader } from "@ui5/webcomponents-base/dist/asset-registries/Themes.js";
54
+ const contentDynamic = `// @ts-nocheck
55
+ import { registerThemePropertiesLoader } from "@ui5/webcomponents-base/dist/asset-registries/Themes.js";
51
56
 
52
57
  const loadThemeProperties = async (themeName) => {
53
58
  switch (themeName) {
54
59
  ${dynamicImportLines}
55
60
  default: throw "unknown theme"
56
61
  }
57
- }
62
+ };
58
63
 
59
64
  const loadAndCheck = async (themeName) => {
60
65
  const data = await loadThemeProperties(themeName);
@@ -62,12 +67,19 @@ const loadAndCheck = async (themeName) => {
62
67
  throw new Error(\`[themes] Invalid bundling detected - dynamic JSON imports bundled as URLs. Switch to inlining JSON files from the build or use 'import ".../Assets-static.js"'. Check the \"Assets\" documentation for more information.\`);
63
68
  }
64
69
  return data;
65
- }
70
+ };
66
71
 
67
72
  ${availableThemesArray}
68
73
  .forEach(themeName => registerThemePropertiesLoader("${packageName}", themeName, loadAndCheck));
69
74
  `;
70
75
 
71
- mkdirp.sync(path.dirname(outputFile));
72
- fs.writeFileSync(outputFile, contentStatic);
73
- fs.writeFileSync(outputFileDynamic, contentDynamic);
76
+ await fs.mkdir(path.dirname(outputFile), { recursive: true });
77
+ return Promise.all([
78
+ fs.writeFile(outputFile, contentStatic),
79
+ fs.writeFile(outputFileDynamic, contentDynamic)
80
+ ]);
81
+ };
82
+
83
+ generate().then(() => {
84
+ console.log("Generated themes JSON imports.");
85
+ });
@@ -12,15 +12,25 @@ const removeWhiteSpaces = (source) => {
12
12
  .replace(/}}\s+{{/g, "}}{{"); // Remove whitespace between }} and {{
13
13
  };
14
14
 
15
- const hbs2lit = (file) => {
16
- let sPreprocessed = includesReplacer.replace(file);
15
+ const hbs2lit = async (file, componentName) => {
16
+ let sPreprocessed = await includesReplacer.replace(file);
17
17
 
18
18
  sPreprocessed = removeWhiteSpaces(sPreprocessed);
19
19
 
20
+ const blockSignature = process.env.UI5_TS ? `this: ${componentName}` : ""
21
+
22
+ // icons hack
23
+ if (sPreprocessed.startsWith("<g ") || sPreprocessed.startsWith("<g>")) {
24
+ return `
25
+ function block0 (${blockSignature}) {
26
+ return svg\`${sPreprocessed}\`
27
+ }`;
28
+ }
29
+
20
30
  const ast = Handlebars.parse(sPreprocessed);
21
31
 
22
32
  const pv = new PartialsVisitor();
23
- const lv = new HTMLLitVisitor();
33
+ const lv = new HTMLLitVisitor(componentName);
24
34
 
25
35
  let result = "";
26
36
 
@@ -30,7 +40,17 @@ const hbs2lit = (file) => {
30
40
  lv.accept(ast);
31
41
 
32
42
  for (let key in lv.blocks) {
33
- result += lv.blocks[key] + "\n";
43
+ let block = lv.blocks[key];
44
+
45
+ if (block.match(/scopeTag/)) {
46
+ // const matches = block.match(/^(.*?)( => )(.*?);$/);
47
+ const matches = block.match(/^(function .*? \{ return )(.*?);\}$/);
48
+ const scopedCode = matches[2];
49
+ const normalCode = scopedCode.replace(/\${scopeTag\("/g, "").replace(/", tags, suffix\)}/g, "");
50
+ block = `${matches[1]}suffix ? ${scopedCode} : ${normalCode};}`;
51
+ }
52
+
53
+ result += block + "\n";
34
54
  }
35
55
 
36
56
  result = svgProcessor.process(result);
@@ -1,9 +1,9 @@
1
1
  const path = require("path");
2
- const fs = require("fs");
2
+ const fs = require("fs").promises;
3
3
 
4
- function replaceIncludes(file) {
4
+ const replaceIncludes = async (file) => {
5
5
  const filePath = path.dirname(file);
6
- let fileContent = fs.readFileSync(file, "utf-8");
6
+ let fileContent = await fs.readFile(file, "utf-8");
7
7
 
8
8
  const inclRegex = /{{>\s*include\s*["'](.+?)["']}}/g;
9
9
  let match;
@@ -20,11 +20,11 @@ function replaceIncludes(file) {
20
20
  targetFile = require.resolve(targetFile);
21
21
  }
22
22
 
23
- fileContent = fileContent.replace(match[0], replaceIncludes(targetFile));
23
+ fileContent = fileContent.replace(match[0], await replaceIncludes(targetFile));
24
24
  }
25
25
 
26
26
  return fileContent;
27
- }
27
+ };
28
28
 
29
29
  module.exports = {
30
30
  replace: replaceIncludes
@@ -28,14 +28,18 @@ if (!String.prototype.replaceAll) {
28
28
  };
29
29
  }
30
30
 
31
- function HTMLLitVisitor(debug) {
31
+ function HTMLLitVisitor(componentName, debug) {
32
32
  this.blockCounter = 0;
33
33
  this.keys = [];
34
34
  this.blocks = {};
35
35
  this.result = "";
36
36
  this.mainBlock = "";
37
- this.blockPath = "context";
38
- this.blockParameters = ["context", "tags", "suffix"];
37
+ this.blockLevel = 0;
38
+ this.componentName = componentName
39
+ const blockParametersDefinitionTS = [`this: ${componentName}`, "context: UI5Element", "tags: string[]", "suffix: string | undefined"];
40
+ const blockParametersDefinitionJS = ["context", "tags", "suffix"];
41
+ this.blockParametersDefinition = process.env.UI5_TS ? blockParametersDefinitionTS : blockParametersDefinitionJS;
42
+ this.blockParametersUsage = ["this", "context", "tags", "suffix"];
39
43
  this.paths = []; //contains all normalized relative paths
40
44
  this.debug = debug;
41
45
  if (this.debug) {
@@ -51,18 +55,18 @@ HTMLLitVisitor.prototype.Program = function(program) {
51
55
  this.keys.push(key);
52
56
  this.debug && this.blockByNumber.push(key);
53
57
 
54
- this.blocks[this.currentKey()] = "const " + this.currentKey() + " = (" + this.blockParameters.join(", ") + ") => ";
58
+ // this.blocks[this.currentKey()] = "function " + this.currentKey() + ` (this: any, ` + this.blockParametersDefinition.join(", ") + ") { ";
59
+ this.blocks[this.currentKey()] = `function ${this.currentKey()} (${this.blockParametersDefinition.join(", ")}) { `;
55
60
 
56
61
  if (this.keys.length > 1) { //it's a nested block
57
- this.blocks[this.prevKey()] += this.currentKey() + "(" + this.blockParameters.join(", ") + ")";
62
+ this.blocks[this.prevKey()] += this.currentKey() + ".call(" + this.blockParametersUsage.join(", ") + ")";
58
63
  } else {
59
64
  this.mainBlock = this.currentKey();
60
- this.paths.push(this.blockPath);
61
65
  }
62
66
 
63
- this.blocks[this.currentKey()] += "html`";
67
+ this.blocks[this.currentKey()] += "return html`";
64
68
  Visitor.prototype.Program.call(this, program);
65
- this.blocks[this.currentKey()] += "`;";
69
+ this.blocks[this.currentKey()] += "`;}";
66
70
 
67
71
  this.keys.pop(key);
68
72
  };
@@ -99,14 +103,18 @@ HTMLLitVisitor.prototype.MustacheStatement = function(mustache) {
99
103
  this.blocks[this.currentKey()] += "${index}";
100
104
  } else {
101
105
  const path = normalizePath.call(this, mustache.path.original);
102
- const hasCalculatingClasses = path.includes("context.classes");
106
+ const hasCalculatingClasses = path.includes("this.classes");
103
107
 
104
108
  let parsedCode = "";
105
109
 
106
110
  if (isNodeValue && !mustache.escaped) {
107
111
  parsedCode = `\${unsafeHTML(${path})}`;
108
112
  } else if (hasCalculatingClasses) {
109
- parsedCode = `\${classMap(${path})}`;
113
+ if (process.env.UI5_TS) {
114
+ parsedCode = `\${classMap(${path} as ClassMapValue)}`;
115
+ } else {
116
+ parsedCode = `\${classMap(${path})}`;
117
+ }
110
118
  } else if (isStyleAttribute) {
111
119
  parsedCode = `\${styleMap(${path})}`;
112
120
  } else if (skipIfDefined){
@@ -171,22 +179,37 @@ function visitEachBlock(block) {
171
179
  var bParamAdded = false;
172
180
  visitSubExpression.call(this, block);
173
181
 
174
- this.blocks[this.currentKey()] += "${ repeat(" + normalizePath.call(this, block.params[0].original) + ", (item, index) => item._id || index, (item, index) => ";
182
+ const reapeatDirectiveParamsTS = "(item, index) => (item as typeof item & {_id?: any})._id || index, (item, index: number)";
183
+ const reapeatDirectiveParamsJS = "(item, index) => item._id || index, (item, index)";
184
+ const repleatDirectiveParams = process.env.UI5_TS ? reapeatDirectiveParamsTS : reapeatDirectiveParamsJS;
185
+ this.blocks[this.currentKey()] += "${ repeat(" + normalizePath.call(this, block.params[0].original) + ", " + repleatDirectiveParams + " => ";
175
186
  this.paths.push(normalizePath.call(this, block.params[0].original));
176
- this.blockPath = "item";
187
+ this.blockLevel++;
177
188
 
178
- if (this.blockParameters.indexOf("item") === -1) {
189
+ // block params is [this, context, tags, suffix] for top level blocks
190
+ // blcok params is [this, context, tags, suffix, item, index] for nested blocks
191
+ if (!this.blockParametersUsage.includes("index")) {
192
+ // last item is not index, but an each block is processed, add the paramters for further nested blocks
179
193
  bParamAdded = true;
180
- this.blockParameters.unshift("index");
181
- this.blockParameters.unshift("item");
194
+ if (process.env.UI5_TS) {
195
+ this.blockParametersDefinition.push("item: any");
196
+ this.blockParametersDefinition.push("index: number");
197
+ } else {
198
+ this.blockParametersDefinition.push("item");
199
+ this.blockParametersDefinition.push("index");
200
+ }
201
+ this.blockParametersUsage.push("item");
202
+ this.blockParametersUsage.push("index");
182
203
  }
183
204
  this.acceptKey(block, "program");
184
205
  if (bParamAdded) {
185
- this.blockParameters.shift("item");
186
- this.blockParameters.shift("index");
206
+ // if parameters were added at this step, remove the last two
207
+ this.blockParametersDefinition.pop();
208
+ this.blockParametersDefinition.pop();
209
+ this.blockParametersUsage.pop();
210
+ this.blockParametersUsage.pop();
187
211
  }
188
- this.blockPath = "context";
189
-
212
+ this.blockLevel--;
190
213
  this.blocks[this.currentKey()] += ") }";
191
214
  }
192
215
 
@@ -195,12 +218,52 @@ function normalizePath(sPath) {
195
218
 
196
219
  //read carefully - https://github.com/wycats/handlebars.js/issues/1028
197
220
  //kpdecker commented on May 20, 2015
198
- if (result.indexOf("../") === 0) {
199
- let absolutePath = replaceAll(this.paths[this.paths.length - 1], ".", "/") + "/" + result;
221
+
222
+ if (result.indexOf("@root") === 0) {
223
+ // Trying to access root context via the HBS "@root" variable.
224
+ // Example: {{@root.property}} compiles to "context.property" - called from anywhere within the template.
225
+ result = result.replace("@root", "this");
226
+
227
+ } else if (result.indexOf("../") === 0) {
228
+ let absolutePath;
229
+ const levelsUp = (result.match(/..\//g) || []).length;
230
+
231
+ if (this.blockLevel <= levelsUp) {
232
+ // Trying to access root context from nested loops.
233
+ // Example: {{../../property}} compiles to "context.property" - when currently in a nested level loop.
234
+ // Example: {{../../../property}} compile to "context.property" - when requested levels are not present. fallback to root context.
235
+ absolutePath = `this.${replaceAll(result,"../", "")}`;
236
+ } else {
237
+ // Trying to access upper context (one-level-up) and based on the current lelev, that could be "context" or "item".
238
+ // Example: {{../property}} compiles to "context.property" - when called in a top level loop.
239
+ // Example: {{../property}} compiles to "item.property" - when called in a nested level loop.
240
+ // TODO: the second example, although correctly generated to "item.property", "item" will point to the current object within the nested loop,
241
+ // not the upper level loop as intended. So accessing the upper loop from nested loop is currently not working.
242
+ absolutePath = replaceAll(this.paths[this.paths.length - 1 - levelsUp], ".", "/") + "/" + result;
243
+ }
244
+
200
245
  result = replaceAll(path.normalize(absolutePath), path.sep, ".");
246
+
201
247
  } else {
202
- result = result ? replaceAll(this.blockPath + "/" + result, "/", ".") : this.blockPath;
248
+ // When neither "@root", nor "../" are used, use the following contexts:
249
+ // - use "context" - for the top level of execution, e.g "this.blockLevel = 0".
250
+ // - use "item" - for any nested level, e.g "this.blockLevel > 0".
251
+ // Example:
252
+ //
253
+ // {{text}} -> compiles to "context.text"
254
+ // {{#each items}}
255
+ // Item text: {{text}}</div> -> compiles to "item.text"
256
+ // {{#each words}}
257
+ // Word text: {{text}}</div> -> compiles to "item.text"
258
+ // {{/each}}
259
+ // Item text: {{text}}</div> -> compiles to "item.text"
260
+ // {{/each}}
261
+ // {{text}} -> compiles to "context.text"
262
+
263
+ const blockPath = this.blockLevel > 0 ? "item" : "this";
264
+ result = result ? replaceAll(blockPath + "/" + result, "/", ".") : blockPath;
203
265
  }
266
+
204
267
  return result;
205
268
  }
206
269
 
@@ -2,7 +2,7 @@
2
2
  const svgrx = new RegExp(/<svg[\s\S]*?>([\s\S]*?)<\/svg>/, 'g');
3
3
  const blockrx = /block[0-9]+/g;
4
4
 
5
- function process(input) {
5
+ function processSVG(input) {
6
6
  let matches;
7
7
  let template = input;
8
8
  let blockCounter = 0;
@@ -45,9 +45,16 @@ function getSVGMatches(template) {
45
45
  }
46
46
 
47
47
  function getSVGBlock(input, blockCounter) {
48
+ const definitionTS = `\nfunction blockSVG${blockCounter} (this: any, context: UI5Element, tags: string[], suffix: string | undefined) {
49
+ return svg\`${input}\`;
50
+ };`;
51
+ const definitionJS = `\nfunction blockSVG${blockCounter} (context, tags, suffix) {
52
+ return svg\`${input}\`;
53
+ };`;
54
+
48
55
  return {
49
- usage: `\${blockSVG${blockCounter}(context, tags, suffix)}`,
50
- definition: `\nconst blockSVG${blockCounter} = (context, tags, suffix) => svg\`${input}\`;`,
56
+ usage: `\${blockSVG${blockCounter}.call(this, context, tags, suffix)}`,
57
+ definition: process.env.UI5_TS ? definitionTS : definitionJS,
51
58
  };
52
59
  }
53
60
 
@@ -55,7 +62,7 @@ function replaceInternalBlocks(template, svgContent) {
55
62
  const internalBlocks = svgContent.match(blockrx) || [];
56
63
 
57
64
  internalBlocks.forEach(blockName => {
58
- const rx = new RegExp(`const ${blockName}.*(html\`).*;`);
65
+ const rx = new RegExp(`function ${blockName}.*(html\`).*;`);
59
66
  template = template.replace(rx, (match, p1) => {
60
67
  return match.replace(p1, "svg\`");
61
68
  });
@@ -65,5 +72,5 @@ function replaceInternalBlocks(template, svgContent) {
65
72
  }
66
73
 
67
74
  module.exports = {
68
- process: process
75
+ process: processSVG,
69
76
  };
@@ -1,7 +1,35 @@
1
- const buildRenderer = (controlName, litTemplate) => {
2
- return `/* eslint no-unused-vars: 0 */
3
- import { html, svg, repeat, classMap, styleMap, ifDefined, unsafeHTML, scopeTag } from "@ui5/webcomponents-base/dist/renderer/LitRenderer.js";
1
+ const tsImports = (controlName, hasTypes) => {
2
+ if (!process.env.UI5_TS) {
3
+ return "";
4
+ }
5
+
6
+ const importPrefix = process.env.UI5_BASE ? "../../../" : "@ui5/webcomponents-base/dist/"
7
+
8
+ return `import type UI5Element from "${importPrefix}UI5Element.js";
9
+ ${importForControl(controlName, hasTypes)}
10
+ import type { ClassMapValue } from "${importPrefix}types.js";
11
+ `;
12
+ }
13
+ const importForControl = (controlName, hasTypes) => {
14
+
15
+ if (!hasTypes) {
16
+ return `type ${controlName} = any;`;
17
+ }
4
18
 
19
+ if (process.env.UI5_BASE) {
20
+ // base package has a component in `test/elements` instead of `src`
21
+ return `import type ${controlName} from "../../../../test/elements/${controlName}.js";`
22
+ }
23
+ return `import type ${controlName} from "../../${controlName}.js";`
24
+ }
25
+
26
+ const buildRenderer = (controlName, litTemplate, hasTypes) => {
27
+ const importPrefix = process.env.UI5_BASE ? "../../../" : "@ui5/webcomponents-base/dist/"
28
+
29
+ // typescript cannot process package imports for the same package and the paths are changed to relative for base package templates
30
+ return `/* eslint no-unused-vars: 0 */
31
+ import { html, svg, repeat, classMap, styleMap, ifDefined, unsafeHTML, scopeTag } from "${importPrefix}renderer/LitRenderer.js";
32
+ ${tsImports(controlName, hasTypes)}
5
33
  ${litTemplate}
6
34
 
7
35
  export default block0;`;
@@ -9,4 +37,4 @@ export default block0;`;
9
37
 
10
38
  module.exports = {
11
39
  generateTemplate: buildRenderer
12
- };
40
+ };
@@ -1,10 +1,12 @@
1
- const fs = require('fs');
1
+ const fs = require('fs').promises;
2
+ const existsSync = require('fs').existsSync;
2
3
  const getopts = require('getopts');
3
4
  const hbs2lit = require('../hbs2lit');
4
5
  const path = require('path');
5
6
  const litRenderer = require('./RenderTemplates/LitRenderer');
6
7
  const recursiveReadDir = require("recursive-readdir");
7
- const mkdirp = require('mkdirp');
8
+
9
+ let missingTypesReported = false;
8
10
 
9
11
  const args = getopts(process.argv.slice(2), {
10
12
  alias: {
@@ -24,13 +26,27 @@ const onError = (place) => {
24
26
 
25
27
  const isHandlebars = (fileName) => fileName.indexOf('.hbs') !== -1;
26
28
 
27
- const processFile = (file, outputDir) => {
28
- const litCode = hbs2lit(file);
29
- const absoluteOutputDir = composeAbsoluteOutputDir(file, outputDir);
29
+ const hasTypes = (file, componentName) => {
30
+ const tsFile = path.join(path.dirname(file), componentName + ".ts")
31
+ const dtsFile = path.join(path.dirname(file), componentName + ".d.ts")
32
+ return existsSync(tsFile) || existsSync(dtsFile);
33
+ }
34
+
35
+ const processFile = async (file, outputDir) => {
30
36
  const componentNameMatcher = /(\w+)(\.hbs)/gim;
31
37
  const componentName = componentNameMatcher.exec(file)[1];
38
+ const componentHasTypes = hasTypes(file, componentName);
39
+ if (!componentHasTypes) {
40
+ if (!missingTypesReported) {
41
+ console.warn("[Warn] The following templates do not have a corresponging .ts or .d.ts file and won't be type checked:")
42
+ missingTypesReported = true;
43
+ }
44
+ console.log(" -> " + componentName + ".hbs");
45
+ }
46
+ const litCode = await hbs2lit(file, componentName);
47
+ const absoluteOutputDir = composeAbsoluteOutputDir(file, outputDir);
32
48
 
33
- writeRenderers(absoluteOutputDir, componentName, litRenderer.generateTemplate(componentName, litCode));
49
+ return writeRenderers(absoluteOutputDir, componentName, litRenderer.generateTemplate(componentName, litCode, componentHasTypes));
34
50
  };
35
51
 
36
52
  const composeAbsoluteOutputDir = (file, outputDir) => {
@@ -40,39 +56,54 @@ const composeAbsoluteOutputDir = (file, outputDir) => {
40
56
  const fileDir = file.split(path.sep).slice(1, -1).join(path.sep);
41
57
 
42
58
  // (2) Compose full output dir - "dist/generated/templates/lvl1/lvl2"
43
- return `${outputDir}${path.sep}${fileDir}`;
59
+ return `${outputDir}${path.sep}${fileDir}`;
44
60
  };
45
61
 
46
62
  const wrapDirectory = (directory, outputDir) => {
47
63
  directory = path.normalize(directory);
48
64
  outputDir = path.normalize(outputDir);
49
65
 
50
- recursiveReadDir(directory, (err, files) => {
51
-
52
- if (err) {
53
- onError('directory');
54
- }
66
+ return new Promise((resolve, reject) => {
67
+ recursiveReadDir(directory, (err, files) => {
55
68
 
56
- files.forEach(fileName => {
57
- if (isHandlebars(fileName)) {
58
- processFile(fileName, outputDir);
69
+ if (err) {
70
+ onError('directory');
71
+ reject();
59
72
  }
73
+
74
+ const promises = files.map(fileName => {
75
+ if (isHandlebars(fileName)) {
76
+ return processFile(fileName, outputDir);
77
+ }
78
+ }).filter(x => !!x);
79
+
80
+ resolve(Promise.all(promises));
60
81
  });
61
- })
82
+ });
62
83
  };
63
84
 
64
- const writeRenderers = (outputDir, controlName, fileContent) => {
85
+ const writeRenderers = async (outputDir, controlName, fileContent) => {
65
86
  try {
66
- if (!fs.existsSync(outputDir)) {
67
- mkdirp.sync(outputDir);
68
- }
69
87
 
70
- const compiledFilePath = `${outputDir}${path.sep}${controlName}Template.lit.js`;
88
+ await fs.mkdir(outputDir, { recursive: true });
89
+
90
+ const compiledFilePath = `${outputDir}${path.sep}${controlName}Template.lit.${process.env.UI5_TS ? "ts" : "js"}`;
71
91
 
72
92
  // strip DOS line endings because the break the source maps
73
93
  let fileContentUnix = fileContent.replace(/\r\n/g, "\n");
74
94
  fileContentUnix = fileContentUnix.replace(/\r/g, "\n");
75
- fs.writeFileSync(compiledFilePath, fileContentUnix);
95
+
96
+ // Only write to the file system actual changes - each updated file, no matter if the same or not, triggers an expensive operation for rollup
97
+ // Note: .hbs files that include a changed .hbs file will also be recompiled as their content will be updated too
98
+
99
+ let existingFileContent = "";
100
+ try {
101
+ existingFileContent = await fs.readFile(compiledFilePath);
102
+ } catch (e) {}
103
+
104
+ if (existingFileContent !== fileContentUnix) {
105
+ return fs.writeFile(compiledFilePath, fileContentUnix);
106
+ }
76
107
 
77
108
  } catch (e) {
78
109
  console.log(e);
@@ -82,5 +113,7 @@ const writeRenderers = (outputDir, controlName, fileContent) => {
82
113
  if (!args['d'] || !args['o']) {
83
114
  console.log('Please provide an input and output directory (-d and -o)');
84
115
  } else {
85
- wrapDirectory(args['d'], args['o']);
116
+ wrapDirectory(args['d'], args['o']).then(() => {
117
+ console.log("Templates generated");
118
+ });
86
119
  }