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,36 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { defineConfig } from '@rspack/cli';
|
|
3
|
+
import { HtmlRspackPlugin } from '@rspack/core';
|
|
4
|
+
|
|
5
|
+
export default defineConfig({
|
|
6
|
+
entry: {
|
|
7
|
+
main: './src/main.<%= jsxExt %>',
|
|
8
|
+
},
|
|
9
|
+
resolve: {
|
|
10
|
+
alias: {
|
|
11
|
+
'@': path.resolve('src'),
|
|
12
|
+
},
|
|
13
|
+
},
|
|
14
|
+
builtins: {
|
|
15
|
+
react: {
|
|
16
|
+
runtime: 'automatic',
|
|
17
|
+
},
|
|
18
|
+
},
|
|
19
|
+
module: {
|
|
20
|
+
rules: [
|
|
21
|
+
{
|
|
22
|
+
test: /\.css$/,
|
|
23
|
+
type: 'css',
|
|
24
|
+
},
|
|
25
|
+
],
|
|
26
|
+
},
|
|
27
|
+
plugins: [
|
|
28
|
+
new HtmlRspackPlugin({
|
|
29
|
+
template: './index.html',
|
|
30
|
+
}),
|
|
31
|
+
],
|
|
32
|
+
devServer: {
|
|
33
|
+
port: 5173,
|
|
34
|
+
historyApiFallback: true,
|
|
35
|
+
},
|
|
36
|
+
});
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { fileURLToPath } from 'node:url';
|
|
3
|
+
import { defineConfig } from 'vite';
|
|
4
|
+
import react from '@vitejs/plugin-react';
|
|
5
|
+
<% if (unitTesting === 'vitest') { -%>
|
|
6
|
+
import { defineConfig as defineVitestConfig, mergeConfig } from 'vitest/config';
|
|
7
|
+
<% } -%>
|
|
8
|
+
|
|
9
|
+
const rootDir = path.dirname(fileURLToPath(import.meta.url));
|
|
10
|
+
|
|
11
|
+
const viteConfig = defineConfig({
|
|
12
|
+
plugins: [react()],
|
|
13
|
+
resolve: {
|
|
14
|
+
alias: {
|
|
15
|
+
'@': path.resolve(rootDir, 'src'),
|
|
16
|
+
},
|
|
17
|
+
},
|
|
18
|
+
server: {
|
|
19
|
+
port: 5173,
|
|
20
|
+
},
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
<% if (unitTesting === 'vitest') { -%>
|
|
24
|
+
export default mergeConfig(
|
|
25
|
+
viteConfig,
|
|
26
|
+
defineVitestConfig({
|
|
27
|
+
test: {
|
|
28
|
+
globals: true,
|
|
29
|
+
environment: 'jsdom',
|
|
30
|
+
setupFiles: './tests/setup.<%= ext %>',
|
|
31
|
+
exclude: ['**/node_modules/**', '**/dist/**', '**/e2e/**', '**/*.e2e.*'],
|
|
32
|
+
},
|
|
33
|
+
}),
|
|
34
|
+
);
|
|
35
|
+
<% } else { -%>
|
|
36
|
+
export default viteConfig;
|
|
37
|
+
<% } -%>
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
<% if (isTypeScript) { -%>
|
|
2
|
+
import type { Configuration } from 'webpack';
|
|
3
|
+
import type { Configuration as DevServerConfiguration } from 'webpack-dev-server';
|
|
4
|
+
<% } -%>
|
|
5
|
+
import path from 'node:path';
|
|
6
|
+
import HtmlWebpackPlugin from 'html-webpack-plugin';
|
|
7
|
+
|
|
8
|
+
<% if (isTypeScript) { -%>
|
|
9
|
+
const config: Configuration & { devServer?: DevServerConfiguration } = {
|
|
10
|
+
<% } else { -%>
|
|
11
|
+
const config = {
|
|
12
|
+
<% } -%>
|
|
13
|
+
entry: './src/main.<%= jsxExt %>',
|
|
14
|
+
output: {
|
|
15
|
+
path: path.resolve('dist'),
|
|
16
|
+
filename: '[name].[contenthash].js',
|
|
17
|
+
clean: true,
|
|
18
|
+
publicPath: '/',
|
|
19
|
+
},
|
|
20
|
+
resolve: {
|
|
21
|
+
extensions: ['.<%= jsxExt %>', '.<%= ext %>', '.js', '.jsx', '.json'],
|
|
22
|
+
alias: {
|
|
23
|
+
'@': path.resolve('src'),
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
module: {
|
|
27
|
+
rules: [
|
|
28
|
+
{
|
|
29
|
+
test: /\.(<%= isTypeScript ? 'ts|tsx' : 'js|jsx' %>)$/,
|
|
30
|
+
exclude: /node_modules/,
|
|
31
|
+
use: {
|
|
32
|
+
loader: 'babel-loader',
|
|
33
|
+
options: {
|
|
34
|
+
presets: [
|
|
35
|
+
'@babel/preset-env',
|
|
36
|
+
['@babel/preset-react', { runtime: 'automatic' }],
|
|
37
|
+
<% if (isTypeScript) { -%>
|
|
38
|
+
'@babel/preset-typescript',
|
|
39
|
+
<% } -%>
|
|
40
|
+
],
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
test: /\.css$/,
|
|
46
|
+
use: ['style-loader', 'css-loader'<% if (styling === 'sass') { %>, 'sass-loader'<% } %>],
|
|
47
|
+
},
|
|
48
|
+
<% if (styling === 'sass') { -%>
|
|
49
|
+
{
|
|
50
|
+
test: /\.scss$/,
|
|
51
|
+
use: ['style-loader', 'css-loader', 'sass-loader'],
|
|
52
|
+
},
|
|
53
|
+
<% } -%>
|
|
54
|
+
],
|
|
55
|
+
},
|
|
56
|
+
plugins: [
|
|
57
|
+
new HtmlWebpackPlugin({
|
|
58
|
+
template: './index.html',
|
|
59
|
+
}),
|
|
60
|
+
],
|
|
61
|
+
devServer: {
|
|
62
|
+
port: 5173,
|
|
63
|
+
historyApiFallback: true,
|
|
64
|
+
hot: true,
|
|
65
|
+
},
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
export default config;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { defineConfig, devices } from '@playwright/test';
|
|
2
|
+
|
|
3
|
+
export default defineConfig({
|
|
4
|
+
testDir: './tests/e2e',
|
|
5
|
+
fullyParallel: true,
|
|
6
|
+
use: {
|
|
7
|
+
baseURL: 'http://127.0.0.1:5173',
|
|
8
|
+
trace: 'on-first-retry',
|
|
9
|
+
},
|
|
10
|
+
webServer: {
|
|
11
|
+
command: '<%= packageManager === 'npm' ? 'npm run dev' : packageManager + ' dev' %>',
|
|
12
|
+
url: 'http://127.0.0.1:5173',
|
|
13
|
+
reuseExistingServer: !process.env.CI,
|
|
14
|
+
},
|
|
15
|
+
projects: [{ name: 'chromium', use: { ...devices['Desktop Chrome'] } }],
|
|
16
|
+
});
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://biomejs.dev/schemas/1.9.4/schema.json",
|
|
3
|
+
"organizeImports": { "enabled": true },
|
|
4
|
+
"linter": {
|
|
5
|
+
"enabled": true,
|
|
6
|
+
"rules": {
|
|
7
|
+
"recommended": true
|
|
8
|
+
}
|
|
9
|
+
},
|
|
10
|
+
"formatter": {
|
|
11
|
+
"enabled": true,
|
|
12
|
+
"indentStyle": "space"
|
|
13
|
+
},
|
|
14
|
+
"javascript": {
|
|
15
|
+
"formatter": {
|
|
16
|
+
"quoteStyle": "single"
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import js from '@eslint/js';
|
|
2
|
+
import globals from 'globals';
|
|
3
|
+
import react from 'eslint-plugin-react';
|
|
4
|
+
import reactHooks from 'eslint-plugin-react-hooks';
|
|
5
|
+
<% if (isTypeScript) { -%>
|
|
6
|
+
import tseslint from 'typescript-eslint';
|
|
7
|
+
<% } -%>
|
|
8
|
+
<% if (formatting === 'prettier') { -%>
|
|
9
|
+
import prettier from 'eslint-config-prettier';
|
|
10
|
+
<% } -%>
|
|
11
|
+
|
|
12
|
+
export default [
|
|
13
|
+
{ ignores: ['dist', 'coverage', 'storybook-static'] },
|
|
14
|
+
js.configs.recommended,
|
|
15
|
+
<% if (isTypeScript) { -%>
|
|
16
|
+
...tseslint.configs.recommended,
|
|
17
|
+
<% } -%>
|
|
18
|
+
{
|
|
19
|
+
files: ['**/*.{<%= isTypeScript ? 'ts,tsx' : 'js,jsx' %>}'],
|
|
20
|
+
languageOptions: {
|
|
21
|
+
ecmaVersion: 2022,
|
|
22
|
+
sourceType: 'module',
|
|
23
|
+
globals: globals.browser,
|
|
24
|
+
},
|
|
25
|
+
plugins: {
|
|
26
|
+
react,
|
|
27
|
+
'react-hooks': reactHooks,
|
|
28
|
+
},
|
|
29
|
+
settings: {
|
|
30
|
+
react: { version: 'detect' },
|
|
31
|
+
},
|
|
32
|
+
rules: {
|
|
33
|
+
...react.configs.recommended.rules,
|
|
34
|
+
...reactHooks.configs.recommended.rules,
|
|
35
|
+
'react/react-in-jsx-scope': 'off',
|
|
36
|
+
'react/prop-types': 'off',
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
<% if (formatting === 'prettier') { -%>
|
|
40
|
+
prettier,
|
|
41
|
+
<% } -%>
|
|
42
|
+
];
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react';
|
|
2
|
+
<% if (isTypeScript) { -%>
|
|
3
|
+
import type { Post } from '@/services/api';
|
|
4
|
+
<% } -%>
|
|
5
|
+
import { env } from '@/config/env';
|
|
6
|
+
|
|
7
|
+
const baseUrl = env.apiBaseUrl;
|
|
8
|
+
|
|
9
|
+
export const postsApi = createApi({
|
|
10
|
+
reducerPath: 'postsApi',
|
|
11
|
+
baseQuery: fetchBaseQuery({ baseUrl }),
|
|
12
|
+
endpoints: (builder) => ({
|
|
13
|
+
getPosts: builder.query<% if (isTypeScript) { %><Post[], void><% } %>({
|
|
14
|
+
query: () => '/posts?_limit=5',
|
|
15
|
+
}),
|
|
16
|
+
}),
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
export const { useGetPostsQuery } = postsApi;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createContext,
|
|
3
|
+
useContext,
|
|
4
|
+
useMemo,
|
|
5
|
+
useReducer,
|
|
6
|
+
<% if (isTypeScript) { -%>
|
|
7
|
+
type ReactNode,
|
|
8
|
+
type Dispatch,
|
|
9
|
+
<% } -%>
|
|
10
|
+
} from 'react';
|
|
11
|
+
|
|
12
|
+
<% if (isTypeScript) { -%>
|
|
13
|
+
interface State {
|
|
14
|
+
value: number;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
type Action =
|
|
18
|
+
| { type: 'increment' }
|
|
19
|
+
| { type: 'decrement' }
|
|
20
|
+
| { type: 'set'; payload: number };
|
|
21
|
+
|
|
22
|
+
interface AppStateContextValue {
|
|
23
|
+
state: State;
|
|
24
|
+
dispatch: Dispatch<Action>;
|
|
25
|
+
}
|
|
26
|
+
<% } -%>
|
|
27
|
+
|
|
28
|
+
const initialState<% if (isTypeScript) { %>: State<% } %> = { value: 0 };
|
|
29
|
+
|
|
30
|
+
function reducer(state<% if (isTypeScript) { %>: State<% } %>, action<% if (isTypeScript) { %>: Action<% } %>)<% if (isTypeScript) { %>: State<% } %> {
|
|
31
|
+
switch (action.type) {
|
|
32
|
+
case 'increment':
|
|
33
|
+
return { value: state.value + 1 };
|
|
34
|
+
case 'decrement':
|
|
35
|
+
return { value: state.value - 1 };
|
|
36
|
+
case 'set':
|
|
37
|
+
return { value: action.payload };
|
|
38
|
+
default:
|
|
39
|
+
return state;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const AppStateContext = createContext<% if (isTypeScript) { %><AppStateContextValue | null><% } %>(null);
|
|
44
|
+
|
|
45
|
+
export function AppStateProvider({ children }<% if (isTypeScript) { %>: { children: ReactNode }<% } %>) {
|
|
46
|
+
const [state, dispatch] = useReducer(reducer, initialState);
|
|
47
|
+
const value = useMemo(() => ({ state, dispatch }), [state]);
|
|
48
|
+
return <AppStateContext.Provider value={value}>{children}</AppStateContext.Provider>;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function useAppState() {
|
|
52
|
+
const ctx = useContext(AppStateContext);
|
|
53
|
+
if (!ctx) {
|
|
54
|
+
throw new Error('useAppState must be used within AppStateProvider');
|
|
55
|
+
}
|
|
56
|
+
return ctx;
|
|
57
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/** @type {import('tailwindcss').Config} */
|
|
2
|
+
export default {
|
|
3
|
+
content: ['./index.html', './src/**/*.{<%= isTypeScript ? 'js,ts,jsx,tsx' : 'js,jsx' %>}'],
|
|
4
|
+
theme: {
|
|
5
|
+
extend: {
|
|
6
|
+
colors: {
|
|
7
|
+
brand: {
|
|
8
|
+
50: '#f0f7ff',
|
|
9
|
+
100: '#dceeff',
|
|
10
|
+
500: '#2563eb',
|
|
11
|
+
600: '#1d4ed8',
|
|
12
|
+
700: '#1e40af',
|
|
13
|
+
900: '#0f172a',
|
|
14
|
+
},
|
|
15
|
+
},
|
|
16
|
+
fontFamily: {
|
|
17
|
+
display: ['"DM Sans"', 'system-ui', 'sans-serif'],
|
|
18
|
+
body: ['"IBM Plex Sans"', 'system-ui', 'sans-serif'],
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
},
|
|
22
|
+
plugins: [],
|
|
23
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
{
|
|
2
|
+
<% if (isTypeScript) { -%>
|
|
3
|
+
"preset": "ts-jest",
|
|
4
|
+
<% } -%>
|
|
5
|
+
"testEnvironment": "jsdom",
|
|
6
|
+
"setupFilesAfterEnv": ["<rootDir>/tests/setup.<%= ext %>"],
|
|
7
|
+
"moduleNameMapper": {
|
|
8
|
+
"^@/(.*)$": "<rootDir>/src/$1",
|
|
9
|
+
"\\\\.(css|scss)$": "<rootDir>/tests/styleMock.<%= ext %>"
|
|
10
|
+
}
|
|
11
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { render, screen } from '@testing-library/react';
|
|
2
|
+
import { AppProviders } from '@/app/providers';
|
|
3
|
+
import { HomePage } from '@/routes/HomePage';
|
|
4
|
+
|
|
5
|
+
test('renders the project hero', () => {
|
|
6
|
+
render(
|
|
7
|
+
<AppProviders>
|
|
8
|
+
<HomePage />
|
|
9
|
+
</AppProviders>,
|
|
10
|
+
);
|
|
11
|
+
|
|
12
|
+
expect(screen.getByRole('heading', { name: /<%= projectName %>/i })).toBeInTheDocument();
|
|
13
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import '@testing-library/jest-dom';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export default {};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { render, screen } from '@testing-library/react';
|
|
2
|
+
import { AppProviders } from '@/app/providers';
|
|
3
|
+
import { HomePage } from '@/routes/HomePage';
|
|
4
|
+
|
|
5
|
+
test('renders the project hero', () => {
|
|
6
|
+
render(
|
|
7
|
+
<AppProviders>
|
|
8
|
+
<HomePage />
|
|
9
|
+
</AppProviders>,
|
|
10
|
+
);
|
|
11
|
+
|
|
12
|
+
expect(screen.getByRole('heading', { name: /<%= projectName %>/i })).toBeInTheDocument();
|
|
13
|
+
});
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { render, screen } from '@testing-library/react';
|
|
3
|
+
import { AppProviders } from '@/app/providers';
|
|
4
|
+
import { HomePage } from '@/routes/HomePage';
|
|
5
|
+
|
|
6
|
+
describe('HomePage', () => {
|
|
7
|
+
it('renders the project hero', () => {
|
|
8
|
+
render(
|
|
9
|
+
<AppProviders>
|
|
10
|
+
<HomePage />
|
|
11
|
+
</AppProviders>,
|
|
12
|
+
);
|
|
13
|
+
|
|
14
|
+
expect(screen.getByRole('heading', { name: /<%= projectName %>/i })).toBeInTheDocument();
|
|
15
|
+
});
|
|
16
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import '@testing-library/jest-dom';
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/** @type { import('@storybook/<%= buildTool === 'vite' || buildTool === 'esbuild' ? 'react-vite' : 'react-webpack5' %>').StorybookConfig } */
|
|
2
|
+
const config = {
|
|
3
|
+
stories: ['../src/**/*.stories.@(<%= isTypeScript ? 'ts|tsx' : 'js|jsx' %>)'],
|
|
4
|
+
addons: [],
|
|
5
|
+
framework: {
|
|
6
|
+
name: '@storybook/<%= buildTool === 'vite' || buildTool === 'esbuild' ? 'react-vite' : 'react-webpack5' %>',
|
|
7
|
+
options: {},
|
|
8
|
+
},
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export default config;
|
package/dist/templates/__when_visualTesting_storybook__/src/components/ui/UiButton.stories.tsx.ejs
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react';
|
|
2
|
+
import { UiButton } from '@/components/ui/UiButton';
|
|
3
|
+
|
|
4
|
+
const meta = {
|
|
5
|
+
title: 'UI/UiButton',
|
|
6
|
+
component: UiButton,
|
|
7
|
+
args: {
|
|
8
|
+
children: 'Click me',
|
|
9
|
+
},
|
|
10
|
+
} satisfies Meta<typeof UiButton>;
|
|
11
|
+
|
|
12
|
+
export default meta;
|
|
13
|
+
type Story = StoryObj<typeof meta>;
|
|
14
|
+
|
|
15
|
+
export const Primary: Story = {};
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# Architecture
|
|
2
|
+
|
|
3
|
+
## Why this stack
|
|
4
|
+
|
|
5
|
+
You selected a coherent default-friendly stack during scaffolding. The generator wires providers, scripts, and a demo page so packages are not orphaned in `package.json`.
|
|
6
|
+
|
|
7
|
+
| Concern | Choice | Role |
|
|
8
|
+
|---------|--------|------|
|
|
9
|
+
| Bundler | <%= stackLabels.buildTool %> | Dev server + production build |
|
|
10
|
+
| Language | <%= stackLabels.language %> | Source language / typing |
|
|
11
|
+
| Routing | <%= stackLabels.router %> | URL → UI mapping |
|
|
12
|
+
| Client state | <%= stackLabels.stateManagement %> | Local interactive state |
|
|
13
|
+
| Server state | <%= stackLabels.serverState %> | Remote data cache |
|
|
14
|
+
| Forms | <%= stackLabels.forms %> | Form state & submit |
|
|
15
|
+
| Validation | <%= stackLabels.validation %> | Schema rules |
|
|
16
|
+
| Styling | <%= stackLabels.styling %> | Visual system |
|
|
17
|
+
| UI kit | <%= stackLabels.uiLibrary %> | Components / primitives |
|
|
18
|
+
| API | <%= stackLabels.apiLayer %> | HTTP transport |
|
|
19
|
+
|
|
20
|
+
## Folder organization (feature-based)
|
|
21
|
+
|
|
22
|
+
This scaffold uses a **feature-based / domain-driven** layout — the pattern most large React teams settle on as apps grow (similar in spirit to how companies structure product domains: keep code that changes together close together).
|
|
23
|
+
|
|
24
|
+
```text
|
|
25
|
+
src/
|
|
26
|
+
app/ # bootstrap, providers, root App shell
|
|
27
|
+
assets/ # images, fonts, static media imported from code
|
|
28
|
+
components/ # shared, feature-agnostic UI (layout/, ui/)
|
|
29
|
+
config/ # env helpers and app-level constants
|
|
30
|
+
features/ # domain modules (counter, posts, feedback, …)
|
|
31
|
+
hooks/ # shared hooks
|
|
32
|
+
lib/ # third-party wrappers / cross-cutting helpers
|
|
33
|
+
routes/ # route-level pages
|
|
34
|
+
services/ # API clients / transport
|
|
35
|
+
store/ # global client state (when selected)
|
|
36
|
+
styles/ # global styles / tokens
|
|
37
|
+
types/ # shared TypeScript types
|
|
38
|
+
utils/ # pure helpers
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
**Rules of thumb**
|
|
42
|
+
|
|
43
|
+
- Put new product work in `src/features/<domain>/`.
|
|
44
|
+
- Keep `components/` dumb and reusable.
|
|
45
|
+
- Import with the `@/` alias (e.g. `import { apiGet } from '@/services/api'`).
|
|
46
|
+
- Prefer a feature's public surface over deep cross-feature imports.
|
|
47
|
+
|
|
48
|
+
## Path aliases
|
|
49
|
+
|
|
50
|
+
`@/*` maps to `src/*` in TypeScript/JS config and in your bundler (`<%= stackLabels.buildTool %>`).
|
|
51
|
+
|
|
52
|
+
```ts
|
|
53
|
+
import { HomePage } from '@/routes/HomePage';
|
|
54
|
+
import { useToast } from '@/components/ui/Toast';
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Runtime composition
|
|
58
|
+
|
|
59
|
+
`src/main.<%= jsxExt %>` → providers (`src/app/providers.<%= jsxExt %>`) → router (`src/app/App.<%= jsxExt %>`) → layouts/pages.
|
|
60
|
+
|
|
61
|
+
Providers are ordered deliberately: state → server cache → UI theme → toasts.
|
|
62
|
+
|
|
63
|
+
## Boundaries
|
|
64
|
+
|
|
65
|
+
- **UI** must not call `fetch`/`axios` directly in deep leaves — go through `services/`.
|
|
66
|
+
- **Schemas** live in `lib/validation` so forms and API payloads share rules.
|
|
67
|
+
- **Env access** stays in `config/`, `services/`, and `lib/monitoring`.
|
|
68
|
+
|
|
69
|
+
## Production readiness patterns
|
|
70
|
+
|
|
71
|
+
- Code-split heavy routes when the app grows.
|
|
72
|
+
- Fail closed on missing monitoring DSN (already gated).
|
|
73
|
+
- Keep lint/format/test scripts green in CI.
|
|
74
|
+
- Document public env vars only in `.env.example`.
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# Best practices
|
|
2
|
+
|
|
3
|
+
## Code organization
|
|
4
|
+
|
|
5
|
+
- Colocate tests next to units or under `tests/` for cross-cutting suites.
|
|
6
|
+
- Prefer small modules with clear exports over “utils dump” files.
|
|
7
|
+
- Name features after domains (`billing`, `auth`), not technical junk drawers.
|
|
8
|
+
|
|
9
|
+
## Linting & formatting
|
|
10
|
+
|
|
11
|
+
- Tooling: **<%= stackLabels.linting %>** + **<%= stackLabels.formatting %>**
|
|
12
|
+
- Run lint before push; treat warnings as debt with a burn-down plan.
|
|
13
|
+
- Husky + lint-staged formats staged files so diffs stay clean.
|
|
14
|
+
- commitlint enforces Conventional Commits on `git commit`.
|
|
15
|
+
|
|
16
|
+
## Environment variables
|
|
17
|
+
|
|
18
|
+
- Never commit `.env`.
|
|
19
|
+
- Prefix client-visible vars for your bundler.
|
|
20
|
+
- Validate required env at boot for production deploys.
|
|
21
|
+
|
|
22
|
+
## Testing strategy
|
|
23
|
+
|
|
24
|
+
<% if (unitTesting === 'vitest' || unitTesting === 'jest') { -%>
|
|
25
|
+
- Unit/integration with **<%= stackLabels.unitTesting %>** + Testing Library for components.
|
|
26
|
+
<% } else if (unitTesting === 'rtl') { -%>
|
|
27
|
+
- React Testing Library is installed — pair it with a runner of your choice.
|
|
28
|
+
<% } else { -%>
|
|
29
|
+
- Unit testing was skipped; add a runner before your first production release.
|
|
30
|
+
<% } -%>
|
|
31
|
+
<% if (e2eTesting !== 'none') { -%>
|
|
32
|
+
- E2E with **<%= stackLabels.e2eTesting %>** for critical user journeys (load home, submit form).
|
|
33
|
+
<% } -%>
|
|
34
|
+
<% if (visualTesting === 'storybook') { -%>
|
|
35
|
+
- Storybook for isolated UI development and visual review.
|
|
36
|
+
<% } else if (visualTesting === 'chromatic') { -%>
|
|
37
|
+
- Chromatic for visual regression against baselines.
|
|
38
|
+
<% } -%>
|
|
39
|
+
|
|
40
|
+
Aim for: fast unit tests on logic, fewer integration tests on features, thin e2e on happy paths.
|
|
41
|
+
|
|
42
|
+
## Deployment notes
|
|
43
|
+
|
|
44
|
+
1. `<%= packageManager === 'npm' ? 'npm run' : packageManager %> build`
|
|
45
|
+
2. Serve `dist/` (or your bundler's output) behind HTTPS.
|
|
46
|
+
3. Configure SPA fallback to `index.html` for client routes.
|
|
47
|
+
4. Inject production env vars in the host (Vercel, Netlify, Cloudflare, nginx, etc.).
|
|
48
|
+
5. Enable error tracking DSN in production only.
|
|
49
|
+
|
|
50
|
+
## Production checklist
|
|
51
|
+
|
|
52
|
+
- [ ] Env vars documented and set
|
|
53
|
+
- [ ] Lint + tests in CI
|
|
54
|
+
- [ ] Bundle analyzed for accidental secrets
|
|
55
|
+
- [ ] Monitoring verified with a test event
|
|
56
|
+
- [ ] Accessibility pass on primary flows
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# Examples index
|
|
2
|
+
|
|
3
|
+
Each file documents a concern wired into this scaffold:
|
|
4
|
+
|
|
5
|
+
| File | Topic |
|
|
6
|
+
|------|-------|
|
|
7
|
+
| `state.md` | Client state patterns |
|
|
8
|
+
| `data-fetching.md` | Server state / caching |
|
|
9
|
+
| `forms.md` | Forms + validation |
|
|
10
|
+
| `styling.md` | Visual system |
|
|
11
|
+
| `api.md` | HTTP layer |
|
|
12
|
+
| `testing.md` | Unit / E2E / visual |
|
|
13
|
+
|
|
14
|
+
Open the home page to see live counterparts of these examples.
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# API layer example
|
|
2
|
+
|
|
3
|
+
**Choice:** <%= stackLabels.apiLayer %>
|
|
4
|
+
|
|
5
|
+
## When to use
|
|
6
|
+
|
|
7
|
+
All HTTP traffic should go through a single module for base URL, headers, and error logging.
|
|
8
|
+
|
|
9
|
+
## Best practice
|
|
10
|
+
|
|
11
|
+
- Type responses at the boundary.
|
|
12
|
+
- Surface user-friendly errors in UI; log technical details to monitoring.
|
|
13
|
+
- Do not scatter `fetch`/`axios` calls across components.
|
|
14
|
+
|
|
15
|
+
## Snippet
|
|
16
|
+
|
|
17
|
+
`src/services/api.<%= ext %>` — `apiGet`, `apiPost`, `fetchPosts`, `createFeedback`.
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# Data fetching example
|
|
2
|
+
|
|
3
|
+
**Library:** <%= stackLabels.serverState %>
|
|
4
|
+
**Transport:** <%= stackLabels.apiLayer %>
|
|
5
|
+
|
|
6
|
+
**Live demo:** Posts panel on `/`
|
|
7
|
+
|
|
8
|
+
## When to use
|
|
9
|
+
|
|
10
|
+
Any remote read/write that benefits from caching, deduplication, retries, or background refresh.
|
|
11
|
+
|
|
12
|
+
## Best practice
|
|
13
|
+
|
|
14
|
+
Separate server cache from client UI state. Key queries by resource identity, not by component instance.
|
|
15
|
+
|
|
16
|
+
## Snippet
|
|
17
|
+
|
|
18
|
+
See `src/features/posts/PostsPanel.<%= jsxExt %>` and `src/services/api.<%= ext %>`.
|