create-lumen 2.0.0-alpha.1 → 2.0.0-beta.1
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 +38 -12
- package/bin/cli.js +3 -1
- package/package.json +3 -3
- package/schema/fixtures/v2/invalid/shadcn-without-tailwind.json +1 -1
- package/schema/fixtures/v2/valid/react-vite-type-based.json +1 -1
- package/schema/lumen.config.v2.json +0 -1
- package/src/cli-args.js +169 -7
- package/src/configure.js +12 -4
- package/src/css.js +14 -30
- package/src/dependencies.js +36 -20
- package/src/engine/capabilities.js +195 -4
- package/src/engine/composition.js +736 -0
- package/src/engine/index.js +1 -0
- package/src/engine/providers/base-provider.js +4 -4
- package/src/injector.js +294 -446
- package/src/main.js +38 -16
- package/src/manifest/emit.js +1 -1
- package/src/manifest/schema.js +1 -1
- package/src/prompts.js +581 -238
- package/src/readme.js +23 -2
- package/src/scaffold.js +17 -0
- package/templates/architectures/hybrid/main.jsx +10 -0
- package/templates/architectures/hybrid/main.tsx +10 -0
- package/templates/architectures/hybrid/src/App.jsx +20 -0
- package/templates/architectures/hybrid/src/App.tsx +20 -0
- package/templates/architectures/hybrid/src/shared/components/ui/Button.jsx +31 -0
- package/templates/architectures/hybrid/src/shared/components/ui/Button.tsx +36 -0
- package/templates/architectures/hybrid/src/shared/components/ui/index.js +1 -0
- package/templates/architectures/hybrid/src/shared/components/ui/index.ts +1 -0
- package/templates/architectures/hybrid/src/shared/hooks/useLocalStorage.jsx +28 -0
- package/templates/architectures/hybrid/src/shared/hooks/useLocalStorage.ts +28 -0
- package/templates/architectures/hybrid/src/shared/layouts/.gitkeep +0 -0
- package/templates/architectures/hybrid/src/shared/stores/.gitkeep +0 -0
- package/templates/architectures/hybrid/src/shared/styles/.gitkeep +0 -0
- package/templates/architectures/hybrid/src/shared/types/index.js +20 -0
- package/templates/architectures/hybrid/src/shared/types/index.ts +16 -0
- package/templates/architectures/hybrid/src/shared/utils/cn.jsx +3 -0
- package/templates/architectures/hybrid/src/shared/utils/cn.ts +5 -0
- package/templates/architectures/none/main.jsx +10 -0
- package/templates/architectures/none/main.tsx +10 -0
- package/templates/architectures/none/src/App.jsx +15 -0
- package/templates/architectures/none/src/App.tsx +15 -0
- package/templates/architectures/none/src/styles/.gitkeep +1 -0
- package/templates/architectures/none/src/types/index.js +20 -0
- package/templates/architectures/none/src/types/index.ts +16 -0
- 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/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/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/hybrid/src/__tests__/App.test.jsx +11 -0
- package/templates/conditional/testing/jest/hybrid/src/__tests__/App.test.tsx +11 -0
- package/templates/conditional/testing/jest/none/src/__tests__/App.test.jsx +11 -0
- package/templates/conditional/testing/jest/none/src/__tests__/App.test.tsx +11 -0
- package/templates/conditional/testing/vitest/hybrid/src/__tests__/App.test.jsx +10 -0
- package/templates/conditional/testing/vitest/hybrid/src/__tests__/App.test.tsx +10 -0
- package/templates/conditional/testing/vitest/none/src/__tests__/App.test.jsx +10 -0
- package/templates/conditional/testing/vitest/none/src/__tests__/App.test.tsx +10 -0
- package/templates/css/tailwind/src/globals.css +10 -0
- package/templates/css/tailwind/src/themes.css +86 -4
- package/templates/ui/shadcn/lib/utils.js +6 -0
- package/templates/ui/shadcn/lib/utils.ts +6 -0
- package/templates/ui/shadcn/primitives/Button.jsx +48 -0
- package/templates/ui/shadcn/primitives/Button.tsx +59 -0
- package/templates/ui/shadcn/primitives/Card.jsx +55 -0
- package/templates/ui/shadcn/primitives/Card.tsx +73 -0
- package/templates/ui/shadcn/primitives/index.js +9 -0
- package/templates/ui/shadcn/primitives/index.ts +9 -0
- package/templates/css/bootstrap/src/themes.css +0 -14
- /package/templates/{css/bootstrap/src/globals.css → architectures/hybrid/src/features/.gitkeep} +0 -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
|
|
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,14 +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
|
|
|
39
|
+
Choose framework and architecture directly:
|
|
40
|
+
|
|
41
|
+
```bash
|
|
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
|
|
49
|
+
```
|
|
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
|
+
|
|
37
53
|
Drive scaffolding directly from a manifest file or inline JSON (manifest v2):
|
|
38
54
|
|
|
39
55
|
```bash
|
|
@@ -64,20 +80,25 @@ create-lumen my-app # after npm link
|
|
|
64
80
|
|
|
65
81
|
## Features
|
|
66
82
|
|
|
67
|
-
-
|
|
83
|
+
- **Two frameworks:** React + Vite (SPA) or Next.js (App Router / Pages Router) — selected interactively or via `-f`
|
|
84
|
+
- Architecture choice: feature-based, type-based, hybrid, or none (per-framework availability)
|
|
68
85
|
- TypeScript or JavaScript output
|
|
69
|
-
- CSS frameworks: Tailwind CSS v4 (CSS-first `@theme`, v3 dropped)
|
|
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`)
|
|
70
88
|
- Optional state management: Zustand or Redux Toolkit
|
|
71
|
-
- Optional router (React Router)
|
|
72
|
-
- 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
|
|
73
93
|
- Linting: ESLint (default), Oxlint, or Biome
|
|
74
94
|
- Code formatter: Prettier or Oxfmt (chosen after the linter; `eslint-config-prettier` is auto-wired for the ESLint + Prettier combo)
|
|
75
|
-
- 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.
|
|
76
96
|
- Explicit path mapping: Target directories declared for `lumen-cli` code generation
|
|
77
97
|
- Optional Axios setup and icon libraries
|
|
78
98
|
- Auto `git init`, generated `README` (with a project description reflecting the chosen tooling) and `LICENSE`
|
|
79
99
|
- `.env.example` scaffolded, and the generated `.gitignore` excludes env files (`.env`, `.env.*`) while keeping `.env.example`
|
|
80
100
|
- `@/` path alias configured (`tsconfig`/`jsconfig` + Vite)
|
|
101
|
+
- **Agent docs:** optional `.lumen/` context directory (project, architecture, conventions) via prompt or `--agent-docs`
|
|
81
102
|
|
|
82
103
|
Your selected options are cached and offered as defaults on subsequent runs.
|
|
83
104
|
|
|
@@ -148,8 +169,8 @@ src/
|
|
|
148
169
|
├── layouts
|
|
149
170
|
├── stores
|
|
150
171
|
├── styles
|
|
151
|
-
│ ├── globals.css # Tailwind
|
|
152
|
-
│ └── themes.css # Theme tokens (Tailwind @theme /
|
|
172
|
+
│ ├── globals.css # Tailwind directives (or main.css for vanilla)
|
|
173
|
+
│ └── themes.css # Theme tokens (Tailwind @theme / CSS vars)
|
|
153
174
|
├── types
|
|
154
175
|
└── utils
|
|
155
176
|
```
|
|
@@ -177,8 +198,8 @@ src/
|
|
|
177
198
|
├── services
|
|
178
199
|
├── store
|
|
179
200
|
├── styles
|
|
180
|
-
│ ├── globals.css # Tailwind
|
|
181
|
-
│ └── themes.css # Theme tokens (Tailwind @theme /
|
|
201
|
+
│ ├── globals.css # Tailwind directives (or main.css for vanilla)
|
|
202
|
+
│ └── themes.css # Theme tokens (Tailwind @theme / CSS vars)
|
|
182
203
|
├── test
|
|
183
204
|
└── utils
|
|
184
205
|
```
|
|
@@ -199,6 +220,11 @@ The manifest now includes:
|
|
|
199
220
|
|
|
200
221
|
### Ecosystem Documentation & Contracts
|
|
201
222
|
|
|
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.
|
|
227
|
+
- **[Generator Engine & Capabilities Guide (M2)](./docs/engine-capabilities.md):** Capability model, template composition, shadcn/ui on Tailwind v4, architecture presets, and Bootstrap removal.
|
|
202
228
|
- **[Manifest v2 Overview](./docs/manifest-v2.md):** Specification of the nested schema, validation behavior, and the new harness and architecture validation fields.
|
|
203
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.
|
|
204
230
|
- **[v1 to v2 Migration Guide](./docs/migration/v1-to-v2.md):** Complete guide for migrating generated projects and legacy flat configs to v2.
|
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,8 @@ main({
|
|
|
36
36
|
projectName: cliArgs.projectName,
|
|
37
37
|
manifest: cliArgs.manifest,
|
|
38
38
|
template: cliArgs.template,
|
|
39
|
+
framework: cliArgs.framework,
|
|
40
|
+
arch: cliArgs.arch,
|
|
39
41
|
}).catch((e) => {
|
|
40
42
|
console.error(chalk.red("\nError:"), e.message || e);
|
|
41
43
|
process.exit(1);
|
package/package.json
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-lumen",
|
|
3
|
-
"version": "2.0.0-
|
|
4
|
-
"description": "Scaffold a React + Vite project with architecture choice: feature-based
|
|
3
|
+
"version": "2.0.0-beta.1",
|
|
4
|
+
"description": "Scaffold a React + Vite project with architecture choice: feature-based, type-based, hybrid, or none.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"create-lumen": "bin/cli.js"
|
|
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/",
|
package/src/cli-args.js
CHANGED
|
@@ -3,6 +3,10 @@ import { readFileSync } from "node:fs";
|
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import chalk from "chalk";
|
|
5
5
|
import { parseManifest } from "./manifest/schema.js";
|
|
6
|
+
import {
|
|
7
|
+
normalizeFramework,
|
|
8
|
+
isPromptVisible,
|
|
9
|
+
} from "./engine/capabilities.js";
|
|
6
10
|
|
|
7
11
|
export const PRESETS = {
|
|
8
12
|
"react-ts": {
|
|
@@ -141,6 +145,112 @@ export const PRESETS = {
|
|
|
141
145
|
gitInit: true,
|
|
142
146
|
readme: true,
|
|
143
147
|
},
|
|
148
|
+
"react-none-ts": {
|
|
149
|
+
frameworkName: "react",
|
|
150
|
+
frameworkVariant: "vite",
|
|
151
|
+
architecture: "none",
|
|
152
|
+
language: "ts",
|
|
153
|
+
cssFramework: "tailwind",
|
|
154
|
+
testing: "vitest",
|
|
155
|
+
router: false,
|
|
156
|
+
stateManagement: "none",
|
|
157
|
+
iconLibrary: "none",
|
|
158
|
+
apiClient: "none",
|
|
159
|
+
linter: "eslint",
|
|
160
|
+
formatter: "prettier",
|
|
161
|
+
docsLanguage: "en",
|
|
162
|
+
gitInit: true,
|
|
163
|
+
readme: true,
|
|
164
|
+
},
|
|
165
|
+
"react-none-js": {
|
|
166
|
+
frameworkName: "react",
|
|
167
|
+
frameworkVariant: "vite",
|
|
168
|
+
architecture: "none",
|
|
169
|
+
language: "js",
|
|
170
|
+
cssFramework: "tailwind",
|
|
171
|
+
testing: "vitest",
|
|
172
|
+
router: false,
|
|
173
|
+
stateManagement: "none",
|
|
174
|
+
iconLibrary: "none",
|
|
175
|
+
apiClient: "none",
|
|
176
|
+
linter: "eslint",
|
|
177
|
+
formatter: "prettier",
|
|
178
|
+
docsLanguage: "en",
|
|
179
|
+
gitInit: true,
|
|
180
|
+
readme: true,
|
|
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
|
+
},
|
|
144
254
|
};
|
|
145
255
|
|
|
146
256
|
/**
|
|
@@ -151,6 +261,8 @@ export const PRESETS = {
|
|
|
151
261
|
* quickSetup: boolean,
|
|
152
262
|
* manifest: string | null,
|
|
153
263
|
* template: string | null,
|
|
264
|
+
* framework: string | null,
|
|
265
|
+
* arch: string | null,
|
|
154
266
|
* help: boolean,
|
|
155
267
|
* version: boolean,
|
|
156
268
|
* positionals: string[]
|
|
@@ -163,6 +275,16 @@ export function parseCliArgs(argv = process.argv.slice(2)) {
|
|
|
163
275
|
yes: { type: "boolean", short: "y", default: false },
|
|
164
276
|
manifest: { type: "string", short: "m" },
|
|
165
277
|
template: { type: "string", short: "t" },
|
|
278
|
+
framework: { type: "string", short: "f" },
|
|
279
|
+
arch: { type: "string", short: "a" },
|
|
280
|
+
architecture: { type: "string" },
|
|
281
|
+
bundler: { type: "string", short: "b" },
|
|
282
|
+
adapter: { type: "string" },
|
|
283
|
+
"react-compiler": { type: "boolean" },
|
|
284
|
+
reactCompiler: { type: "boolean" },
|
|
285
|
+
"agent-docs": { type: "boolean" },
|
|
286
|
+
agentDocs: { type: "boolean" },
|
|
287
|
+
linter: { type: "string" },
|
|
166
288
|
help: { type: "boolean", short: "h", default: false },
|
|
167
289
|
version: { type: "boolean", short: "v", default: false },
|
|
168
290
|
},
|
|
@@ -172,11 +294,31 @@ export function parseCliArgs(argv = process.argv.slice(2)) {
|
|
|
172
294
|
|
|
173
295
|
const projectName = positionals[0] ? positionals[0].trim() : null;
|
|
174
296
|
|
|
297
|
+
let arch = values.arch || values.architecture || null;
|
|
298
|
+
if (arch) {
|
|
299
|
+
arch = arch.trim().toLowerCase();
|
|
300
|
+
if (arch === "feature") arch = "feature-based";
|
|
301
|
+
else if (arch === "component" || arch === "component-based" || arch === "type") arch = "type-based";
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
const bundler = values.bundler ? values.bundler.trim().toLowerCase() : null;
|
|
305
|
+
const adapter = values.adapter ? values.adapter.trim().toLowerCase() : null;
|
|
306
|
+
const reactCompiler = values["react-compiler"] ?? values.reactCompiler ?? null;
|
|
307
|
+
const agentDocs = values["agent-docs"] ?? values.agentDocs ?? null;
|
|
308
|
+
const linter = values.linter ? values.linter.trim().toLowerCase() : null;
|
|
309
|
+
|
|
175
310
|
return {
|
|
176
311
|
projectName,
|
|
177
312
|
quickSetup: Boolean(values.yes),
|
|
178
313
|
manifest: values.manifest || null,
|
|
179
314
|
template: values.template || null,
|
|
315
|
+
framework: values.framework ? values.framework.trim() : null,
|
|
316
|
+
arch,
|
|
317
|
+
bundler,
|
|
318
|
+
adapter,
|
|
319
|
+
reactCompiler,
|
|
320
|
+
agentDocs,
|
|
321
|
+
linter,
|
|
180
322
|
help: Boolean(values.help),
|
|
181
323
|
version: Boolean(values.version),
|
|
182
324
|
positionals,
|
|
@@ -222,14 +364,24 @@ export function loadManifestSource(source, cwd = process.cwd()) {
|
|
|
222
364
|
export function manifestToResponses(manifest, projectName) {
|
|
223
365
|
const frameworkName = manifest.framework?.name || "react";
|
|
224
366
|
const frameworkVariant = manifest.framework?.variant || "vite";
|
|
225
|
-
const
|
|
367
|
+
const framework = normalizeFramework({ name: frameworkName, variant: frameworkVariant });
|
|
368
|
+
const canClientRoute = isPromptVisible(framework, "router");
|
|
369
|
+
const supportsBundler = isPromptVisible(framework, "bundler");
|
|
370
|
+
const supportsAdapter = isPromptVisible(framework, "adapter");
|
|
371
|
+
const supportsCompiler = isPromptVisible(framework, "reactCompiler");
|
|
226
372
|
|
|
227
373
|
return {
|
|
228
374
|
projectName,
|
|
229
375
|
frameworkName,
|
|
230
376
|
frameworkVariant,
|
|
231
|
-
bundler:
|
|
232
|
-
|
|
377
|
+
bundler:
|
|
378
|
+
supportsBundler && manifest.framework?.bundler
|
|
379
|
+
? manifest.framework.bundler
|
|
380
|
+
: undefined,
|
|
381
|
+
adapter:
|
|
382
|
+
supportsAdapter && manifest.framework?.adapter
|
|
383
|
+
? manifest.framework.adapter
|
|
384
|
+
: undefined,
|
|
233
385
|
architecture: manifest.architecture?.type || "feature-based",
|
|
234
386
|
language: manifest.tooling?.language || "ts",
|
|
235
387
|
cssFramework: manifest.styling?.engine || "tailwind",
|
|
@@ -237,11 +389,13 @@ export function manifestToResponses(manifest, projectName) {
|
|
|
237
389
|
formatter: manifest.tooling?.formatter || "prettier",
|
|
238
390
|
uiKit: manifest.ui?.kit || "none",
|
|
239
391
|
docsLanguage: manifest.docs?.language || "en",
|
|
240
|
-
reactCompiler: manifest.reactCompiler,
|
|
392
|
+
reactCompiler: supportsCompiler ? manifest.reactCompiler : undefined,
|
|
241
393
|
agentDocs: manifest.agentDocs,
|
|
242
|
-
// Sensible defaults for scaffolder conditional passes
|
|
243
|
-
|
|
244
|
-
|
|
394
|
+
// Sensible defaults for scaffolder conditional passes (Next.js ships no
|
|
395
|
+
// verified test-runner overlay yet, so it defaults to none; explicit
|
|
396
|
+
// vitest/jest selection still applies the standard overlays)
|
|
397
|
+
testing: frameworkName === "next" ? "none" : "vitest",
|
|
398
|
+
router: canClientRoute,
|
|
245
399
|
stateManagement: "none",
|
|
246
400
|
iconLibrary: "none",
|
|
247
401
|
apiClient: "none",
|
|
@@ -263,8 +417,10 @@ ${chalk.bold("USAGE")}
|
|
|
263
417
|
|
|
264
418
|
${chalk.bold("OPTIONS")}
|
|
265
419
|
${chalk.yellow("-y, --yes")} Quick setup with recommended defaults (TS + Tailwind + Router + ESLint + Prettier)
|
|
420
|
+
${chalk.yellow("-a, --arch")} <name> Architecture preset (feature-based, type-based, none, hybrid)
|
|
266
421
|
${chalk.yellow("-m, --manifest")} <path> Drive scaffolding from a lumen.config.json or inline JSON
|
|
267
422
|
${chalk.yellow("-t, --template")} <name> Scaffold using a preset (${Object.keys(PRESETS).join(", ")})
|
|
423
|
+
${chalk.yellow("-f, --framework")} <name> Target framework (react, next, react:vite, next:app-router, etc.)
|
|
268
424
|
${chalk.yellow("-h, --help")} Show this help message
|
|
269
425
|
${chalk.yellow("-v, --version")} Show version number
|
|
270
426
|
|
|
@@ -273,6 +429,12 @@ ${chalk.bold("PRESETS")}
|
|
|
273
429
|
${chalk.cyan("react-js")} React + Vite + JavaScript (feature-based)
|
|
274
430
|
${chalk.cyan("react-type-ts")} React + Vite + TypeScript (type-based)
|
|
275
431
|
${chalk.cyan("react-type-js")} React + Vite + JavaScript (type-based)
|
|
432
|
+
${chalk.cyan("react-none-ts")} React + Vite + TypeScript (none)
|
|
433
|
+
${chalk.cyan("react-none-js")} React + Vite + JavaScript (none)
|
|
434
|
+
${chalk.cyan("next-ts")} Next.js App Router + TypeScript (feature-based)
|
|
435
|
+
${chalk.cyan("next-js")} Next.js App Router + JavaScript (feature-based)
|
|
436
|
+
${chalk.cyan("next-pages-ts")} Next.js Pages Router + TypeScript (feature-based)
|
|
437
|
+
${chalk.cyan("next-pages-js")} Next.js Pages Router + JavaScript (feature-based)
|
|
276
438
|
|
|
277
439
|
${chalk.bold("EXAMPLES")}
|
|
278
440
|
${chalk.gray("# Interactive setup")}
|
package/src/configure.js
CHANGED
|
@@ -88,6 +88,10 @@ async function configureTsconfig(projectPath) {
|
|
|
88
88
|
const rootTsconfig = {
|
|
89
89
|
files: [],
|
|
90
90
|
references,
|
|
91
|
+
compilerOptions: {
|
|
92
|
+
baseUrl: ".",
|
|
93
|
+
paths,
|
|
94
|
+
},
|
|
91
95
|
};
|
|
92
96
|
await fsp.writeFile(
|
|
93
97
|
rootTsconfigPath,
|
|
@@ -162,12 +166,16 @@ async function configurePackageJson(projectPath, language) {
|
|
|
162
166
|
|
|
163
167
|
if (!pkg.scripts) pkg.scripts = {};
|
|
164
168
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
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";
|
|
168
176
|
|
|
169
177
|
if (language === "ts") {
|
|
170
|
-
pkg.scripts.typecheck
|
|
178
|
+
pkg.scripts.typecheck ??= "tsc -b";
|
|
171
179
|
}
|
|
172
180
|
|
|
173
181
|
await fsp.writeFile(pkgPath, JSON.stringify(pkg, null, 2) + "\n", "utf8");
|
package/src/css.js
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
import { promises as fsp } from "fs";
|
|
2
2
|
import path from "path";
|
|
3
3
|
import { deleteIfExists } from "@/utils/fs.js";
|
|
4
|
+
import {
|
|
5
|
+
normalizeFramework,
|
|
6
|
+
hasCapability,
|
|
7
|
+
CAPABILITIES,
|
|
8
|
+
} from "@/engine/capabilities.js";
|
|
4
9
|
|
|
5
10
|
export async function setupCssFramework({
|
|
6
11
|
projectPath,
|
|
@@ -13,18 +18,20 @@ export async function setupCssFramework({
|
|
|
13
18
|
framework = "vite",
|
|
14
19
|
}) {
|
|
15
20
|
process.chdir(projectPath);
|
|
21
|
+
const fw = normalizeFramework(framework);
|
|
22
|
+
const isFilesystemRouted = hasCapability(fw, CAPABILITIES.FILESYSTEM_ROUTING);
|
|
16
23
|
const cssDir = path.join(templatesDir, "css");
|
|
17
24
|
// Feature-based main.* imports ./shared/styles/globals.css (or main.css when
|
|
18
25
|
// framework is "none"); type-based imports ./styles/globals.css (or
|
|
19
26
|
// main.css). The chosen framework's styles land in the file the architecture
|
|
20
27
|
// actually imports.
|
|
21
28
|
const stylesDir =
|
|
22
|
-
architecture === "feature-based"
|
|
29
|
+
architecture === "feature-based" || architecture === "hybrid"
|
|
23
30
|
? path.join(projectPath, "src", "shared", "styles")
|
|
24
31
|
: path.join(projectPath, "src", "styles");
|
|
25
32
|
|
|
26
|
-
// Vanilla CSS gets a stylesheet named main.css; tailwind
|
|
27
|
-
// globals.css (
|
|
33
|
+
// Vanilla CSS gets a stylesheet named main.css; tailwind keeps
|
|
34
|
+
// globals.css (its idiomatic name — it injects framework directives).
|
|
28
35
|
const mainFileName = cssFramework === "none" ? "main.css" : "globals.css";
|
|
29
36
|
|
|
30
37
|
async function writeCss(content, fileName) {
|
|
@@ -32,7 +39,7 @@ export async function setupCssFramework({
|
|
|
32
39
|
}
|
|
33
40
|
|
|
34
41
|
if (cssFramework === "tailwind") {
|
|
35
|
-
if (
|
|
42
|
+
if (isFilesystemRouted || fw.name === "next") {
|
|
36
43
|
const postcssConfigFile = "postcss.config.mjs";
|
|
37
44
|
const postcssConfigTarget = path.join(projectPath, postcssConfigFile);
|
|
38
45
|
const postcssConfigContent = await fsp.readFile(
|
|
@@ -60,29 +67,6 @@ export async function setupCssFramework({
|
|
|
60
67
|
);
|
|
61
68
|
await writeCss(globalsContent, "globals.css");
|
|
62
69
|
await writeCss(themesContent, "themes.css");
|
|
63
|
-
} else if (cssFramework === "bootstrap") {
|
|
64
|
-
const mainFile = path.join(projectPath, "src", `main.${ext}`);
|
|
65
|
-
try {
|
|
66
|
-
let mainContent = await fsp.readFile(mainFile, "utf8");
|
|
67
|
-
if (
|
|
68
|
-
!mainContent.includes("bootstrap/dist/css/bootstrap.min.css")
|
|
69
|
-
) {
|
|
70
|
-
mainContent =
|
|
71
|
-
"import 'bootstrap/dist/css/bootstrap.min.css';\n" + mainContent;
|
|
72
|
-
await fsp.writeFile(mainFile, mainContent, "utf8");
|
|
73
|
-
}
|
|
74
|
-
} catch {}
|
|
75
|
-
|
|
76
|
-
const globalsContent = await fsp.readFile(
|
|
77
|
-
path.join(cssDir, "bootstrap", "src", "globals.css"),
|
|
78
|
-
"utf8"
|
|
79
|
-
);
|
|
80
|
-
const themesContent = await fsp.readFile(
|
|
81
|
-
path.join(cssDir, "bootstrap", "src", "themes.css"),
|
|
82
|
-
"utf8"
|
|
83
|
-
);
|
|
84
|
-
await writeCss(globalsContent, "globals.css");
|
|
85
|
-
await writeCss(themesContent, "themes.css");
|
|
86
70
|
} else {
|
|
87
71
|
const mainContent = await fsp.readFile(
|
|
88
72
|
path.join(cssDir, "none", "src", "main.css"),
|
|
@@ -110,17 +94,17 @@ export async function setupCssFramework({
|
|
|
110
94
|
}
|
|
111
95
|
|
|
112
96
|
// Rewrite the CSS import in main.{ext} so it matches the file the framework
|
|
113
|
-
// actually wrote (main.css for "none", globals.css for tailwind
|
|
97
|
+
// actually wrote (main.css for "none", globals.css for tailwind).
|
|
114
98
|
async function syncMainCssImport(projectPath, ext, architecture, mainFileName) {
|
|
115
99
|
const mainPath = path.join(projectPath, "src", `main.${ext}`);
|
|
116
100
|
try {
|
|
117
101
|
let content = await fsp.readFile(mainPath, "utf8");
|
|
118
102
|
const expected =
|
|
119
|
-
architecture === "feature-based"
|
|
103
|
+
architecture === "feature-based" || architecture === "hybrid"
|
|
120
104
|
? `./shared/styles/${mainFileName}`
|
|
121
105
|
: `./styles/${mainFileName}`;
|
|
122
106
|
const wrong =
|
|
123
|
-
architecture === "feature-based"
|
|
107
|
+
architecture === "feature-based" || architecture === "hybrid"
|
|
124
108
|
? /import ['"]\.\/shared\/styles\/(globals|main)\.css['"]/
|
|
125
109
|
: /import ['"]\.\/styles\/(globals|main)\.css['"]/;
|
|
126
110
|
content = content.replace(wrong, `import '${expected}'`);
|
package/src/dependencies.js
CHANGED
|
@@ -25,13 +25,13 @@ export async function installDeps(pkgManager, dev, packages, cwd) {
|
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
export async function installAllDeps(responses, pkg, cwd) {
|
|
28
|
-
const { deps,
|
|
28
|
+
const { deps, devDepBatches } = computeDeps(responses);
|
|
29
29
|
|
|
30
30
|
if (deps.length > 0) {
|
|
31
31
|
await installDeps(pkg, false, deps, cwd);
|
|
32
32
|
}
|
|
33
|
-
|
|
34
|
-
await installDeps(pkg, true,
|
|
33
|
+
for (const batch of devDepBatches) {
|
|
34
|
+
await installDeps(pkg, true, batch, cwd);
|
|
35
35
|
}
|
|
36
36
|
}
|
|
37
37
|
|
|
@@ -40,18 +40,23 @@ export async function installAllDeps(responses, pkg, cwd) {
|
|
|
40
40
|
// the conditional wiring stays verifiable without a network install.
|
|
41
41
|
export function computeDeps(responses) {
|
|
42
42
|
const deps = [];
|
|
43
|
-
const
|
|
43
|
+
const fwName =
|
|
44
|
+
typeof responses.framework === "string"
|
|
45
|
+
? responses.framework
|
|
46
|
+
: (responses.framework?.name ?? responses.frameworkName ?? "react");
|
|
47
|
+
const cssDevDeps = [];
|
|
48
|
+
const testingDevDeps = [];
|
|
49
|
+
const linterDevDeps = [];
|
|
50
|
+
const formatterDevDeps = [];
|
|
44
51
|
|
|
45
52
|
// CSS framework
|
|
46
53
|
if (responses.cssFramework === "tailwind") {
|
|
47
|
-
if (
|
|
48
|
-
|
|
54
|
+
if (fwName === "next") {
|
|
55
|
+
cssDevDeps.push("tailwindcss", "@tailwindcss/postcss", "postcss");
|
|
49
56
|
} else {
|
|
50
|
-
|
|
57
|
+
cssDevDeps.push("tailwindcss", "@tailwindcss/vite");
|
|
51
58
|
}
|
|
52
59
|
deps.push("clsx", "tailwind-merge");
|
|
53
|
-
} else if (responses.cssFramework === "bootstrap") {
|
|
54
|
-
deps.push("bootstrap", "react-bootstrap");
|
|
55
60
|
}
|
|
56
61
|
|
|
57
62
|
// State management
|
|
@@ -80,14 +85,14 @@ export function computeDeps(responses) {
|
|
|
80
85
|
|
|
81
86
|
// Testing
|
|
82
87
|
if (responses.testing === "vitest") {
|
|
83
|
-
|
|
88
|
+
testingDevDeps.push(
|
|
84
89
|
"vitest",
|
|
85
90
|
"@testing-library/react",
|
|
86
91
|
"@testing-library/jest-dom",
|
|
87
92
|
"jsdom"
|
|
88
93
|
);
|
|
89
94
|
} else if (responses.testing === "jest") {
|
|
90
|
-
|
|
95
|
+
testingDevDeps.push(
|
|
91
96
|
"jest",
|
|
92
97
|
"jest-environment-jsdom",
|
|
93
98
|
"@testing-library/react",
|
|
@@ -102,30 +107,41 @@ export function computeDeps(responses) {
|
|
|
102
107
|
|
|
103
108
|
// Linter
|
|
104
109
|
if (responses.linter === "eslint") {
|
|
105
|
-
if (
|
|
106
|
-
|
|
110
|
+
if (fwName === "next") {
|
|
111
|
+
linterDevDeps.push("eslint", "eslint-config-next");
|
|
107
112
|
} else {
|
|
108
|
-
|
|
113
|
+
linterDevDeps.push("eslint", "@eslint/js", "eslint-plugin-react-hooks", "eslint-plugin-react-refresh", "globals");
|
|
109
114
|
if (responses.language === "ts") {
|
|
110
|
-
|
|
115
|
+
linterDevDeps.push("typescript-eslint", "jiti");
|
|
111
116
|
}
|
|
112
117
|
}
|
|
113
118
|
} else if (responses.linter === "oxlint") {
|
|
114
|
-
|
|
119
|
+
linterDevDeps.push("oxlint");
|
|
120
|
+
} else if (responses.linter === "biome") {
|
|
121
|
+
linterDevDeps.push("@biomejs/biome");
|
|
115
122
|
}
|
|
116
123
|
|
|
117
124
|
// Formatter
|
|
118
125
|
if (responses.formatter === "prettier") {
|
|
119
|
-
|
|
126
|
+
formatterDevDeps.push("prettier");
|
|
120
127
|
if (responses.linter === "eslint") {
|
|
121
|
-
|
|
128
|
+
formatterDevDeps.push("eslint-config-prettier");
|
|
122
129
|
}
|
|
123
130
|
} else if (responses.formatter === "oxfmt") {
|
|
124
|
-
|
|
131
|
+
formatterDevDeps.push("oxfmt");
|
|
125
132
|
}
|
|
126
133
|
|
|
127
134
|
// Always re-install react + react-dom for consistency
|
|
128
135
|
deps.push("react", "react-dom");
|
|
129
136
|
|
|
130
|
-
|
|
137
|
+
const devDepBatches = [
|
|
138
|
+
cssDevDeps,
|
|
139
|
+
testingDevDeps,
|
|
140
|
+
linterDevDeps,
|
|
141
|
+
formatterDevDeps,
|
|
142
|
+
].filter((batch) => batch.length > 0);
|
|
143
|
+
|
|
144
|
+
const devDeps = devDepBatches.flat();
|
|
145
|
+
|
|
146
|
+
return { deps, devDeps, devDepBatches };
|
|
131
147
|
}
|