@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.
- package/CHANGELOG.md +11 -0
- package/README.md +119 -875
- package/dist/index.js +779 -1045
- package/package.json +9 -4
package/README.md
CHANGED
|
@@ -1,935 +1,179 @@
|
|
|
1
1
|
# @teispace/next-maker
|
|
2
2
|
|
|
3
|
-
|
|
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
|
-
|
|
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
|
-
|
|
11
|
+
## Commands
|
|
95
12
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
74
|
+
## workspace
|
|
152
75
|
|
|
153
76
|
```bash
|
|
154
|
-
npx @teispace/next-maker
|
|
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
|
-
|
|
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
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
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
|
-
|
|
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
|
-
|
|
99
|
+
## setup, remove, doctor, upgrade
|
|
225
100
|
|
|
226
|
-
|
|
101
|
+
These read `.next-maker.json` (written by `init`) and compose reference trees from the starter.
|
|
227
102
|
|
|
228
103
|
```bash
|
|
229
|
-
#
|
|
230
|
-
npx @teispace/next-maker
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
npx @teispace/next-maker
|
|
234
|
-
|
|
235
|
-
|
|
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
|
-
|
|
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
|
-
##
|
|
256
|
-
|
|
257
|
-
### `page` — Generate a page/route
|
|
117
|
+
## Generators
|
|
258
118
|
|
|
259
119
|
```bash
|
|
260
|
-
npx @teispace/next-maker
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
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
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
###
|
|
163
|
+
### Layers
|
|
906
164
|
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
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
|
-
|
|
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).
|