@webjsdev/cli 0.10.42 → 0.10.43
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/lib/create.js
CHANGED
|
@@ -18,6 +18,7 @@ import { existsSync } from 'node:fs';
|
|
|
18
18
|
import { createRequire } from 'node:module';
|
|
19
19
|
import { spawnSync } from 'node:child_process';
|
|
20
20
|
import { bunifyProse, bunifyDockerfile, bunifyCompose, bunifyCi } from './runtime-rewrite.js';
|
|
21
|
+
import { leanComponentSource } from './lean-copy.js';
|
|
21
22
|
|
|
22
23
|
/**
|
|
23
24
|
* Detect which package manager invoked us. Reads `npm_config_user_agent`,
|
|
@@ -120,12 +121,16 @@ async function readUiComponent(name) {
|
|
|
120
121
|
const raw = await readFile(src, 'utf8');
|
|
121
122
|
// The registry component imports cn() via a relative `../lib/utils.ts`; rewrite
|
|
122
123
|
// it to the scaffolded app's aliased path (cn lives at lib/utils/cn.ts).
|
|
123
|
-
|
|
124
|
+
const rewritten = raw
|
|
124
125
|
.replaceAll("'../lib/utils.ts'", "'#lib/utils/cn.ts'")
|
|
125
126
|
.replaceAll('"../lib/utils.ts"', '"#lib/utils/cn.ts"')
|
|
126
127
|
// onBeforeCache lives in its own client-only module so cn() stays pure (#819).
|
|
127
128
|
.replaceAll("'../lib/dom.ts'", "'#lib/utils/dom.ts'")
|
|
128
129
|
.replaceAll('"../lib/dom.ts"', '"#lib/utils/dom.ts"');
|
|
130
|
+
// Strip the worked @example from a Tier-1 helper (same as `webjs ui add`), so
|
|
131
|
+
// the scaffolded component is lean and the example is served on demand. The
|
|
132
|
+
// shared helper is used by the saas-template copier too, so they cannot drift.
|
|
133
|
+
return leanComponentSource(rewritten, name);
|
|
129
134
|
}
|
|
130
135
|
|
|
131
136
|
/**
|
package/lib/lean-copy.js
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The scaffold's lean-copy of a ui component (#983).
|
|
3
|
+
*
|
|
4
|
+
* `webjs create` copies a few `@webjsdev/ui` registry components into a
|
|
5
|
+
* generated app. To match what `webjs ui add` writes, a Tier-1 helper's worked
|
|
6
|
+
* `@example` is stripped (the example is served on demand by `webjs ui view` /
|
|
7
|
+
* the MCP `ui` tool), while a Tier-2 element file is kept whole. Both scaffold
|
|
8
|
+
* copiers (`create.js` and `saas-template.js`) go through THIS one helper so
|
|
9
|
+
* they cannot drift.
|
|
10
|
+
*
|
|
11
|
+
* The strip primitives live in `@webjsdev/ui/registry/extract`; if that subpath
|
|
12
|
+
* cannot be resolved, this degrades to a no-op (keep the example) so the strip
|
|
13
|
+
* is never a reason `webjs create` fails.
|
|
14
|
+
*
|
|
15
|
+
* @module lean-copy
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
let _mod = null;
|
|
19
|
+
|
|
20
|
+
async function loadPrimitives() {
|
|
21
|
+
if (_mod) return _mod;
|
|
22
|
+
try {
|
|
23
|
+
const m = await import('@webjsdev/ui/registry/extract');
|
|
24
|
+
_mod = { stripExample: m.stripExample, isCustomElementSource: m.isCustomElementSource };
|
|
25
|
+
} catch {
|
|
26
|
+
_mod = { stripExample: (s) => s, isCustomElementSource: () => true };
|
|
27
|
+
}
|
|
28
|
+
return _mod;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Return the component source as `webjs ui add` would write it: a Tier-1 helper
|
|
33
|
+
* has its worked `@example` stripped and a pointer left; a Tier-2 element is
|
|
34
|
+
* returned unchanged.
|
|
35
|
+
*
|
|
36
|
+
* @param {string} source the component source (imports already rewritten)
|
|
37
|
+
* @param {string} name the component name (for the pointer)
|
|
38
|
+
* @returns {Promise<string>}
|
|
39
|
+
*/
|
|
40
|
+
export async function leanComponentSource(source, name) {
|
|
41
|
+
const { stripExample, isCustomElementSource } = await loadPrimitives();
|
|
42
|
+
return isCustomElementSource(source) ? source : stripExample(source, name);
|
|
43
|
+
}
|
package/lib/saas-template.js
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
import { mkdir, writeFile, readFile } from 'node:fs/promises';
|
|
7
7
|
import { bunifyProse } from './runtime-rewrite.js';
|
|
8
|
+
import { leanComponentSource } from './lean-copy.js';
|
|
8
9
|
import { existsSync } from 'node:fs';
|
|
9
10
|
import { join, resolve, dirname } from 'node:path';
|
|
10
11
|
import { fileURLToPath } from 'node:url';
|
|
@@ -25,7 +26,7 @@ async function readUiComponent(name) {
|
|
|
25
26
|
const raw = await readFile(src, 'utf8');
|
|
26
27
|
// The registry component imports cn() via a relative `../lib/utils.ts`; rewrite
|
|
27
28
|
// it to the scaffolded app's aliased path (cn lives at lib/utils/cn.ts).
|
|
28
|
-
|
|
29
|
+
const rewritten = raw
|
|
29
30
|
.replaceAll("'../lib/utils.ts'", "'#lib/utils/cn.ts'")
|
|
30
31
|
.replaceAll('"../lib/utils.ts"', '"#lib/utils/cn.ts"')
|
|
31
32
|
// onBeforeCache lives in its own client-only module so cn() stays pure (#819).
|
|
@@ -33,6 +34,9 @@ async function readUiComponent(name) {
|
|
|
33
34
|
// (which resolves to a nonexistent components/lib/dom.ts) and fails typecheck.
|
|
34
35
|
.replaceAll("'../lib/dom.ts'", "'#lib/utils/dom.ts'")
|
|
35
36
|
.replaceAll('"../lib/dom.ts"', '"#lib/utils/dom.ts"');
|
|
37
|
+
// Strip a Tier-1 helper's worked @example (same as create.js + `webjs ui add`)
|
|
38
|
+
// so switch / checkbox are lean, not just the full-stack base set (#983).
|
|
39
|
+
return leanComponentSource(rewritten, name);
|
|
36
40
|
}
|
|
37
41
|
|
|
38
42
|
/** Copy named registry components into `<appDir>/components/ui/`. */
|
package/package.json
CHANGED
|
@@ -44,6 +44,7 @@ Classify the task first, then load the smallest useful reference set. Each refer
|
|
|
44
44
|
| Tailwind, light-DOM tag-prefix rule, tokens, fixed headers, no-reflow layout | `references/styling.md` |
|
|
45
45
|
| Client router, prefetch, frames, view transitions, Suspense streaming | `references/client-router-and-streaming.md` |
|
|
46
46
|
| Optimistic UI for a user-facing mutation | `references/optimistic-ui.md` |
|
|
47
|
+
| The `@webjsdev/ui` component kit (a `components.json` is present): class helpers, tokens, `add` / `view`, the MCP `ui` tool | `references/ui-kit.md` |
|
|
47
48
|
| TypeScript at runtime, erasable syntax, full-stack types | `references/typescript.md` |
|
|
48
49
|
| Unit, browser, e2e tests, the `handle()` harness, Bun parity | `references/testing.md` |
|
|
49
50
|
| Auth, caching, env vars, rate limit, file storage, the `webjs` config block | `references/built-ins.md` |
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# The `@webjsdev/ui` component kit
|
|
2
|
+
|
|
3
|
+
Load this when the app has a `components.json` (it uses `@webjsdev/ui`, the
|
|
4
|
+
shadcn-style kit for WebJs). The source is copied into your repo (`components/ui/`),
|
|
5
|
+
so you own and edit it. Two tiers:
|
|
6
|
+
|
|
7
|
+
- **Tier 1, class helpers (23 components).** Pure functions returning Tailwind
|
|
8
|
+
class strings (`buttonClass({ variant })`, `cardClass()`), composed with
|
|
9
|
+
whatever native element you write. Reach for these instead of expanding
|
|
10
|
+
Tailwind by hand: the call site is a fraction of the tokens and the class list
|
|
11
|
+
cannot drift.
|
|
12
|
+
- **Tier 2, stateful custom elements (9 components).** `<ui-dialog>`, `<ui-tabs>`,
|
|
13
|
+
`<ui-dropdown-menu>`, and friends own their ARIA (focus trap, roving tabindex,
|
|
14
|
+
`aria-controls` / `inert`, live regions). Write the tag and the accessible
|
|
15
|
+
behaviour comes with it. Do NOT hand-roll these; the wiring is easy to get
|
|
16
|
+
subtly wrong.
|
|
17
|
+
|
|
18
|
+
## The workflow: query for the structure, do not guess it
|
|
19
|
+
|
|
20
|
+
`add` copies a Tier-1 component's class helpers plus a lean header (what each
|
|
21
|
+
helper is, the accessibility obligations) and a one-line pointer. It does NOT
|
|
22
|
+
copy the worked structural example, because that example is guidance you consume
|
|
23
|
+
once while composing, not code that should sit in your repo. Get the full
|
|
24
|
+
paste-ready structure on demand:
|
|
25
|
+
|
|
26
|
+
- **MCP `ui` tool** (preferred when available): call `ui` with no args for the
|
|
27
|
+
kit inventory (each component's tier, helper signatures, npm deps); pass
|
|
28
|
+
`{ name: "accordion" }` for one component's helper signatures, the paste-ready
|
|
29
|
+
structural example, the accessibility header, and deps.
|
|
30
|
+
- **CLI**: `webjs ui list` (inventory), `webjs ui view <name>` (the projected
|
|
31
|
+
view plus the full source). Same data as the MCP tool (one shared projector).
|
|
32
|
+
|
|
33
|
+
So the loop is: `add` the component, then query `ui <name>` (MCP) or
|
|
34
|
+
`webjs ui view <name>` for the accessible structure, paste it, and fill it in.
|
|
35
|
+
|
|
36
|
+
## Setup and resolution
|
|
37
|
+
|
|
38
|
+
- `webjs ui init` writes `components.json`, `lib/utils.ts`, and the CSS design
|
|
39
|
+
tokens the helpers render against (`--background`, `--foreground`,
|
|
40
|
+
`--destructive`, ...). It HARD-FAILS if the tokens cannot be written, so a
|
|
41
|
+
clean exit means the kit is styled. `add` self-heals the tokens if they go
|
|
42
|
+
missing.
|
|
43
|
+
- Resolution is LOCAL-FIRST: `init` / `add` / `list` / `view` read the registry
|
|
44
|
+
that ships inside the installed `@webjsdev/ui`, with no network. This pins you
|
|
45
|
+
to the installed version; run `webjs ui diff` to see where your local copies
|
|
46
|
+
drift from the upstream (that command alone compares against the live registry).
|
|
47
|
+
|
|
48
|
+
## Inventory (run `webjs ui list` or the MCP `ui` tool for the authoritative, current set)
|
|
49
|
+
|
|
50
|
+
**Tier 1 (class helpers):** accordion, alert, aspect-ratio, avatar, badge,
|
|
51
|
+
breadcrumb, button, card, checkbox, collapsible, input, kbd, label,
|
|
52
|
+
native-select, pagination, popover, progress, radio-group, separator, skeleton,
|
|
53
|
+
switch, table, textarea.
|
|
54
|
+
|
|
55
|
+
**Tier 2 (custom elements, own their ARIA):** alert-dialog, dialog,
|
|
56
|
+
dropdown-menu, hover-card, sonner, tabs, tooltip, plus toggle and toggle-group
|
|
57
|
+
(these two register an element AND export a `*Class` helper).
|
|
58
|
+
|
|
59
|
+
## Idioms
|
|
60
|
+
|
|
61
|
+
- A helper is a function, so compose it: `class=${buttonClass({ variant: 'outline' })}`.
|
|
62
|
+
The unquoted `${...}` is a normal `html` attribute hole.
|
|
63
|
+
- Tier-1 helpers assume the design tokens exist; if a component paints unstyled,
|
|
64
|
+
the tokens are missing (re-run `webjs ui init` or let `add` self-heal them).
|
|
65
|
+
- Custom elements are display-only-safe at SSR and hydrate in the browser, the
|
|
66
|
+
standard WebJs component model (`references/components.md`).
|
|
67
|
+
|
|
68
|
+
Full per-package reference lives in the installed `@webjsdev/ui/AGENTS.md`.
|