create-react-starter-kit 1.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/LICENSE +21 -0
- package/README.md +119 -0
- package/bin/create-react-starter-kit.js +7 -0
- package/dist/cli-args.d.ts +15 -0
- package/dist/cli-args.d.ts.map +1 -0
- package/dist/cli-args.js +78 -0
- package/dist/cli-args.js.map +1 -0
- package/dist/generator/dependencies.d.ts +8 -0
- package/dist/generator/dependencies.d.ts.map +1 -0
- package/dist/generator/dependencies.js +356 -0
- package/dist/generator/dependencies.js.map +1 -0
- package/dist/generator/index.d.ts +5 -0
- package/dist/generator/index.d.ts.map +1 -0
- package/dist/generator/index.js +358 -0
- package/dist/generator/index.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +38 -0
- package/dist/index.js.map +1 -0
- package/dist/prompts/index.d.ts +3 -0
- package/dist/prompts/index.d.ts.map +1 -0
- package/dist/prompts/index.js +267 -0
- package/dist/prompts/index.js.map +1 -0
- package/dist/templates/.env.example.ejs +21 -0
- package/dist/templates/.husky/commit-msg +2 -0
- package/dist/templates/.husky/pre-commit +2 -0
- package/dist/templates/README.md.ejs +169 -0
- package/dist/templates/__js__/jsconfig.json +9 -0
- package/dist/templates/__ts__/src/vite-env.d.ts +16 -0
- package/dist/templates/__ts__/tsconfig.json +24 -0
- package/dist/templates/__when_buildTool_esbuild__/scripts/esbuild.build.mjs.ejs +23 -0
- package/dist/templates/__when_buildTool_esbuild__/scripts/esbuild.dev.mjs.ejs +48 -0
- package/dist/templates/__when_buildTool_parcel__/.parcelrc +9 -0
- package/dist/templates/__when_buildTool_rollup__/rollup.config.js.ejs +33 -0
- package/dist/templates/__when_buildTool_rspack__/rspack.config.js.ejs +36 -0
- package/dist/templates/__when_buildTool_vite__/vite.config.ts.ejs +37 -0
- package/dist/templates/__when_buildTool_webpack__/webpack.config.js.ejs +68 -0
- package/dist/templates/__when_e2eTesting_cypress__/cypress/e2e/home.cy.ts.ejs +6 -0
- package/dist/templates/__when_e2eTesting_cypress__/cypress.config.ts.ejs +8 -0
- package/dist/templates/__when_e2eTesting_playwright__/playwright.config.ts.ejs +16 -0
- package/dist/templates/__when_e2eTesting_playwright__/tests/e2e/home.spec.ts.ejs +6 -0
- package/dist/templates/__when_formatting_prettier__/.prettierrc +7 -0
- package/dist/templates/__when_formatting_stylelint__/.stylelintrc.json +3 -0
- package/dist/templates/__when_linting_biome__/biome.json +19 -0
- package/dist/templates/__when_linting_eslint__/eslint.config.js.ejs +42 -0
- package/dist/templates/__when_serverState_rtk-query__/src/store/postsApi.ts.ejs +19 -0
- package/dist/templates/__when_stateManagement_context__/src/store/context.tsx.ejs +57 -0
- package/dist/templates/__when_styling_tailwind__/postcss.config.js +6 -0
- package/dist/templates/__when_styling_tailwind__/tailwind.config.js.ejs +23 -0
- package/dist/templates/__when_unitTesting_jest__/jest.config.json.ejs +11 -0
- package/dist/templates/__when_unitTesting_jest__/tests/App.test.tsx.ejs +13 -0
- package/dist/templates/__when_unitTesting_jest__/tests/setup.ts.ejs +1 -0
- package/dist/templates/__when_unitTesting_jest__/tests/styleMock.ts.ejs +1 -0
- package/dist/templates/__when_unitTesting_rtl__/tests/App.test.tsx.ejs +13 -0
- package/dist/templates/__when_unitTesting_vitest__/tests/App.test.tsx.ejs +16 -0
- package/dist/templates/__when_unitTesting_vitest__/tests/setup.ts.ejs +1 -0
- package/dist/templates/__when_visualTesting_storybook__/.storybook/main.js.ejs +11 -0
- package/dist/templates/__when_visualTesting_storybook__/.storybook/preview.js +8 -0
- package/dist/templates/__when_visualTesting_storybook__/src/components/ui/UiButton.stories.tsx.ejs +15 -0
- package/dist/templates/commitlint.config.js +3 -0
- package/dist/templates/docs/architecture.md.ejs +74 -0
- package/dist/templates/docs/best-practices.md.ejs +56 -0
- package/dist/templates/docs/examples/README.md.ejs +14 -0
- package/dist/templates/docs/examples/api.md.ejs +17 -0
- package/dist/templates/docs/examples/data-fetching.md.ejs +18 -0
- package/dist/templates/docs/examples/forms.md.ejs +18 -0
- package/dist/templates/docs/examples/state.md.ejs +17 -0
- package/dist/templates/docs/examples/styling.md.ejs +23 -0
- package/dist/templates/docs/examples/testing.md.ejs +27 -0
- package/dist/templates/docs/getting-started.md.ejs +55 -0
- package/dist/templates/docs/selected-stack.md.ejs +55 -0
- package/dist/templates/index.html.ejs +19 -0
- package/dist/templates/public/vite.svg +4 -0
- package/dist/templates/src/app/App.tsx.ejs +71 -0
- package/dist/templates/src/app/providers.tsx.ejs +93 -0
- package/dist/templates/src/assets/.gitkeep +0 -0
- package/dist/templates/src/components/StackBadge.tsx.ejs +15 -0
- package/dist/templates/src/components/layout/AppLayout.tsx.ejs +64 -0
- package/dist/templates/src/components/ui/Toast.tsx.ejs +61 -0
- package/dist/templates/src/components/ui/UiButton.tsx.ejs +168 -0
- package/dist/templates/src/config/env.ts.ejs +18 -0
- package/dist/templates/src/features/counter/CounterPanel.tsx.ejs +66 -0
- package/dist/templates/src/features/feedback/FeedbackForm.tsx.ejs +373 -0
- package/dist/templates/src/features/posts/PostsPanel.tsx.ejs +91 -0
- package/dist/templates/src/hooks/useMounted.ts.ejs +15 -0
- package/dist/templates/src/lib/cn.ts.ejs +12 -0
- package/dist/templates/src/lib/monitoring.ts.ejs +66 -0
- package/dist/templates/src/lib/validation.ts.ejs +81 -0
- package/dist/templates/src/main.tsx.ejs +22 -0
- package/dist/templates/src/routes/AboutPage.tsx.ejs +30 -0
- package/dist/templates/src/routes/HomePage.tsx.ejs +47 -0
- package/dist/templates/src/services/api.ts.ejs +84 -0
- package/dist/templates/src/store/index.ts.ejs +80 -0
- package/dist/templates/src/styles/global.css.ejs +457 -0
- package/dist/templates/src/types/index.ts.ejs +7 -0
- package/dist/templates/src/utils/format.ts.ejs +3 -0
- package/dist/types.d.ts +46 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/logger.d.ts +12 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +31 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/validate.d.ts +3 -0
- package/dist/utils/validate.d.ts.map +1 -0
- package/dist/utils/validate.js +24 -0
- package/dist/utils/validate.js.map +1 -0
- package/package.json +62 -0
- package/templates/.env.example.ejs +21 -0
- package/templates/.husky/commit-msg +2 -0
- package/templates/.husky/pre-commit +2 -0
- package/templates/README.md.ejs +169 -0
- package/templates/__js__/jsconfig.json +9 -0
- package/templates/__ts__/src/vite-env.d.ts +16 -0
- package/templates/__ts__/tsconfig.json +24 -0
- package/templates/__when_buildTool_esbuild__/scripts/esbuild.build.mjs.ejs +23 -0
- package/templates/__when_buildTool_esbuild__/scripts/esbuild.dev.mjs.ejs +48 -0
- package/templates/__when_buildTool_parcel__/.parcelrc +9 -0
- package/templates/__when_buildTool_rollup__/rollup.config.js.ejs +33 -0
- package/templates/__when_buildTool_rspack__/rspack.config.js.ejs +36 -0
- package/templates/__when_buildTool_vite__/vite.config.ts.ejs +37 -0
- package/templates/__when_buildTool_webpack__/webpack.config.js.ejs +68 -0
- package/templates/__when_e2eTesting_cypress__/cypress/e2e/home.cy.ts.ejs +6 -0
- package/templates/__when_e2eTesting_cypress__/cypress.config.ts.ejs +8 -0
- package/templates/__when_e2eTesting_playwright__/playwright.config.ts.ejs +16 -0
- package/templates/__when_e2eTesting_playwright__/tests/e2e/home.spec.ts.ejs +6 -0
- package/templates/__when_formatting_prettier__/.prettierrc +7 -0
- package/templates/__when_formatting_stylelint__/.stylelintrc.json +3 -0
- package/templates/__when_linting_biome__/biome.json +19 -0
- package/templates/__when_linting_eslint__/eslint.config.js.ejs +42 -0
- package/templates/__when_serverState_rtk-query__/src/store/postsApi.ts.ejs +19 -0
- package/templates/__when_stateManagement_context__/src/store/context.tsx.ejs +57 -0
- package/templates/__when_styling_tailwind__/postcss.config.js +6 -0
- package/templates/__when_styling_tailwind__/tailwind.config.js.ejs +23 -0
- package/templates/__when_unitTesting_jest__/jest.config.json.ejs +11 -0
- package/templates/__when_unitTesting_jest__/tests/App.test.tsx.ejs +13 -0
- package/templates/__when_unitTesting_jest__/tests/setup.ts.ejs +1 -0
- package/templates/__when_unitTesting_jest__/tests/styleMock.ts.ejs +1 -0
- package/templates/__when_unitTesting_rtl__/tests/App.test.tsx.ejs +13 -0
- package/templates/__when_unitTesting_vitest__/tests/App.test.tsx.ejs +16 -0
- package/templates/__when_unitTesting_vitest__/tests/setup.ts.ejs +1 -0
- package/templates/__when_visualTesting_storybook__/.storybook/main.js.ejs +11 -0
- package/templates/__when_visualTesting_storybook__/.storybook/preview.js +8 -0
- package/templates/__when_visualTesting_storybook__/src/components/ui/UiButton.stories.tsx.ejs +15 -0
- package/templates/commitlint.config.js +3 -0
- package/templates/docs/architecture.md.ejs +74 -0
- package/templates/docs/best-practices.md.ejs +56 -0
- package/templates/docs/examples/README.md.ejs +14 -0
- package/templates/docs/examples/api.md.ejs +17 -0
- package/templates/docs/examples/data-fetching.md.ejs +18 -0
- package/templates/docs/examples/forms.md.ejs +18 -0
- package/templates/docs/examples/state.md.ejs +17 -0
- package/templates/docs/examples/styling.md.ejs +23 -0
- package/templates/docs/examples/testing.md.ejs +27 -0
- package/templates/docs/getting-started.md.ejs +55 -0
- package/templates/docs/selected-stack.md.ejs +55 -0
- package/templates/index.html.ejs +19 -0
- package/templates/public/vite.svg +4 -0
- package/templates/src/app/App.tsx.ejs +71 -0
- package/templates/src/app/providers.tsx.ejs +93 -0
- package/templates/src/assets/.gitkeep +0 -0
- package/templates/src/components/StackBadge.tsx.ejs +15 -0
- package/templates/src/components/layout/AppLayout.tsx.ejs +64 -0
- package/templates/src/components/ui/Toast.tsx.ejs +61 -0
- package/templates/src/components/ui/UiButton.tsx.ejs +168 -0
- package/templates/src/config/env.ts.ejs +18 -0
- package/templates/src/features/counter/CounterPanel.tsx.ejs +66 -0
- package/templates/src/features/feedback/FeedbackForm.tsx.ejs +373 -0
- package/templates/src/features/posts/PostsPanel.tsx.ejs +91 -0
- package/templates/src/hooks/useMounted.ts.ejs +15 -0
- package/templates/src/lib/cn.ts.ejs +12 -0
- package/templates/src/lib/monitoring.ts.ejs +66 -0
- package/templates/src/lib/validation.ts.ejs +81 -0
- package/templates/src/main.tsx.ejs +22 -0
- package/templates/src/routes/AboutPage.tsx.ejs +30 -0
- package/templates/src/routes/HomePage.tsx.ejs +47 -0
- package/templates/src/services/api.ts.ejs +84 -0
- package/templates/src/store/index.ts.ejs +80 -0
- package/templates/src/styles/global.css.ejs +457 -0
- package/templates/src/types/index.ts.ejs +7 -0
- package/templates/src/utils/format.ts.ejs +3 -0
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# Forms & validation example
|
|
2
|
+
|
|
3
|
+
**Forms:** <%= stackLabels.forms %>
|
|
4
|
+
**Validation:** <%= stackLabels.validation %>
|
|
5
|
+
|
|
6
|
+
**Live demo:** Feedback form on `/`
|
|
7
|
+
|
|
8
|
+
## When to use
|
|
9
|
+
|
|
10
|
+
Multi-field input with client-side rules and async submit.
|
|
11
|
+
|
|
12
|
+
## Best practice
|
|
13
|
+
|
|
14
|
+
Define schemas once in `src/lib/validation.<%= ext %>` and reuse across UI and API boundaries.
|
|
15
|
+
|
|
16
|
+
## Snippet
|
|
17
|
+
|
|
18
|
+
See `src/features/feedback/FeedbackForm.<%= jsxExt %>`.
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# Client state example
|
|
2
|
+
|
|
3
|
+
**Library:** <%= stackLabels.stateManagement %>
|
|
4
|
+
|
|
5
|
+
**Live demo:** Counter panel on `/`
|
|
6
|
+
|
|
7
|
+
## When to use
|
|
8
|
+
|
|
9
|
+
Shared interactive state that is not fetched from an API (wizard steps, UI toggles, local counters).
|
|
10
|
+
|
|
11
|
+
## Best practice
|
|
12
|
+
|
|
13
|
+
Keep stores/slices focused. Prefer feature-local state; promote to global only when multiple distant trees need it.
|
|
14
|
+
|
|
15
|
+
## Snippet
|
|
16
|
+
|
|
17
|
+
See `src/features/counter/CounterPanel.<%= jsxExt %>` and `src/store/`.
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# Styling & UI example
|
|
2
|
+
|
|
3
|
+
**Styling:** <%= stackLabels.styling %>
|
|
4
|
+
**UI library:** <%= stackLabels.uiLibrary %>
|
|
5
|
+
|
|
6
|
+
## When to use
|
|
7
|
+
|
|
8
|
+
Establish design tokens early; use a component library for complex accessible primitives.
|
|
9
|
+
|
|
10
|
+
## Best practice
|
|
11
|
+
|
|
12
|
+
<% if (uiLibrary === 'shadcn') { -%>
|
|
13
|
+
Treat shadcn-style primitives as owned source — customize freely, keep `cn()` helpers consistent.
|
|
14
|
+
<% } else if (uiLibrary === 'none') { -%>
|
|
15
|
+
Invest in a small set of shared primitives (`UiButton`, layout) before adding a full kit.
|
|
16
|
+
<% } else { -%>
|
|
17
|
+
Wrap third-party components in thin adapters (`UiButton`) so swapping kits later is cheaper.
|
|
18
|
+
<% } -%>
|
|
19
|
+
|
|
20
|
+
## Snippet
|
|
21
|
+
|
|
22
|
+
Global styles: `src/styles/global.<%= styling === 'sass' && uiLibrary !== 'shadcn' ? 'scss' : 'css' %>`
|
|
23
|
+
Button adapter: `src/components/ui/UiButton.<%= jsxExt %>`
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# Testing example
|
|
2
|
+
|
|
3
|
+
| Layer | Choice |
|
|
4
|
+
|-------|--------|
|
|
5
|
+
| Unit | <%= stackLabels.unitTesting %> |
|
|
6
|
+
| E2E | <%= stackLabels.e2eTesting %> |
|
|
7
|
+
| Visual | <%= stackLabels.visualTesting %> |
|
|
8
|
+
|
|
9
|
+
## When to use
|
|
10
|
+
|
|
11
|
+
- Unit: pure logic + component behavior
|
|
12
|
+
- E2E: critical journeys across routes
|
|
13
|
+
- Visual: regressions in UI appearance
|
|
14
|
+
|
|
15
|
+
## Best practice
|
|
16
|
+
|
|
17
|
+
Keep e2e thin. Prefer Testing Library queries that reflect how users find elements (`getByRole`, labels).
|
|
18
|
+
|
|
19
|
+
## Locations
|
|
20
|
+
|
|
21
|
+
- Setup: `tests/setup.<%= ext %>` (when applicable)
|
|
22
|
+
- Component smoke: `tests/App.test.<%= jsxExt %>`
|
|
23
|
+
<% if (e2eTesting === 'playwright') { -%>
|
|
24
|
+
- E2E: `tests/e2e/home.spec.<%= ext %>`
|
|
25
|
+
<% } else if (e2eTesting === 'cypress') { -%>
|
|
26
|
+
- E2E: `cypress/e2e/home.cy.<%= ext %>`
|
|
27
|
+
<% } -%>
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# Getting started
|
|
2
|
+
|
|
3
|
+
Welcome to **<%= projectName %>**, a React starter generated by `create-react-starter-kit`.
|
|
4
|
+
|
|
5
|
+
## Prerequisites
|
|
6
|
+
|
|
7
|
+
- Node.js 18+
|
|
8
|
+
- <%= stackLabels.packageManager %>
|
|
9
|
+
|
|
10
|
+
## Install & run
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
<% if (packageManager === 'pnpm') { -%>
|
|
14
|
+
pnpm install
|
|
15
|
+
pnpm dev
|
|
16
|
+
<% } else if (packageManager === 'yarn') { -%>
|
|
17
|
+
yarn
|
|
18
|
+
yarn dev
|
|
19
|
+
<% } else { -%>
|
|
20
|
+
npm install
|
|
21
|
+
npm run dev
|
|
22
|
+
<% } -%>
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Environment variables
|
|
26
|
+
|
|
27
|
+
Copy `.env.example` → `.env` and fill values. Restart the dev server after changes.
|
|
28
|
+
|
|
29
|
+
Only variables prefixed for your bundler (e.g. `VITE_`) are exposed to the browser. Keep secrets on the server.
|
|
30
|
+
|
|
31
|
+
## What to explore first
|
|
32
|
+
|
|
33
|
+
1. Open `/` — demo panels for state, data fetching, and forms.
|
|
34
|
+
2. Open `/about` — orientation for the folder layout.
|
|
35
|
+
3. Read `docs/selected-stack.md` — why each library is present.
|
|
36
|
+
4. Skim `docs/examples/` — focused usage notes per concern.
|
|
37
|
+
|
|
38
|
+
## Extending the app
|
|
39
|
+
|
|
40
|
+
- Add a domain under `src/features/<name>/`.
|
|
41
|
+
- Register routes in `src/app/App.<%= jsxExt %>`.
|
|
42
|
+
- Put shared UI in `src/components/`.
|
|
43
|
+
- Put HTTP clients in `src/services/`.
|
|
44
|
+
- Import with `@/` (maps to `src/`), e.g. `import { env } from '@/config/env'`.
|
|
45
|
+
|
|
46
|
+
## Testing
|
|
47
|
+
|
|
48
|
+
<% if (unitTesting !== 'none') { -%>
|
|
49
|
+
- Unit: `<%= packageManager === 'npm' ? 'npm run' : packageManager %> test`
|
|
50
|
+
<% } -%>
|
|
51
|
+
<% if (e2eTesting !== 'none') { -%>
|
|
52
|
+
- E2E: `<%= packageManager === 'npm' ? 'npm run' : packageManager %> test:e2e`
|
|
53
|
+
<% } -%>
|
|
54
|
+
|
|
55
|
+
See `docs/best-practices.md` for CI and production notes.
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# Selected stack
|
|
2
|
+
|
|
3
|
+
This document explains each choice made when generating **<%= projectName %>**.
|
|
4
|
+
|
|
5
|
+
## Build — <%= stackLabels.buildTool %>
|
|
6
|
+
|
|
7
|
+
Fast local feedback and a production bundle. Prefer keeping config minimal; add plugins only when needed.
|
|
8
|
+
|
|
9
|
+
**Best practice:** lock Node and package manager versions in CI for reproducible builds.
|
|
10
|
+
|
|
11
|
+
## Language — <%= stackLabels.language %>
|
|
12
|
+
|
|
13
|
+
<% if (isTypeScript) { -%>
|
|
14
|
+
TypeScript catches contract mistakes early. Keep `strict` on; avoid `any` except at true boundaries.
|
|
15
|
+
<% } else { -%>
|
|
16
|
+
JavaScript keeps the barrier low. Consider migrating hot modules to TypeScript as the app grows.
|
|
17
|
+
<% } -%>
|
|
18
|
+
|
|
19
|
+
## Routing — <%= stackLabels.router %>
|
|
20
|
+
|
|
21
|
+
Client-side navigation with nested layouts. Keep route modules thin; push data loading into features.
|
|
22
|
+
|
|
23
|
+
## Client state — <%= stackLabels.stateManagement %>
|
|
24
|
+
|
|
25
|
+
Use for UI state (modals, wizards, ephemeral counters). Do **not** mirror server entities here if you have a server-state library.
|
|
26
|
+
|
|
27
|
+
## Server state — <%= stackLabels.serverState %>
|
|
28
|
+
|
|
29
|
+
Caches remote data, handles loading/error, and deduplicates requests. Example: posts panel on the home page.
|
|
30
|
+
|
|
31
|
+
## Forms — <%= stackLabels.forms %> / Validation — <%= stackLabels.validation %>
|
|
32
|
+
|
|
33
|
+
Schemas define the contract; the form library binds inputs. Share schemas with API payloads when possible.
|
|
34
|
+
|
|
35
|
+
## Styling — <%= stackLabels.styling %> / UI — <%= stackLabels.uiLibrary %>
|
|
36
|
+
|
|
37
|
+
Tokens and primitives establish visual consistency. Prefer composition over one-off CSS.
|
|
38
|
+
|
|
39
|
+
## API — <%= stackLabels.apiLayer %>
|
|
40
|
+
|
|
41
|
+
Centralize base URL, headers, and error logging in `src/services/api.<%= ext %>`.
|
|
42
|
+
|
|
43
|
+
## Quality gates
|
|
44
|
+
|
|
45
|
+
| Area | Choice |
|
|
46
|
+
|------|--------|
|
|
47
|
+
| Unit | <%= stackLabels.unitTesting %> |
|
|
48
|
+
| E2E | <%= stackLabels.e2eTesting %> |
|
|
49
|
+
| Visual | <%= stackLabels.visualTesting %> |
|
|
50
|
+
| Lint | <%= stackLabels.linting %> |
|
|
51
|
+
| Format | <%= stackLabels.formatting %> |
|
|
52
|
+
| Git hooks | Husky + lint-staged + commitlint |
|
|
53
|
+
| Structure | Feature-based + `@/` aliases |
|
|
54
|
+
|
|
55
|
+
See `docs/examples/` for focused usage notes.
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
|
6
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
7
|
+
<title><%= projectName %></title>
|
|
8
|
+
</head>
|
|
9
|
+
<body>
|
|
10
|
+
<div id="root"></div>
|
|
11
|
+
<% if (buildTool === 'vite') { -%>
|
|
12
|
+
<script type="module" src="/src/main.<%= jsxExt %>"></script>
|
|
13
|
+
<% } else if (buildTool === 'parcel') { -%>
|
|
14
|
+
<script type="module" src="./src/main.<%= jsxExt %>"></script>
|
|
15
|
+
<% } else if (buildTool === 'esbuild' || buildTool === 'rollup') { -%>
|
|
16
|
+
<script type="module" src="/dist/main.js"></script>
|
|
17
|
+
<% } -%>
|
|
18
|
+
</body>
|
|
19
|
+
</html>
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
<% if (router === 'react-router') { -%>
|
|
2
|
+
import { BrowserRouter, Navigate, Route, Routes } from 'react-router-dom';
|
|
3
|
+
import { AppLayout } from '@/components/layout/AppLayout';
|
|
4
|
+
import { HomePage } from '@/routes/HomePage';
|
|
5
|
+
import { AboutPage } from '@/routes/AboutPage';
|
|
6
|
+
|
|
7
|
+
export function App() {
|
|
8
|
+
return (
|
|
9
|
+
<BrowserRouter>
|
|
10
|
+
<Routes>
|
|
11
|
+
<Route element={<AppLayout />}>
|
|
12
|
+
<Route path="/" element={<HomePage />} />
|
|
13
|
+
<Route path="/about" element={<AboutPage />} />
|
|
14
|
+
<Route path="*" element={<Navigate to="/" replace />} />
|
|
15
|
+
</Route>
|
|
16
|
+
</Routes>
|
|
17
|
+
</BrowserRouter>
|
|
18
|
+
);
|
|
19
|
+
}
|
|
20
|
+
<% } else { -%>
|
|
21
|
+
import {
|
|
22
|
+
Outlet,
|
|
23
|
+
RouterProvider,
|
|
24
|
+
createRootRoute,
|
|
25
|
+
createRoute,
|
|
26
|
+
createRouter,
|
|
27
|
+
} from '@tanstack/react-router';
|
|
28
|
+
import { AppLayout } from '@/components/layout/AppLayout';
|
|
29
|
+
import { HomePage } from '@/routes/HomePage';
|
|
30
|
+
import { AboutPage } from '@/routes/AboutPage';
|
|
31
|
+
|
|
32
|
+
function RootComponent() {
|
|
33
|
+
return (
|
|
34
|
+
<AppLayout>
|
|
35
|
+
<Outlet />
|
|
36
|
+
</AppLayout>
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const rootRoute = createRootRoute({
|
|
41
|
+
component: RootComponent,
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
const indexRoute = createRoute({
|
|
45
|
+
getParentRoute: () => rootRoute,
|
|
46
|
+
path: '/',
|
|
47
|
+
component: HomePage,
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
const aboutRoute = createRoute({
|
|
51
|
+
getParentRoute: () => rootRoute,
|
|
52
|
+
path: '/about',
|
|
53
|
+
component: AboutPage,
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
const routeTree = rootRoute.addChildren([indexRoute, aboutRoute]);
|
|
57
|
+
|
|
58
|
+
const router = createRouter({ routeTree });
|
|
59
|
+
|
|
60
|
+
<% if (isTypeScript) { -%>
|
|
61
|
+
declare module '@tanstack/react-router' {
|
|
62
|
+
interface Register {
|
|
63
|
+
router: typeof router;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
<% } -%>
|
|
67
|
+
|
|
68
|
+
export function App() {
|
|
69
|
+
return <RouterProvider router={router} />;
|
|
70
|
+
}
|
|
71
|
+
<% } -%>
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
<% if (isTypeScript) { -%>
|
|
2
|
+
import type { ReactNode } from 'react';
|
|
3
|
+
<% } -%>
|
|
4
|
+
<% if (serverState === 'tanstack-query') { -%>
|
|
5
|
+
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
|
6
|
+
<% } -%>
|
|
7
|
+
<% if (serverState === 'swr') { -%>
|
|
8
|
+
import { SWRConfig } from 'swr';
|
|
9
|
+
import { apiGet } from '@/services/api';
|
|
10
|
+
<% } -%>
|
|
11
|
+
<% if (stateManagement === 'redux') { -%>
|
|
12
|
+
import { Provider } from 'react-redux';
|
|
13
|
+
import { store } from '@/store';
|
|
14
|
+
<% } -%>
|
|
15
|
+
<% if (stateManagement === 'jotai') { -%>
|
|
16
|
+
import { Provider as JotaiProvider } from 'jotai';
|
|
17
|
+
<% } -%>
|
|
18
|
+
<% if (stateManagement === 'context') { -%>
|
|
19
|
+
import { AppStateProvider } from '@/store/context';
|
|
20
|
+
<% } -%>
|
|
21
|
+
<% if (uiLibrary === 'chakra') { -%>
|
|
22
|
+
import { ChakraProvider, defaultSystem } from '@chakra-ui/react';
|
|
23
|
+
<% } -%>
|
|
24
|
+
<% if (uiLibrary === 'mui') { -%>
|
|
25
|
+
import { CssBaseline, ThemeProvider, createTheme } from '@mui/material';
|
|
26
|
+
<% } -%>
|
|
27
|
+
import { ToastProvider } from '@/components/ui/Toast';
|
|
28
|
+
|
|
29
|
+
<% if (serverState === 'tanstack-query') { -%>
|
|
30
|
+
const queryClient = new QueryClient({
|
|
31
|
+
defaultOptions: {
|
|
32
|
+
queries: {
|
|
33
|
+
staleTime: 30_000,
|
|
34
|
+
retry: 1,
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
});
|
|
38
|
+
<% } -%>
|
|
39
|
+
<% if (uiLibrary === 'mui') { -%>
|
|
40
|
+
const theme = createTheme({
|
|
41
|
+
palette: {
|
|
42
|
+
primary: { main: '#1d4ed8' },
|
|
43
|
+
},
|
|
44
|
+
});
|
|
45
|
+
<% } -%>
|
|
46
|
+
|
|
47
|
+
<% if (isTypeScript) { -%>
|
|
48
|
+
interface AppProvidersProps {
|
|
49
|
+
children: ReactNode;
|
|
50
|
+
}
|
|
51
|
+
<% } -%>
|
|
52
|
+
|
|
53
|
+
export function AppProviders({ children }<% if (isTypeScript) { %>: AppProvidersProps<% } %>) {
|
|
54
|
+
let tree = children;
|
|
55
|
+
|
|
56
|
+
<% if (stateManagement === 'context') { -%>
|
|
57
|
+
tree = <AppStateProvider>{tree}</AppStateProvider>;
|
|
58
|
+
<% } -%>
|
|
59
|
+
<% if (stateManagement === 'jotai') { -%>
|
|
60
|
+
tree = <JotaiProvider>{tree}</JotaiProvider>;
|
|
61
|
+
<% } -%>
|
|
62
|
+
<% if (stateManagement === 'redux') { -%>
|
|
63
|
+
tree = <Provider store={store}>{tree}</Provider>;
|
|
64
|
+
<% } -%>
|
|
65
|
+
<% if (serverState === 'tanstack-query') { -%>
|
|
66
|
+
tree = <QueryClientProvider client={queryClient}>{tree}</QueryClientProvider>;
|
|
67
|
+
<% } -%>
|
|
68
|
+
<% if (serverState === 'swr') { -%>
|
|
69
|
+
tree = (
|
|
70
|
+
<SWRConfig
|
|
71
|
+
value={{
|
|
72
|
+
fetcher: (url<% if (isTypeScript) { %>: string<% } %>) => apiGet(url),
|
|
73
|
+
revalidateOnFocus: false,
|
|
74
|
+
}}
|
|
75
|
+
>
|
|
76
|
+
{tree}
|
|
77
|
+
</SWRConfig>
|
|
78
|
+
);
|
|
79
|
+
<% } -%>
|
|
80
|
+
<% if (uiLibrary === 'mui') { -%>
|
|
81
|
+
tree = (
|
|
82
|
+
<ThemeProvider theme={theme}>
|
|
83
|
+
<CssBaseline />
|
|
84
|
+
{tree}
|
|
85
|
+
</ThemeProvider>
|
|
86
|
+
);
|
|
87
|
+
<% } -%>
|
|
88
|
+
<% if (uiLibrary === 'chakra') { -%>
|
|
89
|
+
tree = <ChakraProvider value={defaultSystem}>{tree}</ChakraProvider>;
|
|
90
|
+
<% } -%>
|
|
91
|
+
|
|
92
|
+
return <ToastProvider>{tree}</ToastProvider>;
|
|
93
|
+
}
|
|
File without changes
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
<% if (isTypeScript) { -%>
|
|
2
|
+
interface StackBadgeProps {
|
|
3
|
+
label: string;
|
|
4
|
+
value: string;
|
|
5
|
+
}
|
|
6
|
+
<% } -%>
|
|
7
|
+
|
|
8
|
+
export function StackBadge({ label, value }<% if (isTypeScript) { %>: StackBadgeProps<% } %>) {
|
|
9
|
+
return (
|
|
10
|
+
<div className="stack-badge">
|
|
11
|
+
<span className="stack-badge-label">{label}</span>
|
|
12
|
+
<span className="stack-badge-value">{value}</span>
|
|
13
|
+
</div>
|
|
14
|
+
);
|
|
15
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
<% if (isTypeScript) { -%>
|
|
2
|
+
import type { ReactNode } from 'react';
|
|
3
|
+
<% } -%>
|
|
4
|
+
<% if (router === 'react-router') { -%>
|
|
5
|
+
import { NavLink, Outlet } from 'react-router-dom';
|
|
6
|
+
<% } else { -%>
|
|
7
|
+
import { Link, useRouterState } from '@tanstack/react-router';
|
|
8
|
+
<% } -%>
|
|
9
|
+
|
|
10
|
+
<% if (isTypeScript && router === 'tanstack-router') { -%>
|
|
11
|
+
interface AppLayoutProps {
|
|
12
|
+
children?: ReactNode;
|
|
13
|
+
}
|
|
14
|
+
<% } -%>
|
|
15
|
+
|
|
16
|
+
export function AppLayout(<% if (router === 'tanstack-router') { %>{ children }<% if (isTypeScript) { %>: AppLayoutProps<% } %><% } %>) {
|
|
17
|
+
<% if (router === 'tanstack-router') { -%>
|
|
18
|
+
const pathname = useRouterState({ select: (s) => s.location.pathname });
|
|
19
|
+
<% } -%>
|
|
20
|
+
|
|
21
|
+
return (
|
|
22
|
+
<div className="app-shell">
|
|
23
|
+
<header className="app-header">
|
|
24
|
+
<div className="brand">
|
|
25
|
+
<span className="brand-mark" aria-hidden="true" />
|
|
26
|
+
<div>
|
|
27
|
+
<p className="brand-name"><%= projectName %></p>
|
|
28
|
+
<p className="brand-tagline">Production React starter</p>
|
|
29
|
+
</div>
|
|
30
|
+
</div>
|
|
31
|
+
<nav className="app-nav" aria-label="Primary">
|
|
32
|
+
<% if (router === 'react-router') { -%>
|
|
33
|
+
<NavLink to="/" end className={({ isActive }) => (isActive ? 'nav-link active' : 'nav-link')}>
|
|
34
|
+
Home
|
|
35
|
+
</NavLink>
|
|
36
|
+
<NavLink to="/about" className={({ isActive }) => (isActive ? 'nav-link active' : 'nav-link')}>
|
|
37
|
+
About
|
|
38
|
+
</NavLink>
|
|
39
|
+
<% } else { -%>
|
|
40
|
+
<Link to="/" className={pathname === '/' ? 'nav-link active' : 'nav-link'}>
|
|
41
|
+
Home
|
|
42
|
+
</Link>
|
|
43
|
+
<Link to="/about" className={pathname === '/about' ? 'nav-link active' : 'nav-link'}>
|
|
44
|
+
About
|
|
45
|
+
</Link>
|
|
46
|
+
<% } -%>
|
|
47
|
+
</nav>
|
|
48
|
+
</header>
|
|
49
|
+
<main className="app-main">
|
|
50
|
+
<% if (router === 'react-router') { -%>
|
|
51
|
+
<Outlet />
|
|
52
|
+
<% } else { -%>
|
|
53
|
+
{children}
|
|
54
|
+
<% } -%>
|
|
55
|
+
</main>
|
|
56
|
+
<footer className="app-footer">
|
|
57
|
+
<p>
|
|
58
|
+
Scaffolded with create-react-starter-kit · <%= stackLabels.buildTool %> ·{' '}
|
|
59
|
+
<%= stackLabels.language %>
|
|
60
|
+
</p>
|
|
61
|
+
</footer>
|
|
62
|
+
</div>
|
|
63
|
+
);
|
|
64
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createContext,
|
|
3
|
+
useCallback,
|
|
4
|
+
useContext,
|
|
5
|
+
useMemo,
|
|
6
|
+
useState,
|
|
7
|
+
<% if (isTypeScript) { -%>
|
|
8
|
+
type ReactNode,
|
|
9
|
+
<% } -%>
|
|
10
|
+
} from 'react';
|
|
11
|
+
|
|
12
|
+
<% if (isTypeScript) { -%>
|
|
13
|
+
type ToastTone = 'info' | 'success' | 'error';
|
|
14
|
+
|
|
15
|
+
interface ToastItem {
|
|
16
|
+
id: string;
|
|
17
|
+
message: string;
|
|
18
|
+
tone: ToastTone;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
interface ToastContextValue {
|
|
22
|
+
push: (message: string, tone?: ToastTone) => void;
|
|
23
|
+
}
|
|
24
|
+
<% } -%>
|
|
25
|
+
|
|
26
|
+
const ToastContext = createContext<% if (isTypeScript) { %><ToastContextValue | null><% } %>(null);
|
|
27
|
+
|
|
28
|
+
export function ToastProvider({ children }<% if (isTypeScript) { %>: { children: ReactNode }<% } %>) {
|
|
29
|
+
const [items, setItems] = useState<% if (isTypeScript) { %><ToastItem[]><% } %>([]);
|
|
30
|
+
|
|
31
|
+
const push = useCallback((message<% if (isTypeScript) { %>: string<% } %>, tone<% if (isTypeScript) { %>: ToastTone<% } %> = 'info') => {
|
|
32
|
+
const id = crypto.randomUUID();
|
|
33
|
+
setItems((prev) => [...prev, { id, message, tone }]);
|
|
34
|
+
window.setTimeout(() => {
|
|
35
|
+
setItems((prev) => prev.filter((item) => item.id !== id));
|
|
36
|
+
}, 3200);
|
|
37
|
+
}, []);
|
|
38
|
+
|
|
39
|
+
const value = useMemo(() => ({ push }), [push]);
|
|
40
|
+
|
|
41
|
+
return (
|
|
42
|
+
<ToastContext.Provider value={value}>
|
|
43
|
+
{children}
|
|
44
|
+
<div className="toast-viewport" aria-live="polite">
|
|
45
|
+
{items.map((item) => (
|
|
46
|
+
<div key={item.id} className={`toast toast-${item.tone}`}>
|
|
47
|
+
{item.message}
|
|
48
|
+
</div>
|
|
49
|
+
))}
|
|
50
|
+
</div>
|
|
51
|
+
</ToastContext.Provider>
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export function useToast() {
|
|
56
|
+
const ctx = useContext(ToastContext);
|
|
57
|
+
if (!ctx) {
|
|
58
|
+
throw new Error('useToast must be used within ToastProvider');
|
|
59
|
+
}
|
|
60
|
+
return ctx;
|
|
61
|
+
}
|