Skip to content

Commit dd70f3a

Browse files
authored
fix(create-webcomponents-package): fix package creation issues with test and lint (#6976)
The change addresses few issues when generating a web component project with our tools: ### Command `@ui5/webcomponents-package` updated - added new parameter `componentName` and derive the tag name from it - added new (private) parameter `skipSubFolder` to generate all files in the execution context, not in a new folder (defined by the packageName parameter) as by default ### Command `create-web-component` updated - remove the "typescript" parameter and just check for the existence of `tsconfig.json` to generate TS or JS component. - update "componentName" validation to allow only letters, starting with capital one - Foo, FooBar, FooBarBar - remove "tag" parameter, the tag is now produced based on the component name - "foo-tag", "foo-bar", "foo-bar-bar" ### The `test` command - fixes failing `yarn test` command due to a bug in `test-runner.js` ### Themes updated - no longer showcase "belize", "horizon" is now used in the test page instead ### Eslint improved - added eslint config file pointing to the default config in `@ui5/webcomponents-tools` Fixes: #6980
1 parent 415e4a9 commit dd70f3a

File tree

17 files changed

+98
-94
lines changed

17 files changed

+98
-94
lines changed

packages/create-package/README.md

+2-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@ Usage:
1818
1919
Options:
2020
--name <string> - defines the package name
21-
--tag <string> - defines the tag name of the sample web component that will be created in your new package
21+
--componentName <string> - defines the component class name that will be created in your new package
22+
--tag <string> - defines the tag name of the sample web component that will be created in your new package. The tag will be derived from the component name if not provided.
2223
--enable-typescript - enables TypeScript support for the package
2324
--skip - skips configuration and generates package with a default value for each parameter that wasn't passed
2425
```

packages/create-package/create-package.js

+35-17
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,22 @@ const isNPMRC = sourcePath => {
3737
};
3838

3939
// Validation of user input
40-
const isNameValid = name => typeof name === "string" && name.match(/^[a-zA-Z0-9\-_]+$/);
40+
const ComponentNamePattern = /^[A-Z][A-Za-z0-9]+$/;
41+
const isNameValid = name => typeof name === "string" && name.match(/^[a-zA-Z][a-zA-Z0-9\-_]+$/);
42+
const isComponentNameValid = name => typeof name === "string" && ComponentNamePattern.test(name);
4143
const isTagValid = tag => typeof tag === "string" && tag.match(/^[a-z0-9]+?-[a-zA-Z0-9\-_]+?[a-z0-9]$/);
4244

45+
/**
46+
* Hyphanates the given PascalCase string, f.e.:
47+
* Foo -> "my-foo" (adds preffix)
48+
* FooBar -> "foo-bar"
49+
*/
50+
const hyphaneteComponentName = (componentName) => {
51+
const result = componentName.replace(/([a-z])([A-Z])/g, '$1-$2' ).toLowerCase();
52+
53+
return result.includes("-") ? result : `my-${result}`;
54+
};
55+
4356
// Utils for building the file structure
4457
const replaceVarsInFileContent = (vars, content) => {
4558
for (let key in vars) {
@@ -92,14 +105,14 @@ const copyFiles = (vars, sourcePath, destPath) => {
92105
}
93106
};
94107

95-
const generateFilesContent = (name, tag, typescript) => {
96-
const className = capitalizeFirst(kebabToCamelCase(tag));
108+
const generateFilesContent = (name, componentName, typescript, skipSubfolder) => {
109+
const tagName = argv.tag || hyphaneteComponentName(componentName);
97110

98111
// All variables that will be replaced in the content of the resources/
99112
const vars = {
100113
INIT_PACKAGE_VAR_NAME: name,
101-
INIT_PACKAGE_VAR_TAG: tag,
102-
INIT_PACKAGE_VAR_CLASS_NAME: className,
114+
INIT_PACKAGE_VAR_TAG: tagName,
115+
INIT_PACKAGE_VAR_CLASS_NAME: componentName,
103116
INIT_PACKAGE_VAR_TYPESCRIPT: typescript,
104117
};
105118

@@ -141,7 +154,7 @@ const generateFilesContent = (name, tag, typescript) => {
141154
}
142155

143156
// Update package.json
144-
const destDir = path.join(`./`, name);
157+
const destDir = skipSubfolder ? path.join("./") : path.join("./", name);
145158
mkdirp.sync(destDir);
146159
fs.writeFileSync(path.join(destDir, "package.json"), JSON.stringify(packageContent, null, 2));
147160
// Copy files
@@ -170,27 +183,32 @@ const generateFilesContent = (name, tag, typescript) => {
170183
const createWebcomponentsPackage = async () => {
171184
let response;
172185
if (argv.name && !isNameValid(argv.name)) {
173-
throw new Error("The package name should be a string (a-z, A-Z, 0-9).");
186+
throw new Error("The package name should be a string, starting with letter and containing the following symbols [a-z, A-Z, 0-9].");
187+
}
188+
189+
if (argv.componentName && !isComponentNameValid(argv.componentName)) {
190+
throw new Error("The component name should be a string, starting with a capital letter [A-Z][a-z], for example: Button, MyButton, etc.");
174191
}
175192

176193
if (argv.tag && !isTagValid(argv.tag) ) {
177-
throw new Error("The tag should be in kebab-case (my-first-component f.e) and it can't be a single word.");
194+
throw new Error("The tag should be in kebab-case (f.e my-component) and it can't be a single word.");
178195
}
179196

180197
let name = argv.name || "my-package";
181-
let tag = argv.tag || "my-first-component";
198+
let componentName = argv.componentName || "MyComponent";
182199
let typescriptSupport = !!argv.enableTypescript;
200+
const skipSubfolder = !!argv.skipSubfolder;
183201

184202
if (argv.skip) {
185-
return generateFilesContent(name, tag, typescriptSupport);
203+
return generateFilesContent(name, componentName, typescriptSupport, skipSubfolder);
186204
}
187205

188206
if (!argv.name) {
189207
response = await prompts({
190208
type: "text",
191209
name: "name",
192210
message: "Package name:",
193-
validate: isNameValid,
211+
validate: (value) => isNameValid(value) ? true : "Package name should be a string, starting with a letter and containing the following symbols [a-z, A-Z ,0-9, _, -].",
194212
});
195213
name = response.name;
196214
}
@@ -214,18 +232,18 @@ const createWebcomponentsPackage = async () => {
214232
typescriptSupport = response.language;
215233
}
216234

217-
if (!argv.tag) {
235+
if (!argv.componentName) {
218236
response = await prompts({
219237
type: "text",
220-
name: "tag",
238+
name: "componentName",
221239
message: "Component name:",
222-
initial: "my-first-component",
223-
validate: isTagValid,
240+
initial: "MyComponent",
241+
validate: (value) => isComponentNameValid(value) ? true : "Component name should follow PascalCase naming convention (f.e. Button, MyButton, etc.).",
224242
});
225-
tag = response.tag;
243+
componentName = response.componentName;
226244
}
227245

228-
return generateFilesContent(name, tag, typescriptSupport);
246+
return generateFilesContent(name, componentName, typescriptSupport, skipSubfolder);
229247
};
230248

231249
createWebcomponentsPackage();

packages/create-package/template/.eslintignore

+2-1
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,5 @@
22
dist
33
test
44
src/generated
5-
jsdoc-dist
5+
jsdoc-dist
6+
.eslintrc.js
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
const config = require("@ui5/webcomponents-tools/components-package/eslint.js");
2+
3+
// This eslint config is defined @ui5/webcomponents-tools,
4+
// Feel free to override part of the configuration or provide entirely new config to fit your dev requirements.
5+
module.exports = config;

packages/create-package/template/src/themes/sap_belize_hcw/parameters-bundle.css

-3
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
:root {
2+
--my-component-border-color: blue;
3+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
:root {
2+
--my-component-border-color: white;
3+
}

packages/create-package/template/test/pages/index.html

+5-4
Original file line numberDiff line numberDiff line change
@@ -28,13 +28,14 @@
2828
<li><a href="?sap-ui-theme=sap_fiori_3_dark">Fiori 3 Dark</a></li>
2929
<li><a href="?sap-ui-theme=sap_fiori_3_hcb">Fiori 3 High Contrast Black</a></li>
3030
<li><a href="?sap-ui-theme=sap_fiori_3_hcw">Fiori 3 High Contrast White</a></li>
31-
<li><a href="?sap-ui-theme=sap_belize">Belize</a></li>
32-
<li><a href="?sap-ui-theme=sap_belize_hcb">Belize High Contrast Black</a></li>
33-
<li><a href="?sap-ui-theme=sap_belize_hcw">Belize High Contrast White</a></li>
31+
<li><a href="?sap-ui-theme=sap_horizon">Horizon</a></li>
32+
<li><a href="?sap-ui-theme=sap_horizon_dark">Horizon Dark</a></li>
33+
<li><a href="?sap-ui-theme=sap_horizon_hcb">Horizon High Contrast Black</a></li>
34+
<li><a href="?sap-ui-theme=sap_horizon_hcw">Horizon High Contrast White</a></li>
3435
</ul>
3536
<br>
3637
<span>or in the browser console, for example:</span>
37-
<code>window['sap-ui-webcomponents-bundle'].configuration.setTheme("sap_belize_hcb")</code>
38+
<code>window['sap-ui-webcomponents-bundle'].configuration.setTheme("sap_horizon_hcb")</code>
3839

3940
<br><br>
4041

packages/theming/css-vars-usage.json

+12
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,8 @@
218218
"--sapCriticalTextColor",
219219
"--sapElement_BorderCornerRadius",
220220
"--sapElement_BorderWidth",
221+
"--sapElement_Compact_Height",
222+
"--sapElement_Height",
221223
"--sapErrorBackground",
222224
"--sapErrorBorderColor",
223225
"--sapField_Active_BorderColor",
@@ -231,29 +233,39 @@
231233
"--sapField_Hover_Background",
232234
"--sapField_Hover_BackgroundStyle",
233235
"--sapField_Hover_BorderColor",
236+
"--sapField_Hover_InformationShadow",
237+
"--sapField_Hover_InvalidShadow",
238+
"--sapField_Hover_Shadow",
239+
"--sapField_Hover_SuccessShadow",
240+
"--sapField_Hover_WarningShadow",
234241
"--sapField_HoverBorderColor",
235242
"--sapField_InformationBackground",
236243
"--sapField_InformationBackgroundStyle",
237244
"--sapField_InformationBorderWidth",
238245
"--sapField_InformationColor",
246+
"--sapField_InformationShadow",
239247
"--sapField_InvalidBackground",
240248
"--sapField_InvalidBackgroundStyle",
241249
"--sapField_InvalidBorderWidth",
242250
"--sapField_InvalidColor",
251+
"--sapField_InvalidShadow",
243252
"--sapField_PlaceholderTextColor",
244253
"--sapField_ReadOnly_Background",
245254
"--sapField_ReadOnly_BackgroundStyle",
246255
"--sapField_ReadOnly_BorderColor",
247256
"--sapField_RequiredColor",
257+
"--sapField_Shadow",
248258
"--sapField_SuccessBackground",
249259
"--sapField_SuccessBackgroundStyle",
250260
"--sapField_SuccessBorderWidth",
251261
"--sapField_SuccessColor",
262+
"--sapField_SuccessShadow",
252263
"--sapField_TextColor",
253264
"--sapField_WarningBackground",
254265
"--sapField_WarningBackgroundStyle",
255266
"--sapField_WarningBorderWidth",
256267
"--sapField_WarningColor",
268+
"--sapField_WarningShadow",
257269
"--sapFontBoldFamily",
258270
"--sapFontFamily",
259271
"--sapFontHeader1Size",

packages/tools/lib/create-new-component/index.js

+27-57
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,31 @@
11
const fs = require("fs");
2+
const path = require("path");
23
const prompts = require("prompts");
34
const jsFileContentTemplate = require("./jsFileContentTemplate.js");
45
const tsFileContentTemplate = require("./tsFileContentTemplate.js");
56

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+
629
const getPackageName = () => {
730
if (!fs.existsSync("./package.json")) {
831
throw("The current directory doesn't contain package.json file.");
@@ -35,13 +58,6 @@ const getLibraryName = packageName => {
3558
return packageName.substr("webcomponents-".length);
3659
};
3760

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-
4561
const generateFiles = (componentName, tagName, library, packageName, isTypeScript) => {
4662
componentName = capitalizeFirstLetter(componentName);
4763
const filePaths = {
@@ -77,29 +93,17 @@ const createWebComponent = async () => {
7793

7894
const consoleArguments = process.argv.slice(2);
7995
let componentName = consoleArguments[0];
80-
let tagName = consoleArguments[1];
81-
let language = consoleArguments[2];
82-
let isTypeScript;
83-
8496

8597
if (componentName && !isNameValid(componentName)) {
86-
throw new Error("Invalid component name. Please use only letters, numbers, dashes and underscores. The first character must be a letter.");
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.`);
9599
}
96100

