create-lumen 2.0.0-alpha.2 → 2.0.0-beta.2
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 +24 -6
- package/bin/cli.js +2 -1
- package/package.json +2 -2
- package/schema/lumen.config.v2.json +29 -0
- package/src/agent-context.js +71 -47
- package/src/cleanup.js +19 -1
- package/src/cli-args.js +131 -5
- package/src/configure.js +8 -4
- package/src/dependencies.js +46 -13
- package/src/engine/capabilities.js +71 -1
- package/src/engine/composition.js +334 -17
- package/src/engine/providers/base-provider.js +4 -4
- package/src/env.js +24 -6
- package/src/main.js +38 -15
- package/src/manifest/emit.js +14 -1
- package/src/manifest/schema.js +30 -0
- package/src/prompts.js +227 -23
- package/templates/bases/next/app-router/js/.provenance.json +19 -0
- package/templates/bases/next/app-router/js/README.md +36 -0
- package/templates/bases/next/app-router/js/jsconfig.json +7 -0
- package/templates/bases/next/app-router/js/next.config.mjs +6 -0
- package/templates/bases/next/app-router/js/package.json +15 -0
- package/templates/bases/next/app-router/js/public/file.svg +1 -0
- package/templates/bases/next/app-router/js/public/globe.svg +1 -0
- package/templates/bases/next/app-router/js/public/next.svg +1 -0
- package/templates/bases/next/app-router/js/public/vercel.svg +1 -0
- package/templates/bases/next/app-router/js/public/window.svg +1 -0
- package/templates/bases/next/app-router/js/src/app/api/hello/route.js +5 -0
- package/templates/bases/next/app-router/js/src/app/blog/[slug]/page.js +33 -0
- package/templates/bases/next/app-router/js/src/app/favicon.ico +0 -0
- package/templates/bases/next/app-router/js/src/app/globals.css +49 -0
- package/templates/bases/next/app-router/js/src/app/layout.js +25 -0
- package/templates/bases/next/app-router/js/src/app/loading.js +15 -0
- package/templates/bases/next/app-router/js/src/app/not-found.js +29 -0
- package/templates/bases/next/app-router/js/src/app/page.js +69 -0
- package/templates/bases/next/app-router/js/src/app/page.module.css +150 -0
- package/templates/bases/next/app-router/ts/.provenance.json +19 -0
- package/templates/bases/next/app-router/ts/README.md +36 -0
- package/templates/bases/next/app-router/ts/next.config.ts +7 -0
- package/templates/bases/next/app-router/ts/package.json +21 -0
- package/templates/bases/next/app-router/ts/public/file.svg +1 -0
- package/templates/bases/next/app-router/ts/public/globe.svg +1 -0
- package/templates/bases/next/app-router/ts/public/next.svg +1 -0
- package/templates/bases/next/app-router/ts/public/vercel.svg +1 -0
- package/templates/bases/next/app-router/ts/public/window.svg +1 -0
- package/templates/bases/next/app-router/ts/src/app/api/hello/route.ts +5 -0
- package/templates/bases/next/app-router/ts/src/app/blog/[slug]/page.tsx +38 -0
- package/templates/bases/next/app-router/ts/src/app/favicon.ico +0 -0
- package/templates/bases/next/app-router/ts/src/app/globals.css +49 -0
- package/templates/bases/next/app-router/ts/src/app/layout.tsx +30 -0
- package/templates/bases/next/app-router/ts/src/app/loading.tsx +15 -0
- package/templates/bases/next/app-router/ts/src/app/not-found.tsx +29 -0
- package/templates/bases/next/app-router/ts/src/app/page.module.css +150 -0
- package/templates/bases/next/app-router/ts/src/app/page.tsx +69 -0
- package/templates/bases/next/app-router/ts/tsconfig.json +34 -0
- package/templates/bases/next/pages-router/js/.provenance.json +19 -0
- package/templates/bases/next/pages-router/js/README.md +40 -0
- package/templates/bases/next/pages-router/js/jsconfig.json +7 -0
- package/templates/bases/next/pages-router/js/next.config.mjs +7 -0
- package/templates/bases/next/pages-router/js/package.json +15 -0
- package/templates/bases/next/pages-router/js/public/favicon.ico +0 -0
- package/templates/bases/next/pages-router/js/public/file.svg +1 -0
- package/templates/bases/next/pages-router/js/public/globe.svg +1 -0
- package/templates/bases/next/pages-router/js/public/next.svg +1 -0
- package/templates/bases/next/pages-router/js/public/vercel.svg +1 -0
- package/templates/bases/next/pages-router/js/public/window.svg +1 -0
- package/templates/bases/next/pages-router/js/src/pages/404.js +35 -0
- package/templates/bases/next/pages-router/js/src/pages/500.js +35 -0
- package/templates/bases/next/pages-router/js/src/pages/_app.js +20 -0
- package/templates/bases/next/pages-router/js/src/pages/_document.js +15 -0
- package/templates/bases/next/pages-router/js/src/pages/api/hello.js +5 -0
- package/templates/bases/next/pages-router/js/src/pages/blog/[slug].js +41 -0
- package/templates/bases/next/pages-router/js/src/pages/index.js +77 -0
- package/templates/bases/next/pages-router/js/src/styles/Home.module.css +149 -0
- package/templates/bases/next/pages-router/js/src/styles/globals.css +42 -0
- package/templates/bases/next/pages-router/ts/.provenance.json +19 -0
- package/templates/bases/next/pages-router/ts/README.md +40 -0
- package/templates/bases/next/pages-router/ts/next.config.ts +8 -0
- package/templates/bases/next/pages-router/ts/package.json +21 -0
- package/templates/bases/next/pages-router/ts/public/favicon.ico +0 -0
- package/templates/bases/next/pages-router/ts/public/file.svg +1 -0
- package/templates/bases/next/pages-router/ts/public/globe.svg +1 -0
- package/templates/bases/next/pages-router/ts/public/next.svg +1 -0
- package/templates/bases/next/pages-router/ts/public/vercel.svg +1 -0
- package/templates/bases/next/pages-router/ts/public/window.svg +1 -0
- package/templates/bases/next/pages-router/ts/src/pages/404.tsx +35 -0
- package/templates/bases/next/pages-router/ts/src/pages/500.tsx +35 -0
- package/templates/bases/next/pages-router/ts/src/pages/_app.tsx +21 -0
- package/templates/bases/next/pages-router/ts/src/pages/_document.tsx +15 -0
- package/templates/bases/next/pages-router/ts/src/pages/api/hello.ts +13 -0
- package/templates/bases/next/pages-router/ts/src/pages/blog/[slug].tsx +46 -0
- package/templates/bases/next/pages-router/ts/src/pages/index.tsx +77 -0
- package/templates/bases/next/pages-router/ts/src/styles/Home.module.css +149 -0
- package/templates/bases/next/pages-router/ts/src/styles/globals.css +42 -0
- package/templates/bases/next/pages-router/ts/tsconfig.json +29 -0
- package/templates/conditional/database/prisma/db.js +14 -0
- package/templates/conditional/database/prisma/db.ts +16 -0
- package/templates/conditional/database/prisma/schema.prisma +9 -0
- package/templates/conditional/database/prisma/seed.js +16 -0
- package/templates/conditional/database/prisma/seed.ts +16 -0
- package/templates/conditional/docker/.dockerignore +10 -0
- package/templates/conditional/docker/Dockerfile +57 -0
- package/templates/conditional/docker/docker-compose.yml +35 -0
- package/templates/conditional/linting/biome/biome.json +31 -0
- package/templates/conditional/next/config/cloudflare/next.config.mjs +6 -0
- package/templates/conditional/next/config/cloudflare/next.config.ts +7 -0
- package/templates/conditional/next/config/node/next.config.mjs +6 -0
- package/templates/conditional/next/config/node/next.config.ts +7 -0
- package/templates/conditional/next/config/static/next.config.mjs +6 -0
- package/templates/conditional/next/config/static/next.config.ts +7 -0
- package/templates/conditional/next/config/turbopack/next.config.mjs +7 -0
- package/templates/conditional/next/config/turbopack/next.config.ts +8 -0
- package/templates/conditional/next/config/vercel/next.config.mjs +6 -0
- package/templates/conditional/next/config/vercel/next.config.ts +7 -0
- package/templates/conditional/next/config/webpack/next.config.mjs +9 -0
- package/templates/conditional/next/config/webpack/next.config.ts +10 -0
- package/templates/conditional/next/font/BUILD_SIZE_NOTE.md +17 -0
- package/templates/conditional/next/font/app-router/src/app/layout.js +25 -0
- package/templates/conditional/next/font/app-router/src/app/layout.tsx +30 -0
- package/templates/conditional/next/font/pages-router/src/pages/_app.js +20 -0
- package/templates/conditional/next/font/pages-router/src/pages/_app.tsx +21 -0
- package/templates/conditional/next/font/pages-router/src/pages/_document.js +18 -0
- package/templates/conditional/next/font/pages-router/src/pages/_document.tsx +18 -0
- package/templates/conditional/next/font/src/app/layout.js +25 -0
- package/templates/conditional/next/font/src/app/layout.tsx +30 -0
- package/templates/conditional/next/font/src/lib/fonts.js +11 -0
- package/templates/conditional/next/font/src/lib/fonts.ts +11 -0
- package/templates/conditional/next/font/src/pages/_app.js +20 -0
- package/templates/conditional/next/font/src/pages/_app.tsx +21 -0
- package/templates/conditional/next/font/src/pages/_document.js +18 -0
- package/templates/conditional/next/font/src/pages/_document.tsx +18 -0
- package/templates/conditional/next/image/BUILD_SIZE_NOTE.md +15 -0
- package/templates/conditional/next/image/app-router/src/components/ImageExample.jsx +34 -0
- package/templates/conditional/next/image/app-router/src/components/ImageExample.tsx +44 -0
- package/templates/conditional/next/image/pages-router/src/components/ImageExample.jsx +34 -0
- package/templates/conditional/next/image/pages-router/src/components/ImageExample.tsx +44 -0
- package/templates/conditional/next/image/src/components/ImageExample.jsx +44 -0
- package/templates/conditional/next/image/src/components/ImageExample.tsx +54 -0
- package/templates/conditional/next/image/src/components/image/index.js +1 -0
- package/templates/conditional/next/image/src/components/image/index.ts +2 -0
- package/templates/conditional/next/route-groups/js/src/app/(admin)/layout.jsx +23 -0
- package/templates/conditional/next/route-groups/js/src/app/(auth)/layout.jsx +17 -0
- package/templates/conditional/next/route-groups/js/src/app/(private)/layout.jsx +27 -0
- package/templates/conditional/next/route-groups/js/src/app/(public)/layout.jsx +28 -0
- package/templates/conditional/next/route-groups/js/src/app/(public)/page.jsx +38 -0
- package/templates/conditional/next/route-groups/js/src/middleware.js +48 -0
- package/templates/conditional/next/route-groups/ts/src/app/(admin)/layout.tsx +28 -0
- package/templates/conditional/next/route-groups/ts/src/app/(auth)/layout.tsx +22 -0
- package/templates/conditional/next/route-groups/ts/src/app/(private)/layout.tsx +32 -0
- package/templates/conditional/next/route-groups/ts/src/app/(public)/layout.tsx +33 -0
- package/templates/conditional/next/route-groups/ts/src/app/(public)/page.tsx +38 -0
- package/templates/conditional/next/route-groups/ts/src/middleware.ts +49 -0
- package/templates/conditional/next/server-components/README.md +13 -0
- package/templates/conditional/next/server-components/eslint.config.mjs +47 -0
- package/templates/conditional/next/server-components/rules/no-server-imports-in-client.js +89 -0
- package/templates/conditional/next/server-components/rules/no-server-imports-in-client.mjs +88 -0
- package/templates/conditional/next/server-components/src/components/boundaries/ClientCounter.jsx +48 -0
- package/templates/conditional/next/server-components/src/components/boundaries/ClientCounter.tsx +53 -0
- package/templates/conditional/next/server-components/src/components/boundaries/ServerCard.jsx +41 -0
- package/templates/conditional/next/server-components/src/components/boundaries/ServerCard.tsx +46 -0
- package/templates/conditional/next/server-components/src/components/boundaries/index.js +2 -0
- package/templates/conditional/next/server-components/src/components/boundaries/index.ts +4 -0
- package/templates/conditional/testing/jest/next/app-router/src/__tests__/page.test.jsx +10 -0
- package/templates/conditional/testing/jest/next/app-router/src/__tests__/page.test.tsx +10 -0
- package/templates/conditional/testing/jest/next/jest.config.mjs +20 -0
- package/templates/conditional/testing/jest/next/jest.config.ts +20 -0
- package/templates/conditional/testing/jest/next/pages-router/src/__tests__/index.test.jsx +10 -0
- package/templates/conditional/testing/jest/next/pages-router/src/__tests__/index.test.tsx +10 -0
- package/templates/conditional/testing/vitest/next/app-router/src/__tests__/page.test.jsx +11 -0
- package/templates/conditional/testing/vitest/next/app-router/src/__tests__/page.test.tsx +11 -0
- package/templates/conditional/testing/vitest/next/pages-router/src/__tests__/index.test.jsx +11 -0
- package/templates/conditional/testing/vitest/next/pages-router/src/__tests__/index.test.tsx +11 -0
- package/templates/conditional/testing/vitest/next/vitest.config.js +16 -0
- package/templates/conditional/testing/vitest/next/vitest.config.ts +16 -0
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# create-lumen
|
|
2
2
|
|
|
3
|
-
A scaffolder that generates production-ready React + Vite projects (feature-based, type-based, hybrid, or none
|
|
3
|
+
A scaffolder that generates production-ready **React + Vite** or **Next.js** projects (App Router / Pages Router), with a choice of architecture: feature-based, type-based, hybrid, or none.
|
|
4
4
|
|
|
5
5
|
Note: this repository is the CLI scaffolder itself, not a generated app. Generated projects live in the target folder you create.
|
|
6
6
|
|
|
@@ -14,6 +14,8 @@ cd my-app
|
|
|
14
14
|
npm run dev
|
|
15
15
|
```
|
|
16
16
|
|
|
17
|
+
> The v2 line (React + Vite + Next.js, manifest v2) is available on the `beta` dist-tag while v2 is in prerelease: `npm create lumen@beta my-app`.
|
|
18
|
+
|
|
17
19
|
### Non-interactive flags
|
|
18
20
|
|
|
19
21
|
Quick Setup (TypeScript + Tailwind + Feature-based + Router + ESLint + Prettier + Vitest), no prompts:
|
|
@@ -26,20 +28,28 @@ npm create lumen -- -y my-app
|
|
|
26
28
|
npm create lumen my-app -- --yes
|
|
27
29
|
```
|
|
28
30
|
|
|
29
|
-
Scaffold using a template preset (`react-ts`, `react-js`, `react-type-ts`, `react-type-js`):
|
|
31
|
+
Scaffold using a template preset (`react-ts`, `react-js`, `react-type-ts`, `react-type-js`, `next-ts`, `next-js`, `next-pages-ts`, `next-pages-js`):
|
|
30
32
|
|
|
31
33
|
```bash
|
|
32
34
|
npm create lumen my-app -- --template react-ts
|
|
33
35
|
# or
|
|
34
|
-
npm create lumen my-app -- -t
|
|
36
|
+
npm create lumen my-app -- -t next-ts
|
|
35
37
|
```
|
|
36
38
|
|
|
37
39
|
Choose framework and architecture directly:
|
|
38
40
|
|
|
39
41
|
```bash
|
|
40
42
|
npm create lumen my-app -- --framework react --arch none
|
|
43
|
+
|
|
44
|
+
# Next.js — the router is asked interactively (App Router recommended):
|
|
45
|
+
npm create lumen my-app -- -f next
|
|
46
|
+
# ...or pin the variant outright:
|
|
47
|
+
npm create lumen my-app -- -f next:app-router
|
|
48
|
+
npm create lumen my-app -- -f next:pages-router
|
|
41
49
|
```
|
|
42
50
|
|
|
51
|
+
Interactively, the framework is the first prompt (React + Vite / Next.js), followed by a router step when Next.js is selected; every later option gates on that choice.
|
|
52
|
+
|
|
43
53
|
Drive scaffolding directly from a manifest file or inline JSON (manifest v2):
|
|
44
54
|
|
|
45
55
|
```bash
|
|
@@ -70,20 +80,25 @@ create-lumen my-app # after npm link
|
|
|
70
80
|
|
|
71
81
|
## Features
|
|
72
82
|
|
|
83
|
+
- **Two frameworks:** React + Vite (SPA) or Next.js (App Router / Pages Router) — selected interactively or via `-f`
|
|
73
84
|
- Architecture choice: feature-based, type-based, hybrid, or none (per-framework availability)
|
|
74
85
|
- TypeScript or JavaScript output
|
|
75
86
|
- CSS frameworks: Tailwind CSS v4 (CSS-first `@theme`, v3 dropped) or none
|
|
87
|
+
- **UI kit: shadcn/ui** (gated to Tailwind; primitives + `cn` helper land in the mapped `paths.ui`)
|
|
76
88
|
- Optional state management: Zustand or Redux Toolkit
|
|
77
|
-
- Optional router (React Router)
|
|
78
|
-
- Optional testing: Vitest or Jest
|
|
89
|
+
- Optional router (React Router — Vite only; Next.js uses filesystem routing)
|
|
90
|
+
- Optional testing: Vitest or Jest (Next.js defaults to none until a Next-native overlay is verified)
|
|
91
|
+
- **Next.js-native overlays:** server/client boundary demos + `no-server-imports-in-client` ESLint rule, `next/font` (Geist), `next/image`, filesystem routing examples, route handlers (`route.ts`) / API routes (`pages/api`)
|
|
92
|
+
- **Next.js options:** `bundler: turbopack|webpack`, `adapter: node|vercel|cloudflare|static` (`next.config` overlays), `reactCompiler`, `linter: biome` — all validated against the capability model
|
|
79
93
|
- Linting: ESLint (default), Oxlint, or Biome
|
|
80
94
|
- Code formatter: Prettier or Oxfmt (chosen after the linter; `eslint-config-prettier` is auto-wired for the ESLint + Prettier combo)
|
|
81
|
-
- Manifest v2: Emits a deterministic `lumen.config.json`
|
|
95
|
+
- Manifest v2: Emits a deterministic `lumen.config.json` validated against JSON Schema Draft 2020-12, including architecture validation modes and harness command configuration.
|
|
82
96
|
- Explicit path mapping: Target directories declared for `lumen-cli` code generation
|
|
83
97
|
- Optional Axios setup and icon libraries
|
|
84
98
|
- Auto `git init`, generated `README` (with a project description reflecting the chosen tooling) and `LICENSE`
|
|
85
99
|
- `.env.example` scaffolded, and the generated `.gitignore` excludes env files (`.env`, `.env.*`) while keeping `.env.example`
|
|
86
100
|
- `@/` path alias configured (`tsconfig`/`jsconfig` + Vite)
|
|
101
|
+
- **Agent docs:** optional `.lumen/` context directory (project, architecture, conventions) via prompt or `--agent-docs`
|
|
87
102
|
|
|
88
103
|
Your selected options are cached and offered as defaults on subsequent runs.
|
|
89
104
|
|
|
@@ -206,6 +221,9 @@ The manifest now includes:
|
|
|
206
221
|
### Ecosystem Documentation & Contracts
|
|
207
222
|
|
|
208
223
|
- **[Documentation Index](./docs/README.md):** Complete overview of specifications, ADRs, and developer guides.
|
|
224
|
+
- **[Next.js App Router Guide](./docs/next-app-router.md):** App Router scaffold, filesystem routing, route handlers, server/client boundaries, `next/font` and `next/image`.
|
|
225
|
+
- **[Next.js Pages Router Guide](./docs/next-pages-router.md):** Pages Router scaffold, API routes, `_app`/`_document` wiring.
|
|
226
|
+
- **[Deployment Adapters Guide](./docs/next-adapters.md):** Adapter matrix (Node, Vercel, Cloudflare, static), bundler choice and Biome setup for Next.js.
|
|
209
227
|
- **[Generator Engine & Capabilities Guide (M2)](./docs/engine-capabilities.md):** Capability model, template composition, shadcn/ui on Tailwind v4, architecture presets, and Bootstrap removal.
|
|
210
228
|
- **[Manifest v2 Overview](./docs/manifest-v2.md):** Specification of the nested schema, validation behavior, and the new harness and architecture validation fields.
|
|
211
229
|
- **[Shared Contract: `create-lumen` ↔ `lumen-cli`](./docs/contracts/manifest-v2-contract.md):** The agreed contract for path mapping, barrel conventions, version negotiation, and the harness/configuration interface.
|
package/bin/cli.js
CHANGED
|
@@ -25,7 +25,7 @@ console.log(
|
|
|
25
25
|
chalk.bold.cyan("\n ✦ LUMEN") +
|
|
26
26
|
chalk.gray(` v${version}`) +
|
|
27
27
|
chalk.gray(
|
|
28
|
-
"\n Scaffold a React
|
|
28
|
+
"\n Scaffold a React, Vite, or Next.js project with architecture choice\n"
|
|
29
29
|
)
|
|
30
30
|
);
|
|
31
31
|
|
|
@@ -36,6 +36,7 @@ main({
|
|
|
36
36
|
projectName: cliArgs.projectName,
|
|
37
37
|
manifest: cliArgs.manifest,
|
|
38
38
|
template: cliArgs.template,
|
|
39
|
+
framework: cliArgs.framework,
|
|
39
40
|
arch: cliArgs.arch,
|
|
40
41
|
}).catch((e) => {
|
|
41
42
|
console.error(chalk.red("\nError:"), e.message || e);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-lumen",
|
|
3
|
-
"version": "2.0.0-
|
|
3
|
+
"version": "2.0.0-beta.2",
|
|
4
4
|
"description": "Scaffold a React + Vite project with architecture choice: feature-based, type-based, hybrid, or none.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
},
|
|
9
9
|
"repository": {
|
|
10
10
|
"type": "git",
|
|
11
|
-
"url": "https://github.com/
|
|
11
|
+
"url": "https://github.com/strophix-software/create-lumen"
|
|
12
12
|
},
|
|
13
13
|
"files": [
|
|
14
14
|
"bin/",
|
|
@@ -247,6 +247,35 @@
|
|
|
247
247
|
],
|
|
248
248
|
"additionalProperties": false
|
|
249
249
|
},
|
|
250
|
+
"database": {
|
|
251
|
+
"type": "object",
|
|
252
|
+
"properties": {
|
|
253
|
+
"orm": {
|
|
254
|
+
"type": "string",
|
|
255
|
+
"enum": [
|
|
256
|
+
"prisma",
|
|
257
|
+
"none"
|
|
258
|
+
]
|
|
259
|
+
},
|
|
260
|
+
"provider": {
|
|
261
|
+
"default": "postgresql",
|
|
262
|
+
"type": "string",
|
|
263
|
+
"enum": [
|
|
264
|
+
"postgresql"
|
|
265
|
+
]
|
|
266
|
+
}
|
|
267
|
+
},
|
|
268
|
+
"required": [
|
|
269
|
+
"orm"
|
|
270
|
+
],
|
|
271
|
+
"additionalProperties": false
|
|
272
|
+
},
|
|
273
|
+
"docker": {
|
|
274
|
+
"type": "boolean"
|
|
275
|
+
},
|
|
276
|
+
"routeGroups": {
|
|
277
|
+
"type": "boolean"
|
|
278
|
+
},
|
|
250
279
|
"reactCompiler": {
|
|
251
280
|
"type": "boolean"
|
|
252
281
|
},
|
package/src/agent-context.js
CHANGED
|
@@ -19,17 +19,19 @@ function resolveManifest(manifestOrResponses) {
|
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
/**
|
|
22
|
-
* Render the
|
|
23
|
-
*
|
|
22
|
+
* Render the root AGENTS.md document from a manifest.
|
|
23
|
+
* Single Source of Truth (SSOT): references `lumen.config.json` directly.
|
|
24
|
+
* Byte-deterministic: derived only from stable manifest fields (no timestamps).
|
|
24
25
|
* @param {object} manifest - validated manifest v2
|
|
25
26
|
* @returns {string}
|
|
26
27
|
*/
|
|
27
|
-
export function
|
|
28
|
+
export function renderAgentsMd(manifest) {
|
|
28
29
|
const preset = manifest.architecture?.preset ?? manifest.architecture?.type ?? "feature-based";
|
|
29
30
|
const validation = manifest.architecture?.validation ?? "strict";
|
|
30
31
|
const paths = manifest.paths ?? {};
|
|
31
32
|
const harness = manifest.harness ?? { commands: [] };
|
|
32
33
|
const barrelFile = manifest.tooling?.language === "js" ? "index.js" : "index.ts";
|
|
34
|
+
const isNext = manifest.framework?.name === "next";
|
|
33
35
|
|
|
34
36
|
const pathLines = Object.entries(paths)
|
|
35
37
|
.map(([key, value]) => `- ${key}: \`${value}\``)
|
|
@@ -41,74 +43,96 @@ export function renderConventions(manifest) {
|
|
|
41
43
|
|
|
42
44
|
const harnessSummary = (harness.commands ?? []).map((c) => `\`${c.command}\``).join(", ");
|
|
43
45
|
|
|
46
|
+
let nextSection = "";
|
|
47
|
+
if (isNext) {
|
|
48
|
+
nextSection =
|
|
49
|
+
`<!-- BEGIN:nextjs-agent-rules -->\n` +
|
|
50
|
+
`## This is NOT the Next.js you know\n` +
|
|
51
|
+
`This version has breaking changes — APIs, conventions, and file structure may all differ from your training data. Read the relevant guide in node_modules/next/dist/docs/ (resolved from this file's directory...) before writing any code. Heed deprecation notices.\n` +
|
|
52
|
+
`<!-- END:nextjs-agent-rules -->\n` +
|
|
53
|
+
`\n` +
|
|
54
|
+
`### Next.js Component Rules\n` +
|
|
55
|
+
`\n` +
|
|
56
|
+
`- By default, all components in the App Router are React Server Components (RSC).\n` +
|
|
57
|
+
`- Use the \`"use client"\` directive only at the top of files that require browser APIs, React state, or event handlers.\n` +
|
|
58
|
+
`- Never import server-only modules or secrets into client components.\n` +
|
|
59
|
+
`\n`;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const uiPath = paths.ui ?? "@/components/ui";
|
|
63
|
+
const componentsPath = paths.components ?? "@/components";
|
|
64
|
+
|
|
44
65
|
return (
|
|
45
|
-
`#
|
|
66
|
+
`# AGENTS.md\n` +
|
|
67
|
+
`\n` +
|
|
68
|
+
`> **Single Source of Truth (SSOT)**: Project configuration, paths, and harness definitions are defined in \`lumen.config.json\`. Consult \`lumen.config.json\` for the authoritative settings.\n` +
|
|
46
69
|
`\n` +
|
|
47
|
-
|
|
70
|
+
`${nextSection}` +
|
|
71
|
+
`## Architecture & Conventions\n` +
|
|
48
72
|
`\n` +
|
|
49
|
-
|
|
73
|
+
`- **Architecture Preset**: \`${preset}\`\n` +
|
|
74
|
+
`- **Validation Mode**: \`${validation}\` (${VALIDATION_NOTES[validation] ?? VALIDATION_NOTES.strict})\n` +
|
|
75
|
+
`- **Paths Resolution**: Resolve file locations via \`paths.*\` defined in \`lumen.config.json\` — never hardcode arbitrary alternative locations.\n` +
|
|
76
|
+
`- **Feature Barrels**: Every feature directory (under \`${paths.features ?? "src/features"}/<feature>/\`) must re-export its public API via \`${barrelFile}\`.\n` +
|
|
77
|
+
`- **Shared Imports**: Import shared utilities via \`@/shared\` (plus path aliases configured in tsconfig/jsconfig).\n` +
|
|
50
78
|
`\n` +
|
|
51
|
-
|
|
52
|
-
`- Feature barrel: every \`src/features/<feature>/\` must re-export its public API via \`${barrelFile}\`.\n` +
|
|
53
|
-
`- Shared code: import shared utilities via \`@/shared\` (plus the \`@/...\` aliases from tsconfig/jsconfig).\n` +
|
|
54
|
-
`- Harness: before finishing, run each command in \`.lumen/project.json\` \`harness.commands\` in order (${harnessSummary}).\n` +
|
|
55
|
-
`- Validation mode \`${validation}\`: ${VALIDATION_NOTES[validation] ?? VALIDATION_NOTES.strict}\n` +
|
|
79
|
+
`## Component Taxonomy\n` +
|
|
56
80
|
`\n` +
|
|
57
|
-
|
|
81
|
+
`Components must be organized strictly according to \`lumen.config.json\` path mappings:\n` +
|
|
82
|
+
`- **Atomic UI Primitives**: Base atoms and UI primitives (e.g. shadcn components) belong in \`paths.ui\` (\`${uiPath}\`).\n` +
|
|
83
|
+
`- **Compound Components**: Shared composite components belong in \`paths.components\` (\`${componentsPath}\`).\n` +
|
|
84
|
+
`- **Feature Components**: Domain-specific components belong inside their respective feature folder (e.g. \`${paths.features ?? "src/features"}/<feature>/components\`).\n` +
|
|
85
|
+
`\n` +
|
|
86
|
+
`Lumen CLI and AI agents must place primitive UI atoms under \`paths.ui\`, global compound components under \`paths.components\`, and feature-scoped components in their respective feature folders.\n` +
|
|
87
|
+
`\n` +
|
|
88
|
+
`## Path Mappings\n` +
|
|
58
89
|
`\n` +
|
|
59
90
|
`${pathLines}\n` +
|
|
60
91
|
`\n` +
|
|
61
|
-
`## Harness\n` +
|
|
92
|
+
`## Validation Harness\n` +
|
|
93
|
+
`\n` +
|
|
94
|
+
`Before finishing work, run each harness command defined in \`lumen.config.json\` \`harness.commands\` in order (${harnessSummary}):\n` +
|
|
62
95
|
`\n` +
|
|
63
96
|
`${harnessLines}\n`
|
|
64
97
|
);
|
|
65
98
|
}
|
|
66
99
|
|
|
67
100
|
/**
|
|
68
|
-
*
|
|
69
|
-
*
|
|
101
|
+
* Backward compatibility alias for renderAgentsMd.
|
|
102
|
+
* @param {object} manifest - validated manifest v2
|
|
103
|
+
* @returns {string}
|
|
104
|
+
*/
|
|
105
|
+
export function renderConventions(manifest) {
|
|
106
|
+
return renderAgentsMd(manifest);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Build deterministic agent context metadata.
|
|
111
|
+
* Retained for backwards compatibility with tests and callers.
|
|
70
112
|
* @param {object} manifest - validated manifest v2
|
|
71
|
-
* @returns {{
|
|
113
|
+
* @returns {{ agentsMd: string, conventionsMd: string }}
|
|
72
114
|
*/
|
|
73
115
|
export function buildAgentContext(manifest) {
|
|
74
|
-
const
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
tooling: manifest.tooling,
|
|
79
|
-
harness: manifest.harness,
|
|
80
|
-
docs: manifest.docs,
|
|
81
|
-
};
|
|
82
|
-
const architecture = {
|
|
83
|
-
preset: manifest.architecture?.preset ?? manifest.architecture?.type,
|
|
84
|
-
validation: manifest.architecture?.validation ?? "strict",
|
|
85
|
-
paths: manifest.paths,
|
|
116
|
+
const agentsMd = renderAgentsMd(manifest);
|
|
117
|
+
return {
|
|
118
|
+
agentsMd,
|
|
119
|
+
conventionsMd: agentsMd,
|
|
86
120
|
};
|
|
87
|
-
const conventionsMd = renderConventions(manifest);
|
|
88
|
-
return { project, architecture, conventionsMd };
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
function writeJson(filePath, value) {
|
|
92
|
-
return fsp.writeFile(filePath, JSON.stringify(value, null, 2) + "\n", "utf8");
|
|
93
121
|
}
|
|
94
122
|
|
|
95
123
|
/**
|
|
96
|
-
* Emit
|
|
97
|
-
* Accepts either a validated manifest v2 or scaffolder responses
|
|
98
|
-
*
|
|
99
|
-
*
|
|
100
|
-
* with 2-space indent + trailing newline. Byte-deterministic (no timestamps).
|
|
124
|
+
* Emit AGENTS.md directly into project root.
|
|
125
|
+
* Accepts either a validated manifest v2 or scaffolder responses.
|
|
126
|
+
* Writes ${projectPath}/AGENTS.md with trailing newline. Byte-deterministic.
|
|
127
|
+
* Does not emit duplicate JSON files or .lumen/ directory.
|
|
101
128
|
* @param {string} projectPath - absolute path to scaffolded project
|
|
102
129
|
* @param {object} manifestOrResponses
|
|
103
|
-
* @returns {Promise<string>} absolute path to
|
|
130
|
+
* @returns {Promise<string>} absolute path to AGENTS.md
|
|
104
131
|
*/
|
|
105
132
|
export async function emitAgentContext(projectPath, manifestOrResponses) {
|
|
106
133
|
const manifest = resolveManifest(manifestOrResponses);
|
|
107
|
-
const
|
|
108
|
-
const
|
|
109
|
-
await fsp.
|
|
110
|
-
|
|
111
|
-
await writeJson(path.join(dir, "architecture.json"), architecture);
|
|
112
|
-
await fsp.writeFile(path.join(dir, "conventions.md"), conventionsMd, "utf8");
|
|
113
|
-
return dir;
|
|
134
|
+
const content = renderAgentsMd(manifest);
|
|
135
|
+
const filePath = path.join(projectPath, "AGENTS.md");
|
|
136
|
+
await fsp.writeFile(filePath, content, "utf8");
|
|
137
|
+
return filePath;
|
|
114
138
|
}
|
package/src/cleanup.js
CHANGED
|
@@ -1,7 +1,25 @@
|
|
|
1
1
|
import { deleteIfExists } from "@/utils/fs.js";
|
|
2
|
+
import { promises as fsp } from "fs";
|
|
3
|
+
import path from "path";
|
|
2
4
|
|
|
3
|
-
export async function cleanupBoilerplate(projectPath) {
|
|
5
|
+
export async function cleanupBoilerplate(projectPath, responses = {}) {
|
|
4
6
|
await deleteIfExists(`${projectPath}/public`);
|
|
5
7
|
await deleteIfExists(`${projectPath}/src/assets`);
|
|
6
8
|
await deleteIfExists(`${projectPath}/.oxlintrc.json`);
|
|
9
|
+
await deleteIfExists(`${projectPath}/.provenance.json`);
|
|
10
|
+
|
|
11
|
+
const isNext =
|
|
12
|
+
responses?.framework?.name === "next" ||
|
|
13
|
+
responses?.frameworkName === "next" ||
|
|
14
|
+
(await fsp.stat(path.join(projectPath, "next.config.ts")).catch(() => false)) ||
|
|
15
|
+
(await fsp.stat(path.join(projectPath, "next.config.mjs")).catch(() => false)) ||
|
|
16
|
+
(await fsp.stat(path.join(projectPath, "next.config.js")).catch(() => false));
|
|
17
|
+
|
|
18
|
+
if (isNext) {
|
|
19
|
+
await deleteIfExists(`${projectPath}/src/app/App.tsx`);
|
|
20
|
+
await deleteIfExists(`${projectPath}/src/app/App.jsx`);
|
|
21
|
+
await deleteIfExists(`${projectPath}/src/app/hooks`);
|
|
22
|
+
await deleteIfExists(`${projectPath}/src/app/contexts`);
|
|
23
|
+
await deleteIfExists(`${projectPath}/src/app/providers`);
|
|
24
|
+
}
|
|
7
25
|
}
|
package/src/cli-args.js
CHANGED
|
@@ -6,7 +6,6 @@ import { parseManifest } from "./manifest/schema.js";
|
|
|
6
6
|
import {
|
|
7
7
|
normalizeFramework,
|
|
8
8
|
isPromptVisible,
|
|
9
|
-
getDefaultResponses,
|
|
10
9
|
} from "./engine/capabilities.js";
|
|
11
10
|
|
|
12
11
|
export const PRESETS = {
|
|
@@ -180,6 +179,78 @@ export const PRESETS = {
|
|
|
180
179
|
gitInit: true,
|
|
181
180
|
readme: true,
|
|
182
181
|
},
|
|
182
|
+
"next-ts": {
|
|
183
|
+
frameworkName: "next",
|
|
184
|
+
frameworkVariant: "app-router",
|
|
185
|
+
architecture: "feature-based",
|
|
186
|
+
language: "ts",
|
|
187
|
+
cssFramework: "tailwind",
|
|
188
|
+
testing: "vitest",
|
|
189
|
+
router: false,
|
|
190
|
+
bundler: "turbopack",
|
|
191
|
+
stateManagement: "none",
|
|
192
|
+
iconLibrary: "none",
|
|
193
|
+
apiClient: "none",
|
|
194
|
+
linter: "eslint",
|
|
195
|
+
formatter: "prettier",
|
|
196
|
+
docsLanguage: "en",
|
|
197
|
+
gitInit: true,
|
|
198
|
+
readme: true,
|
|
199
|
+
},
|
|
200
|
+
"next-js": {
|
|
201
|
+
frameworkName: "next",
|
|
202
|
+
frameworkVariant: "app-router",
|
|
203
|
+
architecture: "feature-based",
|
|
204
|
+
language: "js",
|
|
205
|
+
cssFramework: "tailwind",
|
|
206
|
+
testing: "vitest",
|
|
207
|
+
router: false,
|
|
208
|
+
bundler: "turbopack",
|
|
209
|
+
stateManagement: "none",
|
|
210
|
+
iconLibrary: "none",
|
|
211
|
+
apiClient: "none",
|
|
212
|
+
linter: "eslint",
|
|
213
|
+
formatter: "prettier",
|
|
214
|
+
docsLanguage: "en",
|
|
215
|
+
gitInit: true,
|
|
216
|
+
readme: true,
|
|
217
|
+
},
|
|
218
|
+
"next-pages-ts": {
|
|
219
|
+
frameworkName: "next",
|
|
220
|
+
frameworkVariant: "pages-router",
|
|
221
|
+
architecture: "feature-based",
|
|
222
|
+
language: "ts",
|
|
223
|
+
cssFramework: "tailwind",
|
|
224
|
+
testing: "vitest",
|
|
225
|
+
router: false,
|
|
226
|
+
bundler: "turbopack",
|
|
227
|
+
stateManagement: "none",
|
|
228
|
+
iconLibrary: "none",
|
|
229
|
+
apiClient: "none",
|
|
230
|
+
linter: "eslint",
|
|
231
|
+
formatter: "prettier",
|
|
232
|
+
docsLanguage: "en",
|
|
233
|
+
gitInit: true,
|
|
234
|
+
readme: true,
|
|
235
|
+
},
|
|
236
|
+
"next-pages-js": {
|
|
237
|
+
frameworkName: "next",
|
|
238
|
+
frameworkVariant: "pages-router",
|
|
239
|
+
architecture: "feature-based",
|
|
240
|
+
language: "js",
|
|
241
|
+
cssFramework: "tailwind",
|
|
242
|
+
testing: "vitest",
|
|
243
|
+
router: false,
|
|
244
|
+
bundler: "turbopack",
|
|
245
|
+
stateManagement: "none",
|
|
246
|
+
iconLibrary: "none",
|
|
247
|
+
apiClient: "none",
|
|
248
|
+
linter: "eslint",
|
|
249
|
+
formatter: "prettier",
|
|
250
|
+
docsLanguage: "en",
|
|
251
|
+
gitInit: true,
|
|
252
|
+
readme: true,
|
|
253
|
+
},
|
|
183
254
|
};
|
|
184
255
|
|
|
185
256
|
/**
|
|
@@ -207,6 +278,18 @@ export function parseCliArgs(argv = process.argv.slice(2)) {
|
|
|
207
278
|
framework: { type: "string", short: "f" },
|
|
208
279
|
arch: { type: "string", short: "a" },
|
|
209
280
|
architecture: { type: "string" },
|
|
281
|
+
bundler: { type: "string", short: "b" },
|
|
282
|
+
adapter: { type: "string" },
|
|
283
|
+
database: { type: "string" },
|
|
284
|
+
db: { type: "string" },
|
|
285
|
+
"react-compiler": { type: "boolean" },
|
|
286
|
+
reactCompiler: { type: "boolean" },
|
|
287
|
+
"route-groups": { type: "boolean" },
|
|
288
|
+
routeGroups: { type: "boolean" },
|
|
289
|
+
"agent-docs": { type: "boolean" },
|
|
290
|
+
agentDocs: { type: "boolean" },
|
|
291
|
+
docker: { type: "boolean" },
|
|
292
|
+
linter: { type: "string" },
|
|
210
293
|
help: { type: "boolean", short: "h", default: false },
|
|
211
294
|
version: { type: "boolean", short: "v", default: false },
|
|
212
295
|
},
|
|
@@ -223,6 +306,18 @@ export function parseCliArgs(argv = process.argv.slice(2)) {
|
|
|
223
306
|
else if (arch === "component" || arch === "component-based" || arch === "type") arch = "type-based";
|
|
224
307
|
}
|
|
225
308
|
|
|
309
|
+
const bundler = values.bundler ? values.bundler.trim().toLowerCase() : null;
|
|
310
|
+
const adapter = values.adapter ? values.adapter.trim().toLowerCase() : null;
|
|
311
|
+
let database = values.database || values.db || null;
|
|
312
|
+
if (database) {
|
|
313
|
+
database = database.trim().toLowerCase();
|
|
314
|
+
}
|
|
315
|
+
const reactCompiler = values["react-compiler"] ?? values.reactCompiler ?? null;
|
|
316
|
+
const routeGroups = values["route-groups"] ?? values.routeGroups ?? null;
|
|
317
|
+
const agentDocs = values["agent-docs"] ?? values.agentDocs ?? null;
|
|
318
|
+
const docker = Boolean(values.docker);
|
|
319
|
+
const linter = values.linter ? values.linter.trim().toLowerCase() : null;
|
|
320
|
+
|
|
226
321
|
return {
|
|
227
322
|
projectName,
|
|
228
323
|
quickSetup: Boolean(values.yes),
|
|
@@ -230,6 +325,14 @@ export function parseCliArgs(argv = process.argv.slice(2)) {
|
|
|
230
325
|
template: values.template || null,
|
|
231
326
|
framework: values.framework ? values.framework.trim() : null,
|
|
232
327
|
arch,
|
|
328
|
+
bundler,
|
|
329
|
+
adapter,
|
|
330
|
+
database,
|
|
331
|
+
reactCompiler,
|
|
332
|
+
routeGroups,
|
|
333
|
+
agentDocs,
|
|
334
|
+
docker,
|
|
335
|
+
linter,
|
|
233
336
|
help: Boolean(values.help),
|
|
234
337
|
version: Boolean(values.version),
|
|
235
338
|
positionals,
|
|
@@ -278,6 +381,11 @@ export function manifestToResponses(manifest, projectName) {
|
|
|
278
381
|
const framework = normalizeFramework({ name: frameworkName, variant: frameworkVariant });
|
|
279
382
|
const canClientRoute = isPromptVisible(framework, "router");
|
|
280
383
|
const supportsBundler = isPromptVisible(framework, "bundler");
|
|
384
|
+
const supportsAdapter = isPromptVisible(framework, "adapter");
|
|
385
|
+
const supportsDatabase = isPromptVisible(framework, "database");
|
|
386
|
+
const supportsCompiler = isPromptVisible(framework, "reactCompiler");
|
|
387
|
+
const supportsRouteGroups = isPromptVisible(framework, "routeGroups");
|
|
388
|
+
const supportsDocker = isPromptVisible(framework, "docker");
|
|
281
389
|
|
|
282
390
|
return {
|
|
283
391
|
projectName,
|
|
@@ -287,7 +395,14 @@ export function manifestToResponses(manifest, projectName) {
|
|
|
287
395
|
supportsBundler && manifest.framework?.bundler
|
|
288
396
|
? manifest.framework.bundler
|
|
289
397
|
: undefined,
|
|
290
|
-
adapter:
|
|
398
|
+
adapter:
|
|
399
|
+
supportsAdapter && manifest.framework?.adapter
|
|
400
|
+
? manifest.framework.adapter
|
|
401
|
+
: undefined,
|
|
402
|
+
database:
|
|
403
|
+
supportsDatabase && manifest.database?.orm
|
|
404
|
+
? manifest.database.orm
|
|
405
|
+
: "none",
|
|
291
406
|
architecture: manifest.architecture?.type || "feature-based",
|
|
292
407
|
language: manifest.tooling?.language || "ts",
|
|
293
408
|
cssFramework: manifest.styling?.engine || "tailwind",
|
|
@@ -295,10 +410,14 @@ export function manifestToResponses(manifest, projectName) {
|
|
|
295
410
|
formatter: manifest.tooling?.formatter || "prettier",
|
|
296
411
|
uiKit: manifest.ui?.kit || "none",
|
|
297
412
|
docsLanguage: manifest.docs?.language || "en",
|
|
298
|
-
reactCompiler: manifest.reactCompiler,
|
|
413
|
+
reactCompiler: supportsCompiler ? manifest.reactCompiler : undefined,
|
|
414
|
+
routeGroups: supportsRouteGroups ? manifest.routeGroups : undefined,
|
|
299
415
|
agentDocs: manifest.agentDocs,
|
|
300
|
-
|
|
301
|
-
|
|
416
|
+
docker: supportsDocker ? (manifest.docker ?? false) : false,
|
|
417
|
+
// Sensible defaults for scaffolder conditional passes (Next.js ships no
|
|
418
|
+
// verified test-runner overlay yet, so it defaults to none; explicit
|
|
419
|
+
// vitest/jest selection still applies the standard overlays)
|
|
420
|
+
testing: frameworkName === "next" ? "none" : "vitest",
|
|
302
421
|
router: canClientRoute,
|
|
303
422
|
stateManagement: "none",
|
|
304
423
|
iconLibrary: "none",
|
|
@@ -325,6 +444,9 @@ ${chalk.bold("OPTIONS")}
|
|
|
325
444
|
${chalk.yellow("-m, --manifest")} <path> Drive scaffolding from a lumen.config.json or inline JSON
|
|
326
445
|
${chalk.yellow("-t, --template")} <name> Scaffold using a preset (${Object.keys(PRESETS).join(", ")})
|
|
327
446
|
${chalk.yellow("-f, --framework")} <name> Target framework (react, next, react:vite, next:app-router, etc.)
|
|
447
|
+
${chalk.yellow("--db, --database")} <orm> Database ORM (prisma, none)
|
|
448
|
+
${chalk.yellow(" --docker")} Include multi-stage Dockerfile & docker-compose for Next.js standalone
|
|
449
|
+
${chalk.yellow("--route-groups")} Include Next.js route groups preset (public, auth, private, admin)
|
|
328
450
|
${chalk.yellow("-h, --help")} Show this help message
|
|
329
451
|
${chalk.yellow("-v, --version")} Show version number
|
|
330
452
|
|
|
@@ -335,6 +457,10 @@ ${chalk.bold("PRESETS")}
|
|
|
335
457
|
${chalk.cyan("react-type-js")} React + Vite + JavaScript (type-based)
|
|
336
458
|
${chalk.cyan("react-none-ts")} React + Vite + TypeScript (none)
|
|
337
459
|
${chalk.cyan("react-none-js")} React + Vite + JavaScript (none)
|
|
460
|
+
${chalk.cyan("next-ts")} Next.js App Router + TypeScript (feature-based)
|
|
461
|
+
${chalk.cyan("next-js")} Next.js App Router + JavaScript (feature-based)
|
|
462
|
+
${chalk.cyan("next-pages-ts")} Next.js Pages Router + TypeScript (feature-based)
|
|
463
|
+
${chalk.cyan("next-pages-js")} Next.js Pages Router + JavaScript (feature-based)
|
|
338
464
|
|
|
339
465
|
${chalk.bold("EXAMPLES")}
|
|
340
466
|
${chalk.gray("# Interactive setup")}
|
package/src/configure.js
CHANGED
|
@@ -166,12 +166,16 @@ async function configurePackageJson(projectPath, language) {
|
|
|
166
166
|
|
|
167
167
|
if (!pkg.scripts) pkg.scripts = {};
|
|
168
168
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
169
|
+
// Fill defaults only — never overwrite scripts owned by the base template
|
|
170
|
+
// (Next.js bases ship next dev/build/start) or merged overlay fragments
|
|
171
|
+
// (e.g. the bundler-aware dev script). React/Vite bases already carry the
|
|
172
|
+
// vite commands, so this is a no-op there too.
|
|
173
|
+
pkg.scripts.dev ??= "vite";
|
|
174
|
+
pkg.scripts.build ??= "vite build";
|
|
175
|
+
pkg.scripts.preview ??= "vite preview";
|
|
172
176
|
|
|
173
177
|
if (language === "ts") {
|
|
174
|
-
pkg.scripts.typecheck
|
|
178
|
+
pkg.scripts.typecheck ??= "tsc -b";
|
|
175
179
|
}
|
|
176
180
|
|
|
177
181
|
await fsp.writeFile(pkgPath, JSON.stringify(pkg, null, 2) + "\n", "utf8");
|