@draekien/create-d9-app 0.0.2 → 0.0.3

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.
Files changed (61) hide show
  1. package/package.json +2 -1
  2. package/templates/base/.agents/skills/shadcn/SKILL.md +295 -0
  3. package/templates/base/.agents/skills/shadcn/agents/openai.yml +5 -0
  4. package/templates/base/.agents/skills/shadcn/assets/shadcn-small.png +0 -0
  5. package/templates/base/.agents/skills/shadcn/assets/shadcn.png +0 -0
  6. package/templates/base/.agents/skills/shadcn/assets/showcase/color-pair.tsx +126 -0
  7. package/templates/base/.agents/skills/shadcn/assets/showcase/preview-states.css +14 -0
  8. package/templates/base/.agents/skills/shadcn/assets/showcase/state-matrix.tsx +72 -0
  9. package/templates/base/.agents/skills/shadcn/cli.md +290 -0
  10. package/templates/base/.agents/skills/shadcn/customization.md +211 -0
  11. package/templates/base/.agents/skills/shadcn/design-system-page.md +268 -0
  12. package/templates/base/.agents/skills/shadcn/design-system.md +386 -0
  13. package/templates/base/.agents/skills/shadcn/evals/evals.json +126 -0
  14. package/templates/base/.agents/skills/shadcn/mcp.md +105 -0
  15. package/templates/base/.agents/skills/shadcn/registry.md +277 -0
  16. package/templates/base/.agents/skills/shadcn/rules/base-vs-radix.md +306 -0
  17. package/templates/base/.agents/skills/shadcn/rules/chat.md +250 -0
  18. package/templates/base/.agents/skills/shadcn/rules/composition.md +213 -0
  19. package/templates/base/.agents/skills/shadcn/rules/forms.md +192 -0
  20. package/templates/base/.agents/skills/shadcn/rules/icons.md +101 -0
  21. package/templates/base/.agents/skills/shadcn/rules/styling.md +185 -0
  22. package/templates/base/.agents/skills/shadcn/scripts/showcase-coverage.mjs +53 -0
  23. package/templates/base/.claude/rules/components.md +11 -0
  24. package/templates/base/.claude/rules/generated-files.md +11 -0
  25. package/templates/base/.claude/rules/testing.md +13 -0
  26. package/templates/base/.claude/rules/typescript.md +29 -0
  27. package/templates/base/.claude/skills/shadcn/SKILL.md +295 -0
  28. package/templates/base/.claude/skills/shadcn/agents/openai.yml +5 -0
  29. package/templates/base/.claude/skills/shadcn/assets/shadcn-small.png +0 -0
  30. package/templates/base/.claude/skills/shadcn/assets/shadcn.png +0 -0
  31. package/templates/base/.claude/skills/shadcn/assets/showcase/color-pair.tsx +126 -0
  32. package/templates/base/.claude/skills/shadcn/assets/showcase/preview-states.css +14 -0
  33. package/templates/base/.claude/skills/shadcn/assets/showcase/state-matrix.tsx +72 -0
  34. package/templates/base/.claude/skills/shadcn/cli.md +290 -0
  35. package/templates/base/.claude/skills/shadcn/customization.md +211 -0
  36. package/templates/base/.claude/skills/shadcn/design-system-page.md +268 -0
  37. package/templates/base/.claude/skills/shadcn/design-system.md +386 -0
  38. package/templates/base/.claude/skills/shadcn/evals/evals.json +126 -0
  39. package/templates/base/.claude/skills/shadcn/mcp.md +105 -0
  40. package/templates/base/.claude/skills/shadcn/registry.md +277 -0
  41. package/templates/base/.claude/skills/shadcn/rules/base-vs-radix.md +306 -0
  42. package/templates/base/.claude/skills/shadcn/rules/chat.md +250 -0
  43. package/templates/base/.claude/skills/shadcn/rules/composition.md +213 -0
  44. package/templates/base/.claude/skills/shadcn/rules/forms.md +192 -0
  45. package/templates/base/.claude/skills/shadcn/rules/icons.md +101 -0
  46. package/templates/base/.claude/skills/shadcn/rules/styling.md +185 -0
  47. package/templates/base/.claude/skills/shadcn/scripts/showcase-coverage.mjs +53 -0
  48. package/templates/base/AGENTS.md +9 -1
  49. package/templates/base/skills-lock.json +11 -0
  50. package/templates/tools/biome/.biome/catch-binding-unknown.grit +7 -0
  51. package/templates/tools/biome/.biome/effect-cleanup.grit +5 -2
  52. package/templates/tools/biome/.biome/handler-naming.grit +19 -6
  53. package/templates/tools/biome/.biome/no-class-error-boundary.grit +11 -0
  54. package/templates/tools/biome/.biome/no-direct-component-call.grit +20 -0
  55. package/templates/tools/biome/.biome/no-fetch-in-handler.grit +12 -0
  56. package/templates/tools/biome/.biome/no-impure-render.grit +7 -2
  57. package/templates/tools/biome/.biome/no-query-client-in-render.grit +4 -1
  58. package/templates/tools/biome/.biome/no-state-mutation.grit +19 -0
  59. package/templates/tools/biome/.biome/no-try-around-jsx.grit +6 -2
  60. package/templates/tools/biome/.biome/use-assert-never.grit +6 -0
  61. 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
+ }
@@ -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; do not edit it.
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,11 @@
1
+ {
2
+ "version": 1,
3
+ "skills": {
4
+ "shadcn": {
5
+ "source": "shadcn/ui",
6
+ "sourceType": "github",
7
+ "skillPath": "skills/shadcn/SKILL.md",
8
+ "computedHash": "2d7bf36e581289fdcc3ffc592c1ec1d7457572baa72ba0d440229ef34bd7f6dd"
9
+ }
10
+ }
11
+ }
@@ -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, $deps)` as $effect where {
3
+ or { `useEffect($callback, $...)`, `useLayoutEffect($callback, $...)` } as $effect where {
4
4
  $callback <: contains or { `$_.addEventListener($_, $_)`, `setInterval($_, $_)`, `setTimeout($_, $_)`, `$_.subscribe($_)` },
5
- $callback <: JsArrowFunctionExpression(body = JsFunctionBody(statements = $statements)),
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
- JsxAttribute() as $attr where {
4
- $attr <: r"on[A-Z]\w*=\{\w+\}",
5
- $attr <: not r"on[A-Z]\w*=\{handle[A-Z]\w*\}",
6
- $attr <: not r"on[A-Z]\w*=\{on[A-Z]\w*\}",
7
- $attr <: not r"on[A-Z]\w*=\{set[A-Z]\w*\}",
8
- register_diagnostic(span = $attr, message = "Name the function passed to an on<Event> prop handle<Action>.", severity = "error")
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 JsFunctionDeclaration(id = $component) where { $component <: r"[A-Z]\w*" },
5
- $call <: not within JsArrowFunctionExpression(),
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 JsFunctionDeclaration(id = $component) where { $component <: r"[A-Z]\w*" },
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 JsFunctionDeclaration(id = $component) where { $component <: r"[A-Z]\w*" },
6
- $try <: not within JsArrowFunctionExpression(),
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": ["**", "!**/routeTree.gen.ts", "!.biome", "!.intent", "!.claude", "!.codex"]
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
- "useReadonlyClassProperties": "error",
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": "error",
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": {