@ui5/webcomponents-tools 1.9.2 → 1.10.0

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.
@@ -5,6 +5,34 @@ module.exports = {
5
5
  },
6
6
  "root": true,
7
7
  "extends": "airbnb-base",
8
+ overrides: [{
9
+ files: ["*.ts"],
10
+ parser: "@typescript-eslint/parser",
11
+ plugins: ["@typescript-eslint"],
12
+ extends: [
13
+ "plugin:@typescript-eslint/recommended",
14
+ "plugin:@typescript-eslint/recommended-requiring-type-checking"
15
+ ],
16
+ parserOptions: {
17
+ "project": ["./tsconfig.json", "./packages/*/tsconfig.json"],
18
+ },
19
+ /**
20
+ * Typescript Rules
21
+ */
22
+ rules: {
23
+ "no-shadow": "off",
24
+ "@typescript-eslint/no-shadow": ["error"],
25
+ "@typescript-eslint/no-unsafe-member-access": "off",
26
+ "@typescript-eslint/no-floating-promises": "off",
27
+ "@typescript-eslint/no-explicit-any": "off",
28
+ "@typescript-eslint/no-unsafe-assignment": "off",
29
+ "@typescript-eslint/ban-ts-comment": "off",
30
+ "@typescript-eslint/no-unsafe-call": "off",
31
+ "@typescript-eslint/no-non-null-assertion": "off",
32
+ "@typescript-eslint/no-empty-function": "off",
33
+ "lines-between-class-members": "off",
34
+ }
35
+ }],
8
36
  "parserOptions": {
9
37
  "ecmaVersion": 2018,
10
38
  "sourceType": "module"
@@ -1,6 +1,8 @@
1
1
  const path = require("path");
2
2
  const fs = require("fs");
3
+ const resolve = require("resolve");
3
4
  const LIB = path.join(__dirname, `../lib/`);
5
+ const preprocessJSDocScript = resolve.sync("@ui5/webcomponents-tools/lib/jsdoc/preprocess.js");
4
6
 
5
7
  const getScripts = (options) => {
6
8
 
@@ -12,6 +14,8 @@ const getScripts = (options) => {
12
14
  // The script creates the "dist/generated/js-imports/Illustration.js" file that registers loaders (dynamic JS imports) for each illustration
13
15
  const illustrationDestinationPaths = illustrationsData.map(illustrations => illustrations.destinationPath);
14
16
  const createIllustrationsLoadersScript = options.fioriPackage ? `node ${LIB}/generate-js-imports/illustrations.js ${illustrationDestinationPaths[0]} ${illustrationDestinationPaths[1]} dist/generated/js-imports` : "";
17
+ const tsCommand = options.typescript ? "tsc" : "";
18
+ const tsWatchCommand = options.typescript ? "tsc --watch" : "";
15
19
 
16
20
  let viteConfig;
17
21
  if (fs.existsSync("config/vite.config.js")) {
@@ -38,14 +42,15 @@ const getScripts = (options) => {
38
42
  }
39
43
 
40
44
  const scripts = {
41
- clean: 'rimraf dist && rimraf .port && nps "scope.testPages.clean"',
45
+ clean: 'rimraf jsdoc-dist && rimraf dist && rimraf .port && nps "scope.testPages.clean"',
42
46
  lint: `eslint . ${eslintConfig}`,
43
47
  lintfix: `eslint . ${eslintConfig}`,
44
48
  prepare: {
45
- default: "nps clean prepare.all",
46
- all: 'concurrently "nps build.templates" "nps build.i18n" "nps prepare.styleRelated" "nps copy" "nps build.api" "nps build.illustrations"',
49
+ default: "nps clean prepare.all generateAPI",
50
+ all: 'concurrently "nps build.templates" "nps build.i18n" "nps prepare.styleRelated" "nps copy" "nps typescript" "nps build.illustrations"',
47
51
  styleRelated: "nps build.styles build.jsonImports build.jsImports",
48
52
  },
53
+ typescript: tsCommand,
49
54
  build: {
50
55
  default: "nps lint prepare build.bundle",
51
56
  templates: `mkdirp dist/generated/templates && node "${LIB}/hbs2ui5/index.js" -d src/ -o dist/generated/templates`,
@@ -69,7 +74,6 @@ const getScripts = (options) => {
69
74
  illustrationsLoaders: createIllustrationsLoadersScript,
70
75
  },
71
76
  bundle: `vite build ${viteConfig}`,
72
- api: `jsdoc -c "${LIB}/jsdoc/config.json"`,
73
77
  illustrations: createIllustrationsJSImportsScript,
74
78
  },
75
79
  copy: {
@@ -78,9 +82,10 @@ const getScripts = (options) => {
78
82
  props: `node "${LIB}/copy-and-watch/index.js" --silent "src/**/*.properties" dist/`,
79
83
  },
80
84
  watch: {
81
- default: 'concurrently "nps watch.templates" "nps watch.api" "nps watch.src" "nps watch.styles" "nps watch.i18n" "nps watch.props"',
85
+ default: 'concurrently "nps watch.templates" "nps watch.api" "nps watch.src" "nps watch.typescript" "nps watch.styles" "nps watch.i18n" "nps watch.props"',
82
86
  devServer: 'concurrently "nps watch.default" "nps watch.bundle"',
83
87
  src: 'nps "copy.src --watch --safe --skip-initial-copy"',
88
+ typescript: tsWatchCommand,
84
89
  props: 'nps "copy.props --watch --safe --skip-initial-copy"',
85
90
  bundle: `node ${LIB}/dev-server/dev-server.js ${viteConfig}`,
86
91
  styles: {
@@ -93,7 +98,7 @@ const getScripts = (options) => {
93
98
  },
94
99
  },
95
100
  templates: 'chokidar "src/**/*.hbs" -c "nps build.templates"',
96
- api: 'chokidar "test/**/*.sample.html" -c "nps build.api"',
101
+ api: 'chokidar "test/**/*.sample.html" -c "nps generateAPI"',
97
102
  i18n: 'chokidar "src/i18n/messagebundle.properties" -c "nps build.i18n.defaultsjs"'
98
103
  },
99
104
  start: "nps prepare watch.devServer",
@@ -113,7 +118,14 @@ const getScripts = (options) => {
113
118
  watchWithBundle: 'concurrently "nps scope.watch" "nps scope.bundle" ',
114
119
  watch: 'concurrently "nps watch.templates" "nps watch.api" "nps watch.src" "nps watch.props" "nps watch.styles"',
115
120
  bundle: `node ${LIB}/dev-server/dev-server.js ${viteConfig}`,
116
- }
121
+ },
122
+ generateAPI: {
123
+ default: "nps generateAPI.prepare generateAPI.preprocess generateAPI.jsdoc generateAPI.cleanup",
124
+ prepare: `node "${LIB}/copy-and-watch/index.js" --silent "dist/**/*.js" jsdoc-dist/`,
125
+ preprocess: `node "${preprocessJSDocScript}" jsdoc-dist/ src`,
126
+ jsdoc: `jsdoc -c "${LIB}/jsdoc/configTypescript.json"`,
127
+ cleanup: "rimraf jsdoc-dist/"
128
+ },
117
129
  };
118
130
 
119
131
  return scripts;
@@ -217,6 +217,12 @@ exports.config = {
217
217
  }, this, className);
218
218
  }, true);
219
219
 
220
+ await browser.addCommand("hasAttribute", async function(attrName) {
221
+ return browser.executeAsync((elem, attrName, done) => {
222
+ done(elem.hasAttribute(attrName));
223
+ }, this, attrName);
224
+ }, true);
225
+
220
226
  await browser.addCommand("getStaticAreaItemClassName", async function(selector) {
221
227
  return browser.executeAsync(async (selector, done) => {
222
228
  const staticAreaItem = await document.querySelector(selector).getStaticAreaItemDomRef();
@@ -238,6 +244,7 @@ exports.config = {
238
244
  "$",
239
245
  "$$",
240
246
  "getAttribute",
247
+ "hasAttribute", // custom
241
248
  "getCSSProperty",
242
249
  "getHTML",
243
250
  "getProperty",
@@ -46,7 +46,7 @@ const getScripts = (options) => {
46
46
  clean: "rimraf dist",
47
47
  copy: copyAssetsCmd,
48
48
  build: {
49
- default: `nps clean copy build.i18n build.icons build.jsonImports`,
49
+ default: `nps clean typescript copy build.i18n build.icons build.jsonImports`,
50
50
  i18n: {
51
51
  default: "nps build.i18n.defaultsjs build.i18n.json",
52
52
  defaultsjs: `mkdirp dist/generated/i18n && node "${LIB}/i18n/defaults.js" src/i18n dist/generated/i18n`,
@@ -58,6 +58,7 @@ const getScripts = (options) => {
58
58
  },
59
59
  icons: createJSImportsCmd,
60
60
  },
61
+ typescript: "tsc",
61
62
  };
62
63
 
63
64
  return scripts;
@@ -3,7 +3,7 @@ const path = require("path");
3
3
 
4
4
  const fileList = process.argv[2];
5
5
  const dest = process.argv[3];
6
- const src = "../../node_modules/@openui5/sap.ui.core/src/";
6
+ const src = "@openui5/sap.ui.core/src/";
7
7
 
8
8
  const generate = async () => {
9
9
  const filesToCopy = (await fs.readFile(fileList)).toString();
@@ -15,7 +15,7 @@ const generate = async () => {
15
15
  const trimFile = file => file.trim();
16
16
 
17
17
  return filesToCopy.split("\n").map(trimFile).filter(shouldCopy).map(async moduleName => {
18
- const srcPath = path.join(src, moduleName);
18
+ const srcPath = require.resolve(path.join(src, moduleName), {paths: [process.cwd()]});
19
19
  const destPath = path.join(dest, moduleName);
20
20
 
21
21
  await fs.mkdir(path.dirname(destPath), { recursive: true });
@@ -13,13 +13,13 @@ import ${componentName}Css from "./generated/themes/${componentName}.css.js";
13
13
  */
14
14
  const metadata = {
15
15
  tag: "${tagName}",
16
- properties: /** @lends sap.ui.webcomponents.${library}.${componentName}.prototype */ {
16
+ properties: /** @lends sap.ui.webc.${library}.${componentName}.prototype */ {
17
17
  //
18
18
  },
19
- slots: /** @lends sap.ui.webcomponents.${library}.${componentName}.prototype */ {
19
+ slots: /** @lends sap.ui.webc.${library}.${componentName}.prototype */ {
20
20
  //
21
21
  },
22
- events: /** @lends sap.ui.webcomponents.${library}.${componentName}.prototype */ {
22
+ events: /** @lends sap.ui.webc.${library}.${componentName}.prototype */ {
23
23
  //
24
24
  },
25
25
  };
@@ -39,8 +39,8 @@ const metadata = {
39
39
  *
40
40
  * @constructor
41
41
  * @author SAP SE
42
- * @alias sap.ui.webcomponents.${library}.${componentName}
43
- * @extends sap.ui.webcomponents.base.UI5Element
42
+ * @alias sap.ui.webc.${library}.${componentName}
43
+ * @extends sap.ui.webc.base.UI5Element
44
44
  * @tagname ${tagName}
45
45
  * @public
46
46
  */
@@ -33,6 +33,7 @@ const virtualIndexPlugin = async () => {
33
33
  const folders = Object.keys(pagesPerFolder);
34
34
 
35
35
  res.statusCode = 200;
36
+ res.setHeader("Content-Type", "text/html; charset=utf-8");
36
37
  res.end(`${folders.map(folder => {
37
38
  const pages = pagesPerFolder[folder];
38
39
  return `<h1>${folder}</h1>
@@ -274,11 +274,15 @@ const generateCustomElementDeclaration = entity => {
274
274
  const generateRefenrece = (entity) => {
275
275
  let packageName;
276
276
 
277
- if (entity.name.includes("sap.ui.webcomponents.main")) {
277
+ if (!entity.name) {
278
+ throw new Error("JSDoc error: entity not found in api.json.");
279
+ }
280
+
281
+ if (entity.name.includes("sap.ui.webc.main")) {
278
282
  packageName = "@ui5/webcomponents";
279
- } else if (entity.name.includes("sap.ui.webcomponents.fiori")) {
283
+ } else if (entity.name.includes("sap.ui.webc.fiori")) {
280
284
  packageName = "@ui5/webcomponents-fiori";
281
- } else if (entity.name.includes("sap.ui.webcomponents.base")) {
285
+ } else if (entity.name.includes("sap.ui.webc.base")) {
282
286
  packageName = "@ui5/webcomponents-base";
283
287
  }
284
288
 
@@ -0,0 +1,29 @@
1
+ {
2
+ "source": {
3
+ "include": "jsdoc-dist",
4
+ "excludePattern": "(/|\\\\)library-all\\.js|(/|\\\\).*-preload\\.js|^jquery-.*\\.js|^sap-.*\\.js|.+Renderer\\.lit\\.js|.*library\\.js|thirdparty"
5
+ },
6
+ "opts" : {
7
+ "recurse": true,
8
+ "template" : "template",
9
+ "destination": ""
10
+ },
11
+ "plugins": [
12
+ "./plugin.js"
13
+ ],
14
+ "templates" : {
15
+ "ui5" : {
16
+ "variants": [
17
+ "apijson"
18
+ ],
19
+ "version": "1.62",
20
+ "apiJsonFolder": "",
21
+ "apiJsonFile": "dist/api.json",
22
+ "includeSettingsInConstructor": false
23
+ }
24
+ },
25
+ "tags": {
26
+ "allowUnknownTags": true,
27
+ "dictionaries": ["jsdoc"]
28
+ }
29
+ }
@@ -0,0 +1,146 @@
1
+ const process = require("process");
2
+ const path = require("path");
3
+ const fs = require("fs/promises");
4
+
5
+ const inputDir = process.argv[2];
6
+ const sourceDir = process.argv[3];
7
+
8
+ const preprocessTypes = async () => {
9
+ try {
10
+ const { globby } = await import("globby");
11
+ const fileNames = await globby(inputDir + "**/types/*.js");
12
+
13
+ return Promise.all(fileNames.map(processTypeFile));
14
+ } catch(e) {
15
+ console.log("JSDoc types preprocess failed: ", e);
16
+ }
17
+ };
18
+
19
+ const processTypeFile = async (fileName) => {
20
+ let fileContent = `${await fs.readFile(fileName)}`;
21
+
22
+ const re = new RegExp(`(\\/\\*\\*\\s*\\n([^\\*]|(\\*(?!\\/)))*\\*\\/)\\s+[\\w\\d]+\\[\\"([\\w\\d]+)\\"\\]\\s*=\\s*\\"([\\w\\d]+)\\";`, "gm")
23
+ let matches = [...fileContent.matchAll(re)];
24
+
25
+ // Get all type values
26
+ const typeData = matches.map(match => {
27
+ return {
28
+ comment: match[1],
29
+ key: match[4],
30
+ value: match[5],
31
+ };
32
+ });
33
+ if (typeData.length === 0) {
34
+ return;
35
+ }
36
+
37
+ const typeName = path.parse(fileName).name;
38
+
39
+ matches = fileContent.match(/\/\*\*\s*\n([^\*]|(\*(?!\/)))*\*\//gm);
40
+ const comment = matches[0];
41
+
42
+ const propsCode = typeData.map(item => {
43
+ return `${item.comment}\n get ${item.key}() { return "${item.value}"; }`;
44
+ }).join("\n");
45
+
46
+ const newClassCode = `
47
+ ${comment}
48
+ class ${typeName} {
49
+ ${propsCode}
50
+ };
51
+
52
+ export default ${typeName};`;
53
+
54
+ fileContent = newClassCode;
55
+
56
+ return fs.writeFile(fileName, fileContent);
57
+ };
58
+
59
+ const preprocessComponents = async () => {
60
+ if (!sourceDir) {
61
+ return; // if the second param was not passed, there are no components
62
+ }
63
+
64
+ try {
65
+ const { globby } = await import("globby");
66
+ const fileNames = await globby(sourceDir + "/*.ts");
67
+
68
+ return Promise.all(fileNames.map(processComponentFile));
69
+ } catch(e) {
70
+ console.log("JSDoc components preprocess failed: ", e);
71
+ }
72
+ };
73
+
74
+ const isClass = text => {
75
+ return text.includes("@abstract") || text.includes("@class");
76
+ };
77
+
78
+ const isAnnotationComment = (comment) => {
79
+ return comment.includes("@name");
80
+ }
81
+
82
+ const processComponentFile = async (fileName) => {
83
+ // source file (src/*.ts)
84
+ let tsFileContent = `${await fs.readFile(fileName)}`;
85
+
86
+ // Skip all non-component files
87
+ if (!isClass(tsFileContent)) {
88
+ return;
89
+ }
90
+
91
+ // Gather all JSDocs from the original .ts file
92
+ const allJSDocsRegExp = new RegExp(`\\/\\*\\*(.|\\n)+?\\s+\\*\\/`, "gm");
93
+ let allJSDocs = [...tsFileContent.matchAll(allJSDocsRegExp)];
94
+ allJSDocs = allJSDocs.map(match => match[0]); // all /** ..... */ comments
95
+
96
+ // Find where the class is defined in the original file
97
+ const tsClassDefinitionRegExp = new RegExp(`^(abstract\\s)?class [\\w\\d_]+`, "gm");
98
+ let tsClassDefinitionMatch = tsFileContent.match(tsClassDefinitionRegExp);
99
+ if (!tsClassDefinitionMatch) {
100
+ return; // no class defined in this .ts file
101
+ }
102
+ const tsClassDefinition = tsClassDefinitionMatch[0];
103
+ const tsClassDefinitionIndex = tsFileContent.indexOf(tsClassDefinition);
104
+
105
+ // Gather all JSDocs that are before the class definition (except for the @class one)
106
+ const JSDocsToAppend = [];
107
+ allJSDocs.forEach(JSDoc => {
108
+ if (!isClass(JSDoc) && (tsFileContent.indexOf(JSDoc) < tsClassDefinitionIndex || isAnnotationComment(JSDoc, tsFileContent))) {
109
+ JSDocsToAppend.push(JSDoc);
110
+ }
111
+ });
112
+
113
+
114
+
115
+ // destination file (jsdoc-dist/*.js)
116
+ const destFileName = fileName.replace(sourceDir, inputDir).replace(/\.ts$/, ".js");
117
+ let jsFileContent = `${await fs.readFile(destFileName)}`;
118
+
119
+ const classDefinitionRegExp = new RegExp(`let.*? = class`, "gm");
120
+ let classDefinitionMatch = jsFileContent.match(classDefinitionRegExp);
121
+ if (!classDefinitionMatch) {
122
+ return; // not a file, generated by typescript, nothing to do here
123
+ }
124
+
125
+ const classDefinition = classDefinitionMatch[0];
126
+ const classDefinitionIndex = jsFileContent.indexOf(classDefinition); // classDefinitionIndex is the position in the file where the class is defined
127
+
128
+ // All comments before the class definition, except for the @class comment, must be removed
129
+ allJSDocs.forEach(JSDoc => {
130
+ if (!isClass(JSDoc) && jsFileContent.indexOf(JSDoc) < classDefinitionIndex) {
131
+ jsFileContent = jsFileContent.replace(JSDoc, "");
132
+ }
133
+ });
134
+
135
+ // Put all other comments at the end of the file
136
+ jsFileContent = jsFileContent + "\n\n" + JSDocsToAppend.join("\n\n");
137
+ return fs.writeFile(destFileName, jsFileContent);
138
+ };
139
+
140
+ Promise.all([
141
+ preprocessTypes(),
142
+ preprocessComponents(),
143
+ ]).then(() => {
144
+ console.log("JSDoc preprocess ready.");
145
+ });
146
+
@@ -8,18 +8,11 @@ const getTag = file => {
8
8
  return matches ? matches[1] : undefined;
9
9
  };
10
10
 
11
- const getAltTag = file => {
12
- const fileContent = String(fs.readFileSync(file)).replace(/\n/g, "");
13
- const matches = fileContent.match(/\baltTag\b:\s*\"(.*?)\"/);
14
- return matches ? matches[1] : undefined;
15
- };
16
-
17
11
  const getPackageTags = (packageDir) => {
18
12
  const srcDir = path.join(packageDir, "src/");
19
13
  return glob.sync(path.join(srcDir, "/**/*.js")).flatMap(file => {
20
14
  const tag = getTag(file);
21
- const altTag = getAltTag(file);
22
- return [tag, altTag];
15
+ return [tag];
23
16
  }).filter(item => !!item);
24
17
  };
25
18
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ui5/webcomponents-tools",
3
- "version": "1.9.2",
3
+ "version": "1.10.0",
4
4
  "description": "UI5 Web Components: webcomponents.tools",
5
5
  "author": "SAP SE (https://www.sap.com)",
6
6
  "license": "Apache-2.0",
@@ -21,6 +21,8 @@
21
21
  "directory": "packages/tools"
22
22
  },
23
23
  "dependencies": {
24
+ "@typescript-eslint/eslint-plugin": "^5.42.1",
25
+ "@typescript-eslint/parser": "^5.42.1",
24
26
  "@wdio/cli": "^7.19.7",
25
27
  "@wdio/devtools-service": "^7.19.7",
26
28
  "@wdio/dot-reporter": "^7.19.7",