97101
if (!componentName) {
98102
const response = await prompts({
99103
type: "text",
100104
name: "componentName",
101105
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.).",
103107
});
104108
componentName = response.componentName;
105109

@@ -108,42 +112,8 @@ const createWebComponent = async () => {
108112
}
109113
}
110114

111-
if (!tagName) {
112-
const response = await prompts({
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);
147117

148118
generateFiles(componentName, tagName, library, packageName, isTypeScript);
149119
};

packages/tools/lib/create-new-component/jsFileContentTemplate.js

-4
Original file line numberDiff line numberDiff line change
@@ -62,10 +62,6 @@ class ${componentName} extends UI5Element {
6262
static get dependencies() {
6363
return [];
6464
}
65-
66-
static async onDefine() {
67-
68-
}
6965
}
7066
7167
${componentName}.define();

packages/tools/lib/create-new-component/tsFileContentTemplate.js

-4
Original file line numberDiff line numberDiff line change
@@ -69,10 +69,6 @@ class ${componentName} extends UI5Element {
6969
*/
7070
@slot({ type: Node, "default": true })
7171
text!: Array<Node>;
72-
73-
static async onDefine() {
74-
75-
}
7672
}
7773
7874
${componentName}.define();

packages/tools/lib/test-runner/test-runner.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -61,8 +61,8 @@ if (process.argv.length > 3) {
6161
let wdioConfig = "";
6262
if (fs.existsSync("config/wdio.conf.cjs")) {
6363
wdioConfig = "config/wdio.conf.cjs";
64-
} else {
65-
fs.existsSync("config/wdio.conf.js")
64+
} else if (fs.existsSync("config/wdio.conf.js")) {
65+
wdioConfig = "config/wdio.conf.js";
6666
}
6767

6868
// run wdio with calculated parameters

packages/tools/package.json

+1
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@
5757
"postcss-cli": "^9.1.0",
5858
"postcss-import": "^14.0.2",
5959
"postcss-selector-parser": "^6.0.10",
60+
"prompts": "^2.4.2",
6061
"properties-reader": "^2.2.0",
6162
"recursive-readdir": "^2.2.2",
6263
"resolve": "^1.20.0",

yarn.lock

+1-1
Original file line numberDiff line numberDiff line change
@@ -11878,7 +11878,7 @@ promise-retry@^2.0.1:
1187811878
err-code "^2.0.2"
1187911879
retry "^0.12.0"
1188011880

11881-
prompts@^2.4.0, prompts@^2.4.1:
11881+
prompts@^2.4.0, prompts@^2.4.1, prompts@^2.4.2:
1188211882
version "2.4.2"
1188311883
resolved "https://registry.yarnpkg.com/prompts/-/prompts-2.4.2.tgz#7b57e73b3a48029ad10ebd44f74b01722a4cb069"
1188411884
integrity sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==

0 commit comments

Comments
 (0)