@webjsdev/cli 0.10.12 → 0.10.14
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 +5 -3
- package/bin/webjs.js +64 -17
- package/lib/create.js +24 -18
- package/lib/doctor.js +277 -0
- package/lib/port.js +60 -0
- package/lib/prisma-preflight.js +168 -0
- package/package.json +3 -7
- package/templates/.claude.json +1 -1
- package/templates/AGENTS.md +66 -17
- package/templates/CONVENTIONS.md +10 -7
- package/lib/check-json.js +0 -47
- package/lib/mcp-docs.js +0 -400
- package/lib/mcp-source.js +0 -244
- package/lib/mcp.js +0 -557
- package/resources/AGENTS.md +0 -404
- package/resources/agent-docs/advanced.md +0 -1090
- package/resources/agent-docs/built-ins.md +0 -367
- package/resources/agent-docs/components.md +0 -486
- package/resources/agent-docs/configuration.md +0 -207
- package/resources/agent-docs/framework-dev.md +0 -65
- package/resources/agent-docs/lit-muscle-memory-gotchas.md +0 -456
- package/resources/agent-docs/metadata.md +0 -334
- package/resources/agent-docs/recipes.md +0 -440
- package/resources/agent-docs/service-worker.md +0 -100
- package/resources/agent-docs/ssr-partial-nav-design.md +0 -214
- package/resources/agent-docs/styling.md +0 -235
- package/resources/agent-docs/testing.md +0 -372
- package/resources/agent-docs/typescript.md +0 -334
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Prisma-client preflight for `webjs dev` (#452).
|
|
3
|
+
*
|
|
4
|
+
* The scaffold's `dev` npm script is `webjs dev`, and `npm run dev` runs the
|
|
5
|
+
* `predev` hook (`prisma generate`) FIRST. Invoking the `webjs dev` binary
|
|
6
|
+
* directly (easy to do, and tempting for an AI/CLI) skips `predev`, so the dev
|
|
7
|
+
* server boots against an ungenerated `@prisma/client` and crashes with a raw
|
|
8
|
+
* "did not initialize yet" error and no hint that the canonical command is
|
|
9
|
+
* `npm run dev`. This turns that crash into a one-line, actionable message.
|
|
10
|
+
*
|
|
11
|
+
* Scope is deliberately narrow: it only fires for an app that actually uses
|
|
12
|
+
* Prisma (a `prisma/schema.prisma` OR an `@prisma/client` dependency), and it
|
|
13
|
+
* only HINTS. It never auto-runs an arbitrary `predev` script and never shells
|
|
14
|
+
* out to `prisma generate` on its own, keeping the no-build promise intact.
|
|
15
|
+
*
|
|
16
|
+
* Detection (verified against a real Prisma 6 install): the GENERATED
|
|
17
|
+
* `.prisma/client` target is resolved through standard Node resolution from the
|
|
18
|
+
* app (so a hoisted monorepo, where the client lives at a PARENT `node_modules`,
|
|
19
|
+
* resolves correctly), then read. An ABSENT target, or a present-but-stub target
|
|
20
|
+
* (the ungenerated client whose `PrismaClient` constructor throws the init
|
|
21
|
+
* error), is "ungenerated". A real generated target older than the schema is
|
|
22
|
+
* "stale". We do NOT grep the static `@prisma/client` re-export shim: it is
|
|
23
|
+
* present in both states and never carries the init-error string itself.
|
|
24
|
+
*/
|
|
25
|
+
import { existsSync, statSync, readFileSync } from 'node:fs';
|
|
26
|
+
import { join, dirname } from 'node:path';
|
|
27
|
+
import { createRequire } from 'node:module';
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Does this app use Prisma? True if a schema is checked in OR `@prisma/client`
|
|
31
|
+
* is a declared dependency. Either alone is enough; a non-Prisma app has
|
|
32
|
+
* neither and gets no warning.
|
|
33
|
+
*
|
|
34
|
+
* @param {string} cwd
|
|
35
|
+
* @returns {boolean}
|
|
36
|
+
*/
|
|
37
|
+
export function usesPrisma(cwd) {
|
|
38
|
+
if (existsSync(join(cwd, 'prisma', 'schema.prisma'))) return true;
|
|
39
|
+
try {
|
|
40
|
+
const pkg = JSON.parse(readFileSync(join(cwd, 'package.json'), 'utf8'));
|
|
41
|
+
const deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
42
|
+
return Boolean(deps && deps['@prisma/client']);
|
|
43
|
+
} catch {
|
|
44
|
+
return false;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Marker the ungenerated `prisma-client-js` stub embeds in its generated target
|
|
49
|
+
// (`node_modules/.prisma/client/index.js`). Verified against a real Prisma 6
|
|
50
|
+
// install: after `npm i @prisma/client` but before `prisma generate`, the
|
|
51
|
+
// generated `.prisma/client` entry IS present but its `PrismaClient` constructor
|
|
52
|
+
// throws `@prisma/client did not initialize yet. Please run "prisma generate"`.
|
|
53
|
+
// A real `prisma generate` replaces that stub with the generated client, which
|
|
54
|
+
// does NOT contain this string. So the marker, read from the GENERATED target
|
|
55
|
+
// (not the static `@prisma/client` shim), is the reliable ungenerated signal.
|
|
56
|
+
const UNGENERATED_MARKER = 'did not initialize yet';
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Resolve the GENERATED Prisma client entry (`.prisma/client/index.js`) for an
|
|
60
|
+
* app, following standard Node resolution so a hoisted monorepo layout (the
|
|
61
|
+
* generated client at a PARENT `node_modules`, the app under `apps/<x>`) still
|
|
62
|
+
* resolves. Returns a discriminated result so the caller can tell the three
|
|
63
|
+
* cases apart:
|
|
64
|
+
* - `{ kind: 'unresolved' }` - `@prisma/client` itself is not resolvable.
|
|
65
|
+
* - `{ kind: 'no-target' }` - the package resolves but `.prisma/client`
|
|
66
|
+
* does not (a custom `output`, ambiguous).
|
|
67
|
+
* - `{ kind: 'target', path }` - the generated target resolves.
|
|
68
|
+
*
|
|
69
|
+
* @param {string} cwd
|
|
70
|
+
* @returns {{ kind: 'unresolved' } | { kind: 'no-target' } | { kind: 'target', path: string }}
|
|
71
|
+
*/
|
|
72
|
+
function resolveGeneratedClient(cwd) {
|
|
73
|
+
let clientDir;
|
|
74
|
+
try {
|
|
75
|
+
// Resolve @prisma/client AS THE APP would (hoisting-aware), then locate its
|
|
76
|
+
// package dir. The shim itself loads `.prisma/client/default` relative to
|
|
77
|
+
// here, so resolving from this dir follows the same (possibly hoisted) path.
|
|
78
|
+
const appRequire = createRequire(join(cwd, 'noop.js'));
|
|
79
|
+
clientDir = dirname(appRequire.resolve('@prisma/client'));
|
|
80
|
+
} catch {
|
|
81
|
+
return { kind: 'unresolved' };
|
|
82
|
+
}
|
|
83
|
+
const shimRequire = createRequire(join(clientDir, 'noop.js'));
|
|
84
|
+
for (const entry of ['.prisma/client/index.js', '.prisma/client/default.js']) {
|
|
85
|
+
try {
|
|
86
|
+
return { kind: 'target', path: shimRequire.resolve(entry) };
|
|
87
|
+
} catch { /* try the next entry */ }
|
|
88
|
+
}
|
|
89
|
+
return { kind: 'no-target' };
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Inspect the generated Prisma client state for a Prisma app.
|
|
94
|
+
*
|
|
95
|
+
* Returns one of:
|
|
96
|
+
* - `{ status: 'ok' }` - client generated and not older than the schema.
|
|
97
|
+
* - `{ status: 'missing' }` - schema/dep present but no generated client.
|
|
98
|
+
* - `{ status: 'stale' }` - client exists but the schema is newer than it.
|
|
99
|
+
*
|
|
100
|
+
* Detection resolves the GENERATED `.prisma/client` target through standard Node
|
|
101
|
+
* resolution (so hoisted monorepos are handled) and reads it: an absent target,
|
|
102
|
+
* or a present-but-stub target (the ungenerated `PrismaClient` that throws on
|
|
103
|
+
* construction), is `missing`. A real generated client that is older than the
|
|
104
|
+
* schema is `stale`. A custom-`output` generator whose target Node cannot
|
|
105
|
+
* resolve falls back to `ok` rather than nag a working app (false positives are
|
|
106
|
+
* worse than a missed hint here).
|
|
107
|
+
*
|
|
108
|
+
* @param {string} cwd
|
|
109
|
+
* @returns {{ status: 'ok' | 'missing' | 'stale' }}
|
|
110
|
+
*/
|
|
111
|
+
export function prismaClientState(cwd) {
|
|
112
|
+
const resolved = resolveGeneratedClient(cwd);
|
|
113
|
+
|
|
114
|
+
// @prisma/client not resolvable: the app declared the dep (usesPrisma gated
|
|
115
|
+
// us here) but it is not installed/generated. That is the boot-crash case.
|
|
116
|
+
if (resolved.kind === 'unresolved') return { status: 'missing' };
|
|
117
|
+
|
|
118
|
+
// The package resolves but the default `.prisma/client` target does not: a
|
|
119
|
+
// custom `output` whose location we cannot cheaply verify. Fall back to `ok`
|
|
120
|
+
// rather than nag a working app (false positives are worse than a missed hint).
|
|
121
|
+
if (resolved.kind === 'no-target') return { status: 'ok' };
|
|
122
|
+
|
|
123
|
+
const generatedIndex = resolved.path;
|
|
124
|
+
|
|
125
|
+
// The generated target exists. Is it still the ungenerated stub (its
|
|
126
|
+
// PrismaClient constructor throws the init error)?
|
|
127
|
+
try {
|
|
128
|
+
const body = readFileSync(generatedIndex, 'utf8');
|
|
129
|
+
if (body.includes(UNGENERATED_MARKER)) return { status: 'missing' };
|
|
130
|
+
} catch { /* unreadable: fall through to the stale check, then ok */ }
|
|
131
|
+
|
|
132
|
+
// Generated for real. Is it older than the schema (a stale client)?
|
|
133
|
+
const schema = join(cwd, 'prisma', 'schema.prisma');
|
|
134
|
+
try {
|
|
135
|
+
if (existsSync(schema)) {
|
|
136
|
+
const schemaMtime = statSync(schema).mtimeMs;
|
|
137
|
+
const clientMtime = statSync(generatedIndex).mtimeMs;
|
|
138
|
+
if (schemaMtime > clientMtime) return { status: 'stale' };
|
|
139
|
+
}
|
|
140
|
+
} catch { /* if we can't stat, treat as ok */ }
|
|
141
|
+
|
|
142
|
+
return { status: 'ok' };
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Build the actionable hint for an ungenerated/stale client, or `null` when the
|
|
147
|
+
* app is fine or does not use Prisma. The caller prints it (a warning, not a
|
|
148
|
+
* hard exit) before booting the dev server.
|
|
149
|
+
*
|
|
150
|
+
* @param {string} cwd
|
|
151
|
+
* @returns {string | null}
|
|
152
|
+
*/
|
|
153
|
+
export function prismaDevHint(cwd) {
|
|
154
|
+
if (!usesPrisma(cwd)) return null;
|
|
155
|
+
const { status } = prismaClientState(cwd);
|
|
156
|
+
if (status === 'ok') return null;
|
|
157
|
+
|
|
158
|
+
const reason =
|
|
159
|
+
status === 'stale'
|
|
160
|
+
? 'Your Prisma client looks stale (the schema changed since it was generated).'
|
|
161
|
+
: 'Your Prisma client is not generated yet.';
|
|
162
|
+
return (
|
|
163
|
+
`webjs: ${reason}\n` +
|
|
164
|
+
` The dev server will crash on an ungenerated client. Fix it with either:\n` +
|
|
165
|
+
` npm run dev # canonical: runs \`prisma generate\` (predev) first\n` +
|
|
166
|
+
` webjs db generate # just regenerate the client, then re-run\n`
|
|
167
|
+
);
|
|
168
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@webjsdev/cli",
|
|
3
|
-
"version": "0.10.
|
|
3
|
+
"version": "0.10.14",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "webjs CLI - dev, start, create, db",
|
|
6
6
|
"bin": {
|
|
@@ -10,10 +10,10 @@
|
|
|
10
10
|
"bin",
|
|
11
11
|
"lib",
|
|
12
12
|
"templates",
|
|
13
|
-
"README.md"
|
|
14
|
-
"resources"
|
|
13
|
+
"README.md"
|
|
15
14
|
],
|
|
16
15
|
"dependencies": {
|
|
16
|
+
"@webjsdev/mcp": "^0.1.0",
|
|
17
17
|
"@webjsdev/server": "^0.8.0",
|
|
18
18
|
"@webjsdev/ui": "^0.3.1"
|
|
19
19
|
},
|
|
@@ -36,9 +36,5 @@
|
|
|
36
36
|
],
|
|
37
37
|
"engines": {
|
|
38
38
|
"node": ">=24.0.0"
|
|
39
|
-
},
|
|
40
|
-
"scripts": {
|
|
41
|
-
"prepack": "node scripts/copy-mcp-resources.js",
|
|
42
|
-
"postpack": "node scripts/clean-mcp-resources.js"
|
|
43
39
|
}
|
|
44
40
|
}
|
package/templates/.claude.json
CHANGED
package/templates/AGENTS.md
CHANGED
|
@@ -89,14 +89,60 @@ node_modules/@webjsdev/
|
|
|
89
89
|
src/actions.js ← .server.ts scanner, RPC, expose()
|
|
90
90
|
src/auth.js, session.js, cache.js, rate-limit.js, csrf.js
|
|
91
91
|
cli/ webjs CLI (dev / start / build / test / check / create / db)
|
|
92
|
-
|
|
92
|
+
intellisense/ tsserver plugin: go-to-definition + diagnostic suppression
|
|
93
93
|
+ attribute auto-complete for Class.register('tag') elements
|
|
94
94
|
```
|
|
95
95
|
|
|
96
96
|
Reaching straight for the source is the fastest way to resolve "why
|
|
97
97
|
doesn't X work?" with no documentation guesswork and no stale blog posts.
|
|
98
98
|
|
|
99
|
-
##
|
|
99
|
+
## Use the webjs MCP server (introspection + framework knowledge)
|
|
100
|
+
|
|
101
|
+
This project ships a **read-only Model Context Protocol server** that gives
|
|
102
|
+
you (the AI agent) live, version-accurate facts about THIS app and the
|
|
103
|
+
framework. Prefer it over guessing or recalling webjs from training data,
|
|
104
|
+
which drifts. It mutates nothing.
|
|
105
|
+
|
|
106
|
+
**It is already available, no install needed:** the webjs CLI (a project
|
|
107
|
+
dependency) has it built in as `webjs mcp`. It is an MCP STDIO server (JSON-RPC
|
|
108
|
+
over stdout), so you do not run it in a terminal and read its output. Your MCP
|
|
109
|
+
host (Claude Code, Cursor, etc.) launches it and surfaces its tools, then you
|
|
110
|
+
invoke those tools through the MCP protocol.
|
|
111
|
+
|
|
112
|
+
Claude Code is pre-wired (see `.claude.json`). For another host, register the
|
|
113
|
+
server by pointing it at the CLI (or the equivalent standalone package):
|
|
114
|
+
|
|
115
|
+
```jsonc
|
|
116
|
+
// Cursor: .cursor/mcp.json (or your host's MCP config)
|
|
117
|
+
{ "mcpServers": { "webjs": {
|
|
118
|
+
"command": "npx", "args": ["@webjsdev/cli", "mcp"] // the built-in CLI route
|
|
119
|
+
// equivalent: "command": "npx", "args": ["@webjsdev/mcp"]
|
|
120
|
+
} } }
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
What it serves:
|
|
124
|
+
|
|
125
|
+
- **Introspection of this app** (read-only, no module load, no DB side
|
|
126
|
+
effects): `list_routes` (the route table), `list_actions` (server actions
|
|
127
|
+
with their `/__webjs/action/<hash>/<fn>` RPC endpoints), `list_components`
|
|
128
|
+
(registered custom-element tags), `check` (the structured `webjs check`
|
|
129
|
+
violations). Use these to learn the real route/action/component surface
|
|
130
|
+
before editing, instead of grepping or assuming.
|
|
131
|
+
- **Framework knowledge**: an `init` primer (the read-first mental model +
|
|
132
|
+
invariants), a `docs` tool (retrieve a topic or search the `agent-docs`
|
|
133
|
+
corpus), MCP `resources` (the docs corpus + this AGENTS.md), recipe
|
|
134
|
+
`prompts` (guided page/route/action/component workflows), and a `source`
|
|
135
|
+
tool that reads the framework's OWN no-build source from
|
|
136
|
+
`node_modules/@webjsdev/*/src` (what actually runs).
|
|
137
|
+
|
|
138
|
+
You have TWO complementary ways to understand the framework, use whichever
|
|
139
|
+
helps (or both): (1) **grep the full framework source** under
|
|
140
|
+
`node_modules/@webjsdev/*/src`, which is the real no-build code that runs (no
|
|
141
|
+
sourcemaps, no guessing), and (2) **the MCP** for live app introspection plus
|
|
142
|
+
the curated `init` / `docs` / `source` knowledge tools. Reach for either before
|
|
143
|
+
guessing from training data or asking the user.
|
|
144
|
+
|
|
145
|
+
## Editor TS plugin: `@webjsdev/intellisense`
|
|
100
146
|
|
|
101
147
|
This scaffold's `tsconfig.json` lists a single tsserver plugin. It is
|
|
102
148
|
editor-only, not required for the framework to run.
|
|
@@ -104,22 +150,24 @@ editor-only, not required for the framework to run.
|
|
|
104
150
|
```jsonc
|
|
105
151
|
// tsconfig.json (already wired by the scaffold)
|
|
106
152
|
"plugins": [
|
|
107
|
-
{ "name": "@webjsdev/
|
|
153
|
+
{ "name": "@webjsdev/intellisense" }
|
|
108
154
|
]
|
|
109
155
|
```
|
|
110
156
|
|
|
111
|
-
`@webjsdev/
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
157
|
+
`@webjsdev/intellisense` is **standalone** (no Lit dependency): one plugin
|
|
158
|
+
entry, its own template parser. Inside `` html`…` `` templates you get:
|
|
159
|
+
|
|
160
|
+
- Go-to-definition on custom-element tags, attribute / property / event
|
|
161
|
+
names, and CSS classes in `class="…"`.
|
|
162
|
+
- Binding-aware completions: reachable tag names after `<`, and
|
|
163
|
+
prefix-keyed attributes (`.prop` property names, `?bool` / plain
|
|
164
|
+
hyphenated attribute names).
|
|
165
|
+
- Diagnostics: value type-checks against `declare propName: T`, unquoted
|
|
166
|
+
`@`/`.`/`?` bindings, and expressionless `.prop` bindings.
|
|
167
|
+
- Hover showing the component class / declared member type.
|
|
117
168
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
- Attribute auto-complete sourced from each component's
|
|
121
|
-
`static properties`.
|
|
122
|
-
- Attribute-value type-check against `declare propName: T` annotations.
|
|
169
|
+
In VS Code / Cursor / Windsurf, the **`webjs` extension** bundles this
|
|
170
|
+
automatically (no `tsconfig.json` edit, no separate Lit extension).
|
|
123
171
|
|
|
124
172
|
See [docs.webjs.com → Editor setup](https://docs.webjs.com/docs/editor-setup)
|
|
125
173
|
for the full walkthrough.
|
|
@@ -472,9 +520,10 @@ Production then has no importmap.json and the server falls back to
|
|
|
472
520
|
calling api.jspm.io on every cold start. The `**/` prefix matters too:
|
|
473
521
|
it ignores `.webjs/` at any depth, so an app nested below its repo root
|
|
474
522
|
(a monorepo package) does not leak its generated `.webjs/routes.d.ts`
|
|
475
|
-
into `git status`. The `
|
|
476
|
-
|
|
477
|
-
|
|
523
|
+
into `git status`. The `vendor-gitignore` check (`webjs doctor`)
|
|
524
|
+
verifies the pattern with `git check-ignore` and warns if it regresses
|
|
525
|
+
(it is a project-config / setup concern, not a source-code-correctness
|
|
526
|
+
CI gate).
|
|
478
527
|
|
|
479
528
|
## Imports
|
|
480
529
|
|
package/templates/CONVENTIONS.md
CHANGED
|
@@ -66,7 +66,10 @@ even if the user doesn't explicitly ask.**
|
|
|
66
66
|
Run `npm run doctor` (which runs `webjs doctor`) once after cloning to assert
|
|
67
67
|
the project is set up correctly: the Node major (the strip-types floor), the
|
|
68
68
|
tsconfig `erasableSyntaxOnly` flag, `.env` drift vs `.env.example`, vendor-pin
|
|
69
|
-
freshness,
|
|
69
|
+
freshness, the `.gitignore` keeping `.webjs/vendor/` committable
|
|
70
|
+
(`vendor-gitignore`), importmap-coherence (the resolved client deps agree on a
|
|
71
|
+
shared transitive version), `@webjsdev/*` version coherence, and the git
|
|
72
|
+
pre-commit hook. It
|
|
70
73
|
prints `[pass]` / `[warn]` / `[fail]` per check with an actionable fix line and
|
|
71
74
|
exits non-zero only on a hard fail (a broken toolchain), so a green run means
|
|
72
75
|
`npm run dev` will boot. It is a local onboarding/setup-verify tool, not a CI
|
|
@@ -348,7 +351,7 @@ variables control infrastructure (no config files needed):
|
|
|
348
351
|
| `AUTH_SECRET` | Required for auth JWT signing (32+ random chars) |
|
|
349
352
|
| `AUTH_GOOGLE_ID` | Google OAuth client ID (optional) |
|
|
350
353
|
| `AUTH_GITHUB_ID` | GitHub OAuth client ID (optional) |
|
|
351
|
-
| `PORT` | Server port
|
|
354
|
+
| `PORT` | Server port. Precedence: `--port` flag > `PORT` (a real exported env var or a `PORT` in `.env`) > 8080. |
|
|
352
355
|
| `WEBJS_PUBLIC_*` | Any env var starting with this prefix is exposed to the browser as `process.env.WEBJS_PUBLIC_X`. Components can read it directly. No build step, no transform. Use for API base URLs, Stripe publishable keys, analytics IDs, anything that is intended to be visible client-side. |
|
|
353
356
|
|
|
354
357
|
**Server-only by default.** Any env var without the `WEBJS_PUBLIC_` prefix never reaches the browser. Reading `process.env.DATABASE_URL` from a component returns `undefined`, the same as a typo. The prefix is fail-closed: secrets cannot accidentally leak.
|
|
@@ -654,11 +657,11 @@ attribute coercion, reflection). `declare` types the field for
|
|
|
654
657
|
TypeScript without emitting a class-field initializer that would
|
|
655
658
|
clobber the reactive accessor at construction time. The two
|
|
656
659
|
declarations together give you full intelligence in any tsserver-backed
|
|
657
|
-
editor. See the Editor Setup docs for the `
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
660
|
+
editor. See the Editor Setup docs for the standalone `@webjsdev/intellisense`
|
|
661
|
+
(no Lit dependency) that extends this to tag / attribute intelligence
|
|
662
|
+
inside `html\`…\`` templates (go-to-definition, binding-aware completions,
|
|
663
|
+
value/binding diagnostics, hover); in VS Code / Cursor / Windsurf the
|
|
664
|
+
`webjs` extension bundles it automatically.
|
|
662
665
|
|
|
663
666
|
**Rules:**
|
|
664
667
|
- One component per file
|
package/lib/check-json.js
DELETED
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Shared JSON projector for `webjs check` violations (#262).
|
|
3
|
-
*
|
|
4
|
-
* `webjs check --json` and the `webjs mcp` server's `check` tool BOTH return
|
|
5
|
-
* the identical shape, so the projection lives here once. The input is the raw
|
|
6
|
-
* `Violation[]` from `checkConventions(appDir)` (each `{ rule, file, message,
|
|
7
|
-
* fix }`); the output adds a `summary` count plus a per-rule breakdown so an
|
|
8
|
-
* agent consuming the structured output never has to regex-scrape stdout.
|
|
9
|
-
*
|
|
10
|
-
* Pure and side-effect-free: it neither reads files nor prints. The caller owns
|
|
11
|
-
* running `checkConventions` and (for the CLI) the non-zero exit when there are
|
|
12
|
-
* violations.
|
|
13
|
-
*
|
|
14
|
-
* @module check-json
|
|
15
|
-
*/
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* @typedef {{ rule: string, file: string, message: string, fix: string }} Violation
|
|
19
|
-
*/
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* @typedef {{
|
|
23
|
-
* violations: Violation[],
|
|
24
|
-
* summary: { count: number, byRule: Record<string, number> },
|
|
25
|
-
* }} CheckReport
|
|
26
|
-
*/
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* Project a raw `Violation[]` into the structured `{ violations, summary }`
|
|
30
|
-
* report shared by `check --json` and the MCP `check` tool. `violations` is
|
|
31
|
-
* passed through verbatim (the `{ rule, file, message, fix }` shape), and
|
|
32
|
-
* `summary.byRule` tallies how many violations each rule produced.
|
|
33
|
-
*
|
|
34
|
-
* @param {Violation[]} violations
|
|
35
|
-
* @returns {CheckReport}
|
|
36
|
-
*/
|
|
37
|
-
export function projectCheck(violations) {
|
|
38
|
-
/** @type {Record<string, number>} */
|
|
39
|
-
const byRule = {};
|
|
40
|
-
for (const v of violations) {
|
|
41
|
-
byRule[v.rule] = (byRule[v.rule] || 0) + 1;
|
|
42
|
-
}
|
|
43
|
-
return {
|
|
44
|
-
violations,
|
|
45
|
-
summary: { count: violations.length, byRule },
|
|
46
|
-
};
|
|
47
|
-
}
|