@ui5/webcomponents-tools 0.0.0-f651a470c → 0.0.0-f79db712b
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 +405 -0
- package/README.md +2 -1
- package/assets-meta.js +16 -7
- package/components-package/eslint.js +2 -0
- package/components-package/nps.js +37 -30
- package/components-package/postcss.components.js +1 -21
- package/components-package/postcss.themes.js +1 -26
- package/components-package/wdio.js +15 -3
- package/components-package/wdio.sync.js +9 -1
- package/icons-collection/nps.js +8 -6
- package/lib/cem/custom-elements-manifest.config.mjs +499 -0
- package/lib/cem/event.mjs +135 -0
- package/lib/cem/schema-internal.json +1354 -0
- package/lib/cem/schema.json +1098 -0
- package/lib/cem/types-internal.d.ts +801 -0
- package/lib/cem/types.d.ts +736 -0
- package/lib/cem/utils.mjs +354 -0
- package/lib/cem/validate.js +67 -0
- package/lib/create-illustrations/index.js +32 -28
- package/lib/create-new-component/index.js +28 -58
- package/lib/create-new-component/jsFileContentTemplate.js +0 -4
- package/lib/create-new-component/tsFileContentTemplate.js +2 -15
- package/lib/css-processors/css-processor-components.mjs +77 -0
- package/lib/css-processors/css-processor-themes.mjs +79 -0
- package/lib/css-processors/scope-variables.mjs +46 -0
- package/lib/{postcss-css-to-esm/index.js → css-processors/shared.mjs} +36 -50
- package/lib/dev-server/custom-hot-update-plugin.js +39 -0
- package/lib/esm-abs-to-rel/index.js +4 -1
- package/lib/generate-custom-elements-manifest/index.js +1 -1
- package/lib/generate-js-imports/illustrations.js +78 -64
- package/lib/generate-json-imports/i18n.js +10 -5
- package/lib/generate-json-imports/themes.js +10 -5
- package/lib/hbs2lit/src/compiler.js +9 -6
- package/lib/hbs2lit/src/litVisitor2.js +42 -17
- package/lib/hbs2lit/src/svgProcessor.js +12 -5
- package/lib/hbs2ui5/RenderTemplates/LitRenderer.js +32 -4
- package/lib/hbs2ui5/index.js +21 -4
- package/lib/jsdoc/preprocess.js +1 -1
- package/lib/postcss-combine-duplicated-selectors/index.js +12 -5
- package/lib/scoping/get-all-tags.js +1 -1
- package/lib/scoping/scope-test-pages.js +2 -1
- package/lib/test-runner/test-runner.js +2 -2
- package/package.json +10 -9
- package/lib/postcss-css-to-json/index.js +0 -47
- package/lib/postcss-new-files/index.js +0 -36
- package/lib/postcss-p/postcss-p.mjs +0 -14
@@ -0,0 +1,354 @@
|
|
1
|
+
import fs from "fs";
|
2
|
+
import path from "path";
|
3
|
+
|
4
|
+
let JSDocErrors = [];
|
5
|
+
|
6
|
+
const getDeprecatedStatus = (jsdocComment) => {
|
7
|
+
const deprecatedTag = findTag(jsdocComment, "deprecated");
|
8
|
+
return deprecatedTag?.name
|
9
|
+
? deprecatedTag.description
|
10
|
+
? `${deprecatedTag.name} ${deprecatedTag.description}`
|
11
|
+
: deprecatedTag.name
|
12
|
+
: deprecatedTag
|
13
|
+
? true
|
14
|
+
: undefined;
|
15
|
+
};
|
16
|
+
|
17
|
+
const normalizeDescription = (description) => {
|
18
|
+
return typeof description === 'string' ? description.replaceAll(/^-\s+|^(\n)+|(\n)+$/g, ""): description;
|
19
|
+
}
|
20
|
+
|
21
|
+
const getTypeRefs = (ts, node, member) => {
|
22
|
+
const extractTypeRefs = (type) => {
|
23
|
+
if (type?.kind === ts.SyntaxKind.TypeReference) {
|
24
|
+
return type.typeArguments?.length
|
25
|
+
? type.typeArguments.map((typeRef) => typeRef.typeName?.text)
|
26
|
+
: [type.typeName?.text];
|
27
|
+
} else if (type?.kind === ts.SyntaxKind.UnionType) {
|
28
|
+
return type.types
|
29
|
+
.map((type) => extractTypeRefs(type))
|
30
|
+
.flat(1);
|
31
|
+
} else if (type?.kind === ts.SyntaxKind.TemplateLiteralType) {
|
32
|
+
if (member?.type) {
|
33
|
+
member.type.text = member.type.text.replaceAll?.(/`|\${|}/g, "");
|
34
|
+
}
|
35
|
+
|
36
|
+
return type.templateSpans?.length
|
37
|
+
? type.templateSpans.map((typeRef) => typeRef.type?.typeName?.text)
|
38
|
+
: [type.typeName?.text];
|
39
|
+
}
|
40
|
+
};
|
41
|
+
|
42
|
+
let typeRefs = extractTypeRefs(node.type) || node?.typeArguments?.map(n => extractTypeRefs(n)).flat(2);
|
43
|
+
|
44
|
+
if (typeRefs) {
|
45
|
+
typeRefs = typeRefs.filter((e) => !!e);
|
46
|
+
}
|
47
|
+
|
48
|
+
return typeRefs?.length ? typeRefs : undefined;
|
49
|
+
};
|
50
|
+
|
51
|
+
const getSinceStatus = (jsdocComment) => {
|
52
|
+
const sinceTag = findTag(jsdocComment, "since");
|
53
|
+
return sinceTag
|
54
|
+
? sinceTag.description
|
55
|
+
? `${sinceTag.name} ${sinceTag.description}`
|
56
|
+
: sinceTag.name
|
57
|
+
: undefined;
|
58
|
+
};
|
59
|
+
|
60
|
+
const getPrivacyStatus = (jsdocComment) => {
|
61
|
+
const privacyTag = findTag(jsdocComment, ["public", "private", "protected"]);
|
62
|
+
return privacyTag?.tag || "private";
|
63
|
+
};
|
64
|
+
|
65
|
+
const findPackageName = (ts, sourceFile, typeName) => {
|
66
|
+
const localStatements = [
|
67
|
+
ts.SyntaxKind.EnumDeclaration,
|
68
|
+
ts.SyntaxKind.InterfaceDeclaration,
|
69
|
+
ts.SyntaxKind.ClassDeclaration,
|
70
|
+
ts.SyntaxKind.TypeAliasDeclaration,
|
71
|
+
];
|
72
|
+
|
73
|
+
const isLocalDeclared = sourceFile.statements.some(
|
74
|
+
(statement) =>
|
75
|
+
localStatements.includes(statement.kind) && statement?.name?.text === typeName
|
76
|
+
);
|
77
|
+
|
78
|
+
if (isLocalDeclared) {
|
79
|
+
return packageJSON?.name;
|
80
|
+
} else {
|
81
|
+
const importStatements = sourceFile.statements?.filter(
|
82
|
+
(statement) => statement.kind === ts.SyntaxKind.ImportDeclaration
|
83
|
+
);
|
84
|
+
const currentModuleSpecifier = importStatements.find((statement) => {
|
85
|
+
if (statement.importClause?.name?.text === typeName) {
|
86
|
+
return true;
|
87
|
+
}
|
88
|
+
|
89
|
+
return statement.importClause?.namedBindings?.elements?.some(
|
90
|
+
(element) => element.name?.text === typeName
|
91
|
+
);
|
92
|
+
})?.moduleSpecifier;
|
93
|
+
|
94
|
+
if (currentModuleSpecifier?.text?.startsWith(".")) {
|
95
|
+
return packageJSON?.name;
|
96
|
+
} else {
|
97
|
+
return Object.keys(packageJSON?.dependencies || {}).find(
|
98
|
+
(dependency) =>
|
99
|
+
currentModuleSpecifier?.text?.startsWith(`${dependency}/`)
|
100
|
+
);
|
101
|
+
}
|
102
|
+
}
|
103
|
+
};
|
104
|
+
|
105
|
+
const findImportPath = (ts, sourceFile, typeName, modulePath) => {
|
106
|
+
const localStatements = [
|
107
|
+
ts.SyntaxKind.EnumDeclaration,
|
108
|
+
ts.SyntaxKind.InterfaceDeclaration,
|
109
|
+
ts.SyntaxKind.ClassDeclaration,
|
110
|
+
ts.SyntaxKind.TypeAliasDeclaration,
|
111
|
+
];
|
112
|
+
|
113
|
+
const isLocalDeclared = sourceFile.statements.some(
|
114
|
+
(statement) =>
|
115
|
+
localStatements.includes(statement.kind) && statement?.name?.text === typeName
|
116
|
+
);
|
117
|
+
|
118
|
+
if (isLocalDeclared) {
|
119
|
+
return (
|
120
|
+
modulePath?.replace("src", "dist")?.replace(".ts", ".js") || undefined
|
121
|
+
);
|
122
|
+
} else {
|
123
|
+
const importStatements = sourceFile.statements?.filter(
|
124
|
+
(statement) => statement.kind === ts.SyntaxKind.ImportDeclaration
|
125
|
+
);
|
126
|
+
const currentModuleSpecifier = importStatements.find((statement) => {
|
127
|
+
if (statement.importClause?.name?.text === typeName) {
|
128
|
+
return true;
|
129
|
+
}
|
130
|
+
|
131
|
+
return statement.importClause?.namedBindings?.elements?.some(
|
132
|
+
(element) => element.name?.text === typeName
|
133
|
+
);
|
134
|
+
})?.moduleSpecifier;
|
135
|
+
|
136
|
+
if (currentModuleSpecifier?.text?.startsWith(".")) {
|
137
|
+
return (
|
138
|
+
path.join(path.dirname(modulePath), currentModuleSpecifier.text)
|
139
|
+
?.replace("src", "dist")?.replace(".ts", ".js") || undefined
|
140
|
+
);
|
141
|
+
} else {
|
142
|
+
const packageName = Object.keys(packageJSON?.dependencies || {}).find(
|
143
|
+
(dependency) =>
|
144
|
+
currentModuleSpecifier?.text?.startsWith(dependency)
|
145
|
+
);
|
146
|
+
return currentModuleSpecifier?.text
|
147
|
+
?.replace(`${packageName}/`, "") || undefined;
|
148
|
+
}
|
149
|
+
}
|
150
|
+
};
|
151
|
+
|
152
|
+
|
153
|
+
const isClass = text => {
|
154
|
+
return text.includes("@abstract") || text.includes("@class") || text.includes("@constructor");
|
155
|
+
};
|
156
|
+
|
157
|
+
const normalizeTagType = (type) => {
|
158
|
+
return type?.trim();
|
159
|
+
}
|
160
|
+
|
161
|
+
const packageJSON = JSON.parse(fs.readFileSync("./package.json"));
|
162
|
+
|
163
|
+
const getReference = (ts, type, classNode, modulePath) => {
|
164
|
+
let sourceFile = classNode.parent;
|
165
|
+
|
166
|
+
while (sourceFile && sourceFile.kind !== ts.SyntaxKind.SourceFile) {
|
167
|
+
sourceFile = sourceFile.parent;
|
168
|
+
}
|
169
|
+
|
170
|
+
const typeName =
|
171
|
+
typeof type === "string"
|
172
|
+
? normalizeTagType(type)
|
173
|
+
: type.class?.expression?.text ||
|
174
|
+
type.typeExpression?.type?.getText() ||
|
175
|
+
type.typeExpression?.type?.elementType?.typeName?.text;
|
176
|
+
const packageName = findPackageName(ts, sourceFile, typeName);
|
177
|
+
const importPath = findImportPath(
|
178
|
+
ts,
|
179
|
+
sourceFile,
|
180
|
+
typeName,
|
181
|
+
modulePath
|
182
|
+
)?.replace(`${packageName}/`, "");
|
183
|
+
|
184
|
+
return packageName && {
|
185
|
+
name: typeName,
|
186
|
+
package: packageName,
|
187
|
+
module: importPath,
|
188
|
+
};
|
189
|
+
};
|
190
|
+
|
191
|
+
const getType = (type) => {
|
192
|
+
const typeName = typeof type === "string" ? normalizeTagType(type) : type?.type;
|
193
|
+
|
194
|
+
const multiple =
|
195
|
+
typeName?.endsWith("[]") || typeName?.startsWith("Array<");
|
196
|
+
const name = multiple
|
197
|
+
? typeName?.replace("[]", "")?.replace("Array<", "")?.replace(">", "")
|
198
|
+
: typeName;
|
199
|
+
|
200
|
+
return typeName ? { typeName: multiple ? `Array<${name}>` : typeName, name, multiple } : undefined;
|
201
|
+
};
|
202
|
+
|
203
|
+
const commonTags = ["public", "protected", "private", "since", "deprecated"];
|
204
|
+
|
205
|
+
const allowedTags = {
|
206
|
+
field: [...commonTags, "formEvents", "formProperty", "default"],
|
207
|
+
slot: [...commonTags, "default"],
|
208
|
+
event: [...commonTags, "param", "allowPreventDefault", "native"],
|
209
|
+
eventParam: [...commonTags],
|
210
|
+
method: [...commonTags, "param", "returns", "override"],
|
211
|
+
class: [...commonTags, "constructor", "class", "abstract", "implements", "extends", "slot", "csspart"],
|
212
|
+
enum: [...commonTags],
|
213
|
+
enumMember: [...commonTags],
|
214
|
+
interface: [...commonTags],
|
215
|
+
};
|
216
|
+
allowedTags.getter = [...allowedTags.field, "override"]
|
217
|
+
|
218
|
+
const tagMatchCallback = (tag, tagName) => {
|
219
|
+
const currentTagName = tag.tag;
|
220
|
+
|
221
|
+
return typeof tagName === "string"
|
222
|
+
? currentTagName === tagName
|
223
|
+
: tagName.includes(currentTagName);
|
224
|
+
};
|
225
|
+
|
226
|
+
const findDecorator = (node, decoratorName) => {
|
227
|
+
return node?.decorators?.find(
|
228
|
+
(decorator) =>
|
229
|
+
decorator?.expression?.expression?.text === decoratorName
|
230
|
+
);
|
231
|
+
};
|
232
|
+
|
233
|
+
const findAllDecorators = (node, decoratorName) => {
|
234
|
+
return (
|
235
|
+
node?.decorators?.filter(
|
236
|
+
(decorator) =>
|
237
|
+
decorator?.expression?.expression?.text === decoratorName
|
238
|
+
) || []
|
239
|
+
);
|
240
|
+
};
|
241
|
+
|
242
|
+
const hasTag = (jsDoc, tagName) => {
|
243
|
+
if (!jsDoc) {
|
244
|
+
return;
|
245
|
+
}
|
246
|
+
|
247
|
+
return jsDoc?.tags?.some((tag) => tagMatchCallback(tag, tagName));
|
248
|
+
};
|
249
|
+
|
250
|
+
const findTag = (jsDoc, tagName) => {
|
251
|
+
if (!jsDoc) {
|
252
|
+
return;
|
253
|
+
}
|
254
|
+
|
255
|
+
return jsDoc?.tags?.find((tag) => tagMatchCallback(tag, tagName));
|
256
|
+
};
|
257
|
+
|
258
|
+
const findAllTags = (jsDoc, tagName) => {
|
259
|
+
if (!jsDoc) {
|
260
|
+
return [];
|
261
|
+
}
|
262
|
+
|
263
|
+
const foundTags = jsDoc?.tags?.filter((tag) => tagMatchCallback(tag, tagName));
|
264
|
+
|
265
|
+
return foundTags || [];
|
266
|
+
};
|
267
|
+
|
268
|
+
const validateJSDocTag = (tag) => {
|
269
|
+
const booleanTags = ["private", "protected", "public", "abstract", "allowPreventDefault", "native", "formProperty", "constructor", "override"];
|
270
|
+
let tagName = tag.tag;
|
271
|
+
|
272
|
+
if (booleanTags.includes(tag.tag)) {
|
273
|
+
tagName = "boolean";
|
274
|
+
}
|
275
|
+
|
276
|
+
switch (tagName) {
|
277
|
+
case "boolean":
|
278
|
+
return !tag.name && !tag.type && !tag.description;
|
279
|
+
case "deprecated":
|
280
|
+
return !tag.type;
|
281
|
+
case "extends":
|
282
|
+
return !tag.type && tag.name && !tag.description;
|
283
|
+
case "implements":
|
284
|
+
return tag.type && !tag.name && !tag.description;
|
285
|
+
case "slot":
|
286
|
+
return tag.type && tag.name && tag.description;
|
287
|
+
case "csspart":
|
288
|
+
return !tag.type && tag.name && tag.description;
|
289
|
+
case "since":
|
290
|
+
return !tag.type && tag.name;
|
291
|
+
case "returns":
|
292
|
+
return !tag.type && tag.name;
|
293
|
+
case "default":
|
294
|
+
return !tag.type && !tag.description;
|
295
|
+
case "class":
|
296
|
+
return !tag.type;
|
297
|
+
case "param":
|
298
|
+
return !tag.type && tag.name;
|
299
|
+
case "eventparam":
|
300
|
+
return tag.type && tag.name;
|
301
|
+
case "formEvents":
|
302
|
+
return !tag.type && tag.name;
|
303
|
+
default:
|
304
|
+
return false;
|
305
|
+
}
|
306
|
+
};
|
307
|
+
|
308
|
+
const validateJSDocComment = (fieldType, jsdocComment, node, moduleDoc) => {
|
309
|
+
return !!jsdocComment?.tags?.every((tag) => {
|
310
|
+
let isValid = false
|
311
|
+
|
312
|
+
if (fieldType === "event" && tag?.tag === "param") {
|
313
|
+
isValid = allowedTags[fieldType]?.includes(tag.tag) && validateJSDocTag({...tag, tag: "eventparam"});
|
314
|
+
} else {
|
315
|
+
isValid = allowedTags[fieldType]?.includes(tag.tag) && validateJSDocTag(tag);
|
316
|
+
}
|
317
|
+
|
318
|
+
if (!isValid) {
|
319
|
+
JSDocErrors.push(
|
320
|
+
`=== 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`
|
321
|
+
);
|
322
|
+
}
|
323
|
+
|
324
|
+
return !!isValid;
|
325
|
+
});
|
326
|
+
};
|
327
|
+
|
328
|
+
const getJSDocErrors = () => {
|
329
|
+
return JSDocErrors;
|
330
|
+
};
|
331
|
+
|
332
|
+
const formatArrays = (typeText) => {
|
333
|
+
return typeText.replaceAll(/(\S+)\[\]/g, "Array<$1>")
|
334
|
+
}
|
335
|
+
|
336
|
+
export {
|
337
|
+
getPrivacyStatus,
|
338
|
+
getSinceStatus,
|
339
|
+
getDeprecatedStatus,
|
340
|
+
getType,
|
341
|
+
getReference,
|
342
|
+
validateJSDocComment,
|
343
|
+
findDecorator,
|
344
|
+
findAllDecorators,
|
345
|
+
hasTag,
|
346
|
+
findTag,
|
347
|
+
findAllTags,
|
348
|
+
getJSDocErrors,
|
349
|
+
getTypeRefs,
|
350
|
+
normalizeDescription,
|
351
|
+
formatArrays,
|
352
|
+
isClass,
|
353
|
+
normalizeTagType
|
354
|
+
};
|
@@ -0,0 +1,67 @@
|
|
1
|
+
const fs = require('fs');
|
2
|
+
const Ajv = require('ajv');
|
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;
|
8
|
+
|
9
|
+
// Load your JSON schema
|
10
|
+
const extenalSchema = require('./schema.json');
|
11
|
+
const internalSchema = require('./schema-internal.json');
|
12
|
+
|
13
|
+
// Load your JSON data from the input file
|
14
|
+
const inputFilePath = path.join(process.cwd(), "dist/custom-elements.json"); // Update with your file path
|
15
|
+
const customManifest = fs.readFileSync(inputFilePath, 'utf8');
|
16
|
+
const inputDataInternal = JSON.parse(customManifest);
|
17
|
+
|
18
|
+
const clearProps = (data) => {
|
19
|
+
if (Array.isArray(data)) {
|
20
|
+
for (let i = 0; i < data.length; i++) {
|
21
|
+
if (typeof data[i] === "object") {
|
22
|
+
if (["enum", "interface"].includes(data[i].kind)) {
|
23
|
+
data.splice(i, 1);
|
24
|
+
i--;
|
25
|
+
} else {
|
26
|
+
clearProps(data[i]);
|
27
|
+
}
|
28
|
+
}
|
29
|
+
}
|
30
|
+
} else if (typeof data === "object") {
|
31
|
+
Object.keys(data).forEach(prop => {
|
32
|
+
if (prop.startsWith("_ui5")) {
|
33
|
+
delete data[prop];
|
34
|
+
} else if (typeof data[prop] === "object") {
|
35
|
+
clearProps(data[prop]);
|
36
|
+
}
|
37
|
+
});
|
38
|
+
}
|
39
|
+
|
40
|
+
return data;
|
41
|
+
}
|
42
|
+
|
43
|
+
const ajv = new Ajv({ allowUnionTypes: true, allError: true })
|
44
|
+
let validate = ajv.compile(internalSchema)
|
45
|
+
|
46
|
+
// Validate the JSON data against the schema
|
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
|
+
}
|
53
|
+
}
|
54
|
+
|
55
|
+
const inputDataExternal = clearProps(JSON.parse(JSON.stringify(inputDataInternal)));
|
56
|
+
validate = ajv.compile(extenalSchema)
|
57
|
+
|
58
|
+
// Validate the JSON data against the schema
|
59
|
+
if (validate(inputDataExternal)) {
|
60
|
+
console.log('Custom element manifest is validated successfully');
|
61
|
+
fs.writeFileSync(inputFilePath, JSON.stringify(inputDataExternal, null, 2), 'utf8');
|
62
|
+
fs.writeFileSync(inputFilePath.replace("custom-elements", "custom-elements-internal"), JSON.stringify(inputDataInternal, null, 2), 'utf8');
|
63
|
+
} else {
|
64
|
+
if (argv.dev) {
|
65
|
+
throw new Error(`Validation of public custom elements manifest failed: ${validate.errors}`);
|
66
|
+
}
|
67
|
+
}
|
@@ -57,15 +57,25 @@ const generate = async () => {
|
|
57
57
|
const illustrationsPrefix = process.argv[4];
|
58
58
|
const illustrationSet = process.argv[5];
|
59
59
|
const destPath = process.argv[6];
|
60
|
+
const collection = process.argv[7];
|
60
61
|
const fileNamePattern = new RegExp(`${illustrationsPrefix}-.+-(.+).svg`);
|
61
|
-
// collect each illustration name because each one should have Sample.js file
|
62
|
+
// collect each illustration name because each one should have Sample.js file
|
62
63
|
const fileNames = new Set();
|
63
64
|
|
65
|
+
try {
|
66
|
+
await fs.access(srcPath);
|
67
|
+
} catch (error) {
|
68
|
+
console.log(`The path ${srcPath} does not exist.`);
|
69
|
+
return Promise.resolve(null);
|
70
|
+
}
|
71
|
+
|
72
|
+
console.log(`Generating illustrations from ${srcPath} to ${destPath}`)
|
73
|
+
|
64
74
|
const svgImportTemplate = svgContent => {
|
65
75
|
return `export default \`${svgContent}\`;`
|
66
76
|
};
|
67
77
|
const svgToJs = async fileName => {
|
68
|
-
const svg = await fs.readFile(path.join(srcPath, fileName), {encoding: "utf-8"});
|
78
|
+
const svg = await fs.readFile(path.join(srcPath, fileName), { encoding: "utf-8" });
|
69
79
|
const fileContent = svgImportTemplate(svg);
|
70
80
|
fileName = fileName.replace(/\.svg$/, ".js");
|
71
81
|
|
@@ -84,54 +94,47 @@ const generate = async () => {
|
|
84
94
|
|
85
95
|
const illustrationNameUpperCase = illustrationNameForTranslation.toUpperCase();
|
86
96
|
|
87
|
-
return
|
97
|
+
return `import { registerIllustration } from "@ui5/webcomponents-base/dist/asset-registries/Illustrations.js";
|
88
98
|
import dialogSvg from "./${illustrationsPrefix}-Dialog-${illustrationName}.js";
|
89
99
|
import sceneSvg from "./${illustrationsPrefix}-Scene-${illustrationName}.js";
|
90
|
-
import spotSvg from "./${illustrationsPrefix}-Spot-${illustrationName}.js"
|
91
|
-
import {
|
100
|
+
import spotSvg from "./${illustrationsPrefix}-Spot-${illustrationName}.js";${
|
101
|
+
defaultText ? `import {
|
92
102
|
IM_TITLE_${illustrationNameUpperCase},
|
93
103
|
IM_SUBTITLE_${illustrationNameUpperCase},
|
94
|
-
} from "../generated/i18n/i18n-defaults.js"
|
104
|
+
} from "../generated/i18n/i18n-defaults.js";` : ``}
|
95
105
|
|
96
106
|
const name = "${illustrationName}";
|
97
107
|
const set = "${illustrationSet}";
|
108
|
+
const collection = "${collection}";${defaultText ? `
|
98
109
|
const title = IM_TITLE_${illustrationNameUpperCase};
|
99
|
-
const subtitle = IM_SUBTITLE_${illustrationNameUpperCase}
|
110
|
+
const subtitle = IM_SUBTITLE_${illustrationNameUpperCase};` : ``}
|
100
111
|
|
101
112
|
registerIllustration(name, {
|
102
113
|
dialogSvg,
|
103
114
|
sceneSvg,
|
104
|
-
spotSvg
|
115
|
+
spotSvg,${defaultText ? `
|
105
116
|
title,
|
106
|
-
subtitle
|
117
|
+
subtitle,` : ``}
|
107
118
|
set,
|
119
|
+
collection,
|
108
120
|
});
|
109
121
|
|
122
|
+
export default "${illustrationSet === "fiori" ? "" : `${illustrationSet}/`}${illustrationName}";
|
110
123
|
export {
|
111
124
|
dialogSvg,
|
112
125
|
sceneSvg,
|
113
126
|
spotSvg,
|
114
|
-
};`
|
115
|
-
|
116
|
-
import dialogSvg from "./${illustrationsPrefix}-Dialog-${illustrationName}.js";
|
117
|
-
import sceneSvg from "./${illustrationsPrefix}-Scene-${illustrationName}.js";
|
118
|
-
import spotSvg from "./${illustrationsPrefix}-Spot-${illustrationName}.js";
|
119
|
-
|
120
|
-
const name = "${illustrationName}";
|
121
|
-
const set = "${illustrationSet}";
|
127
|
+
};`
|
128
|
+
};
|
122
129
|
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
});
|
130
|
+
const illustrationTypeDefinition = illustrationName => {
|
131
|
+
return `declare const dialogSvg: string;
|
132
|
+
declare const sceneSvg: string;
|
133
|
+
declare const spotSvg: string;
|
134
|
+
declare const _default: "${illustrationSet === "fiori" ? "" : `${illustrationSet}/`}${illustrationName}";
|
129
135
|
|
130
|
-
export
|
131
|
-
|
132
|
-
sceneSvg,
|
133
|
-
spotSvg,
|
134
|
-
};`
|
136
|
+
export default _default;
|
137
|
+
export { dialogSvg, sceneSvg, spotSvg };`
|
135
138
|
};
|
136
139
|
|
137
140
|
await fs.mkdir(destPath, { recursive: true });
|
@@ -151,6 +154,7 @@ export {
|
|
151
154
|
|
152
155
|
for (let illustrationName of fileNames) {
|
153
156
|
promises.push(fs.writeFile(path.join(destPath, `${illustrationName}.js`), illustrationImportTemplate(illustrationName)));
|
157
|
+
promises.push(fs.writeFile(path.join(destPath, `${illustrationName}.d.ts`), illustrationTypeDefinition(illustrationName)));
|
154
158
|
}
|
155
159
|
|
156
160
|
return Promise.all(promises);
|
@@ -1,8 +1,31 @@
|
|
1
1
|
const fs = require("fs");
|
2
|
+
const path = require("path");
|
2
3
|
const prompts = require("prompts");
|
3
4
|
const jsFileContentTemplate = require("./jsFileContentTemplate.js");
|
4
5
|
const tsFileContentTemplate = require("./tsFileContentTemplate.js");
|
5
6
|
|
7
|
+
/**
|
8
|
+
* Hyphanates the given PascalCase string, f.e.:
|
9
|
+
* Foo -> "my-foo" (adds preffix)
|
10
|
+
* FooBar -> "foo-bar"
|
11
|
+
*/
|
12
|
+
const hyphaneteComponentName = (componentName) => {
|
13
|
+
const result = componentName.replace(/([a-z])([A-Z])/g, '$1-$2' ).toLowerCase();
|
14
|
+
|
15
|
+
return result.includes("-") ? result : `my-${result}`;
|
16
|
+
};
|
17
|
+
|
18
|
+
/**
|
19
|
+
* Capitalizes first letter of string.
|
20
|
+
*/
|
21
|
+
const capitalizeFirstLetter = string => string.charAt(0).toUpperCase() + string.slice(1);
|
22
|
+
|
23
|
+
/**
|
24
|
+
* Validates component name, enforcing PascalCase pattern - Button, MyButton.
|
25
|
+
*/
|
26
|
+
const PascalCasePattern = /^[A-Z][A-Za-z0-9]+$/;
|
27
|
+
const isNameValid = name => typeof name === "string" && PascalCasePattern.test(name);
|
28
|
+
|
6
29
|
const getPackageName = () => {
|
7
30
|
if (!fs.existsSync("./package.json")) {
|
8
31
|
throw("The current directory doesn't contain package.json file.");
|
@@ -35,13 +58,6 @@ const getLibraryName = packageName => {
|
|
35
58
|
return packageName.substr("webcomponents-".length);
|
36
59
|
};
|
37
60
|
|
38
|
-
// String manipulation
|
39
|
-
const capitalizeFirstLetter = string => string.charAt(0).toUpperCase() + string.slice(1);
|
40
|
-
|
41
|
-
// Validation of user input
|
42
|
-
const isNameValid = name => typeof name === "string" && name.match(/^[a-zA-Z][a-zA-Z0-9_-]*$/);
|
43
|
-
const isTagNameValid = tagName => tagName.match(/^([a-z][a-z0-9]*-)([a-z0-9]+(-[a-z0-9]+)*)$/);
|
44
|
-
|
45
61
|
const generateFiles = (componentName, tagName, library, packageName, isTypeScript) => {
|
46
62
|
componentName = capitalizeFirstLetter(componentName);
|
47
63
|
const filePaths = {
|
@@ -67,7 +83,7 @@ const generateFiles = (componentName, tagName, library, packageName, isTypeScrip
|
|
67
83
|
// Change the color of the output
|
68
84
|
console.warn('\x1b[33m%s\x1b[0m', `
|
69
85
|
Make sure to import the component in your bundle by using:
|
70
|
-
import
|
86
|
+
import "./dist/${componentName}.js";`);
|
71
87
|
}
|
72
88
|
|
73
89
|
// Main function
|
@@ -77,21 +93,9 @@ const createWebComponent = async () => {
|
|
77
93
|
|
78
94
|
const consoleArguments = process.argv.slice(2);
|
79
95
|
let componentName = consoleArguments[0];
|
80
|
-
let tagName = consoleArguments[1];
|
81
|
-
let language = consoleArguments[2];
|
82
|
-
let isTypeScript;
|
83
|
-
|
84
96
|
|
85
97
|
if (componentName && !isNameValid(componentName)) {
|
86
|
-
throw new Error(
|
87
|
-
}
|
88
|
-
|
89
|
-
if (tagName && !isTagNameValid(tagName)) {
|
90
|
-
throw new Error("Invalid tag name. The tag name should only contain lowercase letters, numbers, dashes, and underscores. The first character must be a letter, and it should follow the pattern 'tag-name'.");
|
91
|
-
}
|
92
|
-
|
93
|
-
if (language && language !== "typescript" && language !== "ts" && language !== "javascript" && language !== "js") {
|
94
|
-
throw new Error("Invalid language. Please use 'typescript','javascript' or their respective 'ts','js'.");
|
98
|
+
throw new Error(`${componentName} is invalid component name. Use only letters (at least two) and start with capital one: Button, MyButton, etc.`);
|
95
99
|
}
|
96
100
|
|
97
101
|
if (!componentName) {
|
@@ -99,7 +103,7 @@ const createWebComponent = async () => {
|
|
99
103
|
type: "text",
|
100
104
|
name: "componentName",
|
101
105
|
message: "Please enter a component name:",
|
102
|
-
validate: (value) => isNameValid(value),
|
106
|
+
validate: (value) => isNameValid(value) ? true : "Component name should follow PascalCase naming convention (f.e. Button, MyButton, etc.).",
|
103
107
|
});
|
104
108
|
componentName = response.componentName;
|
105
109
|
|
@@ -108,42 +112,8 @@ const createWebComponent = async () => {
|
|
108
112
|
}
|
109
113
|
}
|
110
114
|
|
111
|
-
|
112
|
-
|
113
|
-
type: "text",
|
114
|
-
name: "tagName",
|
115
|
-
message: "Please enter a tag name:",
|
116
|
-
validate: (value) => isTagNameValid(value),
|
117
|
-
});
|
118
|
-
tagName = response.tagName;
|
119
|
-
|
120
|
-
if (!tagName) {
|
121
|
-
process.exit();
|
122
|
-
}
|
123
|
-
}
|
124
|
-
|
125
|
-
if (!language) {
|
126
|
-
const response = await prompts({
|
127
|
-
type: "select",
|
128
|
-
name: "isTypeScript",
|
129
|
-
message: "Please select a language:",
|
130
|
-
choices: [
|
131
|
-
{
|
132
|
-
title: "TypeScript (recommended)",
|
133
|
-
value: true,
|
134
|
-
},
|
135
|
-
{
|
136
|
-
title: "JavaScript",
|
137
|
-
value: false,
|
138
|
-
},
|
139
|
-
],
|
140
|
-
});
|
141
|
-
isTypeScript = response.isTypeScript;
|
142
|
-
} else if (language === "typescript" || language === "ts") {
|
143
|
-
isTypeScript = true;
|
144
|
-
} else {
|
145
|
-
isTypeScript = false;
|
146
|
-
}
|
115
|
+
const isTypeScript = fs.existsSync(path.join(process.cwd(), "tsconfig.json"));
|
116
|
+
const tagName = hyphaneteComponentName(componentName);
|
147
117
|
|
148
118
|
generateFiles(componentName, tagName, library, packageName, isTypeScript);
|
149
119
|
};
|