@teispace/next-maker 4.0.2 → 5.0.0

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 (4) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/README.md +119 -875
  3. package/dist/index.js +779 -1045
  4. package/package.json +9 -4
package/README.md CHANGED
@@ -1,935 +1,179 @@
1
1
  # @teispace/next-maker
2
2
 
3
- A CLI that scaffolds Next.js 16+ applications (TypeScript, Tailwind v4, Biome, Pino, Zod, Redux Toolkit, next-intl, Vitest) and co-generates feature-based architecture pages, layouts, components, hooks, Redux slices, API services, locales, providers, env vars, and tests. Ships a manifest-driven `doctor` and `remove` for ongoing maintenance.
4
-
5
- ## Installation
6
-
7
- ### Using npx (Recommended)
8
-
9
- ```bash
10
- npx @teispace/next-maker <command> [args] [options]
11
- ```
12
-
13
- ### Global Installation
14
-
15
- ```bash
16
- npm install -g @teispace/next-maker
17
- next-maker <command> [args] [options]
18
- ```
19
-
20
- ---
21
-
22
- ## Commands at a glance
23
-
24
- | Category | Command | Purpose |
25
- | --- | --- | --- |
26
- | Lifecycle | `init [name]` | Create a new Next.js application |
27
- | Lifecycle | `setup [options]` | Add features to an existing project (retrofit) |
28
- | Lifecycle | `doctor [options]` | Diagnose drift against known feature manifests |
29
- | Lifecycle | `remove <feature>` | Reverse a feature install (uses the manifest) |
30
- | Routing | `page <name>` | Generate a page/route |
31
- | Routing | `layout <segment>` | Generate a nested layout.tsx |
32
- | UI | `component <name>` | Shared component with auto-wired barrel exports |
33
- | State | `feature <name>` | Full feature module (DDD) |
34
- | State | `slice <name>` | Redux Toolkit slice (auto-registers in rootReducer) |
35
- | State | `service <name>` | API service (axios/fetch, optional CRUD) |
36
- | State | `provider <name>` | Context provider with RootProvider auto-wiring |
37
- | Config | `env <NAME>` | Add an env var (schema + .env.example + .env) |
38
- | Config | `locale [code]` | Add a new language/locale |
39
- | Code | `hook <name>` | Custom React hook |
40
- | Code | `test <file>` | Sibling test stub for a component/hook/slice |
41
- | Assets | `favicon` | Generate `favicon.ico` (and optionally PWA / OG / Apple icons) from a source image |
42
-
43
- Run `npx @teispace/next-maker <command> --help` for the full option list of any command.
44
-
45
- ---
46
-
47
- ## Lifecycle commands
48
-
49
- ### `init` — Create a New App
3
+ Create and grow Next.js 16 applications from the [Teispace starter](https://github.com/teispace/nextjs-starter). `init` composes a project from the starter's own manifest with exactly the pieces you choose; `setup`, `doctor`, and `remove` keep it aligned with the starter later; the generators add features, pages, slices, and API layers in the starter's server-first shape.
50
4
 
51
5
  ```bash
52
- npx @teispace/next-maker init [project-name] [options]
53
- ```
54
-
55
- | Flag | Effect |
56
- | --- | --- |
57
- | `-y, --yes` | Skip every prompt and bootstrap with the recommended production defaults (see below) |
58
- | `--package-manager <pm>` | Override the package manager (`npm` \| `yarn` \| `pnpm` \| `bun`). Works with or without `--yes`. |
59
-
60
- ```bash
61
- # Interactive (default)
62
6
  npx @teispace/next-maker init my-app
63
-
64
- # One-shot opinionated install
65
- npx @teispace/next-maker init my-app --yes
66
-
67
- # One-shot with a different package manager
68
- npx @teispace/next-maker init my-app -y --package-manager pnpm
69
7
  ```
70
8
 
71
- **`--yes` defaults** every architecture feature on, heavyweight integrations off:
72
-
73
- | | |
74
- | --- | --- |
75
- | package manager | `yarn` |
76
- | HTTP client | `fetch` |
77
- | dark mode, redux, i18n, tests, react-compiler | ✅ on |
78
- | pre-commit hooks, commitizen, copy `.env` | ✅ on |
79
- | WebSocket (requires Redux), docker, GitHub Actions, bundle-analyzer, community files (CODE_OF_CONDUCT etc.) | ⏭ off |
80
-
81
- Anything you don't want? `next-maker remove <feature>` after init.
82
-
83
- The starter at [`teispace/nextjs-starter`](https://github.com/teispace/nextjs-starter) is cloned via `degit` and trimmed to match your prompt answers. The `cleanup` step strips opted-out features so the generated project compiles end-to-end on first install.
84
-
85
- **Interactive prompts:**
86
-
87
- | Section | Prompts |
88
- | --- | --- |
89
- | Identity | project name, description, author, version, support email, package manager, GitHub repo / issues / homepage |
90
- | Architecture | HTTP client (axios / fetch / both / none), dark mode, Redux Toolkit, WebSocket (requires Redux), i18n, testing, React Compiler, Bundle Analyzer |
91
- | Tooling | community files (CODE_OF_CONDUCT, CONTRIBUTING, SECURITY), README, Docker, CI/CD, pre-commit hooks (Husky/Commitlint/Lint-staged), Commitizen, copy `.env.example` → `.env` |
92
- | Templates | keep GitHub issue/PR templates? include `react-secure-storage`? |
9
+ Requires Node 24+. The CLI is pinned to one starter tag (see `src/config/starter.ts`); each major version of the CLI tracks one major line of the starter.
93
10
 
94
- **Always shipped:**
11
+ ## Commands
95
12
 
96
- - Next.js 16+ with App Router
97
- - TypeScript (strict mode)
98
- - Tailwind CSS v4
99
- - Biome (single-tool lint + format)
100
- - Pino structured logger with redaction
101
- - Zod-validated env schema in `src/lib/env/`
102
- - Feature-based DDD architecture
103
- - Dual HTTP clients (`fetchClient` + `axiosClient`) on a shared foundation: two entry points (`@/lib/utils/http` universal, `@/lib/utils/http/server` for Server Components), automatic `X-Request-Id` correlation, single `parseApiError` pipeline, cookie-mode auth by default, typed query params via `{ params }`. A build-time `__bundle-sentinel__` rejects future regressions of the universal/server split.
104
- - WebSocket transport — **opt-in via the `ws` prompt** (requires Redux): typed `socket.io-client` wrapper, lazy singleton, `useWsEvent` / `useWsEmit` / `useWsStatus` hooks, Redux bridge into a dedicated (non-persisted) `wsReducer`. Cookie-mode auth by default; browser-only (SSR throws).
105
- - Hardened security headers in `next.config.ts`
106
- - `scripts/sync-env.ts` and `scripts/check-deprecated.ts` (used by the `validate` chain)
13
+ | Command | Purpose |
14
+ | :------------------------------ | :--------------------------------------------------------------------------------- |
15
+ | `init [name]` | Create a project. Interactive, or `--yes`, `--preset`, `--config`, `--set`. |
16
+ | `workspace <name>` | pnpm + Turborepo monorepo with one starter app per `--apps` entry. |
17
+ | `options` | List the starter's options with the project's current values. |
18
+ | `setup --set k=v` | Turn features on or off in an existing project. |
19
+ | `remove <feature>` | Shorthand for `setup --set <feature>=false`. |
20
+ | `doctor [--fix] [--compile]` | Compare the project with the starter footprint; restore what is missing. |
21
+ | `upgrade [--to <ref>]` | Three-way merge a newer starter into the project. |
22
+ | `feature <name>` | Feature module: `api/`, components, optional slice, `index.ts` and `server.ts`. |
23
+ | `api <name>` (alias `service`) | `api/{schema,keys,server,queries,actions}.ts` for a resource. |
24
+ | `slice <name>` | Redux or Zustand slice, registered in the store. |
25
+ | `page <name>` | Page with SEO metadata, optional route group, dynamic segment, loading and error. |
26
+ | `layout <segment>` | Nested layout. |
27
+ | `component <name>` | Shared or feature component with barrel exports. |
28
+ | `hook <name>` | Custom hook. |
29
+ | `provider <name>` | Context provider wired into `RootProvider`. |
30
+ | `env <NAME>` | Environment variable across `src/lib/env/index.ts`, `.env.example`, and `.env`. |
31
+ | `locale <code>` | New locale: translations, `SUPPORTED_LOCALES`, `appLocales`. |
32
+ | `test <file>` | Sibling test for a component, hook, or slice. |
33
+ | `favicon` | Icons from a source image. |
107
34
 
108
- ---
35
+ Run any command with `--help` for its flags.
109
36
 
110
- ### `setup` — Retrofit a feature
111
-
112
- Add a feature to an existing project (one that wasn't generated by `init`, or one created before a feature existed).
37
+ ## init
113
38
 
114
39
  ```bash
115
- npx @teispace/next-maker setup [options]
40
+ npx @teispace/next-maker init my-app # interactive
41
+ npx @teispace/next-maker init my-app --yes # starter defaults
42
+ npx @teispace/next-maker init my-app --preset full # everything on
43
+ npx @teispace/next-maker init my-app --yes --set state=zustand --set i18n=false --package-manager npm
44
+ npx @teispace/next-maker init --config my-app.json # repeatable, non-interactive
45
+ npx @teispace/next-maker init my-app --yes --dry-run # print the plan, create nothing
116
46
  ```
117
47
 
118
- | Flag | What it does |
119
- | --- | --- |
120
- | `--http-client` | Adds the axios and/or fetch Result-based clients under `src/lib/utils/http/` |
121
- | `--dark-theme` | Installs `@teispace/next-themes` and adds `CustomThemeProvider` |
122
- | `--redux` | Redux Toolkit + `react-redux` + `redux-persist`, `StoreProvider`, `src/store` |
123
- | `--ws` | WebSocket transport — `socket.io-client@^4.8.3`, `src/lib/utils/ws/` subtree, non-persisted `wsReducer`, `attachWsBridge` mount in `StoreProvider`. Requires `--redux` to be installed first. |
124
- | `--i18n` | `next-intl` + `[locale]` routing + `proxy.ts` + `RootProvider` wiring |
125
- | `--tests` | Vitest + React Testing Library + jsdom + `test/test-utils.tsx` |
126
- | `--react-compiler` | `reactCompiler: true` in `next.config.ts` + `babel-plugin-react-compiler` |
127
- | `--bundle-analyzer` | Wraps the default export with `withBundleAnalyzer` and adds the `analyze` script |
128
- | `--security-headers` | Injects the hardened headers block into `next.config.ts` (DNS prefetch, HSTS, X-Frame-Options, X-Content-Type-Options, Referrer-Policy, X-XSS-Protection) |
129
- | `--validate-scripts` | Drops in `scripts/sync-env.ts` + `scripts/check-deprecated.ts` and wires `env:sync`, `check:deprecated`, `type-check`, and the `validate` chain (PM-aware) |
130
- | `--commitizen` | Writes `.czrc`, adds `commit` script, installs `commitizen` + `cz-conventional-changelog` |
131
-
132
- Run `setup` without flags for an interactive picker.
48
+ The starter declares its options in `next-maker.json`; the CLI asks those questions and nothing else. Current options (starter 2.x):
133
49
 
134
- ```bash
135
- # Interactive
136
- npx @teispace/next-maker setup
137
-
138
- # Specific feature
139
- npx @teispace/next-maker setup --redux
140
- npx @teispace/next-maker setup --ws # adds WebSocket layer (run after --redux)
141
- npx @teispace/next-maker setup --security-headers
142
- npx @teispace/next-maker setup --validate-scripts
143
- ```
50
+ | Option | Values | Default |
51
+ | :---------------- | :------------------------------ | :------- |
52
+ | `packageManager` | `pnpm`, `npm`, `yarn`, `bun` | `pnpm` |
53
+ | `state` | `redux`, `zustand`, `none` | `redux` |
54
+ | `http` | `fetch`, `axios`, `both` | `fetch` |
55
+ | `bff` | boolean (same-origin API proxy) | `false` |
56
+ | `ws` | boolean (requires `state=redux`)| `false` |
57
+ | `i18n` | boolean | `true` |
58
+ | `darkMode` | boolean | `true` |
59
+ | `tests` | boolean | `true` |
60
+ | `e2e` | boolean (requires `tests`) | `true` |
61
+ | `docker`, `ci` | boolean | `false` |
62
+ | `hooks`, `commitizen` | boolean | `true` |
63
+ | `analyzer`, `openapi` | boolean | `false` |
64
+ | `reactCompiler` | boolean | `true` |
65
+ | `communityFiles` | `CODE_OF_CONDUCT.md`, `CONTRIBUTING.md`, `SECURITY.md` | none |
66
+ | `githubTemplates`, `agentRules` | boolean | `false`, `true` |
144
67
 
145
- Every `setup` operation is **idempotent** re-running is a no-op on already-installed parts and a fix-up on missing parts.
68
+ Presets: `default`, `minimal`, `full`, `zustand`, `spa`. A `--config` file holds identity fields (`name`, `description`, `author`, `version`, `email`, `gitRemote`) plus `packageManager`, `preset`, and `options`.
146
69
 
147
- ---
70
+ What `init` does, in order: fetch the pinned starter (or `--starter-path` / `NEXT_MAKER_STARTER_PATH`), read its manifest, resolve answers (constraints such as `ws` needing Redux are enforced), apply the package-manager overlay, delete the files of features that are off, copy overlays for chosen variants, strip anchor comments and unwrap provider wrappers, prune `package.json` and `.env.example`, rewrite package-manager commands, stamp the identity, write `README.md` and `.next-maker.json`, install, format, copy `.env`, and initialise git.
148
71
 
149
- ### `doctor` Drift detection
72
+ Every generated project passes the starter's own gates (`lint`, `type-check`, `check:deprecated`, `test`, `build`); the `smoke` script composes a matrix of option combinations and runs them.
150
73
 
151
- `doctor` walks every feature manifest, asks "is this installed?" and (if yes) "are all of its parts present?". Drift is reported per feature.
74
+ ## workspace
152
75
 
153
76
  ```bash
154
- npx @teispace/next-maker doctor [options]
77
+ npx @teispace/next-maker workspace acme --apps web,admin
78
+ npx @teispace/next-maker workspace acme --apps web,admin,docs --yes --set state=zustand
79
+ npx @teispace/next-maker workspace acme --apps web,admin --docker # per-app Dockerfiles + compose
155
80
  ```
156
81
 
157
- | Flag | Behaviour |
158
- | --- | --- |
159
- | `--fix` | Runs each drifted feature's **repair** path, then re-checks and reports FIXED / STILL DRIFTED / NO AUTOMATIC FIX AVAILABLE per feature |
160
- | `--feature <id>` | Only check one manifest (e.g. `redux`, `security-headers`) |
161
- | `--json` | Machine-readable output for CI |
162
-
163
- Exit code is `0` on a clean report and `1` when drift is found — `next-maker doctor --json` makes a useful CI gate. With `--fix`, the exit code reflects the state **after** the repair: `0` only when every feature re-checks clean, `1` when anything is still drifted.
82
+ Creates:
164
83
 
165
- ```bash
166
- # Human report
167
- npx @teispace/next-maker doctor
168
-
169
- # Fix everything that drifted
170
- npx @teispace/next-maker doctor --fix
171
-
172
- # CI
173
- npx @teispace/next-maker doctor --json > health.json
174
84
  ```
175
-
176
- **Sample output:**
177
-
178
- ```
179
- 🩺 Project Doctor
180
-
181
- Security Headers
182
- ! Validation Scripts (2 issues)
183
- missing file: scripts/sync-env.ts
184
- missing script: validate
185
- — Redux Toolkit (not installed)
186
- ✓ Internationalization
187
-
188
- 1 clean, 1 drifted, 1 not installed
189
-
190
- Run with --fix to re-apply drifted features.
191
- ```
192
-
193
- **Sample `--fix` output:**
194
-
195
- ```
196
- 🔧 Applying fixes...
197
-
198
- ✓ Validation Scripts FIXED
199
- ✗ Internationalization STILL DRIFTED
200
- • missing file: src/app/[locale]
201
-
202
- 1 fixed, 1 still drifted
203
- ```
204
-
205
- `--fix` never claims success it can't back up: every feature is re-checked after its repair runs, and only a clean re-check counts as FIXED.
206
-
207
- Manifests live under `src/manifests/`; each one declares the files, packages, scripts, and code blocks the feature consists of. Adding a manifest for a new feature is a single file — `doctor` and `remove` automatically pick it up.
208
-
209
- ---
210
-
211
- ### `remove` — Reverse an install
212
-
213
- Symmetric to `setup`. Uses the feature manifest to compute the reversal: deletes generated files, strips code blocks (when a `removePattern` is recorded), removes scripts, uninstalls packages.
214
-
215
- ```bash
216
- npx @teispace/next-maker remove <feature> [options]
85
+ acme/
86
+ apps/web/ a starter app (same answers for every app)
87
+ apps/admin/
88
+ packages/ shared libraries you add
89
+ package.json turbo run dev | build | lint | type-check | test | validate
90
+ pnpm-workspace.yaml apps/*, packages/*, the starter's install policy, a catalog of shared ranges
91
+ turbo.json task graph, cached build output, NEXT_PUBLIC_* pass-through
92
+ biome.json root config for root files (apps keep their own)
93
+ .husky/, commitlint.config.mjs, .lintstagedrc.mjs (--no-hooks to skip)
94
+ .github/workflows/ci.yml (--no-ci to skip)
217
95
  ```
218
96
 
219
- | Flag | Behaviour |
220
- | --- | --- |
221
- | `--dry-run` | Print the planned changes without writing |
222
- | `-y, --yes` | Skip the confirmation prompt |
97
+ Git hooks, CI, Docker, community files, and the lockfile are root concerns, so those options are forced off inside the apps. The generated root README explains how to add a shared package (`pnpm add @acme/ui --workspace`), add another app, and build one app's Docker image with `turbo prune`. Only pnpm is supported for workspaces.
223
98
 
224
- **Auto-removed when possible:** provider chain unwraps (`<NextIntlClientProvider>`, `<StoreProvider>`, `<CustomThemeProvider>`), the `withNextIntl(...)` and `bundleAnalyzer(...)` wraps in `next.config.ts`, and their orphan import statements. The transforms are conservative — if the file shape has drifted from the canonical pattern (e.g. a multi-line opening tag, no matching close at the same indent), the runner bails out and surfaces it as manual cleanup rather than corrupting your code.
99
+ ## setup, remove, doctor, upgrade
225
100
 
226
- `remove` will **never recursively delete a directory that may hold user-authored content** — `src/app/[locale]/` (your pages), `src/i18n/` (your translations), `src/store/` (your slices), `src/lib/utils/http/` (your service code), and `test/` (your helpers) are flagged in the manifest with `containsUserContent: true` and surface as manual-cleanup with a hint. Move what you want to keep, then `rm -rf` the rest by hand.
101
+ These read `.next-maker.json` (written by `init`) and compose reference trees from the starter.
227
102
 
228
103
  ```bash
229
- # Preview the effect
230
- npx @teispace/next-maker remove redux --dry-run
231
-
232
- # Apply with confirmation
233
- npx @teispace/next-maker remove security-headers
234
-
235
- # Non-interactive
236
- npx @teispace/next-maker remove validate-scripts --yes
104
+ npx @teispace/next-maker options # what can change
105
+ npx @teispace/next-maker setup --set ws=true # add the WebSocket layer
106
+ npx @teispace/next-maker setup --set state=zustand --dry-run
107
+ npx @teispace/next-maker remove docker
108
+ npx @teispace/next-maker doctor --fix --compile
109
+ npx @teispace/next-maker upgrade --dry-run # to the starter tag this CLI is pinned to
110
+ npx @teispace/next-maker upgrade --to v2.1.0
237
111
  ```
238
112
 
239
- **Sample plan:**
113
+ `setup` and `upgrade` share one engine: the starter is composed twice (old answers and new answers, or old tag and new tag) with the project's identity, formatted with the project's Biome, and the project is three-way merged against the two trees. Lines the project never touched follow the starter, files a feature adds appear, files it owns disappear, anchored lines in shared files (a reducer registration, a provider import) merge in place, and `package.json` merges key by key. Only lines the project itself changed can conflict; those get `<<<<<<<` markers and are listed. `--dry-run` shows the file-by-file outcome first.
240
114
 
241
- ```
242
- 🗑 Remove Validation Scripts
243
-
244
- Planned changes:
245
- - delete file: scripts/sync-env.ts
246
- - delete file: scripts/check-deprecated.ts
247
- - remove script: env:sync
248
- - remove script: check:deprecated
249
- - remove script: validate
250
- - uninstall: tsx
251
- ```
252
-
253
- ---
115
+ `doctor` compares the project with the footprint of every feature its record says is on (files, packages, scripts), `--fix` restores what is missing from a pristine starter checkout, and `--compile` runs the project's type-check, which is the only honest signal that the pieces still fit.
254
116
 
255
- ## Code generators
256
-
257
- ### `page` — Generate a page/route
117
+ ## Generators
258
118
 
259
119
  ```bash
260
- npx @teispace/next-maker page <name> [options]
261
- ```
262
-
263
- | Flag | Effect |
264
- | --- | --- |
265
- | `--dynamic <param>` | Dynamic segment (e.g. `--dynamic id` produces `[id]/page.tsx`) |
266
- | `--loading` | Generate `loading.tsx` |
267
- | `--error` | Generate `error.tsx` |
268
-
269
- **What it does:**
270
-
271
- - Writes `page.tsx` under `src/app/[locale]/<name>/` (i18n) or `src/app/<name>/`
272
- - When i18n is detected: `generateMetadata`, `setRequestLocale`, `getTranslations`
273
- - Registers route in `src/lib/config/app-paths.ts`
274
- - Adds translation namespace to `en.json`
275
-
276
- ```bash
277
- npx @teispace/next-maker page about
278
- npx @teispace/next-maker page dashboard --loading --error
279
- npx @teispace/next-maker page products --dynamic id --loading --error
280
- ```
281
-
282
- ---
283
-
284
- ### `layout` — Generate a nested layout
285
-
286
- ```bash
287
- npx @teispace/next-maker layout <segment> [options]
288
- ```
289
-
290
- | Flag | Effect |
291
- | --- | --- |
292
- | `--group` | Wraps the segment in parens for a route group, e.g. `(marketing)` |
293
- | `--at <path>` | Places the layout under a nested path (kebab-case, slash-separated) |
294
- | `--no-locale` | Skip the locale wrapper even when i18n is detected |
295
-
296
- When i18n is detected the generated layout consumes `params: Promise<{ locale: string }>` and calls `setRequestLocale`. Otherwise it's a plain `({ children }) => <>{children}</>` shell.
297
-
298
- ```bash
299
- # /[locale]/dashboard/layout.tsx
300
- npx @teispace/next-maker layout dashboard
301
-
302
- # Route group: /[locale]/(marketing)/layout.tsx
303
- npx @teispace/next-maker layout marketing --group
304
-
305
- # Nested: /[locale]/dashboard/settings/preferences/layout.tsx
306
- npx @teispace/next-maker layout preferences --at dashboard/settings
307
- ```
308
-
309
- Refuses to overwrite an existing `layout.tsx` — explicit error rather than silent loss.
310
-
311
- ---
312
-
313
- ### `component` — Shared component
314
-
315
- ```bash
316
- npx @teispace/next-maker component <name> [options]
317
- ```
318
-
319
- | Flag | Effect |
320
- | --- | --- |
321
- | `--client` | Adds `'use client'` directive |
322
- | `--i18n` | Imports `useTranslations` |
323
- | `--feature <path>` | Generate inside a feature directory |
324
- | `--test` / `--no-test` | Co-generate a sibling `*.test.tsx` (default: on when Vitest is installed) |
325
-
326
- **Generated structure:**
327
-
328
- ```
329
- src/components/common/MyButton/
330
- ├── MyButton.tsx
331
- └── index.ts
332
- ```
333
-
334
- Auto-updates `src/components/common/index.ts` and `src/components/index.ts`.
335
-
336
- ```bash
337
- npx @teispace/next-maker component data-table --client
338
- npx @teispace/next-maker component nav-bar --client --i18n
339
- npx @teispace/next-maker component user-card --client --feature src/features/auth
340
- ```
341
-
342
- ---
343
-
344
- ### `feature` — Full feature module
345
-
346
- ```bash
347
- npx @teispace/next-maker feature <name> [options]
348
- ```
349
-
350
- | Flag | Effect |
351
- | --- | --- |
352
- | `--store <type>` | Generate Redux store: `persist` or `no-persist` |
353
- | `--skip-store` | Skip Redux store |
354
- | `--service <client>` | API service: `axios` or `fetch` |
355
- | `--skip-service` | Skip API service |
356
- | `--path <path>` | Custom path (default `src/features`) |
357
-
358
- **Generated structure:**
359
-
360
- ```
361
- src/features/user-dashboard/
362
- ├── components/UserDashboard.tsx
363
- ├── hooks/useUserDashboard.ts
364
- ├── types/user-dashboard.types.ts
365
- ├── store/ (optional)
366
- ├── services/ (optional)
367
- └── index.ts
368
- ```
369
-
370
- ```bash
371
- npx @teispace/next-maker feature user-profile --store persist --service axios
372
- npx @teispace/next-maker feature shopping-cart --store no-persist --skip-service
373
- npx @teispace/next-maker feature auth --store persist --service fetch --path src/modules
374
- ```
375
-
376
- ---
377
-
378
- ### `slice` — Redux slice
379
-
380
- ```bash
381
- npx @teispace/next-maker slice <name> [options]
382
- ```
383
-
384
- | Flag | Effect |
385
- | --- | --- |
386
- | `--persist` / `--no-persist` | Toggle redux-persist for this slice |
387
- | `--path <path>` | Custom path (default: new feature) |
388
- | `--test` / `--no-test` | Co-generate `*.slice.test.ts` (default: on when Vitest is installed) |
389
-
390
- Auto-registers in `rootReducer` with correct imports and persist config.
391
-
392
- ```bash
393
- npx @teispace/next-maker slice auth --persist
394
- npx @teispace/next-maker slice user-settings --path features/auth/store
395
- ```
396
-
397
- ---
398
-
399
- ### `service` — API service
400
-
401
- ```bash
402
- npx @teispace/next-maker service <name> [options]
403
- ```
404
-
405
- | Flag | Effect |
406
- | --- | --- |
407
- | `--axios` / `--fetch` | Pick the HTTP client |
408
- | `--crud` | Generate full CRUD (`getAll`, `getById`, `create`, `update`, `delete`) |
409
- | `--path <path>` | Custom path |
410
-
411
- CRUD mode also generates `<Name>Summary` (list view) and `<Name>Detail` (detail view) types, `Create<Name>Dto`, `Update<Name>Dto`, and registers the endpoints in `app-apis.ts`.
412
-
413
- Endpoints are emitted as **bare paths** (e.g. `'/users'`, `` `/users/${id}` ``) — the `/api/v{n}` prefix is owned by `getApiBaseUrl()` in `src/lib/config/api-url.ts` and applied at request time. Don't add `${API_PREFIX}` interpolation in `app-apis.ts`; the base URL composer handles it.
414
-
415
- ```bash
416
- npx @teispace/next-maker service payment --axios
417
- npx @teispace/next-maker service users --fetch --crud
418
- npx @teispace/next-maker service orders --axios --crud --path features/products/services
419
- ```
420
-
421
- ---
422
-
423
- ### `provider` — Context provider
424
-
425
- ```bash
426
- npx @teispace/next-maker provider <name>
427
- ```
428
-
429
- Generates `src/providers/<Name>Provider.tsx` with:
430
-
431
- - `'use client'` directive
432
- - `createContext` + typed context value
433
- - `useX()` hook with non-null guard
434
- - `<XProvider>` component memoising the value with `useMemo`
435
-
436
- Then **two-tier auto-wiring**:
437
-
438
- 1. Locates `RootProvider.tsx` — canonical path first (`src/providers/RootProvider.tsx`), heuristic scan as fallback (looks for the deepest `{children}` chain in `src/providers/*.tsx`).
439
- 2. Wraps `{children}` with `<XProvider>` inside that chain, preserving indentation.
440
- 3. Updates `src/providers/index.ts` barrel (re-exports kept sorted).
441
-
442
- If no candidate is found, the generator prints the snippet to wire manually rather than failing silently.
443
-
444
- ```bash
445
- npx @teispace/next-maker provider auth # → AuthProvider, useAuth
446
- npx @teispace/next-maker provider session # → SessionProvider, useSession
447
- npx @teispace/next-maker provider analytics-provider # already-suffixed names work
448
- ```
449
-
450
- The wrap is **idempotent** — running twice is a no-op.
451
-
452
- ---
453
-
454
- ### `env` — Declare an env var
455
-
456
- Adds a new variable across the four files the starter keeps in sync, atomically and idempotently:
457
-
458
- 1. `src/lib/env/schema.ts` — Zod entry with the right wrapper (`preprocess(emptyStringToUndefined, …)` for non-enum types)
459
- 2. `.env.example` — `KEY=` placeholder with optional description comment and `# -public` marker
460
- 3. `.env` — `KEY=<default>` line when the file exists
461
-
462
- ```bash
463
- npx @teispace/next-maker env <NAME> [options]
464
- ```
465
-
466
- | Flag | Effect |
467
- | --- | --- |
468
- | `--type <type>` | `string` (default), `url`, `number`, `boolean`, `enum` |
469
- | `--required` | Skips `.optional()` and `.default()` |
470
- | `--default <value>` | Adds `.default(value)` (mutually exclusive with `--required`) |
471
- | `--public` | Auto-prefixes `NEXT_PUBLIC_` if missing and tags `.env.example` with `# -public` |
472
- | `--describe <text>` | Adds `.describe()` and a comment line in `.env.example` |
473
- | `--enum <list>` | Comma-separated values, required when `--type=enum` |
474
-
475
- ```bash
476
- # Optional URL
477
- npx @teispace/next-maker env SENTRY_DSN --type url --describe "Sentry endpoint"
478
-
479
- # Public default
480
- npx @teispace/next-maker env API_URL --type url --public --default "http://localhost:3000"
481
-
482
- # Required string (no default, no .optional)
483
- npx @teispace/next-maker env DATABASE_URL --type url --required
484
-
485
- # Enum
486
- npx @teispace/next-maker env LOG_LEVEL --type enum --enum debug,info,warn,error --default info
487
-
488
- # Coerced number
489
- npx @teispace/next-maker env PORT --type number --default 3000
490
- ```
491
-
492
- The generator quote-escapes defaults and descriptions, mirrors the starter's house style, and is fully covered by tests against the `schema.ts`/`.env.example`/`.env` shapes.
493
-
494
- ---
495
-
496
- ### `locale` — Add a locale
497
-
498
- ```bash
499
- npx @teispace/next-maker locale [code] [options]
500
- ```
501
-
502
- | Flag | Effect |
503
- | --- | --- |
504
- | `--copy-translations` | Copy English translations as the starting point |
505
-
506
- Creates `src/i18n/translations/<code>.json`, updates `SupportedLocale`, and adds the entry to `src/lib/config/app-locales.ts` (name, flag, country).
507
-
508
- ```bash
509
- npx @teispace/next-maker locale es
510
- npx @teispace/next-maker locale fr --copy-translations
511
- ```
512
-
513
- ---
514
-
515
- ### `hook` — Custom React hook
516
-
517
- ```bash
518
- npx @teispace/next-maker hook <name> [options]
519
- ```
520
-
521
- | Flag | Effect |
522
- | --- | --- |
523
- | `--client` | Add `'use client'` directive (default true) |
524
- | `--feature <path>` | Generate inside a feature directory |
525
- | `--test` / `--no-test` | Co-generate `*.test.ts` |
526
-
527
- ```bash
528
- npx @teispace/next-maker hook auth-session
529
- npx @teispace/next-maker hook user-profile --feature src/features/auth
530
- ```
531
-
532
- ---
533
-
534
- ### `test` — Retrofit a sibling test
535
-
536
- Adds a `*.test.{ts,tsx}` next to existing code that pre-dates the `--test` flag.
537
-
538
- ```bash
539
- npx @teispace/next-maker test <file> [options]
540
- ```
541
-
542
- | Flag | Effect |
543
- | --- | --- |
544
- | `--kind <kind>` | Override inferred kind: `component` / `hook` / `slice` |
545
- | `--force` | Overwrite an existing test file |
546
-
547
- Inference rules:
548
-
549
- - `*.slice.ts` → reducer test
550
- - `use*.ts` (or content starting with `export function useX`) → hook test (`renderHook`)
551
- - `*.tsx` → component test (`renderWithProviders`)
552
-
553
- ```bash
554
- npx @teispace/next-maker test src/features/auth/components/LoginForm.tsx
555
- npx @teispace/next-maker test src/features/auth/store/auth.slice.ts
556
- npx @teispace/next-maker test src/hooks/use-debounce.ts --kind hook --force
557
- ```
558
-
559
- Requires `setup --tests` first.
560
-
561
- ---
562
-
563
- ### `favicon` — Generate icons from a source image
564
-
565
- Generates `favicon.ico` (multi-size) into the App Router root, with optional `icon.png`, `apple-icon.png`, `opengraph-image.png`, `twitter-image.png`, and PWA manifest icons. Source can be PNG, JPG, JPEG, WebP, SVG, or AVIF.
566
-
567
- ```bash
568
- npx @teispace/next-maker favicon [options]
569
- ```
570
-
571
- | Flag | Effect |
572
- | --- | --- |
573
- | `--path <file>` | Source image. If omitted, you'll be prompted. |
574
- | `--out <dir>` | Output directory (default: auto-detected `src/app` or `app`) |
575
- | `--icon` | Also emit `icon.png` (512×512) |
576
- | `--apple` | Also emit `apple-icon.png` (180×180) |
577
- | `--og` | Also emit `opengraph-image.png` (1200×630) |
578
- | `--og-source <file>` | Use a separate image for the OG/Twitter card |
579
- | `--twitter` | Also emit `twitter-image.png` (1200×600) |
580
- | `--all` | Shorthand for `--icon --apple --og` |
581
- | `--pwa` | Detect PWA setup and emit manifest icons (192/512); errors if not detected |
582
- | `--pwa-init` | Bootstrap `public/manifest.webmanifest` with icons |
583
- | `--shape <shape>` | `square` \| `rounded` \| `circle` \| `squircle` (default: `square`) |
584
- | `--radius <percent>` | Corner radius % when `--shape=rounded` (0–50, default: 20) |
585
- | `--bg <color>` | Background: `transparent`, hex (`#0f172a`), CSS name, or `rgb()`/`rgba()`/`hsl()`/`hsla()` (default: `transparent`) |
586
- | `--padding <percent>` | Padding around source content (0–30, default: 0) |
587
- | `--fit <fit>` | `contain` \| `cover` \| `clip` (default: `contain`) |
588
- | `--sizes <list>` | Comma-separated ICO sizes (default: `16,32,48`) |
589
- | `--quality <n>` | PNG compression 1–100 (higher = smaller; PNG is lossless, default: 90) |
590
- | `--force` | Overwrite existing files without prompt |
591
- | `--dry-run` | Show what would be written without writing |
592
-
593
- ```bash
594
- # Minimal: just favicon.ico from a source PNG
595
- npx @teispace/next-maker favicon --path ./brand/logo.png
596
-
597
- # Full set: favicon + icon + apple-icon + OG image, rounded with brand background
598
- npx @teispace/next-maker favicon \
599
- --path ./brand/logo.svg \
600
- --all \
601
- --shape rounded --radius 24 \
602
- --bg "#0f172a" \
603
- --padding 8
604
-
605
- # PWA: emit manifest icons and bootstrap public/manifest.webmanifest
606
- npx @teispace/next-maker favicon --path ./brand/logo.png --pwa-init
607
-
608
- # Separate OG art
609
- npx @teispace/next-maker favicon \
610
- --path ./brand/logo.png \
611
- --og --og-source ./brand/social-card.png
612
-
613
- # Preview without writing
614
- npx @teispace/next-maker favicon --path ./brand/logo.png --all --dry-run
615
- ```
616
-
617
- Outputs land in your App Router root (`src/app/` or `app/`) so Next.js auto-resolves them via the file conventions for [`favicon.ico`](https://nextjs.org/docs/app/api-reference/file-conventions/metadata/app-icons), [`opengraph-image`](https://nextjs.org/docs/app/api-reference/file-conventions/metadata/opengraph-image), and [`apple-icon`](https://nextjs.org/docs/app/api-reference/file-conventions/metadata/app-icons) — no `<head>` wiring required.
618
-
619
- ---
620
-
621
- ## End-to-end examples
622
-
623
- ### Quick start
624
-
625
- ```bash
626
- npx @teispace/next-maker init my-project
627
- cd my-project
628
-
629
- # Routes & layouts
630
- npx @teispace/next-maker page dashboard --loading --error
631
- npx @teispace/next-maker layout dashboard
632
- npx @teispace/next-maker page products --dynamic id --loading --error
633
-
634
- # State + data
635
- npx @teispace/next-maker feature users --store persist --service fetch
636
- npx @teispace/next-maker service users --fetch --crud --path features/users/services
637
-
638
- # Providers & env
639
- npx @teispace/next-maker provider auth
120
+ npx @teispace/next-maker feature invoice --api --store --persist
121
+ npx @teispace/next-maker api order --no-actions
122
+ npx @teispace/next-maker slice cart --persist
123
+ npx @teispace/next-maker page reports --group app --loading --error
124
+ npx @teispace/next-maker page post --dynamic slug
125
+ npx @teispace/next-maker layout dashboard --group
126
+ npx @teispace/next-maker component badge --client --i18n
127
+ npx @teispace/next-maker provider feature-flags
640
128
  npx @teispace/next-maker env SENTRY_DSN --type url --describe "Sentry endpoint"
641
-
642
- # i18n
643
- npx @teispace/next-maker locale es
644
-
645
- # Health check before commit
646
- npx @teispace/next-maker doctor
647
-
648
- npm run dev
649
- ```
650
-
651
- ### Feature-driven workflow
652
-
653
- ```bash
654
- npx @teispace/next-maker feature products --store persist --service axios
655
- npx @teispace/next-maker service products --axios --crud --path features/products/services
656
- npx @teispace/next-maker page products --dynamic id --loading --error
657
- npx @teispace/next-maker component product-card --client --feature src/features/products
658
- npx @teispace/next-maker layout products
659
-
660
- npx @teispace/next-maker feature cart --store persist --skip-service
661
- npx @teispace/next-maker page checkout --loading --error
662
- ```
663
-
664
- ### Maintenance — re-applying drifted features
665
-
666
- ```bash
667
- # What's drifted?
668
- npx @teispace/next-maker doctor
669
-
670
- # Fix in place
671
- npx @teispace/next-maker doctor --fix
672
-
673
- # Pull a feature out cleanly
674
- npx @teispace/next-maker remove i18n --dry-run
675
- npx @teispace/next-maker remove i18n
129
+ npx @teispace/next-maker locale es --name Spanish --country Spain --flag 🇪🇸
676
130
  ```
677
131
 
678
- ---
679
-
680
- ## Architecture
681
-
682
- ### Layers
683
-
684
- ```
685
- src/
686
- ├── commands/ # Commander definitions (one file per CLI command)
687
- ├── prompts/ # Enquirer flows (one per command needing interactivity)
688
- ├── generators/ # generate<X>(): writes files for a single artifact
689
- │ └── templates/ # Pure string-template functions (testable in isolation)
690
- ├── modifiers/ # Pure functions that surgically edit existing files
691
- ├── pipelines/ # PipelineStep[] composers for multi-step generators
692
- ├── services/setup/ # Retrofit installers, one per feature
693
- ├── manifests/ # Declarative feature footprints (drives doctor + remove)
694
- ├── detection/ # "Is feature X installed?" detectors
695
- ├── core/ # File I/O + package-manager helpers (yarn/npm/pnpm/bun)
696
- └── config/ # PROJECT_PATHS, PACKAGES, spinner, output, error handlers
697
- ```
698
-
699
- ### The manifest pattern
700
-
701
- Each feature has a manifest in `src/manifests/<feature>.manifest.ts` describing:
702
-
703
- - **detect** — high-level "is this installed?"
704
- - **files** — paths the feature owns (with `generated: true/false` to control deletion safety)
705
- - **packages** — runtime/dev dependencies
706
- - **scripts** — `package.json` script entries (with optional exact-value match)
707
- - **injections** — code blocks in shared files (with `presence`, optional `removePattern`, and optional `alternativeFiles` when the block's home depends on the project shape — e.g. `[locale]/layout.tsx` *or* `src/app/layout.tsx`; the block only has to be in one of the candidates that exist)
708
- - **apply** — `withRepair(setup<X>, repair<X>)`. Called with no drift it installs from scratch (`setup`); called with a drift array it takes the repair path (`doctor --fix`), fixing exactly the reported findings without consulting the installer's "already set up?" guard
709
- - **remove** — optional custom remover (defaults to the generic reverser)
710
-
711
- The manifest registry is consumed by:
712
-
713
- - `setup` — calls `apply()` directly
714
- - `doctor` — walks every manifest, reports drift via `checkManifest`
715
- - `remove` — runs `reverseManifest` (or a custom `remove`) and prints the plan
716
-
717
- Adding a manifest for a new feature is a single file. The CLI commands don't need to know about it.
718
-
719
- ### Modifier conventions
720
-
721
- - **Pure transformation modules** (e.g. `headers.ts`, `package-modifier.ts`, `env-var.modifier.ts`) take strings or JSON and return strings or JSON. No filesystem.
722
- - **Thin async wrappers** (`index.ts` per setup service, the modifier orchestrators) handle I/O and spinners.
723
- - **Idempotency** is required: re-running anything is a no-op when the target state is already reached. This is what makes `doctor --fix` safe.
724
-
725
- ---
726
-
727
- ## Project structure (generated app)
728
-
729
- The full shape of a scaffolded app (branches marked `(opt)` are stripped when the matching prompt is declined during `init`):
132
+ A generated feature:
730
133
 
731
134
  ```
732
- my-project/
733
- ├── src/
734
- │ ├── app/ # Next.js App Router
735
- │ │ ├── [locale]/ # (opt, i18n) locale-aware routes
736
- │ │ │ ├── layout.tsx
737
- │ │ │ ├── page.tsx
738
- │ │ │ ├── error.tsx
739
- │ │ │ └── not-found.tsx
740
- │ │ ├── global-error.tsx
741
- │ │ ├── not-found.tsx
742
- │ │ ├── robots.ts
743
- │ │ ├── sitemap.ts
744
- │ │ ├── favicon.ico # `favicon` command
745
- │ │ ├── icon.png # (opt, `favicon --icon`/`--all`) 512×512
746
- │ │ ├── apple-icon.png # (opt, `favicon --apple`/`--all`) 180×180
747
- │ │ ├── opengraph-image.png # (opt, `favicon --og`/`--all`) 1200×630
748
- │ │ └── twitter-image.png # (opt, `favicon --twitter`) 1200×600
749
- │ ├── proxy.ts # (opt, i18n) Next 16 middleware replacement
750
- │ ├── features/ # Feature modules (feature-first DDD)
751
- │ │ └── counter/ # (opt, redux) example feature
752
- │ │ ├── components/
753
- │ │ │ ├── Counter.tsx
754
- │ │ │ └── Counter.test.tsx # (opt, tests)
755
- │ │ ├── hooks/useCounter.ts
756
- │ │ ├── store/ # slice + selectors + persist + barrel
757
- │ │ └── types/counter.types.ts
758
- │ ├── components/
759
- │ │ ├── common/ # Shared UI (auto-wired barrel exports)
760
- │ │ └── index.ts
761
- │ ├── providers/
762
- │ │ ├── RootProvider.tsx # Composes Store → Theme → Intl → custom
763
- │ │ ├── StoreProvider.tsx # (opt, redux) useRef + PersistGate
764
- │ │ ├── CustomThemeProvider.tsx # (opt, dark-mode) @teispace/next-themes
765
- │ │ └── index.ts # Barrel — `next-maker provider <name>` keeps it sorted
766
- │ ├── store/ # (opt, redux) makeStore, rootReducer, typed hooks
767
- │ │ └── slices/ws.slice.ts # (opt, ws) non-persisted connection state slice
768
- │ ├── services/
769
- │ │ ├── api/ # API service barrel
770
- │ │ └── storage/ # react-secure-storage wrapper
771
- │ ├── lib/
772
- │ │ ├── config/ # seo, app-apis, app-paths, app-locales, constants, api-url (getApiBaseUrl)
773
- │ │ ├── env/ # Zod-validated env schema (schema.ts, validate.ts)
774
- │ │ ├── logger/ # Pino logger with auto-redaction
775
- │ │ ├── errors/ # ApiException (carries requestId), catchError
776
- │ │ ├── enums/
777
- │ │ └── utils/
778
- │ │ ├── http/ # (opt, http-client)
779
- │ │ │ ├── shared/ # runtime guards, request-id, parseApiError, toSearchParams
780
- │ │ │ ├── axios-client/ # interceptors, token refresh, Result-based
781
- │ │ │ ├── fetch-client/ # same Result pattern on native fetch, typed `params`
782
- │ │ │ ├── __bundle-sentinel__/ # build-time regression gate ('use client' check)
783
- │ │ │ ├── client-utils.ts
784
- │ │ │ ├── token-store.ts # inert in cookie-mode (the default)
785
- │ │ │ ├── index.ts # universal entry — safe in client/server/edge
786
- │ │ │ └── server.ts # server-only entry — forwards next/headers cookies
787
- │ │ ├── ws/ # (opt, ws) socket.io-client wrapper, hooks, Redux bridge
788
- │ │ │ ├── client/ # WsClient + lazy `wsClient` singleton (Proxy)
789
- │ │ │ ├── hooks/ # useWsStatus, useWsEvent, useWsEmit
790
- │ │ │ ├── redux/ # bridge (the only WS dispatcher) + selectors
791
- │ │ │ ├── shared/ # SSR guard, auth carrier, URL composer
792
- │ │ │ ├── types/ # ClientToServerEvents/ServerToClientEvents, payloads
793
- │ │ │ ├── constants.ts # namespace, heartbeat, reconnection bounds
794
- │ │ │ └── index.ts # public barrel
795
- │ │ └── validations/
796
- │ ├── i18n/ # (opt, i18n) routing, request, navigation, translations/
797
- │ ├── styles/globals.css # Tailwind v4 directives
798
- │ └── types/ # common/, utility/ (Result, Either), i18n.ts
799
- ├── test/ # (opt, tests)
800
- │ ├── setup.ts # testing-library + jsdom setup
801
- │ └── test-utils.tsx # renderWithProviders, TestProviders
802
- ├── scripts/
803
- │ ├── sync-env.ts # .env.example ← .env (respects `-public` markers)
804
- │ └── check-deprecated.ts # fails build if @deprecated APIs are referenced
805
- ├── public/
806
- ├── biome.json # single-tool lint + format
807
- ├── next.config.ts # security headers, reactCompiler (opt), bundleAnalyzer (opt), withNextIntl (opt, i18n)
808
- ├── vitest.config.ts # (opt, tests)
809
- ├── postcss.config.mjs # @tailwindcss/postcss
810
- ├── tsconfig.json
811
- ├── .env.example
812
- ├── .czrc # (opt, commitizen)
813
- ├── .husky/ # (opt, pre-commit hooks)
814
- ├── .lintstagedrc.mjs # (opt, pre-commit hooks) runs `biome check --write`
815
- ├── commitlint.config.mjs # (opt, pre-commit hooks)
816
- ├── Dockerfile # (opt, docker) multi-stage, standalone mode
817
- ├── docker-compose.yml # (opt, docker)
818
- ├── AGENTS.md # Agent coding rules (referenced from CLAUDE.md)
819
- ├── CLAUDE.md
820
- └── package.json
135
+ src/features/invoice/
136
+ api/
137
+ schema.ts zod contracts, inferred types
138
+ keys.ts TanStack Query keys
139
+ server.ts DAL over serverHttp ('server-only')
140
+ actions.ts create/update/delete with authActionClient ('use server')
141
+ queries.ts queryOptions + useSuspenseQuery hooks
142
+ components/InvoiceList.tsx (+ .test.tsx when tests are on)
143
+ store/ slice, selectors, persistence entry (Redux) or slice creator (Zustand)
144
+ types/invoice.types.ts
145
+ index.ts client-safe barrel
146
+ server.ts server-only barrel
821
147
  ```
822
148
 
823
- Features that have first-class opt-in/opt-out prompts during `init`: `httpClient`, `darkMode`, `redux`, `ws` (opt-in, requires `redux`), `i18n`, `tests`, `reactCompiler`, `bundleAnalyzer`, `docker`, `ci`, `preCommitHooks`, `commitizen`, `communityFiles`, `readme`, `copyEnv`. Each opt-out has a matching `setup --<feature>` to re-add later. `doctor` and `remove` cover the whole installed footprint via manifests.
824
-
825
- ---
826
-
827
- ## Tech Stack
828
-
829
- **CLI:** TypeScript, esbuild, Commander.js, Enquirer, Vitest, degit.
830
-
831
- **Generated apps:** Next.js 16+, TypeScript, Tailwind CSS v4, Biome, Pino, Zod, Redux Toolkit, `@teispace/next-themes`, next-intl, Fetch / Axios HTTP clients (shared foundation), `socket.io-client` (opt-in via `ws` prompt), Vitest + RTL, React Compiler.
832
-
833
- ---
834
-
835
- ## Recent changes
836
-
837
- Behaviour added in **v2.1.0** beyond the headline features. None of these change the user-facing CLI surface — they harden parts of the generated app that previously needed manual cleanup.
838
-
839
- - **HTTP sentinel + server entry auto-align.** The template ships `src/lib/utils/http/__bundle-sentinel__/client-bundle-sentinel.tsx` and `src/lib/utils/http/server.ts` with imports of **both** axios and fetch client modules. When you pick a single client (at `init` or via `setup --http-client`), the inactive variant's directory is removed — and the two files would normally still reference it, breaking `yarn build`. Both are now rewritten in place to mention only the active client(s). Idempotent: re-running `setup --http-client` against the same configuration is a no-op.
840
- - **`remove ws` strips cleanly without manual intervention.** The WS manifest's reducer-registration and bridge-mount injections now carry `removePattern` functions, so `next-maker remove ws` deletes the `wsReducer` import, the `ws: wsReducer` entry, the unwrapped-on-purpose JSDoc, and the `attachWsBridge` `useEffect` block — without any "manual cleanup" messages. The init-time `cleanupWs` and the `remove ws` flow share the same pure helpers, so the byte-level output is identical.
841
- - **`useEffect` import auto-pruned.** When the WS bridge effect is stripped (init opt-out or `remove ws`), the `useEffect` import is dropped from `StoreProvider.tsx`'s React import line if no other `useEffect(` calls remain — avoiding the unused-import lint warning.
842
- - **Wording-tolerant strip helpers.** The `stripBundleSentinel`, `stripBridgeMount`, and `stripWsReducerRegistration` helpers now anchor on stable code tokens (import paths, function names, the `ws` + `persistReducer` keywords inside JSDoc) rather than literal comment text. Upstream template comment rewording can't silently break cleanup.
843
- - **`doctor` reports sentinel mount drift — and only real drift.** The HTTP manifest tracks the `<HttpClientBundleSentinel />` mount as a single code injection with two possible homes (`alternativeFiles`): `[locale]/layout.tsx` when i18n is installed, `src/app/layout.tsx` otherwise. Only the layout that actually exists is checked, so a healthy project reports clean; if you delete the mount manually, `doctor` reports it, `doctor --fix` re-mounts it, and `remove http-client` strips it.
844
-
845
- ---
846
-
847
- ## Known issues
848
-
849
- - **`setup --redux` post-init when `ws` was opted out.** The template's `StoreProvider.tsx` ships with the WS bridge mount inline. `next-maker setup --redux` copies the template's `StoreProvider.tsx` wholesale into the project — so running it on a project where `ws` is **not** wanted leaves `import { attachWsBridge, wsClient } from '@/lib/utils/ws'` in place, and `yarn build` fails because that module doesn't exist. Workaround until a fix lands: also run `next-maker setup --ws` to install the WS layer, then `next-maker remove ws` if you don't actually want it (clean removal handles both layers). Tracked separately; out of scope for v2.1.0.
850
- - **`doctor --fix` can't recreate `src/app/[locale]/`.** Producing that directory means running the i18n migration, which *moves* app-router pages. Doing that unattended to a project that has since lost the segment could relocate user code with no way back, so the i18n repair skips it and doctor reports it as STILL DRIFTED with guidance. Every other part of the i18n footprint is repaired.
851
- - **Repairs that need starter assets need network.** `doctor --fix` re-copies deleted files (`src/i18n/`, `test/setup.ts`, `CustomThemeProvider.tsx`, …) from the starter repo via `degit`. Offline, those repairs fail and are reported as STILL DRIFTED rather than silently skipped. Package-only and code-block-only repairs work offline.
852
-
853
- ---
149
+ Endpoints are registered in `src/lib/config/app-apis.ts`, Redux slices in `combineSlices` and the persistence `entries`, translation namespaces in `en.json`. Zustand slices print the one manual step (compose the creator into `AppState`).
854
150
 
855
151
  ## Development
856
152
 
857
- ### Setup
858
-
859
153
  ```bash
860
- git clone <repository-url>
861
- cd npm-packages/packages/next-maker
862
154
  yarn install
155
+ yarn workspace @teispace/next-maker test
156
+ yarn workspace @teispace/next-maker type-check
157
+ NEXT_MAKER_STARTER_PATH=../../../starters/nextjs-starter yarn workspace @teispace/next-maker smoke
158
+ yarn workspace @teispace/next-maker build
863
159
  ```
864
160
 
865
- ### Build
866
-
867
- ```bash
868
- yarn build # esbuild bundle + tsc --emitDeclarationOnly
869
- ```
870
-
871
- ### Test
872
-
873
- ```bash
874
- yarn test # one-shot
875
- yarn test:watch
876
- ```
877
-
878
- The test suite covers every modifier, generator template, and manifest runner with deterministic string/JSON inputs. Generators that touch the filesystem are tested against `os.tmpdir()` directories.
879
-
880
- ### Smoke-test the built CLI
881
-
882
- ```bash
883
- node dist/index.js init test-project
884
- cd test-project
885
-
886
- # Generators
887
- node ../dist/index.js page dashboard --loading --error
888
- node ../dist/index.js layout dashboard
889
- node ../dist/index.js component sidebar --client
890
- node ../dist/index.js hook debounce
891
- node ../dist/index.js provider auth
892
- node ../dist/index.js slice filters --persist
893
- node ../dist/index.js feature users --store persist --service axios
894
- node ../dist/index.js service users --fetch --crud
895
- node ../dist/index.js env SENTRY_DSN --type url --describe "Sentry"
896
- node ../dist/index.js locale es
897
- node ../dist/index.js test src/hooks/useDebounce.ts --force
898
-
899
- # Maintenance
900
- node ../dist/index.js doctor
901
- node ../dist/index.js setup --ws # add WebSocket layer (project must already have --redux)
902
- node ../dist/index.js remove redux --dry-run
903
- ```
161
+ `smoke` accepts case names (`default`, `minimal`, `full`, `zustand`, `spa`, `no-i18n`, `no-dark`, `no-state-i18n`, `zustand-no-i18n-axios`, `npm`, `bff`); set `SMOKE_KEEP=1` to keep the generated projects and `SMOKE_E2E=1` to run the default case's Playwright suite (browsers must be installed).
904
162
 
905
- ### Adding a new feature manifest
163
+ ### Layers
906
164
 
907
- 1. Drop a file under `src/manifests/<feature>.manifest.ts` exporting a `FeatureManifest`.
908
- 2. Append it to the `MANIFESTS` array in `src/manifests/index.ts`.
909
- 3. (Optional) Add a `setup --<feature>` flag if you want a flag-based retrofit.
165
+ - `src/composition/`: manifest loading and answer resolution, literal-path globs, anchor stripping and unwrapping, the composition planner and applier, package-manager rewrites, presets, config files, and the project record used by `setup`/`doctor`.
166
+ - `src/commands/`: one file per command; commands parse flags, prompt, and call generators or the composition engine.
167
+ - `src/generators/` and `src/modifiers/`: file templates and the small, idempotent edits to shared files (`rootReducer.ts`, `app-apis.ts`, `RootProvider.tsx`, `env/index.ts`, `i18n.ts`).
168
+ - `src/config/starter.ts`: the pinned starter tag and the local-path override.
910
169
 
911
- `doctor` and `remove` will pick it up automatically.
170
+ ### Bumping the starter
912
171
 
913
- ---
172
+ 1. Tag the starter.
173
+ 2. Set `STARTER_REF` in `src/config/starter.ts`.
174
+ 3. Run `smoke` against the tag; every case must pass.
175
+ 4. Release the CLI.
914
176
 
915
177
  ## License
916
178
 
917
179
  MIT
918
-
919
- ---
920
-
921
- ## Contributing
922
-
923
- Contributions are welcome. Please open a PR.
924
-
925
- 1. Fork the repository
926
- 2. Create your feature branch (`git checkout -b feature/amazing-feature`)
927
- 3. Commit your changes (`git commit -m 'feat: add amazing feature'`)
928
- 4. Push to the branch (`git push origin feature/amazing-feature`)
929
- 5. Open a Pull Request
930
-
931
- ---
932
-
933
- ## Support
934
-
935
- For issues and questions, please visit our [GitHub Issues](https://github.com/teispace/npm-packages/issues).