@webjsdev/cli 0.10.56 → 0.10.58
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/README.md +6 -1
- package/bin/webjs.js +219 -9
- package/lib/app-tasks.js +70 -10
- package/lib/check-target.js +1 -1
- package/lib/ci-config.js +250 -0
- package/lib/ci-runner.js +499 -0
- package/lib/create.js +59 -2
- package/lib/doctor/codes.js +1 -0
- package/lib/doctor/probes/framework-resolves.js +182 -6
- package/lib/doctor/runner.js +2 -1
- package/lib/doctor.js +1 -1
- package/lib/run-tasks.js +23 -3
- package/package.json +3 -3
- package/templates/.agents/rules/workflow.md +19 -12
- package/templates/.agents/skills/webjs/SKILL.md +6 -2
- package/templates/.agents/skills/webjs/references/built-ins.md +45 -1
- package/templates/.agents/skills/webjs/references/client-router-and-streaming.md +32 -3
- package/templates/.agents/skills/webjs/references/components.md +9 -1
- package/templates/.agents/skills/webjs/references/muscle-memory-gotchas.md +26 -2
- package/templates/.agents/skills/webjs/references/optimistic-ui.md +23 -11
- package/templates/.agents/skills/webjs/references/runtime.md +1 -1
- package/templates/.agents/skills/webjs/references/styling.md +48 -3
- package/templates/.agents/skills/webjs/references/testing.md +11 -0
- package/templates/.agents/skills/webjs/references/ui-kit.md +25 -0
- package/templates/.github/pull_request_template.md +3 -8
- package/templates/.github/workflows/ci.yml +38 -88
- package/templates/.hooks/pre-commit +5 -4
- package/templates/gallery/app/features/client-router/page.ts +5 -1
- package/templates/gallery/app/features/metadata/page.ts +7 -1
- package/templates/gallery/modules/client-router/components/router-controls.ts +7 -0
- package/templates/gallery/modules/stream/components/browser/stream-demo.test.js +64 -0
- package/templates/gallery/modules/stream/components/stream-demo.ts +14 -9
- package/templates/gallery/modules/stream/utils/ui/row.ts +41 -0
- package/templates/gallery/test/rate-limit/rate-limit.test.ts +2 -1
- package/templates/partials/agents-playbook-api.md +14 -8
- package/templates/partials/agents-playbook-fullstack.md +16 -10
|
@@ -13,6 +13,15 @@
|
|
|
13
13
|
// region-swap would clobber.
|
|
14
14
|
import { WebComponent, html, renderStream } from '@webjsdev/core';
|
|
15
15
|
import { buttonClass } from '#components/ui/button.ts';
|
|
16
|
+
// The row markup lives in ONE place, a feature-local view fragment under
|
|
17
|
+
// `utils/ui/` (see references/styling.md for that folder). A fragment rather
|
|
18
|
+
// than a display-only <stream-row> element because BOTH of this demo's uses
|
|
19
|
+
// rule an element out, which is the test to apply: the seeded list needs a
|
|
20
|
+
// direct `<ul> > <li>` child (a wrapper tag would sit between them and break
|
|
21
|
+
// the selector and the list semantics), and the streamed payload is an HTML
|
|
22
|
+
// STRING, which a component cannot produce at all. Where a wrapper IS fine,
|
|
23
|
+
// prefer the component.
|
|
24
|
+
import { streamRow, streamRowHTML } from '../utils/ui/row.ts';
|
|
16
25
|
|
|
17
26
|
// Build a <webjs-stream> payload string. It is a plain string (NOT an html``
|
|
18
27
|
// template), so interpolating the row markup here is fine. `remove` needs no
|
|
@@ -22,9 +31,6 @@ function streamPayload(action: string, target: string, inner = '') {
|
|
|
22
31
|
return `<webjs-stream action="${action}" target="${target}">${body}</webjs-stream>`;
|
|
23
32
|
}
|
|
24
33
|
|
|
25
|
-
const rowCls = 'flex items-center gap-2 px-3 py-2 rounded-xl bg-card border border-border text-[15px] text-foreground';
|
|
26
|
-
const row = (id: string, label: string) => `<li id="${id}" class="${rowCls}">${label}</li>`;
|
|
27
|
-
|
|
28
34
|
export class StreamDemo extends WebComponent {
|
|
29
35
|
// A plain instance field, NOT a signal: incremented to mint unique row ids.
|
|
30
36
|
// It is never read inside render(), so appending a row does not re-render the
|
|
@@ -37,16 +43,16 @@ export class StreamDemo extends WebComponent {
|
|
|
37
43
|
// never runs. Name your handlers something else (see muscle-memory-gotchas).
|
|
38
44
|
appendRow() {
|
|
39
45
|
this.#n++;
|
|
40
|
-
renderStream(streamPayload('append', 'stream-list',
|
|
46
|
+
renderStream(streamPayload('append', 'stream-list', streamRowHTML(`row-${this.#n}`, `Row ${this.#n} (appended)`)));
|
|
41
47
|
}
|
|
42
48
|
prependRow() {
|
|
43
49
|
this.#n++;
|
|
44
|
-
renderStream(streamPayload('prepend', 'stream-list',
|
|
50
|
+
renderStream(streamPayload('prepend', 'stream-list', streamRowHTML(`row-${this.#n}`, `Row ${this.#n} (prepended)`)));
|
|
45
51
|
}
|
|
46
52
|
replaceFirst() {
|
|
47
53
|
// `replace` swaps the target element itself. The replacement keeps id row-1,
|
|
48
54
|
// so the button stays repeatable.
|
|
49
|
-
renderStream(streamPayload('replace', 'row-1',
|
|
55
|
+
renderStream(streamPayload('replace', 'row-1', streamRowHTML('row-1', 'Row 1 (replaced)')));
|
|
50
56
|
}
|
|
51
57
|
removeSecond() {
|
|
52
58
|
// `remove` deletes the target and needs no <template>.
|
|
@@ -54,7 +60,7 @@ export class StreamDemo extends WebComponent {
|
|
|
54
60
|
}
|
|
55
61
|
reset() {
|
|
56
62
|
// `update` replaces the target's children, restoring the seed list.
|
|
57
|
-
renderStream(streamPayload('update', 'stream-list',
|
|
63
|
+
renderStream(streamPayload('update', 'stream-list', streamRowHTML('row-1', 'Row 1') + streamRowHTML('row-2', 'Row 2')));
|
|
58
64
|
}
|
|
59
65
|
|
|
60
66
|
render() {
|
|
@@ -71,8 +77,7 @@ export class StreamDemo extends WebComponent {
|
|
|
71
77
|
<!-- The target list. renderStream() mutates it by id; this markup renders
|
|
72
78
|
once and is never re-rendered by the component. -->
|
|
73
79
|
<ul id="stream-list" class="grid gap-2 m-0 p-0 list-none">
|
|
74
|
-
|
|
75
|
-
<li id="row-2" class="flex items-center gap-2 px-3 py-2 rounded-xl bg-card border border-border text-[15px] text-foreground">Row 2</li>
|
|
80
|
+
${streamRow('row-1', 'Row 1')}${streamRow('row-2', 'Row 2')}
|
|
76
81
|
</ul>
|
|
77
82
|
</div>
|
|
78
83
|
`;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
// A feature-local VIEW FRAGMENT: pure, returns markup, used only by the stream
|
|
2
|
+
// demo. It lives in `modules/<feature>/utils/ui/` rather than `utils/` (which
|
|
3
|
+
// holds helpers returning DATA) or `components/` (which holds custom elements).
|
|
4
|
+
// See .agents/skills/webjs/references/styling.md.
|
|
5
|
+
//
|
|
6
|
+
// WHY a fragment and not a display-only <stream-row> element: an element is a
|
|
7
|
+
// tag in the DOM, and neither caller can carry one. The list wants a direct
|
|
8
|
+
// `<ul> > <li>` child, so a wrapper would break that selector and the list
|
|
9
|
+
// semantics; and <webjs-stream> takes its payload as an HTML STRING, which no
|
|
10
|
+
// component can return. That is the test, not bytes: where a wrapper element is
|
|
11
|
+
// harmless, reach for the component instead.
|
|
12
|
+
//
|
|
13
|
+
// Hence the two shapes below, off one class list, so the streamed row and the
|
|
14
|
+
// seeded rows cannot drift apart. That drift was live: the class list used to
|
|
15
|
+
// exist three times in this feature, once in a `rowCls` const and twice inlined
|
|
16
|
+
// in the component's own template, which did not use the const.
|
|
17
|
+
import { html, escapeAttr, escapeText } from '@webjsdev/core';
|
|
18
|
+
|
|
19
|
+
const ROW =
|
|
20
|
+
'flex items-center gap-2 px-3 py-2 rounded-xl bg-card border border-border text-[15px] text-foreground';
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* The row as a template, for the component's own render(). `unknown` on the
|
|
24
|
+
* label because it forwards straight into an `html` hole, which renders a
|
|
25
|
+
* string, a number, a TemplateResult, or an array of those.
|
|
26
|
+
*/
|
|
27
|
+
export function streamRow(id: string, label: unknown) {
|
|
28
|
+
return html`<li id=${id} class=${ROW}>${label}</li>`;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* The same row as an HTML string, for a <webjs-stream> template payload.
|
|
33
|
+
*
|
|
34
|
+
* ESCAPE EVERY HOLE HERE. The `html` tag above escapes its own holes; this
|
|
35
|
+
* plain template literal does NOT, so an unescaped value becomes markup the
|
|
36
|
+
* moment renderStream() puts it in the document. The demo only ever passes its
|
|
37
|
+
* own literals, but this is the shape people copy, so it does the safe thing.
|
|
38
|
+
*/
|
|
39
|
+
export function streamRowHTML(id: string, label: string): string {
|
|
40
|
+
return `<li id="${escapeAttr(id)}" class="${ROW}">${escapeText(label)}</li>`;
|
|
41
|
+
}
|
|
@@ -5,6 +5,7 @@ import { dirname, resolve } from 'node:path';
|
|
|
5
5
|
|
|
6
6
|
import { createRequestHandler } from '@webjsdev/server';
|
|
7
7
|
import { testRequest } from '@webjsdev/server/testing';
|
|
8
|
+
import type { Handle } from '@webjsdev/server/testing';
|
|
8
9
|
|
|
9
10
|
const appDir = resolve(dirname(fileURLToPath(import.meta.url)), '..', '..');
|
|
10
11
|
|
|
@@ -22,7 +23,7 @@ const MAX = 5;
|
|
|
22
23
|
// X-Forwarded-For that DISAGREES, standing in for the CDN egress address the
|
|
23
24
|
// real deploy puts there, so a test that passes only because the two agree
|
|
24
25
|
// cannot exist.
|
|
25
|
-
function ping(handle:
|
|
26
|
+
function ping(handle: Handle, visitor: string, cdnEgress = '172.68.1.9') {
|
|
26
27
|
return testRequest(handle, PING, {
|
|
27
28
|
headers: { 'cf-connecting-ip': visitor, 'x-forwarded-for': cdnEgress },
|
|
28
29
|
});
|
|
@@ -44,15 +44,20 @@ cross-origin access use the `cors()` middleware from `@webjsdev/server`; with
|
|
|
44
44
|
|
|
45
45
|
### 5. Verify before you call it done
|
|
46
46
|
|
|
47
|
-
Run
|
|
47
|
+
Run `npm run ci` and fix what it reports. It is one command for every gate,
|
|
48
|
+
the step list declared in `package.json` under `webjs.ci`, with a result line
|
|
49
|
+
per step:
|
|
48
50
|
|
|
49
|
-
- `
|
|
50
|
-
- `
|
|
51
|
-
`
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
-
|
|
55
|
-
-
|
|
51
|
+
- `webjs check` (correctness: no browser-import or boundary violation).
|
|
52
|
+
- `webjs doctor` (project health). It fails on whatever `package.json`
|
|
53
|
+
`webjs.doctor.gate` marks `error`, plus the two hard toolchain checks that
|
|
54
|
+
are fatal with no gate entry, `NODE_VERSION` and `TSCONFIG_ERASABLE`.
|
|
55
|
+
- `webjs typecheck` (zero type errors).
|
|
56
|
+
- A dependency audit.
|
|
57
|
+
- The test layers for the endpoints and modules you built.
|
|
58
|
+
|
|
59
|
+
The GitHub workflow runs the same list, so a green local run predicts CI.
|
|
60
|
+
While iterating, `npm run ci -- --only Tests` runs one layer.
|
|
56
61
|
|
|
57
62
|
Then boot `npm run dev` and probe each endpoint for the expected status and JSON
|
|
58
63
|
shape.
|
|
@@ -66,6 +71,7 @@ npm run dev # dev server at http://localhost:8080
|
|
|
66
71
|
npm run start # production server
|
|
67
72
|
npm test # unit + browser tests
|
|
68
73
|
npm run typecheck
|
|
74
|
+
npm run ci # every gate, one command (the webjs.ci steps in package.json)
|
|
69
75
|
npm run check # correctness checks
|
|
70
76
|
npm run doctor # project health (severity per check: webjs.doctor.gate)
|
|
71
77
|
npm run db:generate && npm run db:migrate
|
|
@@ -95,16 +95,21 @@ accessor). Use the shorthand for primitives
|
|
|
95
95
|
|
|
96
96
|
### 7. Verify before you call it done
|
|
97
97
|
|
|
98
|
-
Run
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
`
|
|
105
|
-
|
|
106
|
-
- `
|
|
107
|
-
-
|
|
98
|
+
Run `npm run ci` and fix what it reports. It is one command for every gate,
|
|
99
|
+
the step list declared in `package.json` under `webjs.ci`, with a result line
|
|
100
|
+
per step:
|
|
101
|
+
|
|
102
|
+
- `webjs check` (correctness: no browser-import or boundary violation).
|
|
103
|
+
- `webjs doctor` (project health). It fails on whatever `package.json`
|
|
104
|
+
`webjs.doctor.gate` marks `error`, plus the two hard toolchain checks that
|
|
105
|
+
are fatal with no gate entry, `NODE_VERSION` and `TSCONFIG_ERASABLE`.
|
|
106
|
+
- `webjs typecheck` (zero type errors).
|
|
107
|
+
- A dependency audit.
|
|
108
|
+
- The server, browser, and e2e test layers for the features you built.
|
|
109
|
+
|
|
110
|
+
The GitHub workflow runs the same list, so a green local run predicts CI.
|
|
111
|
+
While iterating, `npm run ci -- --only Tests` runs one layer. Then
|
|
112
|
+
`npm run css:build` (compile Tailwind).
|
|
108
113
|
|
|
109
114
|
Then boot `npm run dev`, confirm every page route returns HTTP 200, and open
|
|
110
115
|
every route you changed in a real browser and play through its states: `check`
|
|
@@ -121,6 +126,7 @@ npm run start # production server
|
|
|
121
126
|
npm test # unit + browser tests
|
|
122
127
|
npm run typecheck
|
|
123
128
|
npm run css:build # compile Tailwind
|
|
129
|
+
npm run ci # every gate, one command (the webjs.ci steps in package.json)
|
|
124
130
|
npm run check # correctness checks
|
|
125
131
|
npm run doctor # project health (severity per check: webjs.doctor.gate)
|
|
126
132
|
npx webjsdev ui add <name> # copy a ui primitive into components/ui/
|