@ui5/webcomponents-tools 0.0.0-f42e7c18c → 0.0.0-f6676abdd
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 +134 -0
- package/assets-meta.js +0 -4
- package/components-package/cypress/support/commands.js +28 -1
- package/components-package/cypress/support/component.d.ts +2 -3
- package/components-package/cypress/support/component.js +31 -19
- package/components-package/cypress/support/cypress-ct-preact.js +11 -0
- package/components-package/cypress.config.js +17 -3
- package/components-package/eslint.js +7 -3
- package/components-package/nps.js +6 -3
- package/components-package/vite.config.js +7 -11
- package/components-package/wdio.js +1 -1
- package/lib/cem/custom-elements-manifest.config.mjs +18 -1
- package/lib/cem/event.mjs +4 -4
- package/lib/cem/utils.mjs +45 -30
- package/lib/create-new-component/{tsFileContentTemplate.js → Component.js} +11 -8
- package/lib/create-new-component/ComponentTemplate.js +12 -0
- package/lib/create-new-component/index.js +7 -6
- package/lib/css-processors/css-processor-components.mjs +1 -1
- package/lib/css-processors/css-processor-themes.mjs +2 -7
- package/lib/css-processors/shared.mjs +2 -22
- package/lib/dev-server/virtual-index-html-plugin.js +24 -20
- package/package.json +8 -4
- package/tsconfig.json +2 -0
package/CHANGELOG.md
CHANGED
@@ -3,6 +3,140 @@
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
5
5
|
|
6
|
+
# [2.8.0-rc.1](https://github.com/SAP/ui5-webcomponents/compare/v2.8.0-rc.0...v2.8.0-rc.1) (2025-02-13)
|
7
|
+
|
8
|
+
**Note:** Version bump only for package @ui5/webcomponents-tools
|
9
|
+
|
10
|
+
|
11
|
+
|
12
|
+
|
13
|
+
|
14
|
+
# [2.8.0-rc.0](https://github.com/SAP/ui5-webcomponents/compare/v2.7.0...v2.8.0-rc.0) (2025-02-06)
|
15
|
+
|
16
|
+
**Note:** Version bump only for package @ui5/webcomponents-tools
|
17
|
+
|
18
|
+
|
19
|
+
|
20
|
+
|
21
|
+
|
22
|
+
# [2.7.0](https://github.com/SAP/ui5-webcomponents/compare/v2.7.0-rc.2...v2.7.0) (2025-02-03)
|
23
|
+
|
24
|
+
**Note:** Version bump only for package @ui5/webcomponents-tools
|
25
|
+
|
26
|
+
|
27
|
+
|
28
|
+
|
29
|
+
|
30
|
+
# [2.7.0-rc.2](https://github.com/SAP/ui5-webcomponents/compare/v2.7.0-rc.1...v2.7.0-rc.2) (2025-01-30)
|
31
|
+
|
32
|
+
**Note:** Version bump only for package @ui5/webcomponents-tools
|
33
|
+
|
34
|
+
|
35
|
+
|
36
|
+
|
37
|
+
|
38
|
+
# [2.7.0-rc.1](https://github.com/SAP/ui5-webcomponents/compare/v2.7.0-rc.0...v2.7.0-rc.1) (2025-01-23)
|
39
|
+
|
40
|
+
**Note:** Version bump only for package @ui5/webcomponents-tools
|
41
|
+
|
42
|
+
|
43
|
+
|
44
|
+
|
45
|
+
|
46
|
+
# [2.7.0-rc.0](https://github.com/SAP/ui5-webcomponents/compare/v2.6.2...v2.7.0-rc.0) (2025-01-16)
|
47
|
+
|
48
|
+
**Note:** Version bump only for package @ui5/webcomponents-tools
|
49
|
+
|
50
|
+
|
51
|
+
|
52
|
+
|
53
|
+
|
54
|
+
## [2.6.2](https://github.com/SAP/ui5-webcomponents/compare/v2.6.2-rc.0...v2.6.2) (2025-01-09)
|
55
|
+
|
56
|
+
**Note:** Version bump only for package @ui5/webcomponents-tools
|
57
|
+
|
58
|
+
|
59
|
+
|
60
|
+
|
61
|
+
|
62
|
+
## [2.6.2-rc.0](https://github.com/SAP/ui5-webcomponents/compare/v2.6.1...v2.6.2-rc.0) (2025-01-09)
|
63
|
+
|
64
|
+
**Note:** Version bump only for package @ui5/webcomponents-tools
|
65
|
+
|
66
|
+
|
67
|
+
|
68
|
+
|
69
|
+
|
70
|
+
## [2.6.1](https://github.com/SAP/ui5-webcomponents/compare/v2.6.0...v2.6.1) (2025-01-08)
|
71
|
+
|
72
|
+
**Note:** Version bump only for package @ui5/webcomponents-tools
|
73
|
+
|
74
|
+
|
75
|
+
|
76
|
+
|
77
|
+
|
78
|
+
# [2.6.0](https://github.com/SAP/ui5-webcomponents/compare/v2.6.0-rc.5...v2.6.0) (2025-01-07)
|
79
|
+
|
80
|
+
**Note:** Version bump only for package @ui5/webcomponents-tools
|
81
|
+
|
82
|
+
|
83
|
+
|
84
|
+
|
85
|
+
|
86
|
+
# [2.6.0-rc.5](https://github.com/SAP/ui5-webcomponents/compare/v2.6.0-rc.4...v2.6.0-rc.5) (2025-01-07)
|
87
|
+
|
88
|
+
**Note:** Version bump only for package @ui5/webcomponents-tools
|
89
|
+
|
90
|
+
|
91
|
+
|
92
|
+
|
93
|
+
|
94
|
+
# [2.6.0-rc.4](https://github.com/SAP/ui5-webcomponents/compare/v2.6.0-rc.3...v2.6.0-rc.4) (2025-01-02)
|
95
|
+
|
96
|
+
**Note:** Version bump only for package @ui5/webcomponents-tools
|
97
|
+
|
98
|
+
|
99
|
+
|
100
|
+
|
101
|
+
|
102
|
+
# [2.6.0-rc.3](https://github.com/SAP/ui5-webcomponents/compare/v2.6.0-rc.2...v2.6.0-rc.3) (2024-12-26)
|
103
|
+
|
104
|
+
**Note:** Version bump only for package @ui5/webcomponents-tools
|
105
|
+
|
106
|
+
|
107
|
+
|
108
|
+
|
109
|
+
|
110
|
+
# [2.6.0-rc.2](https://github.com/SAP/ui5-webcomponents/compare/v2.6.0-rc.1...v2.6.0-rc.2) (2024-12-19)
|
111
|
+
|
112
|
+
|
113
|
+
### Bug Fixes
|
114
|
+
|
115
|
+
* **build:** generation of custom-elements.json ([#10403](https://github.com/SAP/ui5-webcomponents/issues/10403)) ([c4eb55c](https://github.com/SAP/ui5-webcomponents/commit/c4eb55cba3e45677c464df1f8cf2a17e6855f2a0))
|
116
|
+
|
117
|
+
|
118
|
+
|
119
|
+
|
120
|
+
|
121
|
+
# [2.6.0-rc.1](https://github.com/SAP/ui5-webcomponents/compare/v2.6.0-rc.0...v2.6.0-rc.1) (2024-12-16)
|
122
|
+
|
123
|
+
**Note:** Version bump only for package @ui5/webcomponents-tools
|
124
|
+
|
125
|
+
|
126
|
+
|
127
|
+
|
128
|
+
|
129
|
+
# [2.6.0-rc.0](https://github.com/SAP/ui5-webcomponents/compare/v2.5.0...v2.6.0-rc.0) (2024-12-12)
|
130
|
+
|
131
|
+
|
132
|
+
### Features
|
133
|
+
|
134
|
+
* **framework:** add JSX template support with TypeScript ([#10046](https://github.com/SAP/ui5-webcomponents/issues/10046)) ([f42e7c1](https://github.com/SAP/ui5-webcomponents/commit/f42e7c18c846f923df4fec6ae02f1b4c20c006fa))
|
135
|
+
|
136
|
+
|
137
|
+
|
138
|
+
|
139
|
+
|
6
140
|
# [2.5.0](https://github.com/SAP/ui5-webcomponents/compare/v2.5.0-rc.3...v2.5.0) (2024-12-05)
|
7
141
|
|
8
142
|
**Note:** Version bump only for package @ui5/webcomponents-tools
|
package/assets-meta.js
CHANGED
@@ -36,4 +36,31 @@
|
|
36
36
|
// }
|
37
37
|
// }
|
38
38
|
|
39
|
-
import "cypress-real-events";
|
39
|
+
import "cypress-real-events";
|
40
|
+
|
41
|
+
const realEventCmdCallback = (originalFn, element, ...args) => {
|
42
|
+
cy.get(element)
|
43
|
+
.should($el => {
|
44
|
+
if ($el[0].tagName.includes("-") && $el[0].shadowRoot) {
|
45
|
+
expect($el[0].shadowRoot.hasChildNodes(), "Custom elements with shadow DOM have content in their shadow DOM").to.be.true;
|
46
|
+
}
|
47
|
+
})
|
48
|
+
.and("be.visible")
|
49
|
+
.then(() => {
|
50
|
+
return originalFn(element, ...args)
|
51
|
+
});
|
52
|
+
};
|
53
|
+
|
54
|
+
const commands = [
|
55
|
+
"realClick",
|
56
|
+
"realHover",
|
57
|
+
"realTouch",
|
58
|
+
"realSwipe",
|
59
|
+
"realMouseDown",
|
60
|
+
"realMouseUp",
|
61
|
+
"realMouseMove"
|
62
|
+
];
|
63
|
+
|
64
|
+
commands.forEach(cmd => {
|
65
|
+
Cypress.Commands.overwrite(cmd, realEventCmdCallback)
|
66
|
+
});
|
@@ -1,5 +1,4 @@
|
|
1
1
|
/// <reference types="cypress" />
|
2
|
-
import { RenderOptions, HTMLTemplateResult } from 'lit';
|
3
2
|
import "cypress-real-events";
|
4
3
|
|
5
4
|
export type Renderable = HTMLTemplateResult;
|
@@ -7,13 +6,13 @@ export interface MountUI5Options extends MountLitTemplateOptions {
|
|
7
6
|
ui5Configuration: object;
|
8
7
|
}
|
9
8
|
export type MountOptions = Partial<MountUI5Options>;
|
10
|
-
export declare function mount
|
9
|
+
export declare function mount(component: JSX.Element, options?: MountOptions): CypressChainable<JQuery<HTMLElement>>;
|
11
10
|
declare global {
|
12
11
|
namespace Cypress {
|
13
12
|
interface Chainable {
|
14
13
|
/**
|
15
14
|
* Mount your component into Cypress sandbox
|
16
|
-
* @param component content to render by
|
15
|
+
* @param component content to render by preact render function
|
17
16
|
* @param options render options for custom rendering
|
18
17
|
*/
|
19
18
|
mount: typeof mount;
|
@@ -1,34 +1,46 @@
|
|
1
|
-
import
|
2
|
-
import {
|
3
|
-
import { mount } from
|
1
|
+
import "@cypress/code-coverage/support";
|
2
|
+
import { setupHooks, getContainerEl } from "@cypress/mount-utils";
|
3
|
+
import { mount as preactMount } from "./cypress-ct-preact.js";
|
4
4
|
import "./commands.js";
|
5
5
|
|
6
|
-
|
6
|
+
function applyConfiguration(options) {
|
7
|
+
const configurationScript = document.head.querySelector("script[data-ui5-config]");
|
8
|
+
|
9
|
+
if (options.ui5Configuration) {
|
10
|
+
configurationScript.innerHTML = JSON.stringify(options.ui5Configuration);
|
11
|
+
}
|
12
|
+
}
|
7
13
|
|
8
14
|
function cleanup() {
|
9
|
-
|
15
|
+
preactMount(null, getContainerEl());
|
10
16
|
}
|
11
17
|
|
12
|
-
function
|
13
|
-
const
|
14
|
-
cleanup();
|
18
|
+
function mount(component, options = {}) {
|
19
|
+
const container = getContainerEl();
|
15
20
|
|
16
|
-
|
17
|
-
|
21
|
+
// Apply custom configuration
|
22
|
+
applyConfiguration(options);
|
18
23
|
|
19
|
-
|
24
|
+
// Mount JSX Element
|
25
|
+
preactMount(component, container);
|
20
26
|
|
21
|
-
|
22
|
-
|
23
|
-
|
27
|
+
cy.get(container)
|
28
|
+
.find("*")
|
29
|
+
.should($el => {
|
30
|
+
const shadowrootsExist = [...$el].every(el => {
|
31
|
+
if (el.tagName.includes("-") && el.shadowRoot) {
|
32
|
+
return el.shadowRoot.hasChildNodes();
|
33
|
+
}
|
24
34
|
|
25
|
-
|
26
|
-
|
27
|
-
|
35
|
+
return true;
|
36
|
+
})
|
37
|
+
|
38
|
+
expect(shadowrootsExist, "Custom elements with shadow DOM have content in their shadow DOM").to.be.true;
|
39
|
+
})
|
28
40
|
|
29
|
-
return
|
41
|
+
return cy.get(container);
|
30
42
|
}
|
31
43
|
|
32
44
|
setupHooks(cleanup);
|
33
45
|
|
34
|
-
Cypress.Commands.add('mount',
|
46
|
+
Cypress.Commands.add('mount', mount)
|
@@ -1,13 +1,27 @@
|
|
1
1
|
const { defineConfig } = require('cypress')
|
2
2
|
const path = require("path");
|
3
|
+
const coverageTask = require('@cypress/code-coverage/task');
|
4
|
+
|
5
|
+
const suites = {
|
6
|
+
SUITE1: [
|
7
|
+
"**/specs/base/*.cy.{jsx,tsx}",
|
8
|
+
"**/specs/[A-I]*.cy.{js,jsx,ts,tsx}",
|
9
|
+
],
|
10
|
+
SUITE2: [
|
11
|
+
"**/specs/[^A-I]*.cy.{js,jsx,ts,tsx}",
|
12
|
+
],
|
13
|
+
};
|
3
14
|
|
4
15
|
module.exports = defineConfig({
|
5
16
|
component: {
|
17
|
+
setupNodeEvents(on, config) {
|
18
|
+
coverageTask(on, config)
|
19
|
+
return config
|
20
|
+
},
|
6
21
|
supportFile: path.join(__dirname, "cypress/support/component.js"),
|
7
22
|
indexHtmlFile: path.join(__dirname, "cypress/support/component-index.html"),
|
8
|
-
specPattern: [
|
23
|
+
specPattern: suites[process.env.TEST_SUITE] || ["**/specs/**/*.cy.{js,ts,jsx,tsx}"],
|
9
24
|
devServer: {
|
10
|
-
framework: 'cypress-ct-lit',
|
11
25
|
bundler: 'vite',
|
12
26
|
}
|
13
27
|
},
|
@@ -16,4 +30,4 @@ module.exports = defineConfig({
|
|
16
30
|
scrollBehavior: false,
|
17
31
|
viewportHeight: 1080,
|
18
32
|
viewportWidth: 1440,
|
19
|
-
})
|
33
|
+
})
|
@@ -4,9 +4,9 @@ const tsMode = fs.existsSync(path.join(process.cwd(), "tsconfig.json"));
|
|
4
4
|
|
5
5
|
/**
|
6
6
|
* Returns eslint rules specific to typescript files
|
7
|
-
* @returns
|
7
|
+
* @returns
|
8
8
|
*/
|
9
|
-
const getTsModeOverrides = () => {
|
9
|
+
const getTsModeOverrides = () => {
|
10
10
|
const tsConfiguration = {
|
11
11
|
files: ["*.ts"],
|
12
12
|
parser: "@typescript-eslint/parser",
|
@@ -25,6 +25,7 @@ const getTsModeOverrides = () => {
|
|
25
25
|
rules: {
|
26
26
|
"no-shadow": "off",
|
27
27
|
"@typescript-eslint/consistent-type-imports": "error",
|
28
|
+
"import/consistent-type-specifier-style": ["error", "prefer-top-level"],
|
28
29
|
"@typescript-eslint/no-shadow": ["error"],
|
29
30
|
"@typescript-eslint/no-unsafe-member-access": "off",
|
30
31
|
"@typescript-eslint/no-floating-promises": "off",
|
@@ -41,8 +42,10 @@ const getTsModeOverrides = () => {
|
|
41
42
|
|
42
43
|
const tsxConfiguration = JSON.parse(JSON.stringify(tsConfiguration));
|
43
44
|
tsxConfiguration.files = ["*.tsx"];
|
45
|
+
tsxConfiguration.plugins.push("jsx-no-leaked-values");
|
44
46
|
tsxConfiguration.rules = {
|
45
47
|
...tsxConfiguration.rules,
|
48
|
+
"jsx-no-leaked-values/jsx-no-leaked-values": "error",
|
46
49
|
"@typescript-eslint/unbound-method": "off", // to be able to attach on* listeners
|
47
50
|
"@typescript-eslint/no-misused-promises": "off", // to be able to have async event listeners
|
48
51
|
"operator-linebreak": "off",
|
@@ -53,7 +56,7 @@ const getTsModeOverrides = () => {
|
|
53
56
|
};
|
54
57
|
|
55
58
|
const cypressConfiguration = {
|
56
|
-
"files": ["**/cypress/**/*.ts"],
|
59
|
+
"files": ["**/cypress/**/*.ts", "**/cypress/**/*.tsx"],
|
57
60
|
|
58
61
|
"plugins": [
|
59
62
|
"cypress"
|
@@ -66,6 +69,7 @@ const getTsModeOverrides = () => {
|
|
66
69
|
},
|
67
70
|
"rules": {
|
68
71
|
"max-nested-callbacks": 0,
|
72
|
+
"no-unused-expressions": 0,
|
69
73
|
"@typescript-eslint/no-namespace": "off",
|
70
74
|
"cypress/no-assigning-return-values": "error",
|
71
75
|
"cypress/no-unnecessary-waiting": "error",
|
@@ -19,7 +19,7 @@ const getScripts = (options) => {
|
|
19
19
|
// The script creates the "src/generated/js-imports/Illustration.js" file that registers loaders (dynamic JS imports) for each illustration
|
20
20
|
const createIllustrationsLoadersScript = illustrationsData.map(illustrations => `node ${LIB}/generate-js-imports/illustrations.js ${illustrations.destinationPath} ${illustrations.dynamicImports.outputFile} ${illustrations.set} ${illustrations.collection} ${illustrations.dynamicImports.location} ${illustrations.dynamicImports.filterOut.join(" ")}`).join(" && ");
|
21
21
|
|
22
|
-
const tsOption = !options.legacy;
|
22
|
+
const tsOption = !options.legacy || options.jsx;
|
23
23
|
const tsCommandOld = tsOption ? "tsc" : "";
|
24
24
|
let tsWatchCommandStandalone = tsOption ? "tsc --watch" : "";
|
25
25
|
// this command is only used for standalone projects. monorepo projects get their watch from vite, so opt-out here
|
@@ -122,8 +122,11 @@ const getScripts = (options) => {
|
|
122
122
|
},
|
123
123
|
start: "nps prepare watch.devServer",
|
124
124
|
test: `node "${LIB}/test-runner/test-runner.js"`,
|
125
|
-
"test-cy-ci": `yarn cypress run --component --browser chrome`,
|
126
|
-
"test-cy-
|
125
|
+
"test-cy-ci": `cross-env CYPRESS_COVERAGE=true yarn cypress run --component --browser chrome`,
|
126
|
+
"test-cy-ci-suite-1": `cross-env CYPRESS_COVERAGE=true TEST_SUITE=SUITE1 yarn cypress run --component --browser chrome`,
|
127
|
+
"test-cy-ci-suite-2": `cross-env CYPRESS_COVERAGE=true TEST_SUITE=SUITE2 yarn cypress run --component --browser chrome`,
|
128
|
+
"test-cy-open": `cross-env CYPRESS_COVERAGE=true yarn cypress open --component --browser chrome`,
|
129
|
+
"test-cy-single": `cross-env yarn cypress run --component --browser chrome --spec ${process.argv[3]}`,
|
127
130
|
"test-suite-1": `node "${LIB}/test-runner/test-runner.js" --suite suite1`,
|
128
131
|
"test-suite-2": `node "${LIB}/test-runner/test-runner.js" --suite suite2`,
|
129
132
|
startWithScope: "nps scope.prepare scope.watchWithBundle",
|
@@ -1,13 +1,9 @@
|
|
1
1
|
// vite.config.js
|
2
|
-
|
3
|
-
import virtualIndex from '../lib/dev-server/virtual-index-html-plugin.js';
|
2
|
+
const virtualIndex = require('../lib/dev-server/virtual-index-html-plugin.js');
|
4
3
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
plugins: [data],
|
12
|
-
}
|
13
|
-
})
|
4
|
+
module.exports = {
|
5
|
+
build: {
|
6
|
+
emptyOutDir: false,
|
7
|
+
},
|
8
|
+
plugins: [virtualIndex()],
|
9
|
+
};
|
@@ -60,7 +60,7 @@ exports.config = {
|
|
60
60
|
// to run chrome headless the following flags are required
|
61
61
|
// (see https://developers.google.com/web/updates/2017/04/headless-chrome)
|
62
62
|
args: [
|
63
|
-
'--headless
|
63
|
+
'--headless',
|
64
64
|
'--disable-search-engine-choice-screen',
|
65
65
|
'--start-maximized',
|
66
66
|
'--no-sandbox',
|
@@ -127,9 +127,26 @@ function processClass(ts, classNode, moduleDoc) {
|
|
127
127
|
}
|
128
128
|
|
129
129
|
// Events
|
130
|
-
currClass.events = findAllDecorators(classNode, "event")
|
130
|
+
currClass.events = findAllDecorators(classNode, ["event", "eventStrict"])
|
131
131
|
?.map(event => processEvent(ts, event, classNode, moduleDoc));
|
132
132
|
|
133
|
+
// TODO: remove after changing Button's click to custom event.
|
134
|
+
// Currently, the Button emits a native click that doesn't need and doesn't have an event decorator,
|
135
|
+
// so we add it manually to the events array.
|
136
|
+
if (currClass.tagName === "ui5-button") {
|
137
|
+
currClass.events.push({
|
138
|
+
"name": "click",
|
139
|
+
"_ui5privacy": "public",
|
140
|
+
"type": {
|
141
|
+
"text": "Event"
|
142
|
+
},
|
143
|
+
"description": "Fired when the component is activated either with a\nmouse/tap or by using the Enter or Space key.\n\n**Note:** The event will not be fired if the `disabled`\nproperty is set to `true`.",
|
144
|
+
"_ui5Cancelable": false,
|
145
|
+
"_ui5allowPreventDefault": false,
|
146
|
+
"_ui5Bubbles": true
|
147
|
+
});
|
148
|
+
}
|
149
|
+
|
133
150
|
const filename = classNode.getSourceFile().fileName;
|
134
151
|
const sourceFile = typeProgram.getSourceFile(filename);
|
135
152
|
const tsProgramClassNode = sourceFile.statements.find(statement => ts.isClassDeclaration(statement) && statement.name?.text === classNode.name?.text);
|
package/lib/cem/event.mjs
CHANGED
@@ -112,12 +112,12 @@ function processEvent(ts, event, classNode, moduleDoc) {
|
|
112
112
|
: sinceTag.name;
|
113
113
|
}
|
114
114
|
|
115
|
-
const eventDetailType = classNode.members?.find(member =>
|
115
|
+
const eventDetailType = classNode.members?.find(member => {
|
116
|
+
return ts.isPropertyDeclaration(member) && member.name.text === "eventDetails"
|
117
|
+
})?.type;
|
116
118
|
const eventDetailRef = eventDetailType?.members?.find(member => member.name.text === name) || eventDetailType?.types?.[eventDetailType?.types?.length - 1]?.members?.find(member => member.name.text === name);
|
117
119
|
const hasGeneric = !!event?.expression?.typeArguments
|
118
|
-
|
119
|
-
// debugger
|
120
|
-
// }
|
120
|
+
|
121
121
|
if (commentParams.length) {
|
122
122
|
if (eventDetailRef && hasGeneric) {
|
123
123
|
logDocumentationError(moduleDoc.path, `Event details for event '${name}' has to be defined either with generic or with eventDetails.`)
|
package/lib/cem/utils.mjs
CHANGED
@@ -3,6 +3,8 @@ import path from "path";
|
|
3
3
|
|
4
4
|
let documentationErrors = new Map();
|
5
5
|
|
6
|
+
const packageRegex = /^((@([a-z0-9._-]+)\/)?([a-z0-9._-]+))/;
|
7
|
+
|
6
8
|
const getDeprecatedStatus = (jsdocComment) => {
|
7
9
|
const deprecatedTag = findTag(jsdocComment, "deprecated");
|
8
10
|
return deprecatedTag?.name
|
@@ -30,7 +32,7 @@ const toKebabCase = str => {
|
|
30
32
|
}
|
31
33
|
|
32
34
|
const normalizeDescription = (description) => {
|
33
|
-
|
35
|
+
return typeof description === 'string' ? description.replaceAll(/^-\s+|^(\n)+|(\n)+$/g, "") : description;
|
34
36
|
}
|
35
37
|
|
36
38
|
const getTypeRefs = (ts, node, member) => {
|
@@ -111,10 +113,22 @@ const findPackageName = (ts, sourceFile, typeName) => {
|
|
111
113
|
if (currentModuleSpecifier?.text?.startsWith(".")) {
|
112
114
|
return packageJSON?.name;
|
113
115
|
} else {
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
116
|
+
// my-package/test
|
117
|
+
// my-package
|
118
|
+
// @scope/my-package
|
119
|
+
// my.package
|
120
|
+
// _mypackage
|
121
|
+
// mypackage-
|
122
|
+
// scope/my-package/test
|
123
|
+
// @scope/my-package/test
|
124
|
+
const match = currentModuleSpecifier?.text.match(packageRegex);
|
125
|
+
let packageName;
|
126
|
+
|
127
|
+
if (match) {
|
128
|
+
packageName = match[1];
|
129
|
+
}
|
130
|
+
|
131
|
+
return packageName || undefined;
|
118
132
|
}
|
119
133
|
}
|
120
134
|
};
|
@@ -156,33 +170,24 @@ const findImportPath = (ts, sourceFile, typeName, modulePath) => {
|
|
156
170
|
?.replace("src", "dist")?.replace(".ts", ".js") || undefined
|
157
171
|
);
|
158
172
|
} else {
|
159
|
-
|
160
|
-
// my-package
|
161
|
-
// @scope/my-package
|
162
|
-
// my.package
|
163
|
-
// _mypackage
|
164
|
-
// mypackage-
|
165
|
-
// scope/my-package/test
|
166
|
-
// @scope/my-package/test
|
167
|
-
const match = currentModuleSpecifier?.text.match(/^((@([a-z0-9._-]+)\/)?([a-z0-9._-]+))/);
|
168
|
-
let packageName;
|
173
|
+
let packageName = currentModuleSpecifier?.text?.replace(packageRegex, "") || undefined;
|
169
174
|
|
170
|
-
if (
|
171
|
-
packageName =
|
175
|
+
if (packageName?.startsWith("/")) {
|
176
|
+
packageName = packageName.replace("/", "");
|
172
177
|
}
|
173
178
|
|
174
|
-
return packageName
|
179
|
+
return packageName;
|
175
180
|
}
|
176
181
|
}
|
177
182
|
};
|
178
183
|
|
179
184
|
|
180
185
|
const isClass = text => {
|
181
|
-
|
186
|
+
return text.includes("@abstract") || text.includes("@class") || text.includes("@constructor");
|
182
187
|
};
|
183
188
|
|
184
189
|
const normalizeTagType = (type) => {
|
185
|
-
|
190
|
+
return type?.trim();
|
186
191
|
}
|
187
192
|
|
188
193
|
const packageJSON = JSON.parse(fs.readFileSync("./package.json"));
|
@@ -258,12 +263,22 @@ const findDecorator = (node, decoratorName) => {
|
|
258
263
|
};
|
259
264
|
|
260
265
|
const findAllDecorators = (node, decoratorName) => {
|
261
|
-
|
262
|
-
node?.decorators?.filter(
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
266
|
+
if (typeof decoratorName === "string") {
|
267
|
+
return node?.decorators?.filter(decorator => decorator?.expression?.expression?.text === decoratorName ) || [];
|
268
|
+
}
|
269
|
+
|
270
|
+
if (Array.isArray(decoratorName)) {
|
271
|
+
return node?.decorators?.filter(decorator => {
|
272
|
+
if (decorator?.expression?.expression?.text) {
|
273
|
+
return decoratorName.includes(decorator.expression.expression.text);
|
274
|
+
}
|
275
|
+
|
276
|
+
return false;
|
277
|
+
}
|
278
|
+
) || [];
|
279
|
+
}
|
280
|
+
|
281
|
+
return [];
|
267
282
|
};
|
268
283
|
|
269
284
|
const hasTag = (jsDoc, tagName) => {
|
@@ -339,7 +354,7 @@ const validateJSDocComment = (fieldType, jsdocComment, node, moduleDoc) => {
|
|
339
354
|
let isValid = false
|
340
355
|
|
341
356
|
if (fieldType === "event" && tag?.tag === "param") {
|
342
|
-
isValid = allowedTags[fieldType]?.includes(tag.tag) && validateJSDocTag({...tag, tag: "eventparam"});
|
357
|
+
isValid = allowedTags[fieldType]?.includes(tag.tag) && validateJSDocTag({ ...tag, tag: "eventparam" });
|
343
358
|
} else {
|
344
359
|
isValid = allowedTags[fieldType]?.includes(tag.tag) && validateJSDocTag(tag);
|
345
360
|
}
|
@@ -368,20 +383,20 @@ const displayDocumentationErrors = () => {
|
|
368
383
|
[...documentationErrors.keys()].forEach(modulePath => {
|
369
384
|
const moduleErrors = documentationErrors.get(modulePath);
|
370
385
|
|
371
|
-
console.log(`=== ERROR: ${moduleErrors.length > 1 ? `${moduleErrors.length} problems` :
|
386
|
+
console.log(`=== ERROR: ${moduleErrors.length > 1 ? `${moduleErrors.length} problems` : "Problem"} found in file: ${modulePath}:`)
|
372
387
|
moduleErrors.forEach(moduleError => {
|
373
388
|
errorsCount++;
|
374
389
|
console.log(`\t- ${moduleError}`)
|
375
390
|
})
|
376
391
|
})
|
377
392
|
|
378
|
-
if(errorsCount) {
|
393
|
+
if (errorsCount) {
|
379
394
|
throw new Error(`Found ${errorsCount} errors in the description of the public API.`);
|
380
395
|
}
|
381
396
|
}
|
382
397
|
|
383
398
|
const formatArrays = (typeText) => {
|
384
|
-
|
399
|
+
return typeText?.replaceAll(/(\S+)\[\]/g, "Array<$1>")
|
385
400
|
}
|
386
401
|
|
387
402
|
export {
|
@@ -1,12 +1,12 @@
|
|
1
|
-
const
|
1
|
+
const Component = (componentName, tagName, library, packageName) => {
|
2
2
|
return `import UI5Element from "@ui5/webcomponents-base/dist/UI5Element.js";
|
3
3
|
import customElement from "@ui5/webcomponents-base/dist/decorators/customElement.js";
|
4
4
|
import property from "@ui5/webcomponents-base/dist/decorators/property.js";
|
5
5
|
import slot from "@ui5/webcomponents-base/dist/decorators/slot.js";
|
6
|
-
import event from "@ui5/webcomponents-base/dist/decorators/event.js";
|
7
|
-
import
|
6
|
+
import event from "@ui5/webcomponents-base/dist/decorators/event-strict.js";
|
7
|
+
import jsxRenderer from "@ui5/webcomponents-base/dist/renderer/JsxRenderer.js";
|
8
8
|
|
9
|
-
import ${componentName}Template from "
|
9
|
+
import ${componentName}Template from "./${componentName}Template.js";
|
10
10
|
|
11
11
|
// Styles
|
12
12
|
import ${componentName}Css from "./generated/themes/${componentName}.css.js";
|
@@ -30,10 +30,9 @@ import ${componentName}Css from "./generated/themes/${componentName}.css.js";
|
|
30
30
|
*/
|
31
31
|
@customElement({
|
32
32
|
tag: "${tagName}",
|
33
|
-
renderer:
|
33
|
+
renderer: jsxRenderer,
|
34
34
|
styles: ${componentName}Css,
|
35
35
|
template: ${componentName}Template,
|
36
|
-
dependencies: [],
|
37
36
|
})
|
38
37
|
|
39
38
|
/**
|
@@ -42,8 +41,12 @@ import ${componentName}Css from "./generated/themes/${componentName}.css.js";
|
|
42
41
|
*
|
43
42
|
* @public
|
44
43
|
*/
|
45
|
-
@event("interact"
|
44
|
+
@event("interact")
|
46
45
|
class ${componentName} extends UI5Element {
|
46
|
+
eventDetails!: {
|
47
|
+
"interact": void,
|
48
|
+
};
|
49
|
+
|
47
50
|
/**
|
48
51
|
* Defines the value of the component.
|
49
52
|
*
|
@@ -68,4 +71,4 @@ export default ${componentName};
|
|
68
71
|
`;
|
69
72
|
};
|
70
73
|
|
71
|
-
module.exports =
|
74
|
+
module.exports = Component;
|
@@ -0,0 +1,12 @@
|
|
1
|
+
const ComponentTemplate = (componentName) => {
|
2
|
+
return `import type ${componentName} from "./${componentName}.js";
|
3
|
+
|
4
|
+
export default function ${componentName}Template(this: ${componentName}) {
|
5
|
+
return (
|
6
|
+
<div>Hello World!</div>
|
7
|
+
);
|
8
|
+
}
|
9
|
+
`;
|
10
|
+
};
|
11
|
+
|
12
|
+
module.exports = ComponentTemplate;
|
@@ -1,6 +1,7 @@
|
|
1
1
|
const fs = require("fs");
|
2
2
|
const prompts = require("prompts");
|
3
|
-
const
|
3
|
+
const Component = require("./Component.js");
|
4
|
+
const ComponentTemplate= require("./ComponentTemplate.js");
|
4
5
|
|
5
6
|
/**
|
6
7
|
* Hyphanates the given PascalCase string, f.e.:
|
@@ -61,12 +62,12 @@ const generateFiles = (componentName, tagName, library, packageName) => {
|
|
61
62
|
const filePaths = {
|
62
63
|
"main": `./src/${componentName}.ts`,
|
63
64
|
"css": `./src/themes/${componentName}.css`,
|
64
|
-
"template": `./src/${componentName}.
|
65
|
+
"template": `./src/${componentName}Template.tsx`,
|
65
66
|
};
|
66
67
|
|
67
|
-
fs.writeFileSync(filePaths.main,
|
68
|
+
fs.writeFileSync(filePaths.main, Component(componentName, tagName, library, packageName), { flag: "wx+" });
|
68
69
|
fs.writeFileSync(filePaths.css, "", { flag: "wx+" });
|
69
|
-
fs.writeFileSync(filePaths.template,
|
70
|
+
fs.writeFileSync(filePaths.template, ComponentTemplate(componentName), { flag: "wx+" });
|
70
71
|
|
71
72
|
console.log(`Successfully generated ${filePaths.main}`);
|
72
73
|
console.log(`Successfully generated ${filePaths.css}`);
|
@@ -74,8 +75,8 @@ const generateFiles = (componentName, tagName, library, packageName) => {
|
|
74
75
|
|
75
76
|
// Change the color of the output
|
76
77
|
console.warn('\x1b[33m%s\x1b[0m', `
|
77
|
-
|
78
|
-
|
78
|
+
Now, import the component via: "import ${componentName} from ./${componentName}.js";
|
79
|
+
And, add it to your HTML: <${tagName}></${tagName}>.`);
|
79
80
|
}
|
80
81
|
|
81
82
|
// Main function
|
@@ -28,7 +28,7 @@ let customPlugin = {
|
|
28
28
|
|
29
29
|
// JS/TS
|
30
30
|
const jsPath = f.path.replace(/dist[\/\\]css/, "src/generated/").replace(".css", extension);
|
31
|
-
const jsContent = getFileContent(
|
31
|
+
const jsContent = getFileContent(packageJSON.name, "\`" + newText + "\`", true);
|
32
32
|
writeFileIfChanged(jsPath, jsContent);
|
33
33
|
});
|
34
34
|
})
|
@@ -42,16 +42,11 @@ let scopingPlugin = {
|
|
42
42
|
// JSON
|
43
43
|
const jsonPath = f.path.replace(/dist[\/\\]css/, "dist/generated/assets").replace(".css", ".css.json");
|
44
44
|
await mkdir(path.dirname(jsonPath), {recursive: true});
|
45
|
-
|
46
|
-
packageName: packageJSON.name,
|
47
|
-
fileName: jsonPath.substr(jsonPath.lastIndexOf("themes")),
|
48
|
-
content: newText,
|
49
|
-
};
|
50
|
-
writeFileIfChanged(jsonPath, JSON.stringify({_: data}));
|
45
|
+
writeFileIfChanged(jsonPath, JSON.stringify(newText));
|
51
46
|
|
52
47
|
// JS/TS
|
53
48
|
const jsPath = f.path.replace(/dist[\/\\]css/, "src/generated/").replace(".css", extension);
|
54
|
-
const jsContent = getFileContent(
|
49
|
+
const jsContent = getFileContent(packageJSON.name, "\`" + newText + "\`");
|
55
50
|
writeFileIfChanged(jsPath, jsContent);
|
56
51
|
});
|
57
52
|
})
|
@@ -47,30 +47,10 @@ registerThemePropertiesLoader("${packageName}", "${DEFAULT_THEME}", async () =>
|
|
47
47
|
`;
|
48
48
|
};
|
49
49
|
|
50
|
-
const getFileContent = (
|
51
|
-
if (tsMode) {
|
52
|
-
return getTSContent(targetFile, packageName, css, includeDefaultTheme);
|
53
|
-
}
|
54
|
-
|
55
|
-
return getJSContent(targetFile, packageName, css, includeDefaultTheme);
|
56
|
-
}
|
57
|
-
|
58
|
-
const getTSContent = (targetFile, packageName, css, includeDefaultTheme) => {
|
59
|
-
const typeImport = "import type { StyleData } from \"@ui5/webcomponents-base/dist/types.js\";"
|
50
|
+
const getFileContent = (packageName, css, includeDefaultTheme) => {
|
60
51
|
const defaultTheme = includeDefaultTheme ? getDefaultThemeCode(packageName) : "";
|
61
|
-
|
62
|
-
// tabs are intentionally mixed to have proper identation in the produced file
|
63
|
-
return `${typeImport}
|
64
|
-
${defaultTheme}
|
65
|
-
const styleData: StyleData = {packageName:"${packageName}",fileName:"${targetFile.substr(targetFile.lastIndexOf("themes"))}",content:${css}};
|
66
|
-
export default styleData;
|
67
|
-
`;
|
52
|
+
return `${defaultTheme}export default ${css.trim()}`
|
68
53
|
}
|
69
54
|
|
70
|
-
const getJSContent = (targetFile, packageName, css, includeDefaultTheme) => {
|
71
|
-
const defaultTheme = includeDefaultTheme ? getDefaultThemeCode(packageName) : "";
|
72
|
-
|
73
|
-
return `${defaultTheme}export default {packageName:"${packageName}",fileName:"${targetFile.substr(targetFile.lastIndexOf("themes"))}",content:${css}}`
|
74
|
-
}
|
75
55
|
|
76
56
|
export { writeFileIfChanged, stripThemingBaseContent, getFileContent}
|
@@ -1,23 +1,17 @@
|
|
1
|
-
const virtualIndexPlugin =
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
let folder = pagesPerFolder[path.dirname(file)] = pagesPerFolder[path.dirname(file)] || [];
|
9
|
-
folder.push(path.basename(file));
|
10
|
-
});
|
1
|
+
const virtualIndexPlugin = () => {
|
2
|
+
return {
|
3
|
+
name: 'virtual-index-html',
|
4
|
+
async config() {
|
5
|
+
const path = (await import("path")).default;
|
6
|
+
const globby = (await import("globby")).globby;
|
7
|
+
const files = await globby(["test/pages/**/*.html", "packages/*/test/pages/**/*.html"]);
|
11
8
|
|
12
|
-
|
9
|
+
const rollupInput = {};
|
13
10
|
|
14
|
-
|
15
|
-
|
16
|
-
|
11
|
+
files.forEach(file => {
|
12
|
+
rollupInput[file] = path.resolve(process.cwd(), file);
|
13
|
+
});
|
17
14
|
|
18
|
-
return {
|
19
|
-
name: 'virtual-index-html',
|
20
|
-
config() {
|
21
15
|
return {
|
22
16
|
build: {
|
23
17
|
rollupOptions: {
|
@@ -26,7 +20,17 @@ const virtualIndexPlugin = async () => {
|
|
26
20
|
}
|
27
21
|
}
|
28
22
|
},
|
29
|
-
configureServer(server) {
|
23
|
+
async configureServer(server) {
|
24
|
+
const path = (await import("path")).default;
|
25
|
+
const globby = (await import("globby")).globby;
|
26
|
+
const files = await globby(["test/pages/**/*.html", "packages/*/test/pages/**/*.html"]);
|
27
|
+
|
28
|
+
const pagesPerFolder = {};
|
29
|
+
files.forEach(file => {
|
30
|
+
let folder = pagesPerFolder[path.dirname(file)] = pagesPerFolder[path.dirname(file)] || [];
|
31
|
+
folder.push(path.basename(file));
|
32
|
+
});
|
33
|
+
|
30
34
|
server.middlewares.use((req, res, next) => {
|
31
35
|
if (req.url === "/") {
|
32
36
|
const folders = Object.keys(pagesPerFolder);
|
@@ -37,8 +41,8 @@ const virtualIndexPlugin = async () => {
|
|
37
41
|
const pages = pagesPerFolder[folder];
|
38
42
|
return `<h1>${folder}</h1>
|
39
43
|
${pages.map(page => {
|
40
|
-
|
41
|
-
|
44
|
+
return `<li><a href='${folder}/${page}'>${page}</a></li>`
|
45
|
+
}).join("")}
|
42
46
|
`
|
43
47
|
}).join("")}`);
|
44
48
|
} else {
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@ui5/webcomponents-tools",
|
3
|
-
"version": "0.0.0-
|
3
|
+
"version": "0.0.0-f6676abdd",
|
4
4
|
"description": "UI5 Web Components: webcomponents.tools",
|
5
5
|
"author": "SAP SE (https://www.sap.com)",
|
6
6
|
"license": "Apache-2.0",
|
@@ -23,6 +23,7 @@
|
|
23
23
|
},
|
24
24
|
"dependencies": {
|
25
25
|
"@custom-elements-manifest/analyzer": "^0.8.4",
|
26
|
+
"@cypress/code-coverage": "^3.13.10",
|
26
27
|
"@typescript-eslint/eslint-plugin": "^6.9.0",
|
27
28
|
"@typescript-eslint/parser": "^6.9.0",
|
28
29
|
"@wdio/cli": "^7.19.7",
|
@@ -48,7 +49,8 @@
|
|
48
49
|
"eslint": "^7.22.0",
|
49
50
|
"eslint-config-airbnb-base": "^14.2.1",
|
50
51
|
"eslint-plugin-cypress": "^3.4.0",
|
51
|
-
"eslint-plugin-import": "^2.
|
52
|
+
"eslint-plugin-import": "^2.31.0",
|
53
|
+
"eslint-plugin-jsx-no-leaked-values": "^0.1.24",
|
52
54
|
"esprima": "^4.0.1",
|
53
55
|
"getopts": "^2.3.0",
|
54
56
|
"glob": "^7.1.6",
|
@@ -62,6 +64,7 @@
|
|
62
64
|
"postcss": "^8.4.5",
|
63
65
|
"postcss-cli": "^9.1.0",
|
64
66
|
"postcss-selector-parser": "^6.0.10",
|
67
|
+
"preact": "^10.25.4",
|
65
68
|
"prompts": "^2.4.2",
|
66
69
|
"properties-reader": "^2.2.0",
|
67
70
|
"recursive-readdir": "^2.2.2",
|
@@ -69,6 +72,7 @@
|
|
69
72
|
"rimraf": "^3.0.2",
|
70
73
|
"slash": "3.0.0",
|
71
74
|
"vite": "^5.4.8",
|
75
|
+
"vite-plugin-istanbul": "^6.0.2",
|
72
76
|
"wdio-chromedriver-service": "^7.3.2"
|
73
77
|
},
|
74
78
|
"peerDependencies": {
|
@@ -81,9 +85,9 @@
|
|
81
85
|
}
|
82
86
|
},
|
83
87
|
"devDependencies": {
|
84
|
-
"cypress-
|
88
|
+
"@cypress/mount-utils": "^4.1.2",
|
85
89
|
"cypress-real-events": "^1.12.0",
|
86
|
-
"esbuild": "^0.
|
90
|
+
"esbuild": "^0.25.0",
|
87
91
|
"yargs": "^17.5.1"
|
88
92
|
}
|
89
93
|
}
|