create-react-starter-kit 1.0.5 → 1.0.7
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/dist/cli-args.d.ts.map +1 -1
- package/dist/cli-args.js +2 -1
- package/dist/cli-args.js.map +1 -1
- package/dist/generator/dependencies.d.ts.map +1 -1
- package/dist/generator/dependencies.js +5 -0
- package/dist/generator/dependencies.js.map +1 -1
- package/dist/generator/index.d.ts.map +1 -1
- package/dist/generator/index.js +5 -0
- package/dist/generator/index.js.map +1 -1
- package/dist/prompts/index.d.ts.map +1 -1
- package/dist/prompts/index.js +8 -0
- package/dist/prompts/index.js.map +1 -1
- package/dist/templates/README.md.ejs +1 -1
- package/dist/templates/__when_apiLayer_axios__/src/services/api.ts.ejs +23 -0
- package/dist/templates/__when_apiLayer_axios__/src/services/http/client.ts.ejs +174 -0
- package/dist/templates/__when_apiLayer_axios__/src/services/http/errors.ts.ejs +81 -0
- package/dist/templates/__when_apiLayer_axios__/src/services/http/index.ts.ejs +5 -0
- package/dist/templates/__when_apiLayer_fetch__/src/services/api.ts.ejs +22 -0
- package/dist/templates/__when_apiLayer_fetch__/src/services/http/client.ts.ejs +195 -0
- package/dist/templates/__when_apiLayer_fetch__/src/services/http/errors.ts.ejs +81 -0
- package/dist/templates/__when_apiLayer_fetch__/src/services/http/index.ts.ejs +5 -0
- package/dist/templates/__when_buildTool_vite__/vite.config.ts.ejs +9 -1
- package/dist/templates/__when_buildTool_webpack__/webpack.config.js.ejs +1 -1
- package/dist/templates/__when_e2eTesting_cypress__/cypress.config.ts.ejs +2 -1
- package/dist/templates/__when_e2eTesting_playwright__/playwright.config.ts.ejs +4 -3
- package/dist/templates/__when_serverState_rtk-query__/src/store/postsApi.ts.ejs +28 -5
- package/dist/templates/__when_unitTesting_jest__/jest.config.json.ejs +8 -0
- package/dist/templates/__when_unitTesting_vitest__/tests/feedback.test.tsx.ejs +16 -0
- package/dist/templates/__when_unitTesting_vitest__/tests/posts.test.tsx.ejs +1 -3
- package/dist/templates/__when_unitTesting_vitest__/vitest.config.ts.ejs +31 -0
- package/dist/templates/docs/architecture.md.ejs +44 -25
- package/dist/templates/docs/best-practices.md.ejs +39 -3
- package/dist/templates/docs/examples/README.md.ejs +10 -10
- package/dist/templates/docs/examples/api.md.ejs +165 -8
- package/dist/templates/docs/examples/data-fetching.md.ejs +107 -6
- package/dist/templates/docs/examples/forms.md.ejs +66 -6
- package/dist/templates/docs/examples/state.md.ejs +50 -6
- package/dist/templates/docs/examples/styling.md.ejs +56 -8
- package/dist/templates/docs/examples/testing.md.ejs +63 -9
- package/dist/templates/docs/getting-started.md.ejs +63 -15
- package/dist/templates/docs/selected-stack.md.ejs +110 -8
- package/dist/templates/src/features/feedback/FeedbackForm.tsx.ejs +10 -0
- package/dist/templates/src/features/posts/PostsPanel.tsx.ejs +1 -1
- package/dist/templates/src/routes/PostsPage.tsx.ejs +7 -2
- package/dist/templates/src/types/index.ts.ejs +1 -1
- package/dist/templates/src/types/models.ts.ejs +18 -0
- package/dist/types.d.ts +1 -1
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
- package/templates/README.md.ejs +1 -1
- package/templates/__when_apiLayer_axios__/src/services/api.ts.ejs +23 -0
- package/templates/__when_apiLayer_axios__/src/services/http/client.ts.ejs +174 -0
- package/templates/__when_apiLayer_axios__/src/services/http/errors.ts.ejs +81 -0
- package/templates/__when_apiLayer_axios__/src/services/http/index.ts.ejs +5 -0
- package/templates/__when_apiLayer_fetch__/src/services/api.ts.ejs +22 -0
- package/templates/__when_apiLayer_fetch__/src/services/http/client.ts.ejs +195 -0
- package/templates/__when_apiLayer_fetch__/src/services/http/errors.ts.ejs +81 -0
- package/templates/__when_apiLayer_fetch__/src/services/http/index.ts.ejs +5 -0
- package/templates/__when_buildTool_vite__/vite.config.ts.ejs +9 -1
- package/templates/__when_buildTool_webpack__/webpack.config.js.ejs +1 -1
- package/templates/__when_e2eTesting_cypress__/cypress.config.ts.ejs +2 -1
- package/templates/__when_e2eTesting_playwright__/playwright.config.ts.ejs +4 -3
- package/templates/__when_serverState_rtk-query__/src/store/postsApi.ts.ejs +28 -5
- package/templates/__when_unitTesting_jest__/jest.config.json.ejs +8 -0
- package/templates/__when_unitTesting_vitest__/tests/feedback.test.tsx.ejs +16 -0
- package/templates/__when_unitTesting_vitest__/tests/posts.test.tsx.ejs +1 -3
- package/templates/__when_unitTesting_vitest__/vitest.config.ts.ejs +31 -0
- package/templates/docs/architecture.md.ejs +44 -25
- package/templates/docs/best-practices.md.ejs +39 -3
- package/templates/docs/examples/README.md.ejs +10 -10
- package/templates/docs/examples/api.md.ejs +165 -8
- package/templates/docs/examples/data-fetching.md.ejs +107 -6
- package/templates/docs/examples/forms.md.ejs +66 -6
- package/templates/docs/examples/state.md.ejs +50 -6
- package/templates/docs/examples/styling.md.ejs +56 -8
- package/templates/docs/examples/testing.md.ejs +63 -9
- package/templates/docs/getting-started.md.ejs +63 -15
- package/templates/docs/selected-stack.md.ejs +110 -8
- package/templates/src/features/feedback/FeedbackForm.tsx.ejs +10 -0
- package/templates/src/features/posts/PostsPanel.tsx.ejs +1 -1
- package/templates/src/routes/PostsPage.tsx.ejs +7 -2
- package/templates/src/types/index.ts.ejs +1 -1
- package/templates/src/types/models.ts.ejs +18 -0
- package/dist/templates/src/services/api.ts.ejs +0 -84
- package/templates/src/services/api.ts.ejs +0 -84
|
@@ -8,20 +8,74 @@
|
|
|
8
8
|
|
|
9
9
|
## When to use
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
11
|
+
| Layer | Use for | Avoid using for |
|
|
12
|
+
|-------|---------|-----------------|
|
|
13
|
+
| Unit / RTL | logic, component behavior, form validation | full auth/payment journeys |
|
|
14
|
+
| E2E | critical user journeys across routes | asserting every CSS class |
|
|
15
|
+
| Visual | regressions in appearance / states | business-rule correctness |
|
|
14
16
|
|
|
15
|
-
##
|
|
17
|
+
## How to run
|
|
16
18
|
|
|
17
|
-
|
|
19
|
+
<% if (unitTesting !== 'none') { -%>
|
|
20
|
+
```bash
|
|
21
|
+
<%= packageManager === 'npm' ? 'npm run' : packageManager %> test
|
|
22
|
+
<%= packageManager === 'npm' ? 'npm run' : packageManager %> test:watch
|
|
23
|
+
```
|
|
24
|
+
<% } -%>
|
|
25
|
+
<% if (e2eTesting === 'playwright') { -%>
|
|
26
|
+
```bash
|
|
27
|
+
<%= packageManager === 'npm' ? 'npm run' : packageManager %> test:e2e:install # once
|
|
28
|
+
<%= packageManager === 'npm' ? 'npm run' : packageManager %> test:e2e
|
|
29
|
+
<%= packageManager === 'npm' ? 'npm run' : packageManager %> test:e2e:ui
|
|
30
|
+
```
|
|
18
31
|
|
|
19
|
-
|
|
32
|
+
E2E base URL: `http://127.0.0.1:<%= buildTool === 'webpack' ? 3000 : 5173 %>`
|
|
33
|
+
<% } else if (e2eTesting === 'cypress') { -%>
|
|
34
|
+
```bash
|
|
35
|
+
<%= packageManager === 'npm' ? 'npm run' : packageManager %> test:e2e
|
|
36
|
+
```
|
|
37
|
+
<% } -%>
|
|
38
|
+
<% if (visualTesting === 'storybook') { -%>
|
|
39
|
+
```bash
|
|
40
|
+
<%= packageManager === 'npm' ? 'npm run' : packageManager %> storybook
|
|
41
|
+
```
|
|
42
|
+
<% } -%>
|
|
43
|
+
|
|
44
|
+
## Project layout
|
|
20
45
|
|
|
21
46
|
- Setup: `tests/setup.<%= ext %>` (when applicable)
|
|
22
|
-
-
|
|
47
|
+
- Unit suites: `tests/*.test.<%= jsxExt %>`
|
|
23
48
|
<% if (e2eTesting === 'playwright') { -%>
|
|
24
|
-
- E2E: `tests/e2e
|
|
49
|
+
- E2E: `tests/e2e/*.spec.<%= ext %>` (excluded from unit runner)
|
|
25
50
|
<% } else if (e2eTesting === 'cypress') { -%>
|
|
26
|
-
- E2E: `cypress/e2e
|
|
51
|
+
- E2E: `cypress/e2e/*`
|
|
52
|
+
<% } -%>
|
|
53
|
+
<% if (visualTesting === 'storybook') { -%>
|
|
54
|
+
- Stories: `src/**/*.stories.<%= jsxExt %>`
|
|
27
55
|
<% } -%>
|
|
56
|
+
|
|
57
|
+
## Best practices
|
|
58
|
+
|
|
59
|
+
- Query the way users do: `getByRole`, label text, placeholder — not implementation details.
|
|
60
|
+
- Mock at the **network/domain boundary**:
|
|
61
|
+
<% if (serverState === 'rtk-query') { -%>
|
|
62
|
+
- mock RTK hooks/endpoints for unit tests when you are not integration-testing the store
|
|
63
|
+
<% } else { -%>
|
|
64
|
+
- mock `@/services/api` helpers rather than axios/fetch internals
|
|
65
|
+
<% } -%>
|
|
66
|
+
- Keep e2e **thin**: home load, posts happy path, feedback submit.
|
|
67
|
+
- Do not let unit tests import Playwright specs (unit config excludes `tests/e2e`).
|
|
68
|
+
- For async UI, prefer `findBy*` / `waitFor` over arbitrary `sleep`.
|
|
69
|
+
|
|
70
|
+
## Example unit test shape
|
|
71
|
+
|
|
72
|
+
```tsx
|
|
73
|
+
renderWithProviders(<FeedbackForm />);
|
|
74
|
+
await user.click(screen.getByRole('button', { name: /Send feedback/i }));
|
|
75
|
+
expect(screen.getAllByText(/required|valid email/i).length).toBeGreaterThan(0);
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## Related
|
|
79
|
+
|
|
80
|
+
- Strategy & CI: `docs/best-practices.md`
|
|
81
|
+
- Forms guide: `docs/examples/forms.md`
|
|
@@ -2,10 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
Welcome to **<%= projectName %>**, a React starter generated by `create-react-starter-kit`.
|
|
4
4
|
|
|
5
|
+
This guide walks you from first install to extending the app with confidence.
|
|
6
|
+
|
|
5
7
|
## Prerequisites
|
|
6
8
|
|
|
7
|
-
- Node.js 18
|
|
8
|
-
-
|
|
9
|
+
- **Node.js 18+** (20 LTS recommended)
|
|
10
|
+
- **<%= stackLabels.packageManager %>** installed globally or via Corepack
|
|
11
|
+
- A modern browser for the demo UI
|
|
9
12
|
|
|
10
13
|
## Install & run
|
|
11
14
|
|
|
@@ -22,26 +25,68 @@ npm run dev
|
|
|
22
25
|
<% } -%>
|
|
23
26
|
```
|
|
24
27
|
|
|
28
|
+
Dev server URL (default): **http://localhost:<%= buildTool === 'webpack' ? 3000 : 5173 %>**
|
|
29
|
+
|
|
30
|
+
Useful scripts:
|
|
31
|
+
|
|
32
|
+
| Script | Purpose |
|
|
33
|
+
|--------|---------|
|
|
34
|
+
| `dev` | Local development server |
|
|
35
|
+
| `build` | Production bundle |
|
|
36
|
+
| `preview` | Serve the production build locally |
|
|
37
|
+
<% if (unitTesting !== 'none') { -%>
|
|
38
|
+
| `test` / `test:watch` | Unit tests |
|
|
39
|
+
<% } -%>
|
|
40
|
+
<% if (e2eTesting !== 'none') { -%>
|
|
41
|
+
| `test:e2e` | End-to-end tests |
|
|
42
|
+
<% } -%>
|
|
43
|
+
<% if (visualTesting === 'storybook') { -%>
|
|
44
|
+
| `storybook` | Component explorer |
|
|
45
|
+
<% } -%>
|
|
46
|
+
| `lint` / `format` | Code quality |
|
|
47
|
+
|
|
25
48
|
## Environment variables
|
|
26
49
|
|
|
27
|
-
Copy `.env.example` → `.env`
|
|
50
|
+
1. Copy `.env.example` → `.env`
|
|
51
|
+
2. Fill values for your environment
|
|
52
|
+
3. Restart `dev` after changes
|
|
28
53
|
|
|
29
|
-
Only variables
|
|
54
|
+
Only client-safe variables (typically `VITE_*`) are exposed to the browser. Never put private API keys in front-end env vars.
|
|
55
|
+
|
|
56
|
+
| Variable | Purpose |
|
|
57
|
+
|----------|---------|
|
|
58
|
+
| `VITE_API_BASE_URL` | REST API base URL (defaults to JSONPlaceholder for demos) |
|
|
59
|
+
<% if (errorTracking === 'sentry') { -%>
|
|
60
|
+
| `VITE_SENTRY_DSN` | Sentry DSN |
|
|
61
|
+
<% } else if (errorTracking === 'bugsnag') { -%>
|
|
62
|
+
| `VITE_BUGSNAG_API_KEY` | Bugsnag API key |
|
|
63
|
+
<% } else if (errorTracking === 'logrocket') { -%>
|
|
64
|
+
| `VITE_LOGROCKET_APP_ID` | LogRocket app id |
|
|
65
|
+
<% } else if (errorTracking === 'datadog') { -%>
|
|
66
|
+
| `VITE_DATADOG_APPLICATION_ID` / `VITE_DATADOG_CLIENT_TOKEN` | Datadog RUM |
|
|
67
|
+
<% } -%>
|
|
30
68
|
|
|
31
69
|
## What to explore first
|
|
32
70
|
|
|
33
|
-
1.
|
|
34
|
-
2.
|
|
35
|
-
3.
|
|
36
|
-
4.
|
|
71
|
+
1. **`/`** — stack badges and orientation
|
|
72
|
+
2. **`/counter`** — client state (<%= stackLabels.stateManagement %>)
|
|
73
|
+
3. **`/posts`** — server state (<%= stackLabels.serverState %>)
|
|
74
|
+
4. **`/feedback`** — forms + validation (<%= stackLabels.forms %> / <%= stackLabels.validation %>)
|
|
75
|
+
5. **`docs/selected-stack.md`** — why each library was chosen
|
|
76
|
+
6. **`docs/examples/`** — deep usage guides with copy-paste patterns
|
|
37
77
|
|
|
38
|
-
## Extending the app
|
|
78
|
+
## Extending the app (recommended flow)
|
|
39
79
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
80
|
+
1. Create a feature folder: `src/features/<domain>/`
|
|
81
|
+
2. Add UI, hooks, and domain types next to that feature
|
|
82
|
+
3. Register a route in `src/App.<%= jsxExt %>`
|
|
83
|
+
4. Put shared primitives in `src/components/`
|
|
84
|
+
<% if (apiLayer === 'none') { -%>
|
|
85
|
+
5. Add RTK Query endpoints in `src/store/` (or a feature-local `*Api` slice)
|
|
86
|
+
<% } else { -%>
|
|
87
|
+
5. Add HTTP helpers in `src/services/api.<%= ext %>` (and keep transport details in `src/services/http/`)
|
|
88
|
+
<% } -%>
|
|
89
|
+
6. Import with `@/` — e.g. `import { env } from '@/config/env'`
|
|
45
90
|
|
|
46
91
|
## Testing
|
|
47
92
|
|
|
@@ -51,5 +96,8 @@ Only variables prefixed for your bundler (e.g. `VITE_`) are exposed to the brows
|
|
|
51
96
|
<% if (e2eTesting !== 'none') { -%>
|
|
52
97
|
- E2E: `<%= packageManager === 'npm' ? 'npm run' : packageManager %> test:e2e`
|
|
53
98
|
<% } -%>
|
|
99
|
+
<% if (visualTesting === 'storybook') { -%>
|
|
100
|
+
- Storybook: `<%= packageManager === 'npm' ? 'npm run' : packageManager %> storybook`
|
|
101
|
+
<% } -%>
|
|
54
102
|
|
|
55
|
-
See `docs/best-practices.md` for
|
|
103
|
+
See `docs/best-practices.md` and `docs/examples/testing.md` for strategy and CI notes.
|
|
@@ -1,44 +1,146 @@
|
|
|
1
1
|
# Selected stack
|
|
2
2
|
|
|
3
|
-
This document explains each choice made when generating **<%= projectName
|
|
3
|
+
This document explains each choice made when generating **<%= projectName %>**, how to use it day-to-day, and the practices that keep the codebase maintainable.
|
|
4
4
|
|
|
5
5
|
## Build — <%= stackLabels.buildTool %>
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
<% if (buildTool === 'vite') { -%>
|
|
8
|
+
Vite gives fast HMR and a Rollup-based production build. Config lives in `vite.config.<%= ext %>`.
|
|
8
9
|
|
|
9
|
-
**
|
|
10
|
+
**Day-to-day:** `<%= packageManager === 'npm' ? 'npm run' : packageManager %> dev` → http://localhost:5173
|
|
11
|
+
<% } else if (buildTool === 'webpack') { -%>
|
|
12
|
+
Webpack is configured in `webpack.config.js` with Babel (or TS pipeline), HtmlWebpackPlugin, and a dev server.
|
|
13
|
+
|
|
14
|
+
**Day-to-day:** `<%= packageManager === 'npm' ? 'npm run' : packageManager %> dev` → http://localhost:3000
|
|
15
|
+
<% } else { -%>
|
|
16
|
+
Your bundler is wired with scripts in `package.json`. Prefer keeping config minimal; add plugins only when needed.
|
|
17
|
+
<% } -%>
|
|
18
|
+
|
|
19
|
+
**Best practices**
|
|
20
|
+
|
|
21
|
+
- Lock Node and package manager versions in CI.
|
|
22
|
+
- Treat `build` as the source of truth for production issues (not only `dev`).
|
|
23
|
+
- Keep path aliases (`@/`) in sync between TS config and the bundler.
|
|
10
24
|
|
|
11
25
|
## Language — <%= stackLabels.language %>
|
|
12
26
|
|
|
13
27
|
<% if (isTypeScript) { -%>
|
|
14
|
-
TypeScript catches contract mistakes early. Keep `strict` on; avoid `any` except at true boundaries.
|
|
28
|
+
TypeScript catches contract mistakes early. Keep `strict` on; avoid `any` except at true boundaries (third-party quirks, gradual migrations).
|
|
29
|
+
|
|
30
|
+
**Pattern:** model API payloads in `src/types/models.<%= ext %>`, not inline in components.
|
|
15
31
|
<% } else { -%>
|
|
16
|
-
JavaScript keeps the barrier low.
|
|
32
|
+
JavaScript keeps the barrier low. Prefer JSDoc for public helpers, and consider migrating hot modules to TypeScript as the app grows.
|
|
17
33
|
<% } -%>
|
|
18
34
|
|
|
19
35
|
## Routing — <%= stackLabels.router %>
|
|
20
36
|
|
|
21
37
|
Client-side navigation with nested layouts. Keep route modules thin; push data loading into features.
|
|
22
38
|
|
|
39
|
+
**Best practices**
|
|
40
|
+
|
|
41
|
+
- Colocate route-level UI under `src/routes/`.
|
|
42
|
+
- Avoid fetching in layout components unless every child needs the data.
|
|
43
|
+
- Use loading/error UI at the feature boundary.
|
|
44
|
+
|
|
23
45
|
## Client state — <%= stackLabels.stateManagement %>
|
|
24
46
|
|
|
25
47
|
Use for UI state (modals, wizards, ephemeral counters). Do **not** mirror server entities here if you have a server-state library.
|
|
26
48
|
|
|
49
|
+
**Live demo:** `/counter` → `src/features/counter/`
|
|
50
|
+
|
|
51
|
+
**Best practices**
|
|
52
|
+
|
|
53
|
+
- Feature-local state first; promote to global only when multiple distant trees need it.
|
|
54
|
+
- Keep stores/slices focused on one domain.
|
|
55
|
+
- Prefer selectors/hooks over reading the whole store in components.
|
|
56
|
+
|
|
27
57
|
## Server state — <%= stackLabels.serverState %>
|
|
28
58
|
|
|
29
|
-
Caches remote data, handles loading/error, and deduplicates requests.
|
|
59
|
+
Caches remote data, handles loading/error, and deduplicates requests.
|
|
60
|
+
|
|
61
|
+
**Live demo:** `/posts` → `src/features/posts/PostsPanel.<%= jsxExt %>`
|
|
62
|
+
|
|
63
|
+
<% if (serverState === 'rtk-query') { -%>
|
|
64
|
+
**How to use**
|
|
65
|
+
|
|
66
|
+
1. Define endpoints in `src/store/postsApi.<%= ext %>` (or a new `*Api` slice).
|
|
67
|
+
2. Export hooks (`useGetPostsQuery`, `useCreateFeedbackMutation`).
|
|
68
|
+
3. Call hooks from features — no manual `useEffect` fetching.
|
|
69
|
+
|
|
70
|
+
**Best practices**
|
|
71
|
+
|
|
72
|
+
- Use `tagTypes` + `providesTags` / `invalidatesTags` for cache coherence.
|
|
73
|
+
- Put auth headers in `prepareHeaders`, not in every endpoint.
|
|
74
|
+
- Prefer mutations for writes; invalidate related query tags after success.
|
|
75
|
+
<% } else if (serverState === 'tanstack-query') { -%>
|
|
76
|
+
**How to use**
|
|
77
|
+
|
|
78
|
+
```tsx
|
|
79
|
+
const { data, isLoading, error, refetch } = useQuery({
|
|
80
|
+
queryKey: ['posts'],
|
|
81
|
+
queryFn: ({ signal }) => fetchPosts(signal),
|
|
82
|
+
});
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
**Best practices**
|
|
86
|
+
|
|
87
|
+
- Stable, hierarchical query keys: `['posts']`, `['posts', id]`.
|
|
88
|
+
- Pass `signal` into your HTTP helpers so navigations cancel in-flight work.
|
|
89
|
+
- Keep `queryFn` thin — call domain API helpers, not raw HTTP.
|
|
90
|
+
<% } else if (serverState === 'swr') { -%>
|
|
91
|
+
**How to use**
|
|
92
|
+
|
|
93
|
+
SWR is configured in `providers.<%= jsxExt %>` with a shared `fetcher`. Components call `useSWR(key)`.
|
|
94
|
+
|
|
95
|
+
**Best practices**
|
|
96
|
+
|
|
97
|
+
- Keys should be URLs or stable resource identifiers.
|
|
98
|
+
- Use `mutate` for local revalidation after writes.
|
|
99
|
+
<% } else { -%>
|
|
100
|
+
No dedicated server-state library was selected. Prefer adding one before the app grows past a few screens.
|
|
101
|
+
<% } -%>
|
|
30
102
|
|
|
31
103
|
## Forms — <%= stackLabels.forms %> / Validation — <%= stackLabels.validation %>
|
|
32
104
|
|
|
33
105
|
Schemas define the contract; the form library binds inputs. Share schemas with API payloads when possible.
|
|
34
106
|
|
|
107
|
+
**Live demo:** `/feedback` → `src/features/feedback/FeedbackForm.<%= jsxExt %>`
|
|
108
|
+
**Schemas:** `src/lib/validation.<%= ext %>`
|
|
109
|
+
|
|
110
|
+
**Best practices**
|
|
111
|
+
|
|
112
|
+
- Validate on submit (and optionally on blur) — avoid blocking every keystroke unless UX requires it.
|
|
113
|
+
- Keep error messages user-facing; log technical details separately.
|
|
114
|
+
- Reuse the same schema on the server if you own both sides.
|
|
115
|
+
|
|
35
116
|
## Styling — <%= stackLabels.styling %> / UI — <%= stackLabels.uiLibrary %>
|
|
36
117
|
|
|
37
118
|
Tokens and primitives establish visual consistency. Prefer composition over one-off CSS.
|
|
38
119
|
|
|
120
|
+
**Best practices**
|
|
121
|
+
|
|
122
|
+
- Wrap third-party primitives in thin adapters (`UiButton`) so swapping kits later is cheaper.
|
|
123
|
+
- Keep global tokens in `src/styles/`; keep feature-specific styles close to the feature.
|
|
124
|
+
|
|
39
125
|
## API — <%= stackLabels.apiLayer %>
|
|
40
126
|
|
|
41
|
-
|
|
127
|
+
<% if (apiLayer === 'none') { -%>
|
|
128
|
+
HTTP is owned by **RTK Query** (`fetchBaseQuery`). There is intentionally **no** separate Axios/Fetch client in this project.
|
|
129
|
+
|
|
130
|
+
See `docs/examples/data-fetching.md` and `src/store/postsApi.<%= ext %>`.
|
|
131
|
+
<% } else if (apiLayer === 'axios') { -%>
|
|
132
|
+
Axios is configured under `src/services/http/` with:
|
|
133
|
+
|
|
134
|
+
- request interceptors (auth token, request IDs)
|
|
135
|
+
- response interceptors (normalized `ApiError`, 401 event)
|
|
136
|
+
- domain helpers in `src/services/api.<%= ext %>`
|
|
137
|
+
|
|
138
|
+
See `docs/examples/api.md` for patterns and examples.
|
|
139
|
+
<% } else { -%>
|
|
140
|
+
A shared Fetch wrapper lives under `src/services/http/` with timeout, auth headers, request IDs, and normalized `ApiError`s. Domain helpers sit in `src/services/api.<%= ext %>`.
|
|
141
|
+
|
|
142
|
+
See `docs/examples/api.md` for patterns and examples.
|
|
143
|
+
<% } -%>
|
|
42
144
|
|
|
43
145
|
## Quality gates
|
|
44
146
|
|
|
@@ -52,4 +154,4 @@ Centralize base URL, headers, and error logging in `src/services/api.<%= ext %>`
|
|
|
52
154
|
| Git hooks | Husky + lint-staged + commitlint |
|
|
53
155
|
| Structure | Feature-based + `@/` aliases |
|
|
54
156
|
|
|
55
|
-
See `docs/examples/` for focused usage notes.
|
|
157
|
+
See `docs/examples/` for focused usage notes with deeper examples.
|
|
@@ -32,7 +32,11 @@ import { validateFeedback<% if (isTypeScript) { %>, type FeedbackValues<% } %> }
|
|
|
32
32
|
import { feedbackSchema<% if (isTypeScript) { %>, type FeedbackValues<% } %> } from '@/lib/validation';
|
|
33
33
|
<% } -%>
|
|
34
34
|
<% } -%>
|
|
35
|
+
<% if (serverState === 'rtk-query') { -%>
|
|
36
|
+
import { useCreateFeedbackMutation } from '@/store/postsApi';
|
|
37
|
+
<% } else { -%>
|
|
35
38
|
import { createFeedback } from '@/services/api';
|
|
39
|
+
<% } -%>
|
|
36
40
|
import { UiButton } from '@/components/ui/UiButton';
|
|
37
41
|
|
|
38
42
|
const defaults<% if (isTypeScript) { %>: FeedbackValues<% } %> = {
|
|
@@ -43,6 +47,12 @@ const defaults<% if (isTypeScript) { %>: FeedbackValues<% } %> = {
|
|
|
43
47
|
|
|
44
48
|
export function FeedbackForm() {
|
|
45
49
|
const [status, setStatus] = useState<% if (isTypeScript) { %><string | null><% } %>(null);
|
|
50
|
+
<% if (serverState === 'rtk-query') { -%>
|
|
51
|
+
const [createFeedbackMutation] = useCreateFeedbackMutation();
|
|
52
|
+
const createFeedback = async (values<% if (isTypeScript) { %>: FeedbackValues<% } %>) => {
|
|
53
|
+
await createFeedbackMutation(values).unwrap();
|
|
54
|
+
};
|
|
55
|
+
<% } -%>
|
|
46
56
|
|
|
47
57
|
<% if (forms === 'react-hook-form') { -%>
|
|
48
58
|
const {
|
|
@@ -14,7 +14,7 @@ export function PostsPanel() {
|
|
|
14
14
|
<% if (serverState === 'tanstack-query') { -%>
|
|
15
15
|
const { data, isLoading, error, refetch, isFetching } = useQuery({
|
|
16
16
|
queryKey: ['posts'],
|
|
17
|
-
queryFn: fetchPosts,
|
|
17
|
+
queryFn: ({ signal }) => fetchPosts(signal),
|
|
18
18
|
});
|
|
19
19
|
<% } else if (serverState === 'swr') { -%>
|
|
20
20
|
const { data, error, isLoading, isValidating, mutate } = useSWR('/posts?_limit=5');
|
|
@@ -7,8 +7,13 @@ export function PostsPage() {
|
|
|
7
7
|
<p className="eyebrow">Server state</p>
|
|
8
8
|
<h1>Posts</h1>
|
|
9
9
|
<p className="lede">
|
|
10
|
-
Fetches sample posts via <strong><%= stackLabels.serverState === 'None' ? stackLabels.apiLayer : stackLabels.serverState %></strong>
|
|
11
|
-
|
|
10
|
+
Fetches sample posts via <strong><%= stackLabels.serverState === 'None' ? stackLabels.apiLayer : stackLabels.serverState %></strong>
|
|
11
|
+
<% if (apiLayer !== 'none' && serverState !== 'rtk-query') { -%>
|
|
12
|
+
{' '}and <strong><%= stackLabels.apiLayer %></strong>
|
|
13
|
+
<% } else if (serverState === 'rtk-query') { -%>
|
|
14
|
+
{' '}(HTTP via <code>fetchBaseQuery</code>)
|
|
15
|
+
<% } -%>
|
|
16
|
+
.
|
|
12
17
|
</p>
|
|
13
18
|
</header>
|
|
14
19
|
<PostsPanel />
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<% if (isTypeScript) { -%>
|
|
2
2
|
/** Shared domain types used across features. */
|
|
3
|
-
export type { Post } from '
|
|
3
|
+
export type { Post, FeedbackPayload } from './models';
|
|
4
4
|
<% } else { -%>
|
|
5
5
|
// Shared types live here when you migrate hot paths to TypeScript.
|
|
6
6
|
export {};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
<% if (isTypeScript) { -%>
|
|
2
|
+
/** Shared domain models used by features and the HTTP / RTK layers. */
|
|
3
|
+
export interface Post {
|
|
4
|
+
userId: number;
|
|
5
|
+
id: number;
|
|
6
|
+
title: string;
|
|
7
|
+
body: string;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export interface FeedbackPayload {
|
|
11
|
+
name: string;
|
|
12
|
+
email: string;
|
|
13
|
+
message: string;
|
|
14
|
+
}
|
|
15
|
+
<% } else { -%>
|
|
16
|
+
// Shared domain models live here when you migrate hot paths to TypeScript.
|
|
17
|
+
export {};
|
|
18
|
+
<% } -%>
|
package/dist/types.d.ts
CHANGED
|
@@ -12,7 +12,7 @@ export type E2ETesting = 'playwright' | 'cypress' | 'none';
|
|
|
12
12
|
export type VisualTesting = 'storybook' | 'chromatic' | 'none';
|
|
13
13
|
export type Linting = 'eslint' | 'biome';
|
|
14
14
|
export type Formatting = 'prettier' | 'stylelint';
|
|
15
|
-
export type ApiLayer = 'axios' | 'fetch';
|
|
15
|
+
export type ApiLayer = 'axios' | 'fetch' | 'none';
|
|
16
16
|
export type PackageManager = 'pnpm' | 'yarn' | 'npm';
|
|
17
17
|
export type ErrorTracking = 'sentry' | 'bugsnag' | 'logrocket' | 'datadog' | 'none';
|
|
18
18
|
export interface ProjectAnswers {
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,SAAS,GAAG,MAAM,GAAG,SAAS,GAAG,QAAQ,GAAG,QAAQ,GAAG,SAAS,GAAG,QAAQ,CAAC;AACxF,MAAM,MAAM,QAAQ,GAAG,YAAY,GAAG,YAAY,CAAC;AACnD,MAAM,MAAM,MAAM,GAAG,cAAc,GAAG,iBAAiB,CAAC;AACxD,MAAM,MAAM,eAAe,GAAG,OAAO,GAAG,SAAS,GAAG,OAAO,GAAG,SAAS,GAAG,MAAM,CAAC;AACjF,MAAM,MAAM,WAAW,GAAG,gBAAgB,GAAG,KAAK,GAAG,WAAW,GAAG,MAAM,CAAC;AAC1E,MAAM,MAAM,KAAK,GAAG,iBAAiB,GAAG,QAAQ,GAAG,eAAe,GAAG,MAAM,CAAC;AAC5E,MAAM,MAAM,UAAU,GAAG,KAAK,GAAG,KAAK,GAAG,SAAS,GAAG,KAAK,GAAG,MAAM,CAAC;AACpE,MAAM,MAAM,OAAO,GAAG,UAAU,GAAG,aAAa,GAAG,mBAAmB,GAAG,MAAM,CAAC;AAChF,MAAM,MAAM,SAAS,GACjB,KAAK,GACL,MAAM,GACN,QAAQ,GACR,QAAQ,GACR,aAAa,GACb,MAAM,CAAC;AACX,MAAM,MAAM,WAAW,GAAG,QAAQ,GAAG,MAAM,GAAG,KAAK,GAAG,MAAM,CAAC;AAC7D,MAAM,MAAM,UAAU,GAAG,YAAY,GAAG,SAAS,GAAG,MAAM,CAAC;AAC3D,MAAM,MAAM,aAAa,GAAG,WAAW,GAAG,WAAW,GAAG,MAAM,CAAC;AAC/D,MAAM,MAAM,OAAO,GAAG,QAAQ,GAAG,OAAO,CAAC;AACzC,MAAM,MAAM,UAAU,GAAG,UAAU,GAAG,WAAW,CAAC;AAClD,MAAM,MAAM,QAAQ,GAAG,OAAO,GAAG,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,SAAS,GAAG,MAAM,GAAG,SAAS,GAAG,QAAQ,GAAG,QAAQ,GAAG,SAAS,GAAG,QAAQ,CAAC;AACxF,MAAM,MAAM,QAAQ,GAAG,YAAY,GAAG,YAAY,CAAC;AACnD,MAAM,MAAM,MAAM,GAAG,cAAc,GAAG,iBAAiB,CAAC;AACxD,MAAM,MAAM,eAAe,GAAG,OAAO,GAAG,SAAS,GAAG,OAAO,GAAG,SAAS,GAAG,MAAM,CAAC;AACjF,MAAM,MAAM,WAAW,GAAG,gBAAgB,GAAG,KAAK,GAAG,WAAW,GAAG,MAAM,CAAC;AAC1E,MAAM,MAAM,KAAK,GAAG,iBAAiB,GAAG,QAAQ,GAAG,eAAe,GAAG,MAAM,CAAC;AAC5E,MAAM,MAAM,UAAU,GAAG,KAAK,GAAG,KAAK,GAAG,SAAS,GAAG,KAAK,GAAG,MAAM,CAAC;AACpE,MAAM,MAAM,OAAO,GAAG,UAAU,GAAG,aAAa,GAAG,mBAAmB,GAAG,MAAM,CAAC;AAChF,MAAM,MAAM,SAAS,GACjB,KAAK,GACL,MAAM,GACN,QAAQ,GACR,QAAQ,GACR,aAAa,GACb,MAAM,CAAC;AACX,MAAM,MAAM,WAAW,GAAG,QAAQ,GAAG,MAAM,GAAG,KAAK,GAAG,MAAM,CAAC;AAC7D,MAAM,MAAM,UAAU,GAAG,YAAY,GAAG,SAAS,GAAG,MAAM,CAAC;AAC3D,MAAM,MAAM,aAAa,GAAG,WAAW,GAAG,WAAW,GAAG,MAAM,CAAC;AAC/D,MAAM,MAAM,OAAO,GAAG,QAAQ,GAAG,OAAO,CAAC;AACzC,MAAM,MAAM,UAAU,GAAG,UAAU,GAAG,WAAW,CAAC;AAClD,MAAM,MAAM,QAAQ,GAAG,OAAO,GAAG,OAAO,GAAG,MAAM,CAAC;AAClD,MAAM,MAAM,cAAc,GAAG,MAAM,GAAG,MAAM,GAAG,KAAK,CAAC;AACrD,MAAM,MAAM,aAAa,GAAG,QAAQ,GAAG,SAAS,GAAG,WAAW,GAAG,SAAS,GAAG,MAAM,CAAC;AAEpF,MAAM,WAAW,cAAc;IAC7B,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,SAAS,CAAC;IACrB,QAAQ,EAAE,QAAQ,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,eAAe,EAAE,eAAe,CAAC;IACjC,WAAW,EAAE,WAAW,CAAC;IACzB,KAAK,EAAE,KAAK,CAAC;IACb,UAAU,EAAE,UAAU,CAAC;IACvB,OAAO,EAAE,OAAO,CAAC;IACjB,SAAS,EAAE,SAAS,CAAC;IACrB,WAAW,EAAE,WAAW,CAAC;IACzB,UAAU,EAAE,UAAU,CAAC;IACvB,aAAa,EAAE,aAAa,CAAC;IAC7B,OAAO,EAAE,OAAO,CAAC;IACjB,UAAU,EAAE,UAAU,CAAC;IACvB,QAAQ,EAAE,QAAQ,CAAC;IACnB,cAAc,EAAE,cAAc,CAAC;IAC/B,aAAa,EAAE,aAAa,CAAC;IAC7B,mBAAmB,EAAE,OAAO,CAAC;CAC9B;AAED,MAAM,WAAW,eAAgB,SAAQ,cAAc;IACrD,YAAY,EAAE,OAAO,CAAC;IACtB,GAAG,EAAE,IAAI,GAAG,IAAI,CAAC;IACjB,MAAM,EAAE,KAAK,GAAG,KAAK,CAAC;IACtB,IAAI,EAAE,MAAM,CAAC;CACd"}
|
package/package.json
CHANGED
package/templates/README.md.ejs
CHANGED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { apiGet, apiPost } from '@/services/http';
|
|
2
|
+
<% if (isTypeScript) { -%>
|
|
3
|
+
import type { FeedbackPayload, Post } from '@/types/models';
|
|
4
|
+
<% } -%>
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Domain API methods — keep React components free of raw URLs and HTTP details.
|
|
8
|
+
* Call these from TanStack Query / SWR queryFns, or directly for simple mutations.
|
|
9
|
+
*/
|
|
10
|
+
export async function fetchPosts(signal<% if (isTypeScript) { %>?: AbortSignal<% } %>) {
|
|
11
|
+
return apiGet<% if (isTypeScript) { %><Post[]><% } %>('/posts?_limit=5', { signal });
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export async function createFeedback(
|
|
15
|
+
payload<% if (isTypeScript) { %>: FeedbackPayload<% } %>,
|
|
16
|
+
signal<% if (isTypeScript) { %>?: AbortSignal<% } %>,
|
|
17
|
+
) {
|
|
18
|
+
return apiPost('/posts', payload, { signal });
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
<% if (isTypeScript) { -%>
|
|
22
|
+
export type { FeedbackPayload, Post } from '@/types/models';
|
|
23
|
+
<% } -%>
|