@keenmate/web-components-devkit 0.0.0-stage → 1.0.0-rc02
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 +126 -0
- package/LICENSE +21 -0
- package/README.md +434 -2
- package/components.json +206 -0
- package/package.json +77 -4
- package/src/cli/args.js +16 -0
- package/src/cli/commands/check-release.js +66 -0
- package/src/cli/commands/check-vars.js +116 -0
- package/src/cli/commands/doctor.js +289 -0
- package/src/cli/commands/new.js +323 -0
- package/src/cli/commands/readme.js +111 -0
- package/src/cli/commands/sync.js +48 -0
- package/src/cli/kmwc.js +73 -0
- package/src/cli/sync-plan.js +163 -0
- package/src/config.d.ts +149 -0
- package/src/config.js +279 -0
- package/src/examples/auto.js +20 -0
- package/src/examples/chapter-nav.js +246 -0
- package/src/examples/constants.js +80 -0
- package/src/examples/controls-persistence.js +97 -0
- package/src/examples/copy-link.js +89 -0
- package/src/examples/css/base.css +215 -0
- package/src/examples/css/chapter-nav.css +179 -0
- package/src/examples/css/content.css +251 -0
- package/src/examples/css/controls.css +205 -0
- package/src/examples/css/copy-link.css +82 -0
- package/src/examples/css/demo.css +50 -0
- package/src/examples/css/index-page.css +118 -0
- package/src/examples/css/style.css +30 -0
- package/src/examples/css/theme-switch.css +38 -0
- package/src/examples/css/tokens.css +124 -0
- package/src/examples/css/widgets.css +133 -0
- package/src/examples/demo-code.js +30 -0
- package/src/examples/index-page.js +78 -0
- package/src/examples/index.d.ts +107 -0
- package/src/examples/index.js +18 -0
- package/src/examples/kit.js +22 -0
- package/src/examples/page-config.js +30 -0
- package/src/examples/sections.js +53 -0
- package/src/examples/theme-switch.js +72 -0
- package/src/examples/version-badge.js +23 -0
- package/src/playwright/index.d.ts +15 -0
- package/src/playwright/index.js +66 -0
- package/src/template.js +11 -0
- package/src/vite/index.d.ts +28 -0
- package/src/vite/index.js +243 -0
- package/src/vite/page-composer.js +216 -0
- package/templates/Dockerfile.tmpl +39 -0
- package/templates/Makefile.tmpl +136 -0
- package/templates/claude-block.md +40 -0
- package/templates/dockerignore.tmpl +10 -0
- package/templates/examples-layout.html +13 -0
- package/templates/new/.claude/commands/publish.md +226 -0
- package/templates/new/CHANGELOG.md +13 -0
- package/templates/new/LICENSE +21 -0
- package/templates/new/README.md +71 -0
- package/templates/new/_gitignore +7 -0
- package/templates/new/component-variables.manifest.json +32 -0
- package/templates/new/custom-elements-manifest.config.mjs +23 -0
- package/templates/new/docs/accessibility.md +24 -0
- package/templates/new/docs/examples.md +33 -0
- package/templates/new/docs/theming.md +54 -0
- package/templates/new/docs/usage.md +51 -0
- package/templates/new/e2e/dark-mode.spec.ts +30 -0
- package/templates/new/e2e/smoke.spec.ts +26 -0
- package/templates/new/examples-basic.html +57 -0
- package/templates/new/examples-theming.html +28 -0
- package/templates/new/index.html +16 -0
- package/templates/new/partials/note.html +5 -0
- package/templates/new/playwright.config.ts +4 -0
- package/templates/new/src/__FEATURE__.ts +55 -0
- package/templates/new/src/css/animations.css +1 -0
- package/templates/new/src/css/base.css +43 -0
- package/templates/new/src/css/controls.css +20 -0
- package/templates/new/src/css/dark-mode.css +36 -0
- package/templates/new/src/css/floating.css +1 -0
- package/templates/new/src/css/main.css +27 -0
- package/templates/new/src/css/states.css +22 -0
- package/templates/new/src/css/variables.css +42 -0
- package/templates/new/src/env.d.ts +7 -0
- package/templates/new/src/index.ts +17 -0
- package/templates/new/src/logger.ts +11 -0
- package/templates/new/src/types.ts +25 -0
- package/templates/new/src/web-component.ts +73 -0
- package/templates/new/test/dark-mode.html +18 -0
- package/templates/new/test/fixture.html +11 -0
- package/templates/new/tsconfig.json +15 -0
- package/templates/new/vite.config.ts +5 -0
- package/templates/new-minimal/CHANGELOG.md +13 -0
- package/templates/new-minimal/README.md +71 -0
- package/templates/new-minimal/component-variables.manifest.json +24 -0
- package/templates/new-minimal/docs/accessibility.md +20 -0
- package/templates/new-minimal/docs/examples.md +17 -0
- package/templates/new-minimal/docs/theming.md +53 -0
- package/templates/new-minimal/docs/usage.md +43 -0
- package/templates/new-minimal/e2e/smoke.spec.ts +10 -0
- package/templates/new-minimal/examples-basic.html +23 -0
- package/templates/new-minimal/examples-theming.html +28 -0
- package/templates/new-minimal/src/__FEATURE__.ts +37 -0
- package/templates/new-minimal/src/css/base.css +48 -0
- package/templates/new-minimal/src/css/controls.css +1 -0
- package/templates/new-minimal/src/css/states.css +1 -0
- package/templates/new-minimal/src/css/variables.css +28 -0
- package/templates/new-minimal/src/index.ts +17 -0
- package/templates/new-minimal/src/types.ts +10 -0
- package/templates/new-minimal/src/web-component.ts +48 -0
- package/templates/new-minimal/test/dark-mode.html +18 -0
- package/templates/new-minimal/test/fixture.html +11 -0
- package/templates/nginx.conf.tmpl +43 -0
package/components.json
ADDED
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$comment": "Registry of Keenmate web components — source for the README catalogue (`npm run readme`). `port` is the repo's block: dev server = port, preview = port + 1, examples image = port + 10. Each kind has its own range with headroom (components 12100–14900, libraries 15000–15900, showcases/docs 16000–16900, tests 17000–17900), numbered by 100 in registry order; `kmwc doctor` checks package.json#kmwc.ports against it and `kmwc new` takes the next block. `prefix` mirrors the reserved-prefix table in BlissFramework guidelines/web-components/base-variables.md (the authority).",
|
|
3
|
+
"components": [
|
|
4
|
+
{
|
|
5
|
+
"id": "web-multiselect",
|
|
6
|
+
"kind": "component",
|
|
7
|
+
"prefix": "ms",
|
|
8
|
+
"port": 12100,
|
|
9
|
+
"path": "../web-multiselect",
|
|
10
|
+
"npm": "@keenmate/web-multiselect",
|
|
11
|
+
"repo": "https://github.com/keenmate/web-multiselect",
|
|
12
|
+
"examples": "https://examples.web-multiselect.keenmate.dev",
|
|
13
|
+
"docs": "https://web-multiselect.keenmate.dev",
|
|
14
|
+
"showcase": "web-multiselect-showcase"
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
"id": "web-daterangepicker",
|
|
18
|
+
"kind": "component",
|
|
19
|
+
"prefix": "drp",
|
|
20
|
+
"port": 12200,
|
|
21
|
+
"path": "../web-daterangepicker",
|
|
22
|
+
"npm": "@keenmate/web-daterangepicker",
|
|
23
|
+
"repo": "https://github.com/keenmate/web-daterangepicker",
|
|
24
|
+
"docs": "https://web-daterangepicker.keenmate.dev",
|
|
25
|
+
"showcase": "web-daterangepicker-showcase"
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
"id": "web-treeview",
|
|
29
|
+
"kind": "component",
|
|
30
|
+
"prefix": "wtv",
|
|
31
|
+
"port": 12300,
|
|
32
|
+
"path": "../web-treeview",
|
|
33
|
+
"npm": "@keenmate/web-treeview",
|
|
34
|
+
"repo": "https://github.com/keenmate/web-treeview",
|
|
35
|
+
"showcase": "web-treeview-showcase"
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
"id": "web-commandpalette",
|
|
39
|
+
"kind": "component",
|
|
40
|
+
"prefix": "cp",
|
|
41
|
+
"port": 12400,
|
|
42
|
+
"path": "../web-commandpalette",
|
|
43
|
+
"npm": "@keenmate/web-commandpalette",
|
|
44
|
+
"repo": "https://github.com/keenmate/web-commandpalette",
|
|
45
|
+
"examples": "https://examples.web-commandpalette.keenmate.dev"
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
"id": "web-dropzone",
|
|
49
|
+
"kind": "component",
|
|
50
|
+
"prefix": "dz",
|
|
51
|
+
"port": 12500,
|
|
52
|
+
"path": "../web-dropzone",
|
|
53
|
+
"npm": "@keenmate/web-dropzone",
|
|
54
|
+
"repo": "https://github.com/keenmate/web-dropzone",
|
|
55
|
+
"monorepo": true,
|
|
56
|
+
"note": "Monorepo: renderer + headless core."
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
"id": "web-grid",
|
|
60
|
+
"kind": "component",
|
|
61
|
+
"prefix": "wg",
|
|
62
|
+
"port": 12600,
|
|
63
|
+
"path": "../web-grid",
|
|
64
|
+
"npm": "@keenmate/web-grid",
|
|
65
|
+
"repo": "https://github.com/keenmate/web-grid",
|
|
66
|
+
"docs": "https://web-grid.keenmate.com",
|
|
67
|
+
"showcase": "web-grid-showcase",
|
|
68
|
+
"monorepo": true
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
"id": "web-editor",
|
|
72
|
+
"kind": "component",
|
|
73
|
+
"prefix": "km",
|
|
74
|
+
"port": 12700,
|
|
75
|
+
"path": "../web-editor",
|
|
76
|
+
"npm": "@keenmate/web-editor",
|
|
77
|
+
"examples": "https://examples.web-editor.keenmate.dev",
|
|
78
|
+
"note": "ProseMirror-based WYSIWYG / Markdown editor."
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
"id": "web-switch",
|
|
82
|
+
"kind": "component",
|
|
83
|
+
"port": 12800,
|
|
84
|
+
"path": "../web-switch",
|
|
85
|
+
"npm": "@keenmate/web-switch",
|
|
86
|
+
"repo": "https://github.com/keenmate/web-switch",
|
|
87
|
+
"note": "Svelte-based wrapper around @keenmate/svelte-switch."
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
"id": "web-timepicker",
|
|
91
|
+
"kind": "component",
|
|
92
|
+
"port": 12900,
|
|
93
|
+
"path": "../web-timepicker",
|
|
94
|
+
"note": "Vanilla-JS prototypes (spiral / slider time pickers); no package yet."
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
"id": "web-wysiwyg",
|
|
98
|
+
"kind": "component",
|
|
99
|
+
"port": 13000,
|
|
100
|
+
"path": "../web-wysiwyg",
|
|
101
|
+
"note": "ProseMirror exploration; superseded by web-editor."
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
"id": "web-components-core",
|
|
105
|
+
"kind": "library",
|
|
106
|
+
"port": 15000,
|
|
107
|
+
"path": "../web-components-core",
|
|
108
|
+
"npm": "@keenmate/web-components-core",
|
|
109
|
+
"note": "Runtime foundation: BlissElement, reactive input table, converters, logging, positioning, CEM preset."
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
"id": "web-components",
|
|
113
|
+
"kind": "library",
|
|
114
|
+
"port": 15100,
|
|
115
|
+
"path": ".",
|
|
116
|
+
"npm": "@keenmate/web-components-devkit",
|
|
117
|
+
"repo": "https://github.com/keenmate/web-components",
|
|
118
|
+
"note": "This repo — dev/demo/build tooling (Vite preset, examples kit, kmwc CLI)."
|
|
119
|
+
},
|
|
120
|
+
{
|
|
121
|
+
"id": "pure-css",
|
|
122
|
+
"kind": "library",
|
|
123
|
+
"port": 15200,
|
|
124
|
+
"path": "../pure-css",
|
|
125
|
+
"npm": "@keenmate/pure-css",
|
|
126
|
+
"note": "CSS framework mirroring the base-css-variables token contract (demo site + e2e on the dev port)."
|
|
127
|
+
},
|
|
128
|
+
{
|
|
129
|
+
"id": "base-css-variables",
|
|
130
|
+
"kind": "library",
|
|
131
|
+
"port": 15300,
|
|
132
|
+
"path": "../base-css-variables",
|
|
133
|
+
"npm": "@keenmate/base-css-variables",
|
|
134
|
+
"repo": "https://github.com/keenmate/base-css-variables",
|
|
135
|
+
"note": "The shared --base-* token contract (base-variables.css) + base-contract-check; examples site + e2e on the dev port."
|
|
136
|
+
},
|
|
137
|
+
{
|
|
138
|
+
"id": "web-multiselect-showcase",
|
|
139
|
+
"kind": "showcase",
|
|
140
|
+
"port": 16000,
|
|
141
|
+
"path": "../web-multiselect-showcase",
|
|
142
|
+
"for": "web-multiselect"
|
|
143
|
+
},
|
|
144
|
+
{
|
|
145
|
+
"id": "web-daterangepicker-showcase",
|
|
146
|
+
"kind": "showcase",
|
|
147
|
+
"port": 16100,
|
|
148
|
+
"path": "../web-daterangepicker-showcase",
|
|
149
|
+
"for": "web-daterangepicker"
|
|
150
|
+
},
|
|
151
|
+
{
|
|
152
|
+
"id": "web-treeview-showcase",
|
|
153
|
+
"kind": "showcase",
|
|
154
|
+
"port": 16200,
|
|
155
|
+
"path": "../web-treeview-showcase",
|
|
156
|
+
"for": "web-treeview"
|
|
157
|
+
},
|
|
158
|
+
{
|
|
159
|
+
"id": "web-grid-showcase",
|
|
160
|
+
"kind": "showcase",
|
|
161
|
+
"port": 16300,
|
|
162
|
+
"path": "../web-grid-showcase",
|
|
163
|
+
"for": "web-grid",
|
|
164
|
+
"url": "https://web-grid.keenmate.com"
|
|
165
|
+
},
|
|
166
|
+
{
|
|
167
|
+
"id": "web-multiselect-docs",
|
|
168
|
+
"kind": "docs",
|
|
169
|
+
"port": 16400,
|
|
170
|
+
"path": "../web-multiselect-docs",
|
|
171
|
+
"for": "web-multiselect",
|
|
172
|
+
"note": "Content-only docs (keendocs)."
|
|
173
|
+
},
|
|
174
|
+
{
|
|
175
|
+
"id": "web-component-framework-test",
|
|
176
|
+
"kind": "test",
|
|
177
|
+
"port": 17000,
|
|
178
|
+
"path": "../web-component-framework-test",
|
|
179
|
+
"note": "Smoke + contrast tests inside CSS host frameworks (plain / Bootstrap / …)."
|
|
180
|
+
},
|
|
181
|
+
{
|
|
182
|
+
"id": "web-multiselect-test",
|
|
183
|
+
"kind": "test",
|
|
184
|
+
"port": 17100,
|
|
185
|
+
"path": "../web-multiselect-test",
|
|
186
|
+
"for": "web-multiselect",
|
|
187
|
+
"note": "npm-install vs direct-file consumption checks before publishing."
|
|
188
|
+
},
|
|
189
|
+
{
|
|
190
|
+
"id": "web-multiselect-positioning-demo",
|
|
191
|
+
"kind": "test",
|
|
192
|
+
"port": 17200,
|
|
193
|
+
"path": "../web-multiselect-positioning-demo",
|
|
194
|
+
"for": "web-multiselect",
|
|
195
|
+
"note": "Single-page positioning repro."
|
|
196
|
+
},
|
|
197
|
+
{
|
|
198
|
+
"id": "web-playground",
|
|
199
|
+
"kind": "test",
|
|
200
|
+
"port": 17300,
|
|
201
|
+
"path": "playground",
|
|
202
|
+
"for": "web-components",
|
|
203
|
+
"note": "Devkit e2e test bed — a component scaffolded with `kmwc new`, linked to this repo."
|
|
204
|
+
}
|
|
205
|
+
]
|
|
206
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,79 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@keenmate/web-components-devkit",
|
|
3
|
-
"version": "
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "1.0.0-rc02",
|
|
4
|
+
"description": "Shared dev, example-site and build infrastructure for Keenmate web components: Vite preset, examples kit (chapter nav, copy-link, controls store, shared styles), Playwright preset and the `kmwc` CLI (sync, doctor, check-vars, new, readme).",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"author": "Keenmate s.r.o.",
|
|
8
|
+
"homepage": "https://github.com/keenmate/web-components",
|
|
9
|
+
"repository": {
|
|
10
|
+
"type": "git",
|
|
11
|
+
"url": "git+https://github.com/keenmate/web-components.git"
|
|
12
|
+
},
|
|
13
|
+
"bin": {
|
|
14
|
+
"kmwc": "./src/cli/kmwc.js"
|
|
15
|
+
},
|
|
16
|
+
"exports": {
|
|
17
|
+
".": {
|
|
18
|
+
"types": "./src/config.d.ts",
|
|
19
|
+
"import": "./src/config.js"
|
|
20
|
+
},
|
|
21
|
+
"./vite": {
|
|
22
|
+
"types": "./src/vite/index.d.ts",
|
|
23
|
+
"import": "./src/vite/index.js"
|
|
24
|
+
},
|
|
25
|
+
"./playwright": {
|
|
26
|
+
"types": "./src/playwright/index.d.ts",
|
|
27
|
+
"import": "./src/playwright/index.js"
|
|
28
|
+
},
|
|
29
|
+
"./examples": {
|
|
30
|
+
"types": "./src/examples/index.d.ts",
|
|
31
|
+
"import": "./src/examples/index.js"
|
|
32
|
+
},
|
|
33
|
+
"./examples/auto": "./src/examples/auto.js",
|
|
34
|
+
"./examples/style.css": "./src/examples/css/style.css",
|
|
35
|
+
"./components.json": "./components.json",
|
|
36
|
+
"./package.json": "./package.json"
|
|
37
|
+
},
|
|
38
|
+
"files": [
|
|
39
|
+
"src",
|
|
40
|
+
"templates",
|
|
41
|
+
"components.json",
|
|
42
|
+
"README.md",
|
|
43
|
+
"CHANGELOG.md"
|
|
44
|
+
],
|
|
45
|
+
"scripts": {
|
|
46
|
+
"readme": "node src/cli/kmwc.js readme",
|
|
47
|
+
"readme:check": "node src/cli/kmwc.js readme --check",
|
|
48
|
+
"test": "node --test \"test/**/*.test.js\"",
|
|
49
|
+
"test:e2e": "cd playground && npx playwright test",
|
|
50
|
+
"check:contract": "base-contract-check src templates playground",
|
|
51
|
+
"playground": "cd playground && vite",
|
|
52
|
+
"playground:build": "cd playground && vite build --mode examples",
|
|
53
|
+
"playground:lib": "cd playground && vite build"
|
|
54
|
+
},
|
|
55
|
+
"dependencies": {
|
|
56
|
+
"@floating-ui/dom": "^1.8.0",
|
|
57
|
+
"vite-plugin-dts": "^4.5.4"
|
|
58
|
+
},
|
|
59
|
+
"peerDependencies": {
|
|
60
|
+
"@playwright/test": "^1.60.0",
|
|
61
|
+
"vite": "^7.0.0"
|
|
62
|
+
},
|
|
63
|
+
"peerDependenciesMeta": {
|
|
64
|
+
"@playwright/test": {
|
|
65
|
+
"optional": true
|
|
66
|
+
}
|
|
67
|
+
},
|
|
68
|
+
"devDependencies": {
|
|
69
|
+
"@keenmate/base-css-variables": "^2.0.0-rc07",
|
|
70
|
+
"typescript": "^5.9.3",
|
|
71
|
+
"vite": "^7.3.7"
|
|
72
|
+
},
|
|
73
|
+
"engines": {
|
|
74
|
+
"node": ">=20.19"
|
|
75
|
+
},
|
|
76
|
+
"allowScripts": {
|
|
77
|
+
"esbuild@0.28.2": true
|
|
78
|
+
}
|
|
79
|
+
}
|
package/src/cli/args.js
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/** Tiny flag parser: --flag, --key value, --key=value, positionals. */
|
|
2
|
+
export function parseArgs(args) {
|
|
3
|
+
const out = { _: [] };
|
|
4
|
+
for (let i = 0; i < args.length; i++) {
|
|
5
|
+
const a = args[i];
|
|
6
|
+
if (!a.startsWith('--')) {
|
|
7
|
+
out._.push(a);
|
|
8
|
+
continue;
|
|
9
|
+
}
|
|
10
|
+
const [key, inline] = a.slice(2).split(/=(.*)/s);
|
|
11
|
+
if (inline !== undefined) out[key] = inline;
|
|
12
|
+
else if (args[i + 1] && !args[i + 1].startsWith('--')) out[key] = args[++i];
|
|
13
|
+
else out[key] = true;
|
|
14
|
+
}
|
|
15
|
+
return out;
|
|
16
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* kmwc check-release --tag latest|rc — gate a publish (publish-command.md).
|
|
3
|
+
*
|
|
4
|
+
* - `latest` takes release versions only (no pre-release suffix);
|
|
5
|
+
* `rc` takes `-rcN` versions only — a mismatch publishes under the wrong tag.
|
|
6
|
+
* - The version must not already be on npm (`npm view` — a re-publish fails
|
|
7
|
+
* late, after the build, and leaves the CHANGELOG lying).
|
|
8
|
+
* - The topmost CHANGELOG heading should be this version (warning only).
|
|
9
|
+
*
|
|
10
|
+
* Exit 1 on a failed gate. `--offline` skips the npm lookup.
|
|
11
|
+
*/
|
|
12
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
13
|
+
import { join } from 'node:path';
|
|
14
|
+
import { spawnSync } from 'node:child_process';
|
|
15
|
+
import { getKmwcConfig } from '../../config.js';
|
|
16
|
+
|
|
17
|
+
const RC_VERSION_PATTERN = /-rc\d+$/;
|
|
18
|
+
const PRE_RELEASE_PATTERN = /-/;
|
|
19
|
+
|
|
20
|
+
/** Pure version-vs-tag verdict — exported for tests. Returns an error message or null. */
|
|
21
|
+
export function checkVersionForTag(version, distTag) {
|
|
22
|
+
if (distTag === 'latest' && PRE_RELEASE_PATTERN.test(version)) {
|
|
23
|
+
return `${version} is a pre-release — publish it with \`make publish-rc\` (dist-tag rc)`;
|
|
24
|
+
}
|
|
25
|
+
if (distTag === 'rc' && !RC_VERSION_PATTERN.test(version)) {
|
|
26
|
+
return `${version} is not an -rcN version — bump to x.y.z-rcN, or publish a release with \`make publish\``;
|
|
27
|
+
}
|
|
28
|
+
if (!['latest', 'rc'].includes(distTag)) return `unknown dist-tag "${distTag}" (expected latest or rc)`;
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function checkIsPublished(packageName, version) {
|
|
33
|
+
const result = spawnSync('npm', ['view', `${packageName}@${version}`, 'version'], { encoding: 'utf8', shell: true });
|
|
34
|
+
return result.status === 0 && result.stdout.trim() === version;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function getTopChangelogVersion(root) {
|
|
38
|
+
const changelogPath = join(root, 'CHANGELOG.md');
|
|
39
|
+
if (!existsSync(changelogPath)) return null;
|
|
40
|
+
return readFileSync(changelogPath, 'utf8').match(/^## \[?([^\]\s]+)\]?/m)?.[1] ?? null;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export async function run(args) {
|
|
44
|
+
const config = getKmwcConfig(args.root);
|
|
45
|
+
const distTag = args.tag || 'latest';
|
|
46
|
+
const failures = [];
|
|
47
|
+
|
|
48
|
+
const versionError = checkVersionForTag(config.version, distTag);
|
|
49
|
+
if (versionError) failures.push(versionError);
|
|
50
|
+
|
|
51
|
+
if (!args.offline && checkIsPublished(config.packageName, config.version)) {
|
|
52
|
+
failures.push(`${config.packageName}@${config.version} is already published — bump the version first`);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const changelogVersion = getTopChangelogVersion(config.root);
|
|
56
|
+
if (changelogVersion !== config.version) {
|
|
57
|
+
console.warn(`⚠ CHANGELOG.md's top entry is ${changelogVersion ?? '(none)'}, not ${config.version}`);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (failures.length) {
|
|
61
|
+
for (const failure of failures) console.error(`✗ ${failure}`);
|
|
62
|
+
return 1;
|
|
63
|
+
}
|
|
64
|
+
console.log(`✓ ${config.packageName}@${config.version} can be published under "${distTag}"`);
|
|
65
|
+
return 0;
|
|
66
|
+
}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* kmwc check-vars — validate component-variables.manifest.json against the CSS.
|
|
3
|
+
*
|
|
4
|
+
* The manifest feeds the IDE-autocomplete outputs (vscode.css-custom-data.json,
|
|
5
|
+
* web-types.json) and theme-designer, but is hand-maintained — so it drifts
|
|
6
|
+
* from the CSS. This makes drift a build failure. (Replaces the three diverged
|
|
7
|
+
* per-repo copies of scripts/check-variable-manifest.mjs.)
|
|
8
|
+
*
|
|
9
|
+
* Contract (prefix = kmwc.prefix or manifest.prefix, e.g. `ms`):
|
|
10
|
+
* - every `--base-*` read via var() must be in baseVariables, and vice versa
|
|
11
|
+
* - checkMode "declared" (default): every `--<prefix>-*` DECLARED (`--x:`)
|
|
12
|
+
* must be in componentVariables, and vice versa
|
|
13
|
+
* - checkMode "consumed": the same, but against `var(--<prefix>-*)` reads (for
|
|
14
|
+
* components whose public knobs are read-with-fallback, never declared)
|
|
15
|
+
* - names in kmwc.cssVariables.internalVariables are runtime internals — exempt
|
|
16
|
+
*
|
|
17
|
+
* Undocumented component variables are a warning unless --strict; everything else errors.
|
|
18
|
+
*/
|
|
19
|
+
import { readFileSync, readdirSync, statSync, existsSync } from 'node:fs';
|
|
20
|
+
import { join, extname, resolve } from 'node:path';
|
|
21
|
+
import { getKmwcConfig } from '../../config.js';
|
|
22
|
+
|
|
23
|
+
const STYLESHEET_EXTENSIONS = new Set(['.css', '.scss', '.sass', '.less', '.pcss']);
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Directories are scanned for stylesheets; a file listed explicitly is read
|
|
27
|
+
* whatever its type — e.g. a built bundle that inlines the compiled CSS, for
|
|
28
|
+
* components whose SCSS generates variable names via interpolation.
|
|
29
|
+
*/
|
|
30
|
+
function getStylesheetTexts(sourcePath, stylesheetTexts, isExplicit = true) {
|
|
31
|
+
if (!existsSync(sourcePath)) throw new Error(`cssVariables source not found: ${sourcePath}`);
|
|
32
|
+
if (statSync(sourcePath).isDirectory()) {
|
|
33
|
+
for (const entryName of readdirSync(sourcePath)) getStylesheetTexts(join(sourcePath, entryName), stylesheetTexts, false);
|
|
34
|
+
} else if (isExplicit || STYLESHEET_EXTENSIONS.has(extname(sourcePath))) {
|
|
35
|
+
stylesheetTexts.push(readFileSync(sourcePath, 'utf8'));
|
|
36
|
+
}
|
|
37
|
+
return stylesheetTexts;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const mapToRegExpSource = (text) => text.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
41
|
+
|
|
42
|
+
/** Pure verdict — exported for tests. */
|
|
43
|
+
export function checkManifestVariables({ cssText, manifest, prefix, internalVariables = [], checkMode = 'declared' }) {
|
|
44
|
+
cssText = cssText.replace(/\/\*[\s\S]*?\*\//g, '');
|
|
45
|
+
const prefixSource = mapToRegExpSource(prefix);
|
|
46
|
+
// A declaration is followed by a colon; a var() read by `)` or `,`.
|
|
47
|
+
const declaredVariables = new Set([...cssText.matchAll(new RegExp(`--(${prefixSource}-[a-z0-9-]+)\\s*:`, 'gi'))].map((match) => match[1]));
|
|
48
|
+
const consumedVariables = new Set([...cssText.matchAll(new RegExp(`var\\(\\s*--(${prefixSource}-[a-z0-9-]+)`, 'gi'))].map((match) => match[1]));
|
|
49
|
+
const consumedBaseVariables = new Set([...cssText.matchAll(/var\(\s*--(base-[a-z0-9-]+)/gi)].map((match) => match[1]));
|
|
50
|
+
|
|
51
|
+
const componentVariablesInCss = checkMode === 'consumed' ? consumedVariables : declaredVariables;
|
|
52
|
+
const internalVariableSet = new Set(internalVariables);
|
|
53
|
+
const manifestComponentVariables = new Set((manifest.componentVariables || []).map((entry) => entry.name));
|
|
54
|
+
const manifestBaseVariables = new Set((manifest.baseVariables || []).map((entry) => entry.name));
|
|
55
|
+
const getDifference = (left, right) => [...left].filter((name) => !right.has(name)).sort();
|
|
56
|
+
|
|
57
|
+
return {
|
|
58
|
+
missingBaseVariables: getDifference(consumedBaseVariables, manifestBaseVariables),
|
|
59
|
+
unusedBaseVariables: getDifference(manifestBaseVariables, consumedBaseVariables),
|
|
60
|
+
unusedComponentVariables: getDifference(manifestComponentVariables, componentVariablesInCss),
|
|
61
|
+
undocumentedComponentVariables: getDifference(componentVariablesInCss, manifestComponentVariables).filter(
|
|
62
|
+
(name) => !internalVariableSet.has(name)
|
|
63
|
+
),
|
|
64
|
+
counts: { base: manifestBaseVariables.size, component: manifestComponentVariables.size },
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export async function run(args) {
|
|
69
|
+
const config = getKmwcConfig(args.root);
|
|
70
|
+
const isStrict = !!args.strict;
|
|
71
|
+
const manifestPath = resolve(config.root, config.cssVariables.manifest);
|
|
72
|
+
if (!existsSync(manifestPath)) {
|
|
73
|
+
console.log(`kmwc check-vars: no ${config.cssVariables.manifest} — nothing to check.`);
|
|
74
|
+
return 0;
|
|
75
|
+
}
|
|
76
|
+
const manifest = JSON.parse(readFileSync(manifestPath, 'utf8'));
|
|
77
|
+
const prefix = config.prefix || manifest.prefix;
|
|
78
|
+
if (!prefix) throw new Error('no variable prefix — set kmwc.prefix or "prefix" in the manifest');
|
|
79
|
+
|
|
80
|
+
const cssText = config.cssVariables.sources.flatMap((sourcePath) => getStylesheetTexts(resolve(config.root, sourcePath), [])).join('\n');
|
|
81
|
+
const verdict = checkManifestVariables({
|
|
82
|
+
cssText,
|
|
83
|
+
manifest,
|
|
84
|
+
prefix,
|
|
85
|
+
internalVariables: config.cssVariables.internalVariables,
|
|
86
|
+
checkMode: config.cssVariables.checkMode,
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
const presenceWord = config.cssVariables.checkMode === 'consumed' ? 'consumed via var()' : 'declared';
|
|
90
|
+
const renderSection = (title, names) => `${title} (${names.length}):\n` + names.map((name) => ` --${name}`).join('\n');
|
|
91
|
+
const errors = [];
|
|
92
|
+
const warnings = [];
|
|
93
|
+
if (verdict.missingBaseVariables.length) errors.push(renderSection('--base-* consumed in CSS but MISSING from manifest.baseVariables', verdict.missingBaseVariables));
|
|
94
|
+
if (verdict.unusedBaseVariables.length) errors.push(renderSection('--base-* in manifest.baseVariables but NOT consumed in CSS (dead)', verdict.unusedBaseVariables));
|
|
95
|
+
if (verdict.unusedComponentVariables.length) {
|
|
96
|
+
errors.push(renderSection(`--${prefix}-* in manifest.componentVariables but NOT ${presenceWord} in CSS (dead)`, verdict.unusedComponentVariables));
|
|
97
|
+
}
|
|
98
|
+
if (verdict.undocumentedComponentVariables.length) {
|
|
99
|
+
const section = renderSection(`--${prefix}-* ${presenceWord} in CSS but MISSING from manifest.componentVariables`, verdict.undocumentedComponentVariables);
|
|
100
|
+
if (isStrict) errors.push(section);
|
|
101
|
+
else warnings.push(section + '\n (warning — --strict enforces; list true internals in kmwc.cssVariables.internalVariables)');
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if (warnings.length) console.warn('\n⚠ variable-manifest warnings:\n\n' + warnings.join('\n\n') + '\n');
|
|
105
|
+
if (errors.length) {
|
|
106
|
+
console.error('\n✗ variable manifest out of sync with CSS:\n\n' + errors.join('\n\n'));
|
|
107
|
+
console.error(`\nEdit ${config.cssVariables.manifest} (source of truth for IDE autocomplete + theme-designer), then rebuild.\n`);
|
|
108
|
+
return 1;
|
|
109
|
+
}
|
|
110
|
+
console.log(
|
|
111
|
+
`✓ variable manifest in sync — ${verdict.counts.base} base, ${verdict.counts.component} component vars` +
|
|
112
|
+
(warnings.length ? ` (${verdict.undocumentedComponentVariables.length} undocumented --${prefix}-* — see warning)` : '') +
|
|
113
|
+
'.'
|
|
114
|
+
);
|
|
115
|
+
return 0;
|
|
116
|
+
}
|