@keenmate/web-components-devkit 1.0.0-rc04 → 1.0.0-rc06
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 +80 -0
- package/README.md +44 -18
- package/package.json +1 -1
- package/src/cli/args.js +32 -9
- package/src/cli/commands/check-vars.js +4 -0
- package/src/cli/commands/doctor.js +32 -15
- package/src/cli/kmwc.js +71 -33
- package/src/cli/sync-plan.js +2 -2
- package/src/config.d.ts +14 -4
- package/src/config.js +13 -2
- package/src/examples/css/content.css +51 -0
- package/src/examples/css/controls.css +28 -0
- package/src/examples/css/form-controls.css +4 -0
- package/src/examples/css/tables.css +3 -2
- package/src/examples/css/tokens.css +5 -0
- package/src/examples/css/widgets.css +127 -23
- package/src/vite/index.js +8 -1
- package/templates/claude-block.md +5 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,85 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [1.0.0-rc06] - 2026-11-01 [PUBLISHED]
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- **`<kbd>` and `<mark>`** — every component documents a keyboard map and most highlight
|
|
8
|
+
search matches, and pages either left them in the browser's look (core's Interaction page:
|
|
9
|
+
18 bare `<kbd>`s) or styled them locally (core's Search page). A `<kbd>` now looks like the
|
|
10
|
+
spotlight's key hints and a `<mark>` like its match highlight. Widgets page WG08.
|
|
11
|
+
- **Field widths** — a native field is ~20 characters wide, so pages set inline widths (core:
|
|
12
|
+
14). A number field is now 7rem (six digits and the spin buttons; a `.form-group` still
|
|
13
|
+
stretches it), and `.w-xs` / `.w-sm` / `.w-md` / `.w-lg` (4 / 6 / 9 / 12rem) size a short
|
|
14
|
+
field. `.push-end` moves an item to the end of a `.controls` row. Form controls page FC12.
|
|
15
|
+
- **`kmwc.library: false`** — for a repo that builds its package itself (core: `tsc`) or ships
|
|
16
|
+
none. Plain `vite build`, the devkit's library mode, refuses to run instead of emptying
|
|
17
|
+
`dist/`; `vite build --mode examples` still builds the site, and sync no longer proposes a
|
|
18
|
+
`build: vite build` script. `{ "shouldBuild": false, "entry": … }` keeps the pages' entry.
|
|
19
|
+
- **`kmwc.cssVariables: false`** (or `checkMode: "off"`) — sync stopped re-adding `check:vars`
|
|
20
|
+
only when there was no manifest; now it can be switched off (core builds its variable names
|
|
21
|
+
by interpolation, which the checker can't see). `kmwc check-vars` then skips.
|
|
22
|
+
- **Doctor knows libraries** — a repo whose registry entry (or `kmwc.kind`) is `library` runs
|
|
23
|
+
only the rulebook suites that apply to it: naming-conventions, readme-structure,
|
|
24
|
+
publish-command, example-pages. The CSS, Custom Elements Manifest, overlay and
|
|
25
|
+
component-structure suites check what a custom element ships; on core they gave 12 errors
|
|
26
|
+
and 4 "suite could not run" warnings. No prefix warning for a library.
|
|
27
|
+
|
|
28
|
+
### Fixed
|
|
29
|
+
|
|
30
|
+
- **`kmwc <command> --help` ran the command** — only a top-level `--help` was handled and
|
|
31
|
+
unknown flags were ignored, so `kmwc sync --help` did a full sync (it overwrote core's
|
|
32
|
+
Makefile). `--help` / `-h` after any command now prints that command's usage and exits 0,
|
|
33
|
+
and an unknown option is an error (exit 2). Every command declares its flags; a boolean
|
|
34
|
+
flag no longer swallows the next word as its value.
|
|
35
|
+
- **Inline `<code>` widened a phone page** — a long identifier has no break opportunity, so
|
|
36
|
+
`resolvePresentation(mode, env, map)` in a heading made core's page 350px wide at 320px.
|
|
37
|
+
Inline code now breaks anywhere; code in a `<pre>` keeps its lines.
|
|
38
|
+
- **Muted log text failed contrast on the light panel** — `--muted` lines, the seq / time /
|
|
39
|
+
source cells and the empty placeholder used `--kmwc-text-color-3`: 2.83:1 on the light
|
|
40
|
+
`.log-panel` in the light theme (WCAG AA wants 4.5:1). They now use `--kmwc-text-color-2`.
|
|
41
|
+
A spec checks every tone on both panels in both themes.
|
|
42
|
+
- **`.css-readout` sat on the demo above it** — it had no top margin (core carried 8 inline
|
|
43
|
+
margins); it now has 0.75rem.
|
|
44
|
+
|
|
45
|
+
- **Timeline log lines on a phone** — the cells (`.log-line__seq`, `__time`, `__source`,
|
|
46
|
+
`__name`) never shrink and the row never wrapped, so on a narrow panel, or with a long
|
|
47
|
+
source name, the detail got the few pixels left over: one letter per line. The row now
|
|
48
|
+
wraps, and the detail wants ~14rem beside the cells; with less room it drops under them at
|
|
49
|
+
full width. Wide panels are unchanged. A cell is capped at the panel width.
|
|
50
|
+
|
|
51
|
+
## [1.0.0-rc05] - 2026-11-01 [PUBLISHED]
|
|
52
|
+
|
|
53
|
+
### Changed
|
|
54
|
+
|
|
55
|
+
- **`.event-log` is now the same panel as `.log-panel--dark`** — the legacy console differed
|
|
56
|
+
in small ways (grey base text, 0.8rem, 200px cap, no columns), so old and new logs looked
|
|
57
|
+
slightly different. It now renders exactly as `.log-panel--dark` (light base text,
|
|
58
|
+
0.85rem, `--log-max-height`, default 220px), and an `.event-entry` holding `.event-time`
|
|
59
|
+
/ `.event-name` is a timeline row: aligned time column, the detail wrapping in its own
|
|
60
|
+
column, tones via `.log-line--*`. Other `.event-log` markup (lines without
|
|
61
|
+
`.event-entry`, page-own classes) just gets the panel look.
|
|
62
|
+
|
|
63
|
+
### Added
|
|
64
|
+
|
|
65
|
+
- **Log lines: one tone set, timeline columns, text logs, empty placeholder** —
|
|
66
|
+
web-components-core's pages each coloured their log lines (four schemes mixing severity
|
|
67
|
+
with "what happened"). A `.log-line` now takes one of the pill tones — `--ok`, `--info`,
|
|
68
|
+
`--warn`, `--err`, `--muted` — and `--action` marks a "you clicked X" line. On
|
|
69
|
+
`.log-panel--dark` and `.event-log` the tones switch to fixed light tints
|
|
70
|
+
(`--kmwc-console-ok|info|warn|err-color`), so they stay readable on the console in both
|
|
71
|
+
themes. Every line in a log keeps its line breaks and indentation. The cells
|
|
72
|
+
`.log-line__seq`, `__time`, `__source`, `__name` turn a line into aligned columns, with
|
|
73
|
+
the detail wrapping in its own column. `<pre class="log-panel">` is a log fed by
|
|
74
|
+
`textContent`; `data-empty` is shown while a panel is empty.
|
|
75
|
+
- **`.pill--info` / `--muted` and `.status-dot--info` / `--muted`**, so pills, dots and log
|
|
76
|
+
lines share the same five tones.
|
|
77
|
+
- **`.mono`** (monospace text without the code chip; `textarea.mono` for a JSON editor),
|
|
78
|
+
**`td.is-highlight`** (one highlighted cell) and **`.check-list`** (checkbox / radio
|
|
79
|
+
options one per line, a long label wrapping beside its box).
|
|
80
|
+
- Playground: WG01 and WG03 show the tones, new WG05 (timeline), WG06 (text log, empty
|
|
81
|
+
placeholder), WG07 (`.mono`), FC11 (check list), TB06 (highlighted cell); e2e specs.
|
|
82
|
+
|
|
3
83
|
## [1.0.0-rc04] - 2026-11-01 [PUBLISHED]
|
|
4
84
|
|
|
5
85
|
### Fixed
|
package/README.md
CHANGED
|
@@ -19,20 +19,19 @@ them in the same shape: `@keenmate/web-components-devkit`.
|
|
|
19
19
|
> positioning) lives in [`@keenmate/web-components-core`](../web-components-core).
|
|
20
20
|
> Nothing here ships inside a component's bundle.
|
|
21
21
|
|
|
22
|
-
## What's New in v1.0.0-
|
|
22
|
+
## What's New in v1.0.0-rc06
|
|
23
23
|
|
|
24
|
-
- **Example
|
|
25
|
-
-
|
|
26
|
-
- **
|
|
24
|
+
- **Example pages — `<kbd>`, `<mark>`, field widths and `.push-end`** — Every component documents a keyboard map and most highlight search matches. Pages either left `<kbd>` in the browser's look (core's Interaction page had 18 bare keys) or styled `<mark>` themselves. A `<kbd>` now looks like the spotlight's key hints and a `<mark>` like its match highlight. Native fields are about 20 characters wide, so pages set inline widths; core had 14. A number field is now 7rem by default. `.w-xs` / `.w-sm` / `.w-md` / `.w-lg` (4 / 6 / 9 / 12rem) size a short field, and a `.form-group` still stretches it to the row. `.push-end` moves an item to the end of a `.controls` row. See the playground's WG08 and FC12.
|
|
25
|
+
- **Example pages — phone and contrast fixes** — Inline `<code>` now breaks anywhere. Before, a long identifier such as `resolvePresentation(mode, env, map)` in a heading made a page 350px wide on a 320px screen; code inside a `<pre>` keeps its lines. Muted log text on the light `.log-panel` (`--muted` lines, the seq / time / source cells, the empty placeholder) measured 2.83:1 in the light theme. It now uses `--kmwc-text-color-2`, and a spec checks every tone on both panels in both themes against WCAG AA's 4.5:1. Timeline rows wrap, so on a phone or with a long source name the detail drops under the cells instead of showing one letter per line. `.css-readout` gets a top margin after the demo it measures.
|
|
26
|
+
- **CLI — `kmwc <command> --help` no longer runs the command** — Only a top-level `--help` was handled, and unknown flags were silently ignored, so `kmwc sync --help` did a full sync and overwrote core's hand-written Makefile. Every command now lists the flags it accepts. `--help` / `-h` after any command prints that command's usage and exits 0, and an unknown option is an error (exit 2). A boolean flag no longer takes the next word as its value.
|
|
27
|
+
- **Libraries — `library: false`, `cssVariables: false`, and a doctor that knows the kind** — A repo like web-components-core builds its package with `tsc`. A plain `vite build` (the devkit's library mode) would empty its `dist/`, so `"kmwc": { "library": false }` now makes it refuse to run, while `vite build --mode examples` still builds the site. `"cssVariables": false` turns off the variables-manifest check, so sync stops re-adding `check:vars`. Doctor reads the registry kind (or `kmwc.kind`): a `library` runs only naming-conventions, readme-structure, publish-command and example-pages. On core that drops 12 errors and 4 "suite could not run" warnings that didn't apply to a library.
|
|
27
28
|
|
|
28
|
-
## What's New in v1.0.0-
|
|
29
|
+
## What's New in v1.0.0-rc05
|
|
29
30
|
|
|
30
|
-
- **
|
|
31
|
-
- **
|
|
32
|
-
-
|
|
33
|
-
- **
|
|
34
|
-
- **Example pages — short chapter links, phones and the example-pages suite** — `#th07`, `#th7` or an uncoded heading's slug now scroll to that chapter on arrival. On a phone, example pages no longer grow wider than the screen, which made mobile browsers report a wrong viewport to pages that demo breakpoints. `kmwc doctor` runs the rulebook's new `example-pages` suite: every example `<h2>` carries a permanent section code.
|
|
35
|
-
- **`make dev` at the devkit root** — the standard `dev` / `kill-port` / `build-examples` / `preview-examples` targets, run against the playground.
|
|
31
|
+
- **Log panels — one tone set for every log line** — web-components-core's example pages each coloured their log lines with their own classes (`.warn`, `.lv-error`, `data-kind="hook"` …), four schemes mixing severity with "what happened". A `.log-line` now takes one of five tones — `--ok`, `--info`, `--warn`, `--err`, `--muted`, the same names as `.pill` and `.status-dot` (which gain `--info` / `--muted`) — and `--action` marks a "▶ you clicked X" line. Kinds such as hook or callback map onto those tones; the kind itself goes in a cell as text. On `.log-panel--dark` the tones switch to fixed light tints (`--kmwc-console-ok|info|warn|err-color`), so they stay readable on the console in both themes, while a light `.log-panel` follows the page theme. Every line keeps its line breaks and indentation.
|
|
32
|
+
- **Log panels — timeline columns, text logs and an empty placeholder** — The cells `.log-line__seq`, `__time`, `__source` and `__name` turn a line into aligned columns, with the detail after them wrapping in its own column; the name takes the line's tone. A log fed by `textContent` is a `<pre class="log-panel">`, `data-empty="(nothing yet)"` shows while a panel is empty, and `--log-max-height: none` lets a log grow. This replaces the page-local timeline CSS core's Lifecycle and Interaction pages carried.
|
|
33
|
+
- **`.event-log` — the legacy console now renders as `.log-panel--dark`** — Many component pages (web-grid, web-dropzone, web-treeview, the showcases) still use `.event-log` / `.event-entry` / `.event-time` / `.event-name`, which differed slightly from the newer panel (grey base text, 0.8rem, a 200px cap, no columns). It is now the same panel, and an `.event-entry` holding `.event-time` / `.event-name` is a timeline row with an aligned time column and tones, so old and new logs look the same. Other `.event-log` markup — lines without `.event-entry`, page-own classes — only gets the panel look, so existing layouts don't break.
|
|
34
|
+
- **Small helpers — `.mono`, `td.is-highlight`, `.check-list`** — `.mono` sets the monospace font without the code chip, and `textarea.mono` makes a JSON or config editor. `td.is-highlight` (or `th`) tints one table cell, where only whole rows could be highlighted before. `.check-list` stacks checkbox / radio options one per line, a long label wrapping beside its box, with room for a trailing `.pill`. The playground shows everything on the Widgets (WG01–WG07), Form controls (FC11) and Tables (TB06) pages.
|
|
36
35
|
|
|
37
36
|
## Catalogue
|
|
38
37
|
|
|
@@ -144,7 +143,8 @@ Every field is optional. Defaults are derived from the package.
|
|
|
144
143
|
"tagline": "Feature-rich multiselect …", // default: package.json description
|
|
145
144
|
"tag": "web-multiselect",
|
|
146
145
|
"prefix": "ms", // reserved CSS prefix (rulebook base-variables.md)
|
|
147
|
-
"
|
|
146
|
+
"kind": "component", // default: the components.json entry's kind
|
|
147
|
+
"library": { // false: no library build (see below)
|
|
148
148
|
"entry": "src/index.ts", // default
|
|
149
149
|
"globalName": "MultiSelect", // UMD global
|
|
150
150
|
"fileName": "multiselect", // → dist/multiselect.js + .umd.js
|
|
@@ -167,7 +167,7 @@ Every field is optional. Defaults are derived from the package.
|
|
|
167
167
|
"runner": "podman",
|
|
168
168
|
"packageJsonPaths": ["package.json"] // monorepos: list workspace package.jsons too
|
|
169
169
|
},
|
|
170
|
-
"cssVariables": {
|
|
170
|
+
"cssVariables": { // false: no variables manifest, no check:vars
|
|
171
171
|
"sources": ["src/css"], // dirs (stylesheets) or explicit files (any type)
|
|
172
172
|
"internalVariables": ["ms-input-current-width"], // runtime internals, not theming knobs
|
|
173
173
|
"checkMode": "declared" // or "consumed" (var() reads)
|
|
@@ -181,6 +181,16 @@ Every field is optional. Defaults are derived from the package.
|
|
|
181
181
|
}
|
|
182
182
|
```
|
|
183
183
|
|
|
184
|
+
**A library** (registry `kind: "library"`, e.g. web-components-core) builds its
|
|
185
|
+
package itself (`tsc`) or ships none. `"library": false` (or `{ "shouldBuild":
|
|
186
|
+
false, "entry": … }` to keep the pages' entry script) makes plain `vite build`
|
|
187
|
+
refuse to run — it is the devkit's library mode and would empty `dist/` — while
|
|
188
|
+
`vite build --mode examples` still builds the site, and `kmwc sync` no longer
|
|
189
|
+
proposes a `build: vite build` script. `"cssVariables": false` turns off the
|
|
190
|
+
manifest check (no `check:vars` script, `kmwc check-vars` skips), for variables
|
|
191
|
+
the checker can't see, such as names built by string interpolation. Doctor runs
|
|
192
|
+
only the rulebook suites that apply to a library (see below).
|
|
193
|
+
|
|
184
194
|
### Vite preset
|
|
185
195
|
|
|
186
196
|
```ts
|
|
@@ -267,7 +277,8 @@ doctor` lists the ones still repeating the head/header boilerplate.
|
|
|
267
277
|
`.demo-area`, `.demo-card`, `.code-block`, `.note(.warning|.info|.success|.error)`,
|
|
268
278
|
`.grid`/`.card-grid`, `.reference-table` (+ `.table-*` modifiers), `.log-panel`, `.pill`,
|
|
269
279
|
`.status-dot`, `.control-group`, buttons (`.btn-secondary`, `.btn-outline`,
|
|
270
|
-
`.btn-danger`)
|
|
280
|
+
`.btn-danger`), `<kbd>` keys and `<mark>` search matches (the spotlight's look),
|
|
281
|
+
`.push-end` (to the end of a flex row) — while everything the kit **generates** is prefixed BEM
|
|
271
282
|
(`kmwc-chapter-nav__item--active`, `kmwc-copy-link--copied`, …).
|
|
272
283
|
- **Form controls** look the same on every page without a class: text fields,
|
|
273
284
|
selects and list boxes (`multiple` / `size`), textareas, checkboxes, radios,
|
|
@@ -277,7 +288,9 @@ doctor` lists the ones still repeating the head/header boilerplate.
|
|
|
277
288
|
selects and buttons share `--kmwc-control-height`, so a row of them lines up
|
|
278
289
|
in any container, not only `.controls`. The rules are zero-specificity
|
|
279
290
|
(`:where()`); `kmwc-native` on an element or an ancestor keeps the browser's
|
|
280
|
-
own controls (a light-DOM component that draws its own inputs).
|
|
291
|
+
own controls (a light-DOM component that draws its own inputs). A number field
|
|
292
|
+
is 7rem wide; `.w-xs` / `.w-sm` / `.w-md` / `.w-lg` (4 / 6 / 9 / 12rem) size a
|
|
293
|
+
short field without an inline style. The
|
|
281
294
|
playground's `examples-forms.html` shows them all.
|
|
282
295
|
- **Tables:** any `<table>` gets a header fill, row lines and a hover tint;
|
|
283
296
|
`.reference-table` is the full-width, fully bordered API table. Modifiers
|
|
@@ -285,9 +298,15 @@ doctor` lists the ones still repeating the head/header boilerplate.
|
|
|
285
298
|
(tighter cells; the controls in it shrink via `--kmwc-control-height`) — plus
|
|
286
299
|
`tr.is-highlight` and a `.num` cell (right-aligned, tabular figures). On a phone
|
|
287
300
|
a wide table scrolls inside its card. See the playground's `examples-tables.html`.
|
|
288
|
-
- **Widgets
|
|
289
|
-
`.
|
|
290
|
-
`
|
|
301
|
+
- **Widgets:** `.log-panel` (light, following the theme; `--dark` for a console
|
|
302
|
+
stream; the legacy `.event-log` / `.event-entry` markup renders the same). Lines keep their indentation; a
|
|
303
|
+
`.log-line` takes one tone — `--ok`, `--info`, `--warn`, `--err`, `--muted`,
|
|
304
|
+
the same five as `.pill` / `.status-dot` — and `--action` marks a "you clicked
|
|
305
|
+
X" line. Timeline cells (`.log-line__seq`, `__time`, `__source`, `__name`)
|
|
306
|
+
turn a line into aligned columns. A `<pre class="log-panel">` is a text-fed
|
|
307
|
+
log; `data-empty` shows while a panel is empty. Also `.mono`, `.check-list`
|
|
308
|
+
(options one per line), `td.is-highlight`, `.display-sliders` and
|
|
309
|
+
`.css-readout`. The playground's `examples-widgets.html` shows them all.
|
|
291
310
|
- **Theme switch** (top right) cycles light → dark → auto and sets
|
|
292
311
|
`data-theme` on `<html>` — the same signal components react to via
|
|
293
312
|
`:host-context([data-theme])`, so every example page is a dark-mode smoke
|
|
@@ -347,8 +366,12 @@ kmwc check-release --tag t latest ⇔ release version, rc ⇔ -rcN, version n
|
|
|
347
366
|
kmwc new web-<name> scaffold a component (--prefix, --title, --tag, --dir, --port, --minimal, --register)
|
|
348
367
|
--adopt: scaffold an ALREADY registered component (its port/prefix) to migrate into
|
|
349
368
|
kmwc readme [--check] regenerate this README's catalogue (devkit repo only)
|
|
369
|
+
kmwc <command> --help that command's usage — never runs the command
|
|
350
370
|
```
|
|
351
371
|
|
|
372
|
+
An unknown option is an error (exit 2), never ignored — a typo can't run a
|
|
373
|
+
command with its defaults.
|
|
374
|
+
|
|
352
375
|
Generated files start with a `GENERATED by kmwc sync` header. Repo-specific
|
|
353
376
|
make targets go in `Makefile.local`, which the generated Makefile includes at
|
|
354
377
|
the end. Per-developer overrides (`DOCKER_RUNNER`, `IMAGE_PORT`) go in
|
|
@@ -360,6 +383,9 @@ and the e2e suite before building and publishing.
|
|
|
360
383
|
base-variables, color-scheme, readme-structure, custom-elements-manifest,
|
|
361
384
|
responsive-overlay, publish-command, example-pages) with `bash`, passing `kmwc.prefix`, and
|
|
362
385
|
reports each `FAIL` as an error pointing at the matching `.checks.md` entry.
|
|
386
|
+
A `library` runs only naming-conventions, readme-structure, publish-command
|
|
387
|
+
and example-pages; the others check what a custom element ships (its CSS,
|
|
388
|
+
manifest, overlay).
|
|
363
389
|
When the rulebook isn't checked out (or there's no bash) the suites are
|
|
364
390
|
skipped with a warning — the rulebook stays the single source of truth; the
|
|
365
391
|
devkit never copies its rules.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@keenmate/web-components-devkit",
|
|
3
|
-
"version": "1.0.0-
|
|
3
|
+
"version": "1.0.0-rc06",
|
|
4
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
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
package/src/cli/args.js
CHANGED
|
@@ -1,16 +1,39 @@
|
|
|
1
|
-
/**
|
|
2
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Flag parser: --flag, --key value, --key=value, positionals. `flagSpec` names
|
|
3
|
+
* every flag a command takes — `booleans` never take a value, `values` always
|
|
4
|
+
* do. Anything else is an error: an ignored typo (or `--help` before it was
|
|
5
|
+
* handled) used to run the command for real.
|
|
6
|
+
*
|
|
7
|
+
* @param {string[]} args
|
|
8
|
+
* @param {{ booleans?: string[], values?: string[] }} [flagSpec]
|
|
9
|
+
*/
|
|
10
|
+
export function parseArgs(args, flagSpec = {}) {
|
|
11
|
+
const booleanFlags = new Set(flagSpec.booleans || []);
|
|
12
|
+
const valueFlags = new Set(['root', ...(flagSpec.values || [])]);
|
|
3
13
|
const out = { _: [] };
|
|
4
14
|
for (let i = 0; i < args.length; i++) {
|
|
5
|
-
const
|
|
6
|
-
if (!
|
|
7
|
-
out._.push(
|
|
15
|
+
const arg = args[i];
|
|
16
|
+
if (!arg.startsWith('-') || arg === '-') {
|
|
17
|
+
out._.push(arg);
|
|
8
18
|
continue;
|
|
9
19
|
}
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
20
|
+
if (!arg.startsWith('--')) throw new Error(`unknown option "${arg}"`);
|
|
21
|
+
const [key, inline] = arg.slice(2).split(/=(.*)/s);
|
|
22
|
+
if (booleanFlags.has(key)) {
|
|
23
|
+
if (inline !== undefined) throw new Error(`--${key} takes no value`);
|
|
24
|
+
out[key] = true;
|
|
25
|
+
} else if (valueFlags.has(key)) {
|
|
26
|
+
if (inline !== undefined) out[key] = inline;
|
|
27
|
+
else if (args[i + 1] !== undefined && !args[i + 1].startsWith('--')) out[key] = args[++i];
|
|
28
|
+
else throw new Error(`--${key} needs a value`);
|
|
29
|
+
} else {
|
|
30
|
+
throw new Error(`unknown option "--${key}"`);
|
|
31
|
+
}
|
|
14
32
|
}
|
|
15
33
|
return out;
|
|
16
34
|
}
|
|
35
|
+
|
|
36
|
+
/** True when the arguments ask for help (`--help` or `-h` anywhere). */
|
|
37
|
+
export function checkIsHelpRequested(args) {
|
|
38
|
+
return args.some((arg) => arg === '--help' || arg === '-h');
|
|
39
|
+
}
|
|
@@ -68,6 +68,10 @@ export function checkManifestVariables({ cssText, manifest, prefix, internalVari
|
|
|
68
68
|
export async function run(args) {
|
|
69
69
|
const config = getKmwcConfig(args.root);
|
|
70
70
|
const isStrict = !!args.strict;
|
|
71
|
+
if (!config.cssVariables.isEnabled) {
|
|
72
|
+
console.log('kmwc check-vars: off (kmwc.cssVariables is false) — nothing to check.');
|
|
73
|
+
return 0;
|
|
74
|
+
}
|
|
71
75
|
const manifestPath = resolve(config.root, config.cssVariables.manifest);
|
|
72
76
|
if (!existsSync(manifestPath)) {
|
|
73
77
|
console.log(`kmwc check-vars: no ${config.cssVariables.manifest} — nothing to check.`);
|
|
@@ -25,21 +25,33 @@ const LEGACY_PAGE_REFERENCES = ['examples-shared.css', 'examples-chapter-nav.js'
|
|
|
25
25
|
const VITE_CONFIG_FILES = ['vite.config.ts', 'vite.config.mjs', 'vite.config.js'];
|
|
26
26
|
const PLAYWRIGHT_CONFIG_FILES = ['playwright.config.ts', 'playwright.config.mjs', 'playwright.config.js'];
|
|
27
27
|
|
|
28
|
-
/**
|
|
28
|
+
/**
|
|
29
|
+
* Rulebook suites in the rulebook's canonical reading order; the prefix is
|
|
30
|
+
* passed where the script takes one. `isComponentOnly` suites check what a
|
|
31
|
+
* custom element ships (its CSS, manifest, overlay) — a `library` (registry
|
|
32
|
+
* kind, or kmwc.kind) skips them.
|
|
33
|
+
*/
|
|
29
34
|
export const RULEBOOK_SUITES = [
|
|
30
|
-
{ name: 'component-structure', isPrefixed: false },
|
|
31
|
-
{ name: 'naming-conventions', isPrefixed: true },
|
|
32
|
-
{ name: 'css-structure', isPrefixed: true },
|
|
33
|
-
{ name: 'theme-container', isPrefixed: true },
|
|
34
|
-
{ name: 'base-variables', isPrefixed: true },
|
|
35
|
-
{ name: 'color-scheme', isPrefixed: true },
|
|
36
|
-
{ name: 'readme-structure', isPrefixed: false },
|
|
37
|
-
{ name: 'custom-elements-manifest', isPrefixed: false },
|
|
38
|
-
{ name: 'responsive-overlay', isPrefixed: true },
|
|
39
|
-
{ name: 'publish-command', isPrefixed: false },
|
|
40
|
-
{ name: 'example-pages', isPrefixed: false },
|
|
35
|
+
{ name: 'component-structure', isPrefixed: false, isComponentOnly: true },
|
|
36
|
+
{ name: 'naming-conventions', isPrefixed: true, isComponentOnly: false },
|
|
37
|
+
{ name: 'css-structure', isPrefixed: true, isComponentOnly: true },
|
|
38
|
+
{ name: 'theme-container', isPrefixed: true, isComponentOnly: true },
|
|
39
|
+
{ name: 'base-variables', isPrefixed: true, isComponentOnly: true },
|
|
40
|
+
{ name: 'color-scheme', isPrefixed: true, isComponentOnly: true },
|
|
41
|
+
{ name: 'readme-structure', isPrefixed: false, isComponentOnly: false },
|
|
42
|
+
{ name: 'custom-elements-manifest', isPrefixed: false, isComponentOnly: true },
|
|
43
|
+
{ name: 'responsive-overlay', isPrefixed: true, isComponentOnly: true },
|
|
44
|
+
{ name: 'publish-command', isPrefixed: false, isComponentOnly: false },
|
|
45
|
+
{ name: 'example-pages', isPrefixed: false, isComponentOnly: false },
|
|
41
46
|
];
|
|
42
47
|
|
|
48
|
+
const LIBRARY_KIND = 'library';
|
|
49
|
+
|
|
50
|
+
/** The suites that apply to a repo of `kind`. Pure — exported for tests. */
|
|
51
|
+
export function getRulebookSuites(kind) {
|
|
52
|
+
return RULEBOOK_SUITES.filter((suite) => kind !== LIBRARY_KIND || !suite.isComponentOnly);
|
|
53
|
+
}
|
|
54
|
+
|
|
43
55
|
function getMajorVersion(versionRange) {
|
|
44
56
|
const match = String(versionRange || '').match(/(\d+)/);
|
|
45
57
|
return match ? Number(match[1]) : null;
|
|
@@ -85,7 +97,7 @@ export function checkComponent(config, doctorConfig = {}) {
|
|
|
85
97
|
} else {
|
|
86
98
|
addPass('config', 'package.json#kmwc present');
|
|
87
99
|
if (!config.packageJson.kmwc.title) addWarning('config', 'kmwc.title not set (falls back to a title-cased id)', 'set kmwc.title');
|
|
88
|
-
if (!config.prefix) addWarning('config', 'kmwc.prefix not set — rulebook suites run without a CSS prefix', 'set kmwc.prefix to the prefix reserved in base-variables.md');
|
|
100
|
+
if (!config.prefix && config.kind !== LIBRARY_KIND) addWarning('config', 'kmwc.prefix not set — rulebook suites run without a CSS prefix', 'set kmwc.prefix to the prefix reserved in base-variables.md');
|
|
89
101
|
if (!config.packageJson.kmwc.ports?.dev) addWarning('config', 'kmwc.ports.dev not set (falls back to 5173)', 'take the port from the devkit components.json');
|
|
90
102
|
}
|
|
91
103
|
|
|
@@ -248,7 +260,12 @@ function checkRulebook(config, { addPass, addWarning, addError }) {
|
|
|
248
260
|
return;
|
|
249
261
|
}
|
|
250
262
|
const skippedSuites = new Set(config.rulebook.skippedChecks);
|
|
251
|
-
|
|
263
|
+
const suites = getRulebookSuites(config.kind);
|
|
264
|
+
if (suites.length < RULEBOOK_SUITES.length) {
|
|
265
|
+
const componentOnlySuiteNames = RULEBOOK_SUITES.filter((suite) => !suites.includes(suite)).map((suite) => suite.name);
|
|
266
|
+
addPass('rulebook', `${config.kind}: component-only suites not run (${componentOnlySuiteNames.join(', ')})`);
|
|
267
|
+
}
|
|
268
|
+
for (const suite of suites) {
|
|
252
269
|
if (skippedSuites.has(suite.name)) continue;
|
|
253
270
|
if (!existsSync(join(config.rulebook.path, `${suite.name}.checks.sh`))) continue;
|
|
254
271
|
const suiteResult = runRulebookSuite(config, suite);
|
|
@@ -280,7 +297,7 @@ export async function run(args) {
|
|
|
280
297
|
}
|
|
281
298
|
|
|
282
299
|
const LEVEL_ICONS = { pass: '✓', warning: '⚠', error: '✗' };
|
|
283
|
-
console.log(`kmwc doctor — ${config.packageName} (${config.root})\n`);
|
|
300
|
+
console.log(`kmwc doctor — ${config.packageName}${config.kind === 'component' ? '' : ` [${config.kind}]`} (${config.root})\n`);
|
|
284
301
|
for (const result of results) {
|
|
285
302
|
if (result.level === 'pass' && !args.verbose) continue;
|
|
286
303
|
console.log(`${LEVEL_ICONS[result.level]} [${result.checkName}] ${result.message}${result.fixHint ? `\n → ${result.fixHint}` : ''}`);
|
package/src/cli/kmwc.js
CHANGED
|
@@ -8,59 +8,97 @@
|
|
|
8
8
|
* kmwc check-release --tag <tag> gate a publish (version ⇔ dist-tag, not yet on npm)
|
|
9
9
|
* kmwc new <name> [options] scaffold a new component repo next to the devkit
|
|
10
10
|
* kmwc readme [--check] regenerate the devkit README catalogue (devkit repo only)
|
|
11
|
+
* kmwc <command> --help that command's usage (never runs it)
|
|
11
12
|
*
|
|
12
13
|
* All commands act on the current directory (override with --root <dir>).
|
|
13
|
-
* Each command module exports `run(args)` returning the process exit code
|
|
14
|
+
* Each command module exports `run(args)` returning the process exit code; an
|
|
15
|
+
* unknown option is an error (exit 2), never ignored.
|
|
14
16
|
*/
|
|
15
17
|
import { argv, exit } from 'node:process';
|
|
16
|
-
import { parseArgs } from './args.js';
|
|
18
|
+
import { parseArgs, checkIsHelpRequested } from './args.js';
|
|
17
19
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
readme: () => import('./commands/readme.js'),
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
function renderUsage() {
|
|
28
|
-
console.log(`kmwc — Keenmate web-components devkit
|
|
29
|
-
|
|
30
|
-
Usage: kmwc <command> [options]
|
|
31
|
-
|
|
32
|
-
sync [--check] Regenerate Dockerfile, nginx.conf, Makefile, .dockerignore,
|
|
20
|
+
/** Every command: its module, the flags it takes and its usage text. */
|
|
21
|
+
const COMMANDS = {
|
|
22
|
+
sync: {
|
|
23
|
+
load: () => import('./commands/sync.js'),
|
|
24
|
+
flags: { booleans: ['check'] },
|
|
25
|
+
usage: `sync [--check] Regenerate Dockerfile, nginx.conf, Makefile, .dockerignore,
|
|
33
26
|
the CLAUDE.md devkit block, .gitignore entries and missing
|
|
34
|
-
standard scripts. --check: report drift, write nothing
|
|
35
|
-
|
|
27
|
+
standard scripts. --check: report drift, write nothing.`,
|
|
28
|
+
},
|
|
29
|
+
doctor: {
|
|
30
|
+
load: () => import('./commands/doctor.js'),
|
|
31
|
+
flags: { booleans: ['json', 'verbose', 'no-rulebook', 'no-pack'] },
|
|
32
|
+
usage: `doctor [--json] Check this repo against the devkit standard and the
|
|
36
33
|
BlissFramework rulebook (*.checks.sh), and that npm pack ships no
|
|
37
|
-
dev-only files. --verbose, --no-rulebook, --no-pack
|
|
38
|
-
|
|
39
|
-
check-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
34
|
+
dev-only files. --verbose, --no-rulebook, --no-pack.`,
|
|
35
|
+
},
|
|
36
|
+
'check-vars': {
|
|
37
|
+
load: () => import('./commands/check-vars.js'),
|
|
38
|
+
flags: { booleans: ['strict'] },
|
|
39
|
+
usage: `check-vars [--strict] Validate component-variables.manifest.json against the CSS.`,
|
|
40
|
+
},
|
|
41
|
+
'check-release': {
|
|
42
|
+
load: () => import('./commands/check-release.js'),
|
|
43
|
+
flags: { booleans: ['offline'], values: ['tag'] },
|
|
44
|
+
usage: `check-release --tag <t> Gate a publish: latest ⇔ release version, rc ⇔ -rcN,
|
|
45
|
+
version not on npm yet. --offline skips the npm lookup.`,
|
|
46
|
+
},
|
|
47
|
+
new: {
|
|
48
|
+
load: () => import('./commands/new.js'),
|
|
49
|
+
flags: { booleans: ['register', 'minimal', 'adopt', 'offline'], values: ['title', 'tagline', 'tag', 'prefix', 'dir', 'port'] },
|
|
50
|
+
usage: `new <name> Scaffold a new component (e.g. \`kmwc new web-colorpicker --prefix cpk\`).
|
|
51
|
+
--title "…" --tagline "…" --tag … --prefix … --dir … --port …
|
|
52
|
+
--register --offline
|
|
43
53
|
--minimal: empty skeleton (no inputs/events) instead of the sample
|
|
44
|
-
--adopt: an already registered component (migration scratch scaffold)
|
|
45
|
-
|
|
54
|
+
--adopt: an already registered component (migration scratch scaffold)`,
|
|
55
|
+
},
|
|
56
|
+
readme: {
|
|
57
|
+
load: () => import('./commands/readme.js'),
|
|
58
|
+
flags: { booleans: ['check'] },
|
|
59
|
+
usage: `readme [--check] Regenerate the README catalogue from components.json (devkit repo).`,
|
|
60
|
+
},
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
const GLOBAL_USAGE = `Global: --root <dir> Act on <dir> instead of the current directory.
|
|
64
|
+
--help, -h Show the usage (of one command: kmwc <command> --help).`;
|
|
46
65
|
|
|
47
|
-
|
|
66
|
+
function renderUsage(commandName) {
|
|
67
|
+
if (commandName) {
|
|
68
|
+
console.log(`Usage: kmwc ${COMMANDS[commandName].usage}\n\n${GLOBAL_USAGE}`);
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
const commandLines = Object.values(COMMANDS).map((command) => ` ${command.usage}`).join('\n');
|
|
72
|
+
console.log(`kmwc — Keenmate web-components devkit\n\nUsage: kmwc <command> [options]\n\n${commandLines}\n\n${GLOBAL_USAGE}`);
|
|
48
73
|
}
|
|
49
74
|
|
|
50
75
|
async function main() {
|
|
51
76
|
const [commandName, ...commandArgs] = argv.slice(2);
|
|
52
77
|
if (!commandName || ['help', '--help', '-h'].includes(commandName)) {
|
|
53
|
-
renderUsage();
|
|
78
|
+
renderUsage(COMMANDS[commandArgs[0]] ? commandArgs[0] : undefined);
|
|
54
79
|
return 0;
|
|
55
80
|
}
|
|
56
|
-
const
|
|
57
|
-
if (!
|
|
81
|
+
const command = COMMANDS[commandName];
|
|
82
|
+
if (!command) {
|
|
58
83
|
console.error(`kmwc: unknown command "${commandName}"\n`);
|
|
59
84
|
renderUsage();
|
|
60
85
|
return 2;
|
|
61
86
|
}
|
|
62
|
-
|
|
63
|
-
|
|
87
|
+
// Help never runs the command (`kmwc sync --help` once overwrote a Makefile).
|
|
88
|
+
if (checkIsHelpRequested(commandArgs)) {
|
|
89
|
+
renderUsage(commandName);
|
|
90
|
+
return 0;
|
|
91
|
+
}
|
|
92
|
+
let args;
|
|
93
|
+
try {
|
|
94
|
+
args = parseArgs(commandArgs, command.flags);
|
|
95
|
+
} catch (error) {
|
|
96
|
+
console.error(`kmwc ${commandName}: ${error.message}\n`);
|
|
97
|
+
renderUsage(commandName);
|
|
98
|
+
return 2;
|
|
99
|
+
}
|
|
100
|
+
const commandModule = await command.load();
|
|
101
|
+
return (await commandModule.run(args)) ?? 0;
|
|
64
102
|
}
|
|
65
103
|
|
|
66
104
|
main().then(
|
package/src/cli/sync-plan.js
CHANGED
|
@@ -69,14 +69,14 @@ const hasAnyFile = (root, fileNames) => fileNames.some((fileName) => existsSync(
|
|
|
69
69
|
export function getStandardScripts(config) {
|
|
70
70
|
const scripts = {
|
|
71
71
|
dev: 'vite',
|
|
72
|
-
build: 'vite build',
|
|
72
|
+
...(config.library.shouldBuild ? { build: 'vite build' } : {}),
|
|
73
73
|
'build:examples': 'vite build --mode examples',
|
|
74
74
|
'preview:examples': 'vite preview --mode examples',
|
|
75
75
|
'clean:dist': generateRemoveCommand(['dist']),
|
|
76
76
|
clean: generateRemoveCommand(['dist', config.examples.outDir]),
|
|
77
77
|
doctor: 'kmwc doctor',
|
|
78
78
|
};
|
|
79
|
-
if (existsSync(join(config.root, config.cssVariables.manifest))) scripts['check:vars'] = 'kmwc check-vars';
|
|
79
|
+
if (config.cssVariables.isEnabled && existsSync(join(config.root, config.cssVariables.manifest))) scripts['check:vars'] = 'kmwc check-vars';
|
|
80
80
|
if (existsSync(join(config.root, CEM_CONFIG_FILE))) scripts.analyze = 'cem analyze';
|
|
81
81
|
if (hasAnyFile(config.root, PLAYWRIGHT_CONFIG_FILES)) {
|
|
82
82
|
Object.assign(scripts, {
|
package/src/config.d.ts
CHANGED
|
@@ -10,7 +10,12 @@ export interface KmwcPackageConfig {
|
|
|
10
10
|
tag?: string;
|
|
11
11
|
/** Reserved CSS prefix (base-variables.md "Existing prefixes"), e.g. `ms`. */
|
|
12
12
|
prefix?: string;
|
|
13
|
-
library
|
|
13
|
+
/** Registry kind; defaults to the components.json entry's kind (else `component`). A `library` runs only the doctor checks that apply to it. */
|
|
14
|
+
kind?: 'component' | 'library' | 'showcase' | 'docs' | 'test';
|
|
15
|
+
/** The library build (`vite build`). `false` = the repo builds its package itself or ships none: plain `vite build` refuses to run, only `--mode examples` builds. */
|
|
16
|
+
library?: false | {
|
|
17
|
+
/** `false` = same as `library: false`, keeping `entry` for the example pages. */
|
|
18
|
+
shouldBuild?: boolean;
|
|
14
19
|
entry?: string;
|
|
15
20
|
/** UMD global name. */
|
|
16
21
|
globalName?: string;
|
|
@@ -51,14 +56,15 @@ export interface KmwcPackageConfig {
|
|
|
51
56
|
packageJsonPaths?: string[];
|
|
52
57
|
installCommand?: string;
|
|
53
58
|
};
|
|
54
|
-
|
|
59
|
+
/** `false` (or `checkMode: 'off'`): no variables manifest check — sync adds no `check:vars`, `kmwc check-vars` skips. */
|
|
60
|
+
cssVariables?: false | {
|
|
55
61
|
manifest?: string;
|
|
56
62
|
/** Directories (stylesheets scanned) or explicit files (any type, e.g. a built bundle). */
|
|
57
63
|
sources?: string[];
|
|
58
64
|
/** Prefixed names (without `--`) that are runtime internals, not theming knobs. */
|
|
59
65
|
internalVariables?: string[];
|
|
60
66
|
/** `declared`: manifest ⇔ `--x:` declarations; `consumed`: manifest ⇔ `var(--x)` reads. */
|
|
61
|
-
checkMode?: 'declared' | 'consumed';
|
|
67
|
+
checkMode?: 'declared' | 'consumed' | 'off';
|
|
62
68
|
};
|
|
63
69
|
rulebook?: {
|
|
64
70
|
/** Path to BlissFramework `guidelines/web-components` (env KMWC_RULEBOOK_PATH wins). */
|
|
@@ -75,6 +81,7 @@ export interface KmwcConfig {
|
|
|
75
81
|
packageJson: Record<string, any>;
|
|
76
82
|
id: string;
|
|
77
83
|
packageName: string;
|
|
84
|
+
kind: string;
|
|
78
85
|
version: string;
|
|
79
86
|
title: string;
|
|
80
87
|
tagline: string;
|
|
@@ -85,6 +92,7 @@ export interface KmwcConfig {
|
|
|
85
92
|
author: string;
|
|
86
93
|
license: string;
|
|
87
94
|
library: {
|
|
95
|
+
shouldBuild: boolean;
|
|
88
96
|
entry: string;
|
|
89
97
|
globalName: string;
|
|
90
98
|
fileName: string;
|
|
@@ -118,7 +126,8 @@ export interface KmwcConfig {
|
|
|
118
126
|
manifest: string;
|
|
119
127
|
sources: string[];
|
|
120
128
|
internalVariables: string[];
|
|
121
|
-
checkMode: 'declared' | 'consumed';
|
|
129
|
+
checkMode: 'declared' | 'consumed' | 'off';
|
|
130
|
+
isEnabled: boolean;
|
|
122
131
|
};
|
|
123
132
|
rulebook: { path: string; skippedChecks: string[] };
|
|
124
133
|
sync: { skippedFiles: string[] };
|
|
@@ -137,6 +146,7 @@ export const PORT_RANGES: Array<{ kinds: string[]; firstBlock: number; lastBlock
|
|
|
137
146
|
export const PREVIEW_PORT_OFFSET: number;
|
|
138
147
|
export const IMAGE_PORT_OFFSET: number;
|
|
139
148
|
export function getPortsForBlock(portBlock: number): { dev: number; preview: number; image: number };
|
|
149
|
+
export function getRegistryKind(id: string, registry?: { components: Array<{ id: string; kind: string }> }): string;
|
|
140
150
|
export function getPortRange(kind: string): { kinds: string[]; firstBlock: number; lastBlock: number };
|
|
141
151
|
export function getNextPortBlock(registry?: { components: Array<Record<string, any>> }, kind?: string): number;
|
|
142
152
|
export function getPeerModule(root: string, moduleId: string): any;
|
package/src/config.js
CHANGED
|
@@ -139,6 +139,11 @@ export function getRegistry() {
|
|
|
139
139
|
return getJsonFile(join(DEVKIT_ROOT, 'components.json'));
|
|
140
140
|
}
|
|
141
141
|
|
|
142
|
+
/** The registry kind of `id` (`component`, `library` …); `component` when it isn't registered. */
|
|
143
|
+
export function getRegistryKind(id, registry = getRegistry()) {
|
|
144
|
+
return registry.components.find((entry) => entry.id === id)?.kind || 'component';
|
|
145
|
+
}
|
|
146
|
+
|
|
142
147
|
/** Strip the npm scope: `@keenmate/web-multiselect` → `web-multiselect`. */
|
|
143
148
|
export function getUnscopedName(packageName) {
|
|
144
149
|
return String(packageName || '').replace(/^@[^/]+\//, '');
|
|
@@ -182,13 +187,16 @@ export function getKmwcConfig(root = process.cwd()) {
|
|
|
182
187
|
export function mapKmwcConfig(packageJson, root = process.cwd()) {
|
|
183
188
|
const packageConfig = packageJson.kmwc || {};
|
|
184
189
|
const id = packageConfig.id || getUnscopedName(packageJson.name) || 'component';
|
|
185
|
-
|
|
190
|
+
// `library: false` — the repo builds its package itself (tsc …) or ships none;
|
|
191
|
+
// Vite serves and builds only the examples site.
|
|
192
|
+
const library = packageConfig.library === false ? { shouldBuild: false } : packageConfig.library || {};
|
|
186
193
|
const examples = packageConfig.examples || {};
|
|
187
194
|
const ports = packageConfig.ports || {};
|
|
188
195
|
const image = packageConfig.image || {};
|
|
189
196
|
const theme = packageConfig.theme || {};
|
|
190
197
|
const analytics = packageConfig.analytics || {};
|
|
191
|
-
|
|
198
|
+
// `cssVariables: false` — no variables manifest to check (no check:vars script).
|
|
199
|
+
const cssVariables = packageConfig.cssVariables === false ? { checkMode: 'off' } : packageConfig.cssVariables || {};
|
|
192
200
|
const rulebook = packageConfig.rulebook || {};
|
|
193
201
|
const devPort = ports.dev ?? 5173;
|
|
194
202
|
|
|
@@ -198,6 +206,7 @@ export function mapKmwcConfig(packageJson, root = process.cwd()) {
|
|
|
198
206
|
packageJson,
|
|
199
207
|
id,
|
|
200
208
|
packageName: packageJson.name || id,
|
|
209
|
+
kind: packageConfig.kind || getRegistryKind(id),
|
|
201
210
|
version: packageJson.version || '0.1.0',
|
|
202
211
|
title: packageConfig.title || mapToTitleCase(id),
|
|
203
212
|
tagline: packageConfig.tagline || packageJson.description || '',
|
|
@@ -208,6 +217,7 @@ export function mapKmwcConfig(packageJson, root = process.cwd()) {
|
|
|
208
217
|
author: typeof packageJson.author === 'string' ? packageJson.author : packageJson.author?.name || 'Keenmate s.r.o.',
|
|
209
218
|
license: packageJson.license || 'MIT',
|
|
210
219
|
library: {
|
|
220
|
+
shouldBuild: library.shouldBuild ?? true,
|
|
211
221
|
entry: library.entry || 'src/index.ts',
|
|
212
222
|
globalName: library.globalName || mapToPascalCase(id),
|
|
213
223
|
fileName: library.fileName || id,
|
|
@@ -254,6 +264,7 @@ export function mapKmwcConfig(packageJson, root = process.cwd()) {
|
|
|
254
264
|
sources: cssVariables.sources || ['src/css'],
|
|
255
265
|
internalVariables: cssVariables.internalVariables || [],
|
|
256
266
|
checkMode: cssVariables.checkMode || 'declared',
|
|
267
|
+
isEnabled: cssVariables.checkMode !== 'off',
|
|
257
268
|
},
|
|
258
269
|
rulebook: {
|
|
259
270
|
path:
|
|
@@ -120,6 +120,9 @@
|
|
|
120
120
|
font-size: 0.9em;
|
|
121
121
|
color: var(--kmwc-accent-color);
|
|
122
122
|
font-family: var(--kmwc-mono-font-family);
|
|
123
|
+
/* A long identifier has no break opportunity — break it anywhere
|
|
124
|
+
rather than widen the page on a phone. */
|
|
125
|
+
overflow-wrap: anywhere;
|
|
123
126
|
}
|
|
124
127
|
|
|
125
128
|
pre code,
|
|
@@ -130,6 +133,32 @@
|
|
|
130
133
|
border-radius: 0;
|
|
131
134
|
color: inherit;
|
|
132
135
|
font-size: inherit;
|
|
136
|
+
overflow-wrap: inherit;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/* A key in a keyboard map — the spotlight's key-hint look. */
|
|
140
|
+
kbd {
|
|
141
|
+
display: inline-block;
|
|
142
|
+
padding: 0.1rem 0.35rem;
|
|
143
|
+
background-color: var(--kmwc-elevated-bg);
|
|
144
|
+
color: var(--kmwc-text-color-1);
|
|
145
|
+
border: 1px solid var(--kmwc-border-color);
|
|
146
|
+
border-bottom-width: 2px;
|
|
147
|
+
border-radius: var(--kmwc-radius-sm);
|
|
148
|
+
font-family: var(--kmwc-font-family);
|
|
149
|
+
font-weight: 600;
|
|
150
|
+
font-size: 0.8em;
|
|
151
|
+
line-height: 1.2;
|
|
152
|
+
white-space: nowrap;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/* A search match — the spotlight's match highlight. */
|
|
156
|
+
mark {
|
|
157
|
+
background-color: var(--kmwc-accent-light-bg-hover);
|
|
158
|
+
color: var(--kmwc-accent-color);
|
|
159
|
+
font-weight: 600;
|
|
160
|
+
padding: 0 2px;
|
|
161
|
+
border-radius: var(--kmwc-radius-sm);
|
|
133
162
|
}
|
|
134
163
|
|
|
135
164
|
/* ==========================================================================
|
|
@@ -214,10 +243,32 @@
|
|
|
214
243
|
color: var(--kmwc-text-color-3);
|
|
215
244
|
}
|
|
216
245
|
|
|
246
|
+
/* Monospace text that isn't code (folded values, code-unit dumps); on a
|
|
247
|
+
textarea, a JSON / config editor. */
|
|
248
|
+
.mono {
|
|
249
|
+
font-family: var(--kmwc-mono-font-family);
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
textarea.mono {
|
|
253
|
+
font-size: 0.8125rem;
|
|
254
|
+
}
|
|
255
|
+
|
|
217
256
|
.mt-1 {
|
|
218
257
|
margin-top: 1rem;
|
|
219
258
|
}
|
|
220
259
|
|
|
260
|
+
/* Push an item to the end of a flex row (`.controls`, `.btn-group` …). */
|
|
261
|
+
.push-end {
|
|
262
|
+
margin-inline-start: auto;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
/* Field widths: a native field is ~20 characters wide; these size a short
|
|
266
|
+
one (a page size, a caption) without an inline style. */
|
|
267
|
+
.w-xs { width: 4rem; max-width: 100%; }
|
|
268
|
+
.w-sm { width: 6rem; max-width: 100%; }
|
|
269
|
+
.w-md { width: 9rem; max-width: 100%; }
|
|
270
|
+
.w-lg { width: 12rem; max-width: 100%; }
|
|
271
|
+
|
|
221
272
|
@media (max-width: 768px) {
|
|
222
273
|
.grid,
|
|
223
274
|
.grid-2,
|
|
@@ -86,6 +86,34 @@
|
|
|
86
86
|
margin-bottom: 0.75rem;
|
|
87
87
|
}
|
|
88
88
|
|
|
89
|
+
/* Check list — checkbox / radio options one per line; a long label wraps
|
|
90
|
+
beside its box, and anything after the text (a .pill) follows it. */
|
|
91
|
+
.check-list {
|
|
92
|
+
display: grid;
|
|
93
|
+
gap: 0.35rem;
|
|
94
|
+
margin-bottom: 1rem;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
.check-list > label {
|
|
98
|
+
display: flex;
|
|
99
|
+
align-items: flex-start;
|
|
100
|
+
gap: 0.5rem;
|
|
101
|
+
cursor: pointer;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
.check-list > label > input {
|
|
105
|
+
flex: none;
|
|
106
|
+
margin-top: calc((1lh - var(--kmwc-checkbox-size)) / 2);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
.check-list > label > :not(input) {
|
|
110
|
+
min-width: 0;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
.check-list code {
|
|
114
|
+
overflow-wrap: anywhere;
|
|
115
|
+
}
|
|
116
|
+
|
|
89
117
|
/* Control group — a label stacked over an input/select; rows of them wrap
|
|
90
118
|
via `.control-row`. */
|
|
91
119
|
.control-row {
|
|
@@ -74,7 +74,11 @@
|
|
|
74
74
|
cursor: not-allowed;
|
|
75
75
|
}
|
|
76
76
|
|
|
77
|
+
/* A number is short: 7rem holds six digits and the spin buttons (a
|
|
78
|
+
.form-group still stretches it to the row; .w-* sizes it). */
|
|
77
79
|
:where(input[type="number"]):not(:where(.kmwc-native, .kmwc-native *)) {
|
|
80
|
+
width: 7rem;
|
|
81
|
+
max-width: 100%;
|
|
78
82
|
padding-inline-end: 0.25rem; /* room for the spin buttons */
|
|
79
83
|
}
|
|
80
84
|
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
.table-striped zebra rows
|
|
10
10
|
.table-compact tighter cells; controls inside shrink with them
|
|
11
11
|
.table-full full width
|
|
12
|
-
tr.is-highlight an accent-tinted row
|
|
12
|
+
tr.is-highlight an accent-tinted row (td.is-highlight: one cell)
|
|
13
13
|
.num a right-aligned, tabular-figure cell
|
|
14
14
|
|
|
15
15
|
The base is zero-specificity (:where()), so a page or component rule wins;
|
|
@@ -103,7 +103,8 @@
|
|
|
103
103
|
}
|
|
104
104
|
|
|
105
105
|
tr.is-highlight,
|
|
106
|
-
tr.is-highlight:hover
|
|
106
|
+
tr.is-highlight:hover,
|
|
107
|
+
:is(th, td).is-highlight {
|
|
107
108
|
background: var(--kmwc-accent-light-bg);
|
|
108
109
|
}
|
|
109
110
|
|
|
@@ -113,6 +113,11 @@
|
|
|
113
113
|
--kmwc-console-text-color: #e2e8f0;
|
|
114
114
|
--kmwc-console-muted-color: #94a3b8;
|
|
115
115
|
--kmwc-console-highlight-color: #68d391;
|
|
116
|
+
/* Log-line tones on the console (always dark, so fixed light tints). */
|
|
117
|
+
--kmwc-console-ok-color: #68d391;
|
|
118
|
+
--kmwc-console-info-color: #7dd3fc;
|
|
119
|
+
--kmwc-console-warn-color: #fcd34d;
|
|
120
|
+
--kmwc-console-err-color: #fca5a5;
|
|
116
121
|
--kmwc-inline-code-bg: light-dark(#edf2f7, #2b2b2b);
|
|
117
122
|
|
|
118
123
|
/* ======================================================================
|
|
@@ -7,11 +7,32 @@
|
|
|
7
7
|
/* ==========================================================================
|
|
8
8
|
LOG PANELS
|
|
9
9
|
==========================================================================
|
|
10
|
-
`.log-panel` — monospace, scrollable box for streaming output
|
|
11
|
-
default
|
|
12
|
-
`--log-max-height
|
|
13
|
-
|
|
14
|
-
|
|
10
|
+
`.log-panel` — monospace, scrollable box for streaming output. Light by
|
|
11
|
+
default (it follows the page theme); `.log-panel--dark` for a console
|
|
12
|
+
stream (always dark). `--log-max-height` caps the height (`none` lets it
|
|
13
|
+
grow).
|
|
14
|
+
|
|
15
|
+
Lines: any child keeps its line breaks and indentation. `.log-line` takes
|
|
16
|
+
one tone — `--ok`, `--info`, `--warn`, `--err`, `--muted` (the pill
|
|
17
|
+
tones) — and `--action` marks a "you clicked X" line. Timeline cells
|
|
18
|
+
`.log-line__seq`, `__time`, `__source`, `__name` turn a line into columns;
|
|
19
|
+
the element after them is the detail. A panel fed by textContent is a
|
|
20
|
+
`<pre class="log-panel">`. `data-empty="(nothing yet)"` shows while the
|
|
21
|
+
panel is empty.
|
|
22
|
+
|
|
23
|
+
Legacy markup: `.event-log` is a `.log-panel--dark`, an `.event-entry`
|
|
24
|
+
holding `.event-time` / `.event-name` is a timeline line with those two
|
|
25
|
+
cells. Prefer the `.log-*` classes in new pages. */
|
|
26
|
+
.log-panel,
|
|
27
|
+
.event-log {
|
|
28
|
+
--kmwc-log-ok-color: var(--kmwc-success-color);
|
|
29
|
+
--kmwc-log-info-color: var(--kmwc-info-color);
|
|
30
|
+
--kmwc-log-warn-color: var(--kmwc-warning-color);
|
|
31
|
+
--kmwc-log-err-color: var(--kmwc-danger-color);
|
|
32
|
+
/* text-color-3 is 2.8:1 on the light panel — under WCAG AA's 4.5. */
|
|
33
|
+
--kmwc-log-muted-color: var(--kmwc-text-color-2);
|
|
34
|
+
--kmwc-log-name-color: var(--kmwc-accent-color);
|
|
35
|
+
--kmwc-log-rule-color: var(--kmwc-border-color);
|
|
15
36
|
font-family: var(--kmwc-mono-font-family);
|
|
16
37
|
font-size: 0.85rem;
|
|
17
38
|
line-height: 1.5;
|
|
@@ -24,31 +45,108 @@
|
|
|
24
45
|
border: 1px solid var(--kmwc-border-color);
|
|
25
46
|
}
|
|
26
47
|
|
|
27
|
-
.log-panel--dark
|
|
48
|
+
.log-panel--dark,
|
|
49
|
+
.event-log {
|
|
50
|
+
--kmwc-log-ok-color: var(--kmwc-console-ok-color);
|
|
51
|
+
--kmwc-log-info-color: var(--kmwc-console-info-color);
|
|
52
|
+
--kmwc-log-warn-color: var(--kmwc-console-warn-color);
|
|
53
|
+
--kmwc-log-err-color: var(--kmwc-console-err-color);
|
|
54
|
+
--kmwc-log-muted-color: var(--kmwc-console-muted-color);
|
|
55
|
+
--kmwc-log-name-color: var(--kmwc-console-highlight-color);
|
|
56
|
+
--kmwc-log-rule-color: color-mix(in srgb, var(--kmwc-console-text-color) 12%, transparent);
|
|
28
57
|
background: var(--kmwc-console-bg);
|
|
29
58
|
color: var(--kmwc-console-text-color);
|
|
30
59
|
border-color: transparent;
|
|
31
60
|
}
|
|
32
61
|
|
|
33
|
-
.
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
border-radius: var(--kmwc-radius-md);
|
|
38
|
-
font-family: var(--kmwc-mono-font-family);
|
|
39
|
-
font-size: 0.8rem;
|
|
40
|
-
max-height: 200px;
|
|
41
|
-
overflow-y: auto;
|
|
42
|
-
line-height: 1.5;
|
|
62
|
+
pre.log-panel {
|
|
63
|
+
margin: 0;
|
|
64
|
+
white-space: pre-wrap;
|
|
65
|
+
overflow-wrap: anywhere;
|
|
43
66
|
}
|
|
44
67
|
|
|
45
|
-
.
|
|
46
|
-
|
|
47
|
-
|
|
68
|
+
:is(.log-panel, .event-log) > * {
|
|
69
|
+
white-space: pre-wrap;
|
|
70
|
+
overflow-wrap: anywhere;
|
|
48
71
|
}
|
|
49
72
|
|
|
50
|
-
.
|
|
51
|
-
|
|
73
|
+
:is(.log-panel, .event-log)[data-empty]:empty::before {
|
|
74
|
+
content: attr(data-empty);
|
|
75
|
+
color: var(--kmwc-log-muted-color);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
.log-line,
|
|
79
|
+
.event-entry {
|
|
80
|
+
color: var(--kmwc-log-tone, currentColor);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
.log-line--ok { --kmwc-log-tone: var(--kmwc-log-ok-color); }
|
|
84
|
+
.log-line--info { --kmwc-log-tone: var(--kmwc-log-info-color); }
|
|
85
|
+
.log-line--warn { --kmwc-log-tone: var(--kmwc-log-warn-color); }
|
|
86
|
+
.log-line--err { --kmwc-log-tone: var(--kmwc-log-err-color); }
|
|
87
|
+
.log-line--muted { --kmwc-log-tone: var(--kmwc-log-muted-color); }
|
|
88
|
+
|
|
89
|
+
.log-line--action {
|
|
90
|
+
font-weight: 600;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
.log-line--action:not(:first-child) {
|
|
94
|
+
margin-top: 0.35rem;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/* Timeline: a line with cells is a row; the cells keep their width and the
|
|
98
|
+
detail after them wraps in its own column. When the cells leave the detail
|
|
99
|
+
less than ~14rem (a phone, a long source name) it drops under them at full
|
|
100
|
+
width instead of being squeezed to a letter per line. */
|
|
101
|
+
.log-line:has(> :is(.log-line__seq, .log-line__time, .log-line__source, .log-line__name)),
|
|
102
|
+
.event-entry:has(> :is(.event-time, .event-name)) {
|
|
103
|
+
display: flex;
|
|
104
|
+
column-gap: 0.6rem;
|
|
105
|
+
padding: 0.1rem 0;
|
|
106
|
+
border-bottom: 1px solid var(--kmwc-log-rule-color);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
.log-line:has(> :is(.log-line__seq, .log-line__time, .log-line__source, .log-line__name)) {
|
|
110
|
+
flex-wrap: wrap;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
.log-line:has(> :is(.log-line__seq, .log-line__time, .log-line__source, .log-line__name)) > :last-child:not(.log-line__name) {
|
|
114
|
+
flex: 1 1 14rem;
|
|
115
|
+
min-width: 0;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
.log-line__seq,
|
|
119
|
+
.log-line__time,
|
|
120
|
+
.log-line__source,
|
|
121
|
+
.log-line__name,
|
|
122
|
+
.event-time,
|
|
123
|
+
.event-name {
|
|
124
|
+
flex: none;
|
|
125
|
+
max-width: 100%;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
.log-line__seq,
|
|
129
|
+
.log-line__time,
|
|
130
|
+
.event-time {
|
|
131
|
+
text-align: end;
|
|
132
|
+
font-variant-numeric: tabular-nums;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
.log-line__seq { min-width: 2.2rem; }
|
|
136
|
+
.log-line__time { min-width: 4.6rem; }
|
|
137
|
+
|
|
138
|
+
.log-line__seq,
|
|
139
|
+
.log-line__time,
|
|
140
|
+
.log-line__source,
|
|
141
|
+
.event-time {
|
|
142
|
+
color: var(--kmwc-log-muted-color);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
.log-line__name,
|
|
146
|
+
.event-name {
|
|
147
|
+
color: var(--kmwc-log-tone, var(--kmwc-log-name-color));
|
|
148
|
+
font-weight: 600;
|
|
149
|
+
}
|
|
52
150
|
|
|
53
151
|
/* ==========================================================================
|
|
54
152
|
CSS-VARIABLE DEMO HELPERS
|
|
@@ -89,7 +187,7 @@
|
|
|
89
187
|
display: flex;
|
|
90
188
|
flex-wrap: wrap;
|
|
91
189
|
gap: 0.4rem;
|
|
92
|
-
margin: 0.
|
|
190
|
+
margin: 0.75rem 0 1rem; /* it follows the demo it measures */
|
|
93
191
|
}
|
|
94
192
|
|
|
95
193
|
.css-readout code { font-size: 0.78rem; }
|
|
@@ -98,7 +196,9 @@
|
|
|
98
196
|
|
|
99
197
|
/* ==========================================================================
|
|
100
198
|
STATUS DOTS & PILLS
|
|
101
|
-
==========================================================================
|
|
199
|
+
==========================================================================
|
|
200
|
+
One tone set with the log lines: --ok, --info, --warn, --err, --muted
|
|
201
|
+
(= the neutral default). */
|
|
102
202
|
.status-dot {
|
|
103
203
|
--kmwc-status-color: var(--kmwc-neutral-color);
|
|
104
204
|
display: inline-block;
|
|
@@ -113,6 +213,8 @@
|
|
|
113
213
|
.status-dot--ok { --kmwc-status-color: var(--kmwc-success-color); }
|
|
114
214
|
.status-dot--warn { --kmwc-status-color: var(--kmwc-warning-color); }
|
|
115
215
|
.status-dot--err { --kmwc-status-color: var(--kmwc-danger-color); }
|
|
216
|
+
.status-dot--info { --kmwc-status-color: var(--kmwc-info-color); }
|
|
217
|
+
.status-dot--muted { --kmwc-status-color: var(--kmwc-neutral-color); }
|
|
116
218
|
|
|
117
219
|
.pill {
|
|
118
220
|
--kmwc-status-color: var(--kmwc-neutral-color);
|
|
@@ -130,4 +232,6 @@
|
|
|
130
232
|
.pill--ok { --kmwc-status-color: var(--kmwc-success-color); }
|
|
131
233
|
.pill--warn { --kmwc-status-color: var(--kmwc-warning-color); }
|
|
132
234
|
.pill--err { --kmwc-status-color: var(--kmwc-danger-color); }
|
|
235
|
+
.pill--info { --kmwc-status-color: var(--kmwc-info-color); }
|
|
236
|
+
.pill--muted { --kmwc-status-color: var(--kmwc-neutral-color); }
|
|
133
237
|
}
|
package/src/vite/index.js
CHANGED
|
@@ -199,9 +199,16 @@ export function defineComponentConfig(presetConfig = {}) {
|
|
|
199
199
|
const config = getKmwcConfig(presetConfig.root);
|
|
200
200
|
// The component's own vite — the one running this config (see loadPeerModule).
|
|
201
201
|
const { mergeConfig, searchForWorkspaceRoot } = await loadPeerModule(config.root, 'vite');
|
|
202
|
-
const library = { ...config.library, ...(presetConfig.library || {}) };
|
|
202
|
+
const library = { ...config.library, ...(presetConfig.library === false ? { shouldBuild: false } : presetConfig.library || {}) };
|
|
203
203
|
const isExamplesMode = env.mode === EXAMPLES_MODE;
|
|
204
204
|
const isLibraryBuild = env.command === 'build' && !isExamplesMode;
|
|
205
|
+
if (isLibraryBuild && !library.shouldBuild) {
|
|
206
|
+
// The library build empties dist/ — it would wipe a package the repo builds itself.
|
|
207
|
+
throw new Error(
|
|
208
|
+
`${config.id}: kmwc.library is false — plain \`vite build\` has nothing to build. ` +
|
|
209
|
+
'Build the package with its own script (npm run build), the examples site with `vite build --mode examples`.'
|
|
210
|
+
);
|
|
211
|
+
}
|
|
205
212
|
|
|
206
213
|
const plugins = [createExamplesPlugin({ root: config.root }), ...(presetConfig.plugins || [])];
|
|
207
214
|
|
|
@@ -25,7 +25,11 @@ locally — every local fork is how they drifted apart.
|
|
|
25
25
|
controls (inputs, selects, checkboxes, radios, `role="switch"`, buttons) and
|
|
26
26
|
tables are already styled by the kit (`.reference-table`, `.table-striped`,
|
|
27
27
|
`.table-compact`, …) — don't restyle them per page; `kmwc-native` opts an
|
|
28
|
-
element out.
|
|
28
|
+
element out. Log lines take the kit tones (`.log-line--ok|info|warn|err|muted`,
|
|
29
|
+
`--action`, timeline cells `.log-line__time` …) — don't colour them per page.
|
|
30
|
+
`<kbd>`, `<mark>`, field widths (`.w-xs` … `.w-lg`) and `.push-end` are kit
|
|
31
|
+
too — no inline styles for them.
|
|
32
|
+
If a second component needs a pattern, it belongs in the devkit.
|
|
29
33
|
- **Example pages are fragments:** no `<html>`/`<head>`/`<header>` — start with
|
|
30
34
|
`<meta name="kmwc:title" content="…">`, the devkit layout adds the rest. Shared text
|
|
31
35
|
goes in `partials/` via `<kmwc-include>`; show code with `<kmwc-demo>` (the live
|