@draekien/create-d9-app 0.0.2 → 0.0.4
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/package.json +2 -1
- package/templates/base/.agents/skills/shadcn/SKILL.md +295 -0
- package/templates/base/.agents/skills/shadcn/agents/openai.yml +5 -0
- package/templates/base/.agents/skills/shadcn/assets/shadcn-small.png +0 -0
- package/templates/base/.agents/skills/shadcn/assets/shadcn.png +0 -0
- package/templates/base/.agents/skills/shadcn/assets/showcase/color-pair.tsx +126 -0
- package/templates/base/.agents/skills/shadcn/assets/showcase/preview-states.css +14 -0
- package/templates/base/.agents/skills/shadcn/assets/showcase/state-matrix.tsx +72 -0
- package/templates/base/.agents/skills/shadcn/cli.md +290 -0
- package/templates/base/.agents/skills/shadcn/customization.md +211 -0
- package/templates/base/.agents/skills/shadcn/design-system-page.md +268 -0
- package/templates/base/.agents/skills/shadcn/design-system.md +386 -0
- package/templates/base/.agents/skills/shadcn/evals/evals.json +126 -0
- package/templates/base/.agents/skills/shadcn/mcp.md +105 -0
- package/templates/base/.agents/skills/shadcn/registry.md +277 -0
- package/templates/base/.agents/skills/shadcn/rules/base-vs-radix.md +306 -0
- package/templates/base/.agents/skills/shadcn/rules/chat.md +250 -0
- package/templates/base/.agents/skills/shadcn/rules/composition.md +213 -0
- package/templates/base/.agents/skills/shadcn/rules/forms.md +192 -0
- package/templates/base/.agents/skills/shadcn/rules/icons.md +101 -0
- package/templates/base/.agents/skills/shadcn/rules/styling.md +185 -0
- package/templates/base/.agents/skills/shadcn/scripts/showcase-coverage.mjs +53 -0
- package/templates/base/.agents/skills/upgrade-dependencies/SKILL.md +99 -0
- package/templates/base/.claude/rules/components.md +11 -0
- package/templates/base/.claude/rules/generated-files.md +11 -0
- package/templates/base/.claude/rules/testing.md +13 -0
- package/templates/base/.claude/rules/typescript.md +29 -0
- package/templates/base/.claude/skills/shadcn/SKILL.md +295 -0
- package/templates/base/.claude/skills/shadcn/agents/openai.yml +5 -0
- package/templates/base/.claude/skills/shadcn/assets/shadcn-small.png +0 -0
- package/templates/base/.claude/skills/shadcn/assets/shadcn.png +0 -0
- package/templates/base/.claude/skills/shadcn/assets/showcase/color-pair.tsx +126 -0
- package/templates/base/.claude/skills/shadcn/assets/showcase/preview-states.css +14 -0
- package/templates/base/.claude/skills/shadcn/assets/showcase/state-matrix.tsx +72 -0
- package/templates/base/.claude/skills/shadcn/cli.md +290 -0
- package/templates/base/.claude/skills/shadcn/customization.md +211 -0
- package/templates/base/.claude/skills/shadcn/design-system-page.md +268 -0
- package/templates/base/.claude/skills/shadcn/design-system.md +386 -0
- package/templates/base/.claude/skills/shadcn/evals/evals.json +126 -0
- package/templates/base/.claude/skills/shadcn/mcp.md +105 -0
- package/templates/base/.claude/skills/shadcn/registry.md +277 -0
- package/templates/base/.claude/skills/shadcn/rules/base-vs-radix.md +306 -0
- package/templates/base/.claude/skills/shadcn/rules/chat.md +250 -0
- package/templates/base/.claude/skills/shadcn/rules/composition.md +213 -0
- package/templates/base/.claude/skills/shadcn/rules/forms.md +192 -0
- package/templates/base/.claude/skills/shadcn/rules/icons.md +101 -0
- package/templates/base/.claude/skills/shadcn/rules/styling.md +185 -0
- package/templates/base/.claude/skills/shadcn/scripts/showcase-coverage.mjs +53 -0
- package/templates/base/.claude/skills/upgrade-dependencies/SKILL.md +99 -0
- package/templates/base/AGENTS.md +9 -1
- package/templates/base/package.json +1 -1
- package/templates/base/skills-lock.json +11 -0
- package/templates/tools/biome/.biome/catch-binding-unknown.grit +7 -0
- package/templates/tools/biome/.biome/effect-cleanup.grit +5 -2
- package/templates/tools/biome/.biome/handler-naming.grit +19 -6
- package/templates/tools/biome/.biome/no-class-error-boundary.grit +11 -0
- package/templates/tools/biome/.biome/no-direct-component-call.grit +20 -0
- package/templates/tools/biome/.biome/no-fetch-in-handler.grit +12 -0
- package/templates/tools/biome/.biome/no-impure-render.grit +7 -2
- package/templates/tools/biome/.biome/no-query-client-in-render.grit +4 -1
- package/templates/tools/biome/.biome/no-state-mutation.grit +19 -0
- package/templates/tools/biome/.biome/no-try-around-jsx.grit +6 -2
- package/templates/tools/biome/.biome/use-assert-never.grit +6 -0
- package/templates/tools/biome/biome.json +41 -3
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Lists installed UI components that no file in the showcase imports.
|
|
3
|
+
// Usage: node showcase-coverage.mjs <ui-dir> <showcase-dir...>
|
|
4
|
+
import { readdirSync, readFileSync, statSync } from "node:fs"
|
|
5
|
+
import { basename, extname, join, resolve } from "node:path"
|
|
6
|
+
|
|
7
|
+
const [uiArg, ...roots] = process.argv.slice(2)
|
|
8
|
+
|
|
9
|
+
if (!uiArg || roots.length === 0) {
|
|
10
|
+
console.error("Usage: node showcase-coverage.mjs <ui-dir> <showcase-dir...>")
|
|
11
|
+
process.exit(1)
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const uiDir = resolve(uiArg)
|
|
15
|
+
|
|
16
|
+
const components = readdirSync(uiDir)
|
|
17
|
+
.filter((file) => [".tsx", ".ts"].includes(extname(file)))
|
|
18
|
+
.map((file) => basename(file, extname(file)))
|
|
19
|
+
|
|
20
|
+
function walk(path) {
|
|
21
|
+
if (statSync(path).isFile()) {
|
|
22
|
+
return [path]
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
return readdirSync(path).flatMap((entry) => {
|
|
26
|
+
const child = join(path, entry)
|
|
27
|
+
// Skip the ui directory so components don't count as using each other.
|
|
28
|
+
if (entry === "node_modules" || child === uiDir) {
|
|
29
|
+
return []
|
|
30
|
+
}
|
|
31
|
+
return walk(child)
|
|
32
|
+
})
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const source = roots
|
|
36
|
+
.map((root) => resolve(root))
|
|
37
|
+
.flatMap(walk)
|
|
38
|
+
.filter((file) => /\.(tsx?|jsx?)$/.test(file))
|
|
39
|
+
.map((file) => readFileSync(file, "utf8"))
|
|
40
|
+
.join("\n")
|
|
41
|
+
|
|
42
|
+
const missing = components.filter(
|
|
43
|
+
(name) => !new RegExp(`/ui/${name}["']`).test(source)
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
console.log(
|
|
47
|
+
`${components.length - missing.length}/${components.length} components shown.`
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
if (missing.length > 0) {
|
|
51
|
+
console.log(`Missing: ${missing.join(", ")}`)
|
|
52
|
+
process.exit(1)
|
|
53
|
+
}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: upgrade-dependencies
|
|
3
|
+
description: Upgrades outdated patch and minor dependency pins in a pnpm, npm, yarn or bun workspace to exact versions, runs build, check, typecheck and test, and reports every change. Applies when asked to "upgrade dependencies", "update packages", "bump versions", or "update deps".
|
|
4
|
+
argument-hint: "[--accept]"
|
|
5
|
+
allowed-tools: Bash(npm view *), Bash(gh release view *), Bash(gh api *), Bash(pnpm *), Bash(npm *), Bash(yarn *), Bash(bun *), Bash(git diff *), Bash(git status *)
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Upgrade dependencies
|
|
9
|
+
|
|
10
|
+
Arguments: `$ARGUMENTS`. `--accept` means the developer accepts the breaking-change list in advance; proceed at step 4 without stopping.
|
|
11
|
+
|
|
12
|
+
Apply patch and minor upgrades only. Never apply a major; list it as `held: major`.
|
|
13
|
+
|
|
14
|
+
## 1. Read the workspace
|
|
15
|
+
|
|
16
|
+
- Package manager: root `package.json` field `packageManager` (`pnpm@11.25.0` means `pnpm`). Call it `<pm>` below.
|
|
17
|
+
- Manifests: root `package.json`, `apps/*/package.json`, `packages/*/package.json`.
|
|
18
|
+
- Pins: entries in `dependencies`, `devDependencies`, `optionalDependencies`. Skip `workspace:*`, `link:`, `file:`, `git`, `npm:` alias and URL specs.
|
|
19
|
+
- Minimum age in minutes: `minimumReleaseAge` in `pnpm-workspace.yaml` or `.npmrc`; if unset, `1440`.
|
|
20
|
+
|
|
21
|
+
## 2. Find upgrades
|
|
22
|
+
|
|
23
|
+
Run once per distinct package name:
|
|
24
|
+
|
|
25
|
+
```sh
|
|
26
|
+
npm view <pkg> versions time --json
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
```json
|
|
30
|
+
{
|
|
31
|
+
"versions": ["5.9.2", "5.9.3", "6.0.0-beta", "6.0.2", "6.0.3"],
|
|
32
|
+
"time": {
|
|
33
|
+
"created": "2012-10-01T15:35:39.553Z",
|
|
34
|
+
"modified": "2026-10-10T08:24:07.208Z",
|
|
35
|
+
"5.9.3": "2025-09-30T21:19:38.784Z",
|
|
36
|
+
"6.0.0-beta": "2026-02-11T18:26:37.557Z",
|
|
37
|
+
"6.0.2": "2026-03-23T16:14:45.521Z",
|
|
38
|
+
"6.0.3": "2026-04-16T23:38:27.905Z"
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
A package with one version returns `versions` as a string, not an array.
|
|
44
|
+
|
|
45
|
+
Candidates: versions without a `-` prerelease suffix, with `time[version]` at least the minimum age before now.
|
|
46
|
+
|
|
47
|
+
- Same major as the pin, newer than the pin: take the highest. Type is `minor` if the minor differs, else `patch`.
|
|
48
|
+
- Higher major exists: record `held: major` with the highest eligible version. Do not apply it.
|
|
49
|
+
- No candidate newer than the pin: current.
|
|
50
|
+
|
|
51
|
+
If every pin is current, change no files, report `all pins current`, and stop.
|
|
52
|
+
|
|
53
|
+
## 3. Read release notes
|
|
54
|
+
|
|
55
|
+
For each package to upgrade, read the notes for every release after the old version up to and including the new one.
|
|
56
|
+
|
|
57
|
+
```sh
|
|
58
|
+
gh release view v<version> --repo <owner>/<repo> --json body
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
- Repository: `npm view <pkg> repository.url`.
|
|
62
|
+
- No GitHub release for the tag: read `CHANGELOG.md` in the repository for the same range.
|
|
63
|
+
- Record each breaking change, deprecation and migration step per package.
|
|
64
|
+
|
|
65
|
+
## 4. Surface and stop
|
|
66
|
+
|
|
67
|
+
Print one list: package, old version, new version, type, breaking changes and migrations (or `none found`). Then:
|
|
68
|
+
|
|
69
|
+
- Without `--accept`: stop and ask the developer to accept or exclude packages. Edit nothing before the answer.
|
|
70
|
+
- With `--accept`: continue.
|
|
71
|
+
|
|
72
|
+
## 5. Apply
|
|
73
|
+
|
|
74
|
+
- Set each upgraded pin to the exact new version: no `^`, no `~`, no `latest`.
|
|
75
|
+
- Run `<pm> install`.
|
|
76
|
+
- Run each project script with `<pm> run <script>` in this order: `build`, `check`, `typecheck`, `test`. Skip a script the root `package.json` does not define.
|
|
77
|
+
|
|
78
|
+
## 6. Handle a failing check
|
|
79
|
+
|
|
80
|
+
For each failure:
|
|
81
|
+
|
|
82
|
+
- The release notes read in step 3 name the change and its migration: apply the migration, then re-run `<pm> install` and all four scripts.
|
|
83
|
+
- Otherwise: set the package that caused the failure back to its old version, re-run `<pm> install` and all four scripts. Report it as `held: check failed` with the error text.
|
|
84
|
+
|
|
85
|
+
Repeat until `build`, `check`, `typecheck` and `test` all exit 0. Never finish with a failing check.
|
|
86
|
+
|
|
87
|
+
## 7. Report
|
|
88
|
+
|
|
89
|
+
Print a table of changed packages:
|
|
90
|
+
|
|
91
|
+
| Package | Old | New | Type |
|
|
92
|
+
| --- | --- | --- | --- |
|
|
93
|
+
| typescript | 5.9.2 | 5.9.3 | patch |
|
|
94
|
+
|
|
95
|
+
Below it list:
|
|
96
|
+
|
|
97
|
+
- `held: major`: package, current version, newest eligible major.
|
|
98
|
+
- `held: check failed`: package, old version, attempted version, error text.
|
|
99
|
+
- Migrations applied, with the file changed.
|
package/templates/base/AGENTS.md
CHANGED
|
@@ -14,9 +14,17 @@ Before editing files for a substantial task:
|
|
|
14
14
|
|
|
15
15
|
- Turborepo monorepo managed with pnpm. Apps in `apps/`, shared packages in `packages/`.
|
|
16
16
|
- `apps/web`: TanStack Start on Vite, React, TypeScript, Vitest.
|
|
17
|
+
- UI components come from `@workspace/ui` (`packages/ui`, shadcn).
|
|
17
18
|
- Run tasks from the repo root: `pnpm build`, `pnpm check`, `pnpm typecheck`, `pnpm test`, `pnpm dev`.
|
|
19
|
+
- Run `pnpm typecheck`, never a global `tsc`.
|
|
20
|
+
- Use `pnpm exec` and `pnpm dlx`, never `npx`.
|
|
18
21
|
- Dependency versions are exact. Never use `^`, `~` or `latest`.
|
|
19
|
-
- Routes live in `apps/web/src/routes`. `routeTree.gen.ts` is generated;
|
|
22
|
+
- Routes live in `apps/web/src/routes`. `routeTree.gen.ts` is generated; never edit it.
|
|
23
|
+
- Test files sit beside their source. Never put one under `src/routes/` except inside a `-`-prefixed folder.
|
|
24
|
+
- Never suppress `useExhaustiveDependencies`; fix the dependency list.
|
|
25
|
+
- Write comments only to document a public API surface.
|
|
26
|
+
- `pnpm check` and the pre-commit hook delete every `//` comment except `///`, `biome-ignore` and `@ts-` directives.
|
|
27
|
+
- Path-scoped TypeScript and React rules live in `.claude/rules/` and load for matching files; do not restate them here.
|
|
20
28
|
|
|
21
29
|
<!-- BEGIN:turborepo-agent-rules -->
|
|
22
30
|
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
language js
|
|
2
|
+
|
|
3
|
+
JsCatchClause(declaration = JsCatchDeclaration(type_annotation = $annotation)) where {
|
|
4
|
+
$annotation <: TsTypeAnnotation(ty = $type),
|
|
5
|
+
$type <: not `unknown`,
|
|
6
|
+
register_diagnostic(span = $annotation, message = "Type the catch binding as unknown and narrow it.", severity = "error")
|
|
7
|
+
}
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
language js
|
|
2
2
|
|
|
3
|
-
`useEffect($callback, $
|
|
3
|
+
or { `useEffect($callback, $...)`, `useLayoutEffect($callback, $...)` } as $effect where {
|
|
4
4
|
$callback <: contains or { `$_.addEventListener($_, $_)`, `setInterval($_, $_)`, `setTimeout($_, $_)`, `$_.subscribe($_)` },
|
|
5
|
-
|
|
5
|
+
or {
|
|
6
|
+
$callback <: JsArrowFunctionExpression(body = JsFunctionBody(statements = $statements)),
|
|
7
|
+
$callback <: JsFunctionExpression(body = JsFunctionBody(statements = $statements))
|
|
8
|
+
},
|
|
6
9
|
$statements <: not some `return $cleanup`,
|
|
7
10
|
register_diagnostic(span = $effect, message = "Return a cleanup function from an effect that sets up a listener, timer, or subscription.")
|
|
8
11
|
}
|
|
@@ -1,9 +1,22 @@
|
|
|
1
1
|
language js
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
$attr
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
3
|
+
or {
|
|
4
|
+
JsxAttribute() as $attr where {
|
|
5
|
+
$attr <: r"on[A-Z]\w*=\{\w+\}",
|
|
6
|
+
$attr <: not r"on[A-Z]\w*=\{handle[A-Z]\w*\}",
|
|
7
|
+
$attr <: not r"on[A-Z]\w*=\{on[A-Z]\w*\}",
|
|
8
|
+
$attr <: not r"on[A-Z]\w*=\{set[A-Z]\w*\}",
|
|
9
|
+
register_diagnostic(span = $attr, message = "Name the function passed to an on<Event> prop handle<Action>.", severity = "error")
|
|
10
|
+
},
|
|
11
|
+
or {
|
|
12
|
+
JsFunctionDeclaration(id = $name),
|
|
13
|
+
JsVariableDeclarator(id = $name, initializer = JsInitializerClause(expression = or { JsArrowFunctionExpression(), JsFunctionExpression() }))
|
|
14
|
+
} as $local where {
|
|
15
|
+
$name <: r"on[A-Z]\w*",
|
|
16
|
+
$local <: within or {
|
|
17
|
+
JsFunctionDeclaration(id = $component),
|
|
18
|
+
JsVariableDeclarator(id = $component, initializer = JsInitializerClause(expression = or { JsArrowFunctionExpression(), JsFunctionExpression() }))
|
|
19
|
+
} where { $component <: r"[A-Z]\w*" },
|
|
20
|
+
register_diagnostic(span = $name, message = "Name a locally declared handler handle<Action>; on<Event> is for props.", severity = "error")
|
|
21
|
+
}
|
|
9
22
|
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
language js
|
|
2
|
+
|
|
3
|
+
JsClassDeclaration() as $class where {
|
|
4
|
+
$class <: contains JsMethodClassMember(modifiers = $modifiers, name = $name) where {
|
|
5
|
+
or {
|
|
6
|
+
$name <: r"componentDidCatch",
|
|
7
|
+
and { $name <: r"getDerivedStateFromError", $modifiers <: r".*static.*" }
|
|
8
|
+
}
|
|
9
|
+
},
|
|
10
|
+
register_diagnostic(span = $class, message = "Use the route errorComponent, or CatchBoundary from @tanstack/react-router, instead of a class error boundary.", severity = "error")
|
|
11
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
language js
|
|
2
|
+
|
|
3
|
+
`$fn($...)` as $call where {
|
|
4
|
+
$fn <: r"[A-Z]\w*",
|
|
5
|
+
$fn <: not r"String",
|
|
6
|
+
$fn <: not r"Number",
|
|
7
|
+
$fn <: not r"Boolean",
|
|
8
|
+
$fn <: not r"Symbol",
|
|
9
|
+
$fn <: not r"BigInt",
|
|
10
|
+
$fn <: not r"Date",
|
|
11
|
+
$fn <: not r"Error",
|
|
12
|
+
$fn <: not r"Object",
|
|
13
|
+
$fn <: not r"Array",
|
|
14
|
+
$fn <: not r"Promise",
|
|
15
|
+
$fn <: not r"Map",
|
|
16
|
+
$fn <: not r"Set",
|
|
17
|
+
$fn <: not r"RegExp",
|
|
18
|
+
$call <: not within or { JsxElement(), JsxSelfClosingElement(), JsxFragment() },
|
|
19
|
+
register_diagnostic(span = $call, message = "Render a component in JSX instead of calling it as a function.", severity = "error")
|
|
20
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
language js
|
|
2
|
+
|
|
3
|
+
`fetch($...)` as $call where {
|
|
4
|
+
$call <: not within JsPropertyObjectMember(name = $query) where { $query <: r"queryFn" },
|
|
5
|
+
$call <: not within JsPropertyObjectMember(name = $mutation) where { $mutation <: r"mutationFn" },
|
|
6
|
+
or {
|
|
7
|
+
$call <: within JsFunctionDeclaration(id = $declared) where { $declared <: r"handle[A-Z]\w*" },
|
|
8
|
+
$call <: within JsVariableDeclarator(id = $assigned) where { $assigned <: r"handle[A-Z]\w*" },
|
|
9
|
+
$call <: within JsxAttribute(name = $prop) where { $prop <: r"on[A-Z]\w*" }
|
|
10
|
+
},
|
|
11
|
+
register_diagnostic(span = $call, message = "Call a useMutation mutate function from the handler; put fetch in mutationFn.", severity = "error")
|
|
12
|
+
}
|
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
language js
|
|
2
2
|
|
|
3
3
|
or { `Math.random()`, `Date.now()`, `new Date()`, `DateTime.now()` } as $call where {
|
|
4
|
-
$call <: within
|
|
5
|
-
|
|
4
|
+
$call <: within or {
|
|
5
|
+
JsFunctionDeclaration(id = $component),
|
|
6
|
+
JsVariableDeclarator(id = $component, initializer = JsInitializerClause(expression = or { JsArrowFunctionExpression(), JsFunctionExpression() }))
|
|
7
|
+
} where { $component <: r"[A-Z]\w*" },
|
|
8
|
+
$call <: not within or { `useEffect($...)`, `useLayoutEffect($...)`, `useMemo($...)`, `useCallback($...)`, `useState($...)` },
|
|
9
|
+
$call <: not within JsxAttribute(name = $prop) where { $prop <: r"on[A-Z]\w*" },
|
|
6
10
|
$call <: not within JsFunctionDeclaration(id = $handler) where { $handler <: r"handle[A-Z]\w*" },
|
|
11
|
+
$call <: not within JsVariableDeclarator(id = $assigned) where { $assigned <: r"handle[A-Z]\w*" },
|
|
7
12
|
register_diagnostic(span = $call, message = "Non-deterministic value during render; move it into an effect, handler, or useState initialiser.", severity = "error")
|
|
8
13
|
}
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
language js
|
|
2
2
|
|
|
3
3
|
`new QueryClient($_)` as $client where {
|
|
4
|
-
$client <: within
|
|
4
|
+
$client <: within or {
|
|
5
|
+
JsFunctionDeclaration(id = $component),
|
|
6
|
+
JsVariableDeclarator(id = $component, initializer = JsInitializerClause(expression = or { JsArrowFunctionExpression(), JsFunctionExpression() }))
|
|
7
|
+
} where { $component <: r"[A-Z]\w*" },
|
|
5
8
|
$client <: not within `useState($_)`,
|
|
6
9
|
register_diagnostic(span = $client, message = "Create the QueryClient once at module scope or in a useState initialiser.")
|
|
7
10
|
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
language js
|
|
2
|
+
|
|
3
|
+
or {
|
|
4
|
+
`$state.push($...)`,
|
|
5
|
+
`$state.pop($...)`,
|
|
6
|
+
`$state.shift($...)`,
|
|
7
|
+
`$state.unshift($...)`,
|
|
8
|
+
`$state.splice($...)`,
|
|
9
|
+
`$state.sort($...)`,
|
|
10
|
+
`$state.reverse($...)`,
|
|
11
|
+
`$state.fill($...)`,
|
|
12
|
+
`$state.$prop = $_`,
|
|
13
|
+
`$state[$i] = $_`
|
|
14
|
+
} as $mutation where {
|
|
15
|
+
$mutation <: within or { JsFunctionDeclaration(), JsFunctionExpression(), JsArrowFunctionExpression() } as $fn where {
|
|
16
|
+
$fn <: contains `const [$state, $_] = useState($_)`
|
|
17
|
+
},
|
|
18
|
+
register_diagnostic(span = $mutation, message = "State is immutable; call the setter with a new array or object.", severity = "error")
|
|
19
|
+
}
|
|
@@ -2,8 +2,12 @@ language js
|
|
|
2
2
|
|
|
3
3
|
JsTryStatement(body = $block) as $try where {
|
|
4
4
|
$block <: contains or { JsxElement(), JsxSelfClosingElement(), JsxFragment() },
|
|
5
|
-
$try <: within
|
|
6
|
-
|
|
5
|
+
$try <: within or {
|
|
6
|
+
JsFunctionDeclaration(id = $component),
|
|
7
|
+
JsVariableDeclarator(id = $component, initializer = JsInitializerClause(expression = or { JsArrowFunctionExpression(), JsFunctionExpression() }))
|
|
8
|
+
} where { $component <: r"[A-Z]\w*" },
|
|
9
|
+
$try <: not within or { `useEffect($...)`, `useLayoutEffect($...)`, `useMemo($...)`, `useCallback($...)`, `useState($...)` },
|
|
7
10
|
$try <: not within JsFunctionDeclaration(id = $handler) where { $handler <: r"handle[A-Z]\w*" },
|
|
11
|
+
$try <: not within JsVariableDeclarator(id = $assigned) where { $assigned <: r"handle[A-Z]\w*" },
|
|
8
12
|
register_diagnostic(span = $try, message = "try/catch cannot catch render errors; wrap the subtree in an ErrorBoundary.")
|
|
9
13
|
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
language js
|
|
2
|
+
|
|
3
|
+
`const $x: never = $y` as $declaration where {
|
|
4
|
+
$declaration <: within or { JsDefaultClause(), JsElseClause() },
|
|
5
|
+
register_diagnostic(span = $declaration, message = "Use the shared assertNever helper instead of an inline never assignment.", severity = "error")
|
|
6
|
+
}
|
|
@@ -6,7 +6,15 @@
|
|
|
6
6
|
"useIgnoreFile": true
|
|
7
7
|
},
|
|
8
8
|
"files": {
|
|
9
|
-
"includes": [
|
|
9
|
+
"includes": [
|
|
10
|
+
"**",
|
|
11
|
+
"!**/routeTree.gen.ts",
|
|
12
|
+
"!.biome",
|
|
13
|
+
"!.intent",
|
|
14
|
+
"!.claude",
|
|
15
|
+
"!.agents",
|
|
16
|
+
"!.codex"
|
|
17
|
+
]
|
|
10
18
|
},
|
|
11
19
|
"formatter": {
|
|
12
20
|
"enabled": true,
|
|
@@ -25,7 +33,13 @@
|
|
|
25
33
|
},
|
|
26
34
|
"style": {
|
|
27
35
|
"noInferrableTypes": "error",
|
|
28
|
-
"
|
|
36
|
+
"noNonNullAssertion": "error",
|
|
37
|
+
"useReadonlyClassProperties": {
|
|
38
|
+
"level": "error",
|
|
39
|
+
"options": {
|
|
40
|
+
"checkAllProperties": true
|
|
41
|
+
}
|
|
42
|
+
},
|
|
29
43
|
"useThrowOnlyError": "error",
|
|
30
44
|
"useConsistentTypeDefinitions": {
|
|
31
45
|
"level": "error",
|
|
@@ -41,6 +55,7 @@
|
|
|
41
55
|
}
|
|
42
56
|
},
|
|
43
57
|
"suspicious": {
|
|
58
|
+
"noExplicitAny": "error",
|
|
44
59
|
"noVar": "error"
|
|
45
60
|
},
|
|
46
61
|
"nursery": {
|
|
@@ -58,7 +73,12 @@
|
|
|
58
73
|
"useArraySome": "error",
|
|
59
74
|
"useAwaitThenable": "error",
|
|
60
75
|
"useConsistentTestIt": "error",
|
|
61
|
-
"useExhaustiveSwitchCases":
|
|
76
|
+
"useExhaustiveSwitchCases": {
|
|
77
|
+
"level": "error",
|
|
78
|
+
"options": {
|
|
79
|
+
"requireExplicitCase": true
|
|
80
|
+
}
|
|
81
|
+
},
|
|
62
82
|
"useExpect": "error",
|
|
63
83
|
"useIncludes": "error",
|
|
64
84
|
"useNullishCoalescing": "error",
|
|
@@ -124,6 +144,24 @@
|
|
|
124
144
|
{
|
|
125
145
|
"path": ".biome/no-impure-render.grit",
|
|
126
146
|
"includes": ["**/*.tsx"]
|
|
147
|
+
},
|
|
148
|
+
".biome/catch-binding-unknown.grit",
|
|
149
|
+
".biome/use-assert-never.grit",
|
|
150
|
+
{
|
|
151
|
+
"path": ".biome/no-direct-component-call.grit",
|
|
152
|
+
"includes": ["**/*.tsx"]
|
|
153
|
+
},
|
|
154
|
+
{
|
|
155
|
+
"path": ".biome/no-state-mutation.grit",
|
|
156
|
+
"includes": ["**/*.tsx"]
|
|
157
|
+
},
|
|
158
|
+
{
|
|
159
|
+
"path": ".biome/no-class-error-boundary.grit",
|
|
160
|
+
"includes": ["**/*.tsx"]
|
|
161
|
+
},
|
|
162
|
+
{
|
|
163
|
+
"path": ".biome/no-fetch-in-handler.grit",
|
|
164
|
+
"includes": ["**/*.tsx"]
|
|
127
165
|
}
|
|
128
166
|
],
|
|
129
167
|
"css": {
|