create-sprinkles 0.2.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 (59) hide show
  1. package/dist/bin.mjs +295 -0
  2. package/dist/index.d.mts +46 -0
  3. package/dist/index.mjs +180 -0
  4. package/package.json +45 -0
  5. package/templates/react-router-convex/.env.local +2 -0
  6. package/templates/react-router-convex/convex/schema.ts +9 -0
  7. package/templates/react-router-rsc/app/home.tsx.hbs +31 -0
  8. package/templates/react-router-rsc/app/root.tsx.hbs +55 -0
  9. package/templates/react-router-rsc/tsconfig.json.hbs +31 -0
  10. package/templates/react-router-rsc/workers/entry.rsc.tsx +44 -0
  11. package/templates/react-router-rsc/workers/entry.ssr.tsx +41 -0
  12. package/templates/react-router-rsc/wrangler.rsc.jsonc.hbs +25 -0
  13. package/templates/react-router-rsc/wrangler.ssr.jsonc.hbs +14 -0
  14. package/templates/react-router-rsc-content-layer/app/content.config.ts.hbs +26 -0
  15. package/templates/react-router-rsc-content-layer/content-layer/api.ts +350 -0
  16. package/templates/react-router-rsc-content-layer/content-layer/codegen.ts +89 -0
  17. package/templates/react-router-rsc-content-layer/content-layer/config.ts +20 -0
  18. package/templates/react-router-rsc-content-layer/content-layer/digest.ts +6 -0
  19. package/templates/react-router-rsc-content-layer/content-layer/frontmatter.ts +19 -0
  20. package/templates/react-router-rsc-content-layer/content-layer/loaders/file.ts +55 -0
  21. package/templates/react-router-rsc-content-layer/content-layer/loaders/glob.ts +82 -0
  22. package/templates/react-router-rsc-content-layer/content-layer/loaders/index.ts +2 -0
  23. package/templates/react-router-rsc-content-layer/content-layer/plugin.ts +419 -0
  24. package/templates/react-router-rsc-content-layer/content-layer/resolve-hook.js +12 -0
  25. package/templates/react-router-rsc-content-layer/content-layer/runtime.ts +73 -0
  26. package/templates/react-router-rsc-content-layer/content-layer/store.ts +59 -0
  27. package/templates/react-router-spa/app/home.tsx.hbs +7 -0
  28. package/templates/react-router-spa/app/root.tsx.hbs +60 -0
  29. package/templates/react-router-spa/tsconfig.json.hbs +26 -0
  30. package/templates/react-router-spa/wrangler.jsonc.hbs +9 -0
  31. package/templates/react-router-ssr/app/home.tsx.hbs +21 -0
  32. package/templates/react-router-ssr/app/root.tsx.hbs +105 -0
  33. package/templates/react-router-ssr/convex/schema.ts +7 -0
  34. package/templates/react-router-ssr/tsconfig.json.hbs +28 -0
  35. package/templates/react-router-ssr/wrangler.jsonc.hbs +13 -0
  36. package/templates/react-router-ssr-convex/app/lib/client.ts +19 -0
  37. package/templates/react-router-ssr-convex/app/tanstack-query-integration/middleware.ts +18 -0
  38. package/templates/react-router-ssr-convex/app/tanstack-query-integration/query-preloader.ts +125 -0
  39. package/templates/react-shared/app/routes.ts.hbs +3 -0
  40. package/templates/react-shared/app/styles/tailwind.css +1 -0
  41. package/templates/react-shared/react-compiler.plugin.ts.hbs +10 -0
  42. package/templates/react-shared/react-router.config.ts.hbs +9 -0
  43. package/templates/shared/.gitignore.hbs +23 -0
  44. package/templates/shared/.node-version +1 -0
  45. package/templates/shared/.vscode/extensions.json.hbs +8 -0
  46. package/templates/shared/.vscode/settings.json.hbs +72 -0
  47. package/templates/shared/AGENTS.md.hbs +599 -0
  48. package/templates/shared/README.md.hbs +24 -0
  49. package/templates/shared/package.json.hbs +41 -0
  50. package/templates/shared/vite.config.ts.hbs +384 -0
  51. package/templates/ts-package/src/index.ts +3 -0
  52. package/templates/ts-package/tests/index.test.ts +9 -0
  53. package/templates/ts-package/tsconfig.json +18 -0
  54. package/templates/ts-package-cli/bin/index.ts.hbs +1 -0
  55. package/templates/ts-package-cli/src/cli.ts.hbs +37 -0
  56. package/templates/ts-package-generator/bin/create.ts.hbs +2 -0
  57. package/templates/ts-package-generator/src/template.ts.hbs +22 -0
  58. package/templates/ts-package-sea/sea-config.json.hbs +2 -0
  59. package/templates/ts-package-sea/src/sea-entry.ts.hbs +4 -0
@@ -0,0 +1,599 @@
1
+ # Contributor Guidelines
2
+
3
+ ## Technology Overview
4
+
5
+ - **Development runtime**: Node.js (v24)
6
+ - **Package manager**: pnpm
7
+ - **Toolchain**: Vite+ (`vp`)
8
+ - **Bundler**: Vite 8 (with Rolldown)
9
+ {{#if isReactRouter}}
10
+ - **JavaScript framework**: React 19
11
+ - **Meta-framework**: React Router
12
+ {{#if isSPA}}
13
+ - **Rendering**: Single-Page Application (SPA)
14
+ {{/if}}
15
+ {{#if isSSR}}
16
+ - **Rendering**: Server-Side Rendering (SSR)
17
+ {{/if}}
18
+ {{#if isRSC}}
19
+ - **Rendering**: React Server Components (RSC)
20
+ {{/if}}
21
+ {{#if hasConvex}}
22
+ - **Backend**: Convex
23
+ - **Data fetching**: TanStack Query with @convex-dev/react-query
24
+ {{/if}}
25
+ {{#if isRSC}}
26
+ {{#if hasContentLayer}}
27
+ - **Content**: Content-layer plugin with MDX
28
+ {{/if}}
29
+ {{/if}}
30
+ - **Styling**: Tailwind CSS v4
31
+ {{/if}}
32
+ - **Formatter**: Oxfmt
33
+ - **Linter**: Oxlint
34
+ {{#if isReactRouter}}
35
+ - **Deployment runtime**: Cloudflare `workerd`
36
+ {{/if}}
37
+
38
+ ## Common Commands
39
+
40
+ - Dev server: `vp dev` (**NEVER** run this yourself)
41
+ - Build: `vp build`
42
+ - Format: `vp fmt`
43
+ - Lint: `vp lint`
44
+ - Typecheck: `vp run typecheck`
45
+ - Project check: `vp check`
46
+ - Tests: `vp test`
47
+ {{#if isPackage}}
48
+ - Pack library: `vp pack`
49
+ - Watch mode: `vp run dev`
50
+ {{/if}}
51
+
52
+ ## Debugging Approach
53
+
54
+ - Use `console.log` with clear prefixes during development
55
+ - **NEVER** attempt to run the dev server yourself
56
+ - If you need to debug, add `console.log` statements, tell the user where to look for the output (client, server, both, etc.), and they will paste back the results
57
+
58
+ ## Node.js
59
+
60
+ - Always use the `node:` prefix for Node built-in module imports
61
+ - Format code with `vp fmt` before committing
62
+ - Run `vp run typecheck` and fix all info, warnings, and errors
63
+ - Prefer `unknown` over `any` unless absolutely necessary
64
+ - Use JSDoc/TSDoc comments for public APIs
65
+ - Document important lines of code with a single line comment
66
+ - Prefer `jsr:@std/*` for standard library packages
67
+ - Use relative imports for local modules
68
+ - Prefer import aliases (`~/*`) over deep relative imports
69
+ - Validate environment variables at startup using `node:assert`
70
+
71
+ {{#if isReactRouter}}
72
+ ## React 19
73
+
74
+ ### Document Metadata in Components
75
+
76
+ React 19 supports rendering `<title>`, `<meta>`, and `<link>` tags directly inside components. React hoists them to `<head>` automatically:
77
+
78
+ ```tsx
79
+ export function ProductPage({ product }) {
80
+ return (
81
+ <>
82
+ <title>`${product.name} | Store`</title>
83
+ <meta name="description" content={product.description} />
84
+ <link rel="canonical" href={`/products/${product.id}`} />
85
+ <main>
86
+ <h1>{product.name}</h1>
87
+ </main>
88
+ </>
89
+ );
90
+ }
91
+ ```
92
+
93
+ ### `use()` for Reading Promises and Context
94
+
95
+ `use()` reads the value of a promise or context. Unlike hooks, `use()` can be called inside loops and conditionals:
96
+
97
+ ```tsx
98
+ import { use } from "react";
99
+
100
+ // Reading a promise (suspends until resolved)
101
+ function ProductDetails({ productPromise }) {
102
+ let product = use(productPromise);
103
+ return <h1>{product.name}</h1>;
104
+ }
105
+
106
+ // With TanStack Query, use useQuery alongside use() for query promises:
107
+ import { useQuery } from "@tanstack/react-query";
108
+
109
+ function ProductDetails({ queryOptions }) {
110
+ let { promise } = useQuery(queryOptions);
111
+ let product = use(promise);
112
+
113
+ return <h1>{product.name}</h1>;
114
+ }
115
+ {{#if isRSC}}
116
+
117
+ // Reading data from a server component in a client component:
118
+ // Server component passes a promise as a prop
119
+ export async function ServerComponent() {
120
+ let dataPromise = fetchData(); // Don't await - pass as promise
121
+ return <ClientDisplay dataPromise={dataPromise} />;
122
+ }
123
+
124
+ // Client component uses use() to read it
125
+ "use client";
126
+ function ClientDisplay({ dataPromise }) {
127
+ let data = use(dataPromise);
128
+ return <div>{data.value}</div>;
129
+ }
130
+ {{/if}}
131
+ ```
132
+
133
+ ### `useActionState()` for Form Actions
134
+
135
+ `useActionState()` manages form submission state with automatic pending tracking:
136
+
137
+ ```tsx
138
+ "use client";
139
+
140
+ import { useActionState } from "react";
141
+
142
+ function LoginForm() {
143
+ let [state, action, isPending] = useActionState(
144
+ async (prevState, formData: FormData) => {
145
+ let result = await login(formData);
146
+ if (result.error) return { error: result.error };
147
+ return { success: true };
148
+ },
149
+ { error: null },
150
+ );
151
+
152
+ return (
153
+ <form action={action}>
154
+ <input name="email" type="email" />
155
+ <input name="password" type="password" />
156
+ {state.error && <p>{state.error}</p>}
157
+ <button disabled={isPending} type="submit">
158
+ {isPending ? "Logging in..." : "Log in"}
159
+ </button>
160
+ </form>
161
+ );
162
+ }
163
+ ```
164
+
165
+ ### `useOptimistic()` for Optimistic Updates
166
+
167
+ `useOptimistic()` shows a temporary state while an async action is pending:
168
+
169
+ ```tsx
170
+ "use client";
171
+
172
+ import { useOptimistic, useActionState } from "react";
173
+
174
+ function CartItem({ item, updateQuantity }) {
175
+ let [optimisticQuantity, setOptimisticQuantity] = useOptimistic(
176
+ item.quantity,
177
+ (_current, newQuantity: number) => newQuantity,
178
+ );
179
+
180
+ let [, action, isPending] = useActionState(
181
+ async (_prev, formData: FormData) => {
182
+ let newQuantity = Number(formData.get("quantity"));
183
+ setOptimisticQuantity(newQuantity);
184
+ return updateQuantity(item.id, newQuantity);
185
+ },
186
+ null,
187
+ );
188
+
189
+ return (
190
+ <form action={action}>
191
+ <input
192
+ defaultValue={optimisticQuantity}
193
+ name="quantity"
194
+ type="number"
195
+ />
196
+ <span style=\{{ opacity: isPending ? 0.5 : 1 }}>{item.name}</span>
197
+ </form>
198
+ );
199
+ }
200
+ ```
201
+
202
+ ### `<form action={fn}>` for Direct Form Actions
203
+
204
+ React 19 allows passing a function directly to a form's `action` prop. This works with both client functions and server actions:
205
+
206
+ ```tsx
207
+ // Client-side action
208
+ function SearchForm() {
209
+ let navigate = useNavigate();
210
+
211
+ async function handleSearch(formData: FormData) {
212
+ let query = formData.get("q") as string;
213
+ navigate(`/search?q=${query}`);
214
+ }
215
+
216
+ return (
217
+ <form action={handleSearch}>
218
+ <input name="q" placeholder="Search..." type="search" />
219
+ <button type="submit">Search</button>
220
+ </form>
221
+ );
222
+ }
223
+ {{#if isRSC}}
224
+
225
+ // Server action (RSC)
226
+ export async function ServerComponent() {
227
+ async function createItem(formData: FormData) {
228
+ "use server";
229
+ let name = formData.get("name") as string;
230
+ await db.insert({ name });
231
+ redirect("/items");
232
+ }
233
+
234
+ return (
235
+ <form action={createItem}>
236
+ <input name="name" />
237
+ <button type="submit">Create</button>
238
+ </form>
239
+ );
240
+ }
241
+ {{/if}}
242
+ ```
243
+
244
+ ## React Router
245
+
246
+ ### Critical Package Guidelines
247
+
248
+ **Use these packages:**
249
+
250
+ - `react-router` - Main package for routing components and hooks
251
+ - `@react-router/dev` - Development tools and route configuration
252
+ - `@cloudflare/vite-plugin` - Cloudflare server adapter
253
+
254
+ **NEVER use these packages:**
255
+
256
+ - `react-router-dom` - Legacy package, use `react-router` instead
257
+ - `@remix-run/*` - Old packages, replaced by `@react-router/*`
258
+ - React Router v6 patterns - Completely different architecture
259
+
260
+ ### Hooks
261
+
262
+ These hooks are acceptable when needed:
263
+
264
+ - `useLocation`, `useNavigation`, `useNavigate`
265
+ - `useParams`, `useSearchParams`
266
+ - `useSubmit` (for `GET` actions only)
267
+ - `useMatch` / `useMatches`
268
+ - `useRevalidator`
269
+
270
+ **NEVER use these hooks** (data should flow through loaders, actions, and server components instead):
271
+
272
+ - `useLoaderData`, `useActionData`
273
+ - `useAsyncValue`, `useAsyncError`
274
+ - `useFormAction`
275
+ - `useFetcher`
276
+
277
+ ### Route Configuration (`app/routes.ts`)
278
+
279
+ ```tsx
280
+ import { type RouteConfig, index, route } from "@react-router/dev/routes";
281
+
282
+ export default [
283
+ index("routes/home.tsx"),
284
+ route("about", "routes/about.tsx"),
285
+ route("products/:id", "routes/product.tsx", [
286
+ index("routes/product-overview.tsx"),
287
+ route("reviews", "routes/product-reviews.tsx"),
288
+ ]),
289
+ ] satisfies RouteConfig;
290
+ ```
291
+
292
+ ### Route Type Imports
293
+
294
+ **ALWAYS use `./+types/[routeName]` for route type imports.**
295
+
296
+ ```tsx
297
+ // CORRECT - always use this pattern:
298
+ import type { Route } from "./+types/product-details";
299
+
300
+ // WRONG - NEVER use relative parent paths:
301
+ // import type { Route } from "../+types/product-details";
302
+ ```
303
+
304
+ **If you see TypeScript errors about missing `./+types/[routeName]` modules:**
305
+
306
+ 1. **IMMEDIATELY run `vp run typecheck`** to generate the types
307
+ 2. **Or run `vp run typegen:react-router`** to generate types
308
+ 3. **NEVER try to "fix" it by changing the import path**
309
+
310
+ Types are generated by `@react-router/dev` in `./+types/[routeName]` relative to each route file. The dev server also generates types automatically if the user is running it.
311
+
312
+ ### Type-Safe URL Generation with `href()`
313
+
314
+ ```tsx
315
+ import { Link, href } from "react-router";
316
+
317
+ // Static routes
318
+ <Link to={href("/products/new")}>New Product</Link>
319
+
320
+ // Dynamic routes - automatic type safety
321
+ <Link to={href("/products/:id", { id: product.id })}>View</Link>
322
+
323
+ // Works with redirects too
324
+ return redirect(href("/products/:id", { id: newProduct.id }));
325
+
326
+ // NEVER manually construct URLs:
327
+ // <Link to={`/products/${product.id}`}>Product</Link> // WRONG
328
+ ```
329
+
330
+ {{#if isRSC}}
331
+ ### Route Module Pattern (RSC)
332
+
333
+ Routes export `ServerComponent` instead of `default`. All route components (`ServerComponent`, `ErrorBoundary`, `HydrateFallback`, `Layout`) are server components.
334
+
335
+ ```tsx
336
+ import type { Route } from "./+types/route";
337
+ import { Outlet } from "react-router";
338
+
339
+ export async function ServerComponent({ loaderData, params }: Route.ComponentProps) {
340
+ let product = await getProduct(params.id);
341
+
342
+ async function buyProduct(formData: FormData) {
343
+ "use server";
344
+ await updateProduct(formData);
345
+ return redirect(href("/products/:id", { id: params.id }));
346
+ }
347
+
348
+ return (
349
+ <form action={buyProduct}>
350
+ <h2>{product.name}</h2>
351
+ <button type="submit">Buy Now</button>
352
+ </form>
353
+ );
354
+ }
355
+ ```
356
+
357
+ For client interactivity, extract to `"use client"` modules:
358
+
359
+ ```tsx
360
+ // counter.tsx
361
+ "use client";
362
+
363
+ import { useState } from "react";
364
+
365
+ export function Counter() {
366
+ let [count, setCount] = useState(0);
367
+ return <button onClick={() => setCount(count + 1)}>Count: {count}</button>;
368
+ }
369
+ ```
370
+
371
+ ```tsx
372
+ // route.tsx
373
+ import { Counter } from "./counter.tsx";
374
+
375
+ export function ServerComponent() {
376
+ return (
377
+ <>
378
+ <h1>Counter</h1>
379
+ <Counter />
380
+ </>
381
+ );
382
+ }
383
+ ```
384
+
385
+ ### `.server`/`.client` Module Naming
386
+
387
+ Support for `.server.ts(x)` and `.client.ts(x)` file naming is **not supported** in RSC Framework Mode. Use the `"server-only"` and `"client-only"` side-effect imports instead:
388
+
389
+ ```ts
390
+ // app/utils/db.ts
391
+ import "server-only";
392
+
393
+ // This module will cause a build error if accidentally imported on the client
394
+ ```
395
+
396
+ > `@vitejs/plugin-rsc` handles these imports internally — the `server-only` and `client-only` npm packages do not need to be installed.
397
+
398
+ ### MDX Route Support
399
+
400
+ MDX routes are supported with `@mdx-js/rollup` v3.1.1+. Components exported from MDX routes must be valid in RSC environments (no client hooks). Extract interactive components to `"use client"` modules.
401
+
402
+ ### Unsupported RSC Config Options
403
+
404
+ The following `react-router.config.ts` options are not yet supported in RSC Framework Mode:
405
+
406
+ - `buildEnd`, `prerender`, `presets`, `routeDiscovery`, `serverBundles`
407
+ - `ssr: false` (SPA Mode)
408
+ - `future.unstable_splitRouteModules`, `future.unstable_subResourceIntegrity`
409
+
410
+ {{else}}
411
+ ### Route Module Pattern
412
+
413
+ ```tsx
414
+ import type { Route } from "./+types/product";
415
+
416
+ {{#if isSPA}}
417
+ export async function clientLoader({ params }: Route.ClientLoaderArgs) {
418
+ let product = await fetchProduct(params.id);
419
+ return { product };
420
+ }
421
+ {{else}}
422
+ export async function loader({ params }: Route.LoaderArgs) {
423
+ return { product: await getProduct(params.id) };
424
+ }
425
+ {{/if}}
426
+
427
+ export default function Product({ loaderData }: Route.ComponentProps) {
428
+ return <h1>{loaderData.product.name}</h1>;
429
+ }
430
+ ```
431
+
432
+ {{/if}}
433
+ ### Layout Routes with `<Outlet />`
434
+
435
+ For layout routes that have child routes, **ALWAYS** use `<Outlet />` to render child routes:
436
+
437
+ ```tsx
438
+ import { Outlet } from "react-router";
439
+
440
+ {{#if isRSC}}
441
+ export function ServerComponent() {
442
+ {{else}}
443
+ export default function Layout() {
444
+ {{/if}}
445
+ return (
446
+ <div className="layout">
447
+ <nav>{/* Navigation */}</nav>
448
+ <main>
449
+ <Outlet />
450
+ </main>
451
+ </div>
452
+ );
453
+ }
454
+ ```
455
+
456
+ ### Error Boundaries
457
+
458
+ Only set up `ErrorBoundary` for routes if the user explicitly asks. All errors bubble up to the `ErrorBoundary` in `root.tsx` by default.
459
+
460
+ ```tsx
461
+ import { isRouteErrorResponse } from "react-router";
462
+
463
+ export function ErrorBoundary({ error }: Route.ErrorBoundaryProps) {
464
+ if (isRouteErrorResponse(error)) {
465
+ return (
466
+ <div>
467
+ <h1>{error.status} {error.statusText}</h1>
468
+ <p>{error.data}</p>
469
+ </div>
470
+ );
471
+ }
472
+
473
+ return (
474
+ <div>
475
+ <h1>Oops!</h1>
476
+ <p>{error.message}</p>
477
+ </div>
478
+ );
479
+ }
480
+ ```
481
+
482
+ ### File Organization
483
+
484
+ React Router uses **explicit route configuration** in `app/routes.ts`. You are NOT constrained by file-based routing conventions. Use descriptive, kebab-case file names organized by feature.
485
+
486
+ ### Correct Imports
487
+
488
+ ```tsx
489
+ import { Link, Form, Outlet, NavLink } from "react-router";
490
+ import { type RouteConfig, index, route } from "@react-router/dev/routes";
491
+ import { data, redirect, href } from "react-router";
492
+ ```
493
+
494
+ {{#if isSPA}}
495
+ ### SPA Mode
496
+
497
+ This project runs as a single-page application with `ssr: false` in `react-router.config.ts`. All rendering happens on the client. Use `clientLoader` for data loading.
498
+
499
+ {{#if hasConvex}}
500
+ ### Convex Integration
501
+
502
+ The app uses Convex with TanStack Query via `@convex-dev/react-query`. The provider stack is in `root.tsx`:
503
+
504
+ - `ConvexProvider` wraps the app with `ConvexReactClient`
505
+ - `QueryClientProvider` wraps with a `QueryClient` configured with Convex's hash and query functions
506
+ - Use `queryClient.ensureQueryData()` in `clientLoader` to pre-populate the cache
507
+ {{/if}}
508
+
509
+ {{/if}}
510
+ {{#if isSSR}}
511
+ ### SSR Mode
512
+
513
+ This project uses server-side rendering deployed to Cloudflare Workers.
514
+
515
+ {{#if hasConvex}}
516
+ ### Convex Integration
517
+
518
+ The app uses Convex with TanStack Query via middleware-based `QueryClient` hydration:
519
+
520
+ - `middleware.ts` - Sets up the query client per request via `setQueryClient`
521
+ - `query-preloader.ts` - Provides `createPreloader` for server-side data loading and `useDehydratedState` for client hydration
522
+ - `lib/client.ts` - Creates the base `QueryClient` with Convex integration
523
+
524
+ **Route pattern with createPreloader:**
525
+
526
+ ```tsx
527
+ import { createPreloader } from "~/tanstack-query-integration/query-preloader.ts";
528
+
529
+ export const loader = createPreloader<Route.LoaderArgs, void>(async ({ preload }) => {
530
+ await preload(yourQueryOptions);
531
+ });
532
+ ```
533
+ {{/if}}
534
+
535
+ {{/if}}
536
+ {{/if}}
537
+ ## General Rules
538
+
539
+ ### Correctness
540
+
541
+ - Remove anything that isn't used - unused imports, parameters, and members
542
+ {{#if isReactRouter}}
543
+ - Exhaust every hook dependency in React hook dependency arrays
544
+ - Call React hooks only at the top level of a component (never in loops, conditions, or nested functions)
545
+ {{/if}}
546
+ - Use only valid CSS selectors - real pseudo-classes, pseudo-elements, and type selectors only
547
+
548
+ ### Suspicious Code
549
+
550
+ - Skip the `any` shortcut - prefer precise TypeScript types
551
+ {{#if isReactRouter}}
552
+ - Hands off `document.cookie` - manipulating cookies directly is forbidden; use React Router's cookie utilities instead
553
+ {{/if}}
554
+
555
+ ### Performance
556
+
557
+ - Compile regexes once - declare regular expressions at module scope, not inside hot functions
558
+
559
+ ### Style & Consistency
560
+
561
+ - Stick to ES modules - no `require` or CommonJS patterns
562
+ - Prefer `import type` for type-only imports
563
+ - Use `node:` protocol for Node.js built-in imports
564
+ - Use `T[]` shorthand array syntax
565
+ - Don't reassign function parameters
566
+ - Favor `let` over `const` for bindings, unless declaring a top-level constant with ALL_CAPS
567
+ - One `let` per line - declare variables individually
568
+ - Skip non-null assertions - rewrite code so `!` isn't necessary
569
+ - Avoid `enum` - choose unions, objects, or literal types
570
+ - Use `trimStart`/`trimEnd` instead of `trimLeft`/`trimRight`
571
+ - Default parameters go last
572
+ {{#if isReactRouter}}
573
+ - Self-close empty JSX elements (`<Component />` not `<Component></Component>`)
574
+ - Export only the component and whitelisted helpers (`loader`, `action`, `meta`, etc.) from route files
575
+ {{/if}}
576
+ - No unused template literals - convert to quotes if not interpolating
577
+ - Use `slice` instead of `substr`
578
+ - Flatten simple `if`/`else if` chains
579
+ - Kill useless `else` blocks when the `if` branch returns or throws
580
+ - Leverage `as const` for immutability where appropriate
581
+
582
+ ## Using Vite+
583
+
584
+ This project uses Vite+, a unified toolchain built on top of Vite, Rolldown, Vitest, tsdown, Oxlint, Oxfmt, and Vite Task. Vite+ wraps runtime management, package management, and frontend tooling in a single global CLI called `vp`.
585
+
586
+ ### Common Pitfalls
587
+
588
+ - **Using the package manager directly:** Do not use pnpm, npm, or Yarn directly. Vite+ handles all package manager operations.
589
+ - **Always use Vite commands to run tools:** Don't attempt to run `vp vitest` or `vp oxlint`. Use `vp test` and `vp lint` instead.
590
+ - **Running scripts:** Vite+ commands take precedence over `package.json` scripts. If there is a conflict, run scripts using `vp run <script>`.
591
+ - **Do not install Vitest, Oxlint, Oxfmt, or tsdown directly:** Vite+ wraps these tools.
592
+ - **Use vpx for one-off binaries:** Use `vpx` instead of `npx`, `pnpm dlx`, or `vp dlx`.
593
+ - **Import from `vite-plus`:** Instead of importing from `vite` or `vitest`, import from `vite-plus`. For example: `import { defineConfig } from 'vite-plus'` or `import { expect, test } from 'vite-plus/test'`.
594
+ - **Type-Aware Linting:** No need to install `oxlint-tsgolint`, `vp lint --type-aware` works out of the box.
595
+
596
+ ### Review Checklist
597
+
598
+ - [ ] Run `vp install` after pulling remote changes and before getting started
599
+ - [ ] Run `vp check` and `vp test` to validate changes
@@ -0,0 +1,24 @@
1
+ # {{repository}}
2
+
3
+ {{#if isSPA}}
4
+ A React Router single-page application{{#if hasConvex}} with Convex and TanStack Query{{/if}}.
5
+ {{/if}}
6
+ {{#if isSSR}}
7
+ A server-rendered React Router application{{#if hasConvex}} with Convex and TanStack Query{{/if}}.
8
+ {{/if}}
9
+ {{#if isRSC}}
10
+ A React Router application with React Server Components{{#if hasContentLayer}} and content-layer{{/if}}.
11
+ {{/if}}
12
+ {{#if isPackage}}
13
+ A TypeScript package.
14
+ {{/if}}
15
+
16
+ ## Getting Started
17
+
18
+ ```bash
19
+ {{#if isReactRouter}}
20
+ vp dev
21
+ {{else}}
22
+ vp run dev
23
+ {{/if}}
24
+ ```
@@ -0,0 +1,41 @@
1
+ {
2
+ {{#if isPackage}}
3
+ "name": "@{{owner}}/{{repository}}",
4
+ "version": "0.0.0",
5
+ {{else}}
6
+ "name": "{{repository}}",
7
+ "private": true,
8
+ {{/if}}
9
+ "type": "module",
10
+ {{#if isPackage}}
11
+ "exports": {
12
+ ".": "./dist/index.mjs",
13
+ "./package.json": "./package.json"
14
+ },
15
+ "types": "./dist/index.d.mts",
16
+ "files": [
17
+ "dist"
18
+ ],
19
+ "scripts": {
20
+ "prepublishOnly": "vp run build"
21
+ },
22
+ {{else}}
23
+ "scripts": {
24
+ "postinstall": "vpx react-router typegen"
25
+ },
26
+ {{/if}}
27
+ "packageManager": "pnpm@10.32.1",
28
+ "pnpm": {
29
+ {{#if isReactRouter}}
30
+ "onlyBuiltDependencies": [
31
+ "esbuild",
32
+ "sharp",
33
+ "workerd"
34
+ ],
35
+ {{/if}}
36
+ "overrides": {
37
+ "vite": "npm:@voidzero-dev/vite-plus-core@latest",
38
+ "vitest": "npm:@voidzero-dev/vite-plus-test@latest"
39
+ }
40
+ }
41
+ }