beaver-build 1.0.3 → 1.0.5
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/index.js +479 -30
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -271,9 +271,22 @@ var init_vite_config = __esm({
|
|
|
271
271
|
TanStackRouterVite({ routesDirectory: './src/routes', generatedRouteTree: './src/routes/routeTree.gen.ts' }),` : "";
|
|
272
272
|
return `import { defineConfig } from 'vite';
|
|
273
273
|
import react from '@vitejs/plugin-react';
|
|
274
|
+
import path from 'path';
|
|
274
275
|
${tailwindImport}${tanstackImport}
|
|
275
276
|
// https://vitejs.dev/config/
|
|
276
277
|
export default defineConfig({
|
|
278
|
+
resolve: {
|
|
279
|
+
alias: {
|
|
280
|
+
'@': path.resolve(__dirname, './src'),
|
|
281
|
+
'@components': path.resolve(__dirname, './src/components'),
|
|
282
|
+
'@pages': path.resolve(__dirname, './src/pages'),
|
|
283
|
+
'@utils': path.resolve(__dirname, './src/utils'),
|
|
284
|
+
'@types': path.resolve(__dirname, './src/types'),
|
|
285
|
+
'@hooks': path.resolve(__dirname, './src/hooks'),
|
|
286
|
+
'@layouts': path.resolve(__dirname, './src/layouts'),
|
|
287
|
+
'@assets': path.resolve(__dirname, './src/assets'),
|
|
288
|
+
},
|
|
289
|
+
},
|
|
277
290
|
plugins: [
|
|
278
291
|
react(),${tailwindPlugin}${tanstackPlugin}
|
|
279
292
|
],
|
|
@@ -305,7 +318,18 @@ var init_tsconfig = __esm({
|
|
|
305
318
|
strict: true,
|
|
306
319
|
noUnusedLocals: true,
|
|
307
320
|
noUnusedParameters: true,
|
|
308
|
-
noFallthroughCasesInSwitch: true
|
|
321
|
+
noFallthroughCasesInSwitch: true,
|
|
322
|
+
baseUrl: ".",
|
|
323
|
+
paths: {
|
|
324
|
+
"@/*": ["./src/*"],
|
|
325
|
+
"@components/*": ["./src/components/*"],
|
|
326
|
+
"@pages/*": ["./src/pages/*"],
|
|
327
|
+
"@utils/*": ["./src/utils/*"],
|
|
328
|
+
"@types/*": ["./src/types/*"],
|
|
329
|
+
"@hooks/*": ["./src/hooks/*"],
|
|
330
|
+
"@layouts/*": ["./src/layouts/*"],
|
|
331
|
+
"@assets/*": ["./src/assets/*"]
|
|
332
|
+
}
|
|
309
333
|
},
|
|
310
334
|
include: ["src"],
|
|
311
335
|
references: [{ path: "./tsconfig.node.json" }]
|
|
@@ -408,11 +432,11 @@ declare module '@tanstack/react-router' {
|
|
|
408
432
|
}
|
|
409
433
|
}
|
|
410
434
|
` : "";
|
|
411
|
-
const
|
|
412
|
-
|
|
413
|
-
|
|
435
|
+
const homeImport = !hasRouter ? `import { HomePage } from '@/pages/home';
|
|
436
|
+
` : "";
|
|
437
|
+
const inner = hasRouter ? `<RouterProvider router={router} />` : `<HomePage />`;
|
|
414
438
|
if (hasQuery) {
|
|
415
|
-
return `${queryImports}${routerImports}${queryClientDef}
|
|
439
|
+
return `${queryImports}${homeImport}${routerImports}${queryClientDef}
|
|
416
440
|
export const App = () => {
|
|
417
441
|
return (
|
|
418
442
|
<QueryClientProvider client={queryClient}>
|
|
@@ -430,12 +454,9 @@ export const App = () => {
|
|
|
430
454
|
};
|
|
431
455
|
`;
|
|
432
456
|
}
|
|
433
|
-
return
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
<h1>Hello from FSD</h1>
|
|
437
|
-
</div>
|
|
438
|
-
);
|
|
457
|
+
return `${homeImport}
|
|
458
|
+
export const App = () => {
|
|
459
|
+
return <HomePage />;
|
|
439
460
|
};
|
|
440
461
|
`;
|
|
441
462
|
};
|
|
@@ -453,13 +474,13 @@ declare module '@tanstack/react-router' {
|
|
|
453
474
|
}
|
|
454
475
|
}
|
|
455
476
|
` : "";
|
|
456
|
-
const
|
|
457
|
-
|
|
458
|
-
|
|
477
|
+
const homeImport = !hasRouter ? `import Home from '@/pages/Home';
|
|
478
|
+
` : "";
|
|
479
|
+
const inner = hasRouter ? `<RouterProvider router={router} />` : `<Home />`;
|
|
459
480
|
const body = hasQuery ? `<AppProvider>
|
|
460
481
|
${inner}
|
|
461
482
|
</AppProvider>` : inner;
|
|
462
|
-
return `${providerImport}${routerImports}
|
|
483
|
+
return `${providerImport}${homeImport}${routerImports}
|
|
463
484
|
const App = () => {
|
|
464
485
|
return (
|
|
465
486
|
${body}
|
|
@@ -473,7 +494,7 @@ export default App;
|
|
|
473
494
|
});
|
|
474
495
|
|
|
475
496
|
// src/scaffold/react-vite/templates/router.ts
|
|
476
|
-
var routeTreeGenTemplate, rootRouteTemplate,
|
|
497
|
+
var routeTreeGenTemplate, rootRouteTemplate, indexRouteFsdTemplate, indexRouteBprTemplate;
|
|
477
498
|
var init_router = __esm({
|
|
478
499
|
"src/scaffold/react-vite/templates/router.ts"() {
|
|
479
500
|
"use strict";
|
|
@@ -524,19 +545,19 @@ export const Route = createRootRoute({
|
|
|
524
545
|
),
|
|
525
546
|
});
|
|
526
547
|
`;
|
|
527
|
-
|
|
548
|
+
indexRouteFsdTemplate = () => `import { createFileRoute } from '@tanstack/react-router';
|
|
549
|
+
import { HomePage } from '@/pages/home';
|
|
528
550
|
|
|
529
551
|
export const Route = createFileRoute('/')({
|
|
530
|
-
component:
|
|
552
|
+
component: HomePage,
|
|
531
553
|
});
|
|
554
|
+
`;
|
|
555
|
+
indexRouteBprTemplate = () => `import { createFileRoute } from '@tanstack/react-router';
|
|
556
|
+
import Home from '@/pages/Home';
|
|
532
557
|
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
<h3>Welcome Home!</h3>
|
|
537
|
-
</div>
|
|
538
|
-
);
|
|
539
|
-
}
|
|
558
|
+
export const Route = createFileRoute('/')({
|
|
559
|
+
component: Home,
|
|
560
|
+
});
|
|
540
561
|
`;
|
|
541
562
|
}
|
|
542
563
|
});
|
|
@@ -655,6 +676,25 @@ var init_styles = __esm({
|
|
|
655
676
|
"src/scaffold/react-vite/templates/styles.ts"() {
|
|
656
677
|
"use strict";
|
|
657
678
|
stylesCssTemplate = () => `@import "tailwindcss";
|
|
679
|
+
|
|
680
|
+
@theme {
|
|
681
|
+
/* Brand */
|
|
682
|
+
--color-primary: oklch(0.55 0.22 265);
|
|
683
|
+
--color-primary-fg: oklch(0.97 0.01 265);
|
|
684
|
+
|
|
685
|
+
/* Surfaces (dark-first) */
|
|
686
|
+
--color-surface: oklch(0.11 0.015 265);
|
|
687
|
+
--color-surface-muted: oklch(0.16 0.012 265);
|
|
688
|
+
--color-surface-high: oklch(0.21 0.010 265);
|
|
689
|
+
|
|
690
|
+
/* Text */
|
|
691
|
+
--color-text: oklch(0.97 0.000 0);
|
|
692
|
+
--color-text-muted: oklch(0.62 0.010 265);
|
|
693
|
+
--color-text-subtle: oklch(0.42 0.010 265);
|
|
694
|
+
|
|
695
|
+
/* Border */
|
|
696
|
+
--color-border: oklch(0.28 0.010 265);
|
|
697
|
+
}
|
|
658
698
|
`;
|
|
659
699
|
}
|
|
660
700
|
});
|
|
@@ -675,7 +715,7 @@ declare module '*.css' {
|
|
|
675
715
|
});
|
|
676
716
|
|
|
677
717
|
// src/scaffold/react-vite/templates/copilot-instructions.ts
|
|
678
|
-
var FSD_PATHS, BPR_PATHS, frontmatter, generalTemplate, componentsTemplate, hooksTemplate, routerTemplate, zustandTemplate, queryTemplate, tailwindTemplate, linterTemplate, getCopilotInstructionFiles;
|
|
718
|
+
var FSD_PATHS, BPR_PATHS, frontmatter, generalTemplate, componentsTemplate, hooksTemplate, routerTemplate, zustandTemplate, queryTemplate, tailwindTemplate, importsTemplate, linterTemplate, getCopilotInstructionFiles;
|
|
679
719
|
var init_copilot_instructions = __esm({
|
|
680
720
|
"src/scaffold/react-vite/templates/copilot-instructions.ts"() {
|
|
681
721
|
"use strict";
|
|
@@ -755,9 +795,89 @@ These are the project-wide conventions GitHub Copilot must follow. Match the str
|
|
|
755
795
|
|
|
756
796
|
Path-specific rules live in \`.github/instructions/*.instructions.md\` \u2014 those files apply automatically to the files their \`applyTo\` glob matches.
|
|
757
797
|
|
|
798
|
+
## Behavioral Guidelines
|
|
799
|
+
|
|
800
|
+
Behavioral guidelines to reduce common LLM coding mistakes. Merge with project-specific instructions as needed.
|
|
801
|
+
|
|
802
|
+
**Tradeoff:** These guidelines bias toward caution over speed. For trivial tasks, use judgment.
|
|
803
|
+
|
|
804
|
+
### 1. Think Before Coding
|
|
805
|
+
|
|
806
|
+
**Don't assume. Don't hide confusion. Surface tradeoffs.**
|
|
807
|
+
|
|
808
|
+
Before implementing:
|
|
809
|
+
- State your assumptions explicitly. If uncertain, ask.
|
|
810
|
+
- If multiple interpretations exist, present them - don't pick silently.
|
|
811
|
+
- If a simpler approach exists, say so. Push back when warranted.
|
|
812
|
+
- If something is unclear, stop. Name what's confusing. Ask.
|
|
813
|
+
|
|
814
|
+
### 2. Simplicity First
|
|
815
|
+
|
|
816
|
+
**Minimum code that solves the problem. Nothing speculative.**
|
|
817
|
+
|
|
818
|
+
- No features beyond what was asked.
|
|
819
|
+
- No abstractions for single-use code.
|
|
820
|
+
- No "flexibility" or "configurability" that wasn't requested.
|
|
821
|
+
- No error handling for impossible scenarios.
|
|
822
|
+
- If you write 200 lines and it could be 50, rewrite it.
|
|
823
|
+
|
|
824
|
+
Ask yourself: "Would a senior engineer say this is overcomplicated?" If yes, simplify.
|
|
825
|
+
|
|
826
|
+
### 3. Surgical Changes
|
|
827
|
+
|
|
828
|
+
**Touch only what you must. Clean up only your own mess.**
|
|
829
|
+
|
|
830
|
+
When editing existing code:
|
|
831
|
+
- Don't "improve" adjacent code, comments, or formatting.
|
|
832
|
+
- Don't refactor things that aren't broken.
|
|
833
|
+
- Match existing style, even if you'd do it differently.
|
|
834
|
+
- If you notice unrelated dead code, mention it - don't delete it.
|
|
835
|
+
|
|
836
|
+
When your changes create orphans:
|
|
837
|
+
- Remove imports/variables/functions that YOUR changes made unused.
|
|
838
|
+
- Don't remove pre-existing dead code unless asked.
|
|
839
|
+
|
|
840
|
+
The test: Every changed line should trace directly to the user's request.
|
|
841
|
+
|
|
842
|
+
### 4. Goal-Driven Execution
|
|
843
|
+
|
|
844
|
+
**Define success criteria. Loop until verified.**
|
|
845
|
+
|
|
846
|
+
Transform tasks into verifiable goals:
|
|
847
|
+
- "Add validation" \u2192 "Write tests for invalid inputs, then make them pass"
|
|
848
|
+
- "Fix the bug" \u2192 "Write a test that reproduces it, then make it pass"
|
|
849
|
+
- "Refactor X" \u2192 "Ensure tests pass before and after"
|
|
850
|
+
|
|
851
|
+
For multi-step tasks, state a brief plan:
|
|
852
|
+
|
|
853
|
+
\`\`\`
|
|
854
|
+
1. [Step] \u2192 verify: [check]
|
|
855
|
+
2. [Step] \u2192 verify: [check]
|
|
856
|
+
3. [Step] \u2192 verify: [check]
|
|
857
|
+
\`\`\`
|
|
858
|
+
|
|
859
|
+
Strong success criteria let you loop independently. Weak criteria ("make it work") require constant clarification.
|
|
860
|
+
|
|
861
|
+
**These guidelines are working if:** fewer unnecessary changes in diffs, fewer rewrites due to overcomplication, and clarifying questions come before implementation rather than after mistakes.
|
|
862
|
+
|
|
758
863
|
## Stack
|
|
759
864
|
${stackLines}
|
|
760
865
|
|
|
866
|
+
## Import aliases
|
|
867
|
+
|
|
868
|
+
Always use path aliases instead of relative imports. Project-wide aliases are pre-configured in \`tsconfig.json\` and \`vite.config.ts\`:
|
|
869
|
+
|
|
870
|
+
- \`@/*\` \u2192 \`./src/\` \u2014 use for any import from src, e.g. \`import Button from '@/components/Button'\`.
|
|
871
|
+
- \`@components/*\` \u2192 \`./src/components/\` \u2014 e.g. \`import { Modal } from '@components/Modal'\`.
|
|
872
|
+
- \`@pages/*\` \u2192 \`./src/pages/\` \u2014 e.g. \`import { HomePage } from '@pages/Home'\`.
|
|
873
|
+
- \`@utils/*\` \u2192 \`./src/utils/\` \u2014 e.g. \`import { cn } from '@utils/cn'\`.
|
|
874
|
+
- \`@types/*\` \u2192 \`./src/types/\` \u2014 e.g. \`import type { User } from '@types/user'\`.
|
|
875
|
+
- \`@hooks/*\` \u2192 \`./src/hooks/\` \u2014 e.g. \`import { useAuth } from '@hooks/useAuth'\`.
|
|
876
|
+
- \`@layouts/*\` \u2192 \`./src/layouts/\` \u2014 e.g. \`import { MainLayout } from '@layouts/MainLayout'\`.
|
|
877
|
+
- \`@assets/*\` \u2192 \`./src/assets/\` \u2014 e.g. \`import logo from '@assets/logo.svg'\`.
|
|
878
|
+
|
|
879
|
+
**Never use relative imports** like \`import Foo from '../../../components/Foo'\`. Always resolve through an alias.
|
|
880
|
+
|
|
761
881
|
## Naming conventions
|
|
762
882
|
|
|
763
883
|
- **Components**: \`PascalCase.tsx\`, one component per file, named export (no default).
|
|
@@ -985,6 +1105,100 @@ Use \`:root\` for runtime values (e.g., animation targets, JS-readable tokens) t
|
|
|
985
1105
|
}
|
|
986
1106
|
}
|
|
987
1107
|
\`\`\`
|
|
1108
|
+
|
|
1109
|
+
### Color format \u2014 prefer oklch
|
|
1110
|
+
|
|
1111
|
+
Tailwind v4's built-in palette uses oklch. Use oklch for custom colors too so tokens stay perceptually uniform and mix cleanly with the defaults:
|
|
1112
|
+
|
|
1113
|
+
\`\`\`css
|
|
1114
|
+
@theme {
|
|
1115
|
+
--color-primary: oklch(0.55 0.22 265); /* L C H */
|
|
1116
|
+
--color-muted: oklch(0.62 0.01 265);
|
|
1117
|
+
}
|
|
1118
|
+
\`\`\`
|
|
1119
|
+
|
|
1120
|
+
Avoid hex or \`rgb()\` unless matching a fixed brand value.
|
|
1121
|
+
|
|
1122
|
+
### Theme variables are also CSS custom properties
|
|
1123
|
+
|
|
1124
|
+
Every \`@theme\` variable is a real CSS custom property \u2014 read it anywhere:
|
|
1125
|
+
|
|
1126
|
+
\`\`\`css
|
|
1127
|
+
.my-element {
|
|
1128
|
+
color: var(--color-primary); /* same value as the text-primary utility */
|
|
1129
|
+
}
|
|
1130
|
+
\`\`\`
|
|
1131
|
+
|
|
1132
|
+
Use \`var()\` for CSS that Tailwind utilities cannot express (e.g. complex \`box-shadow\`, SVG \`fill\`, pseudo-element content).
|
|
1133
|
+
|
|
1134
|
+
### Semantic token pattern \u2014 naming convention
|
|
1135
|
+
|
|
1136
|
+
Prefer **semantic names** (\`surface\`, \`text-muted\`, \`border\`) over raw scale names (\`slate-900\`, \`zinc-300\`). Semantic names decouple the design from the specific value and make theming easy:
|
|
1137
|
+
|
|
1138
|
+
\`\`\`css
|
|
1139
|
+
@theme {
|
|
1140
|
+
/* Brand */
|
|
1141
|
+
--color-primary: oklch(0.55 0.22 265);
|
|
1142
|
+
--color-primary-fg: oklch(0.97 0.01 265);
|
|
1143
|
+
|
|
1144
|
+
/* Surfaces (dark-first) */
|
|
1145
|
+
--color-surface: oklch(0.11 0.015 265);
|
|
1146
|
+
--color-surface-muted: oklch(0.16 0.012 265);
|
|
1147
|
+
--color-surface-high: oklch(0.21 0.010 265);
|
|
1148
|
+
|
|
1149
|
+
/* Text */
|
|
1150
|
+
--color-text: oklch(0.97 0.000 0);
|
|
1151
|
+
--color-text-muted: oklch(0.62 0.010 265);
|
|
1152
|
+
--color-text-subtle: oklch(0.42 0.010 265);
|
|
1153
|
+
|
|
1154
|
+
/* Border */
|
|
1155
|
+
--color-border: oklch(0.28 0.010 265);
|
|
1156
|
+
}
|
|
1157
|
+
\`\`\`
|
|
1158
|
+
|
|
1159
|
+
This generates: \`bg-primary\`, \`text-primary-fg\`, \`bg-surface\`, \`bg-surface-high/60\`, \`text-text\`, \`text-text-muted\`, \`border-border\`, etc. The \`/opacity\` modifier works on all generated utilities.
|
|
1160
|
+
`;
|
|
1161
|
+
};
|
|
1162
|
+
importsTemplate = () => {
|
|
1163
|
+
return frontmatter("**") + `# Import aliases and code organization
|
|
1164
|
+
|
|
1165
|
+
All imports use path aliases defined in \`tsconfig.json\` and \`vite.config.ts\`. This avoids fragile relative paths and makes moving files safe.
|
|
1166
|
+
|
|
1167
|
+
## Available aliases
|
|
1168
|
+
|
|
1169
|
+
- \`@/*\` \u2192 \`./src/\` \u2014 shortest form, use when unambiguous
|
|
1170
|
+
- \`@components/*\` \u2014 components in \`./src/components/\`
|
|
1171
|
+
- \`@pages/*\` \u2014 pages in \`./src/pages/\`
|
|
1172
|
+
- \`@utils/*\` \u2014 utilities in \`./src/utils/\`
|
|
1173
|
+
- \`@types/*\` \u2014 types in \`./src/types/\`
|
|
1174
|
+
- \`@hooks/*\` \u2014 hooks in \`./src/hooks/\`
|
|
1175
|
+
- \`@layouts/*\` \u2014 layouts in \`./src/layouts/\`
|
|
1176
|
+
- \`@assets/*\` \u2014 assets in \`./src/assets/\`
|
|
1177
|
+
|
|
1178
|
+
## Rules
|
|
1179
|
+
|
|
1180
|
+
- **Never use relative imports** (\`../../../\`). Always use an alias.
|
|
1181
|
+
- **Group imports**: standard library, third-party, then project aliases.
|
|
1182
|
+
- **Organize by type within a file**: imports, types, constants, then code.
|
|
1183
|
+
|
|
1184
|
+
### Examples
|
|
1185
|
+
|
|
1186
|
+
\u2705 Good:
|
|
1187
|
+
\`\`\`typescript
|
|
1188
|
+
import { ReactNode } from 'react';
|
|
1189
|
+
import { QueryClient } from '@tanstack/react-query';
|
|
1190
|
+
|
|
1191
|
+
import { Button } from '@components/Button';
|
|
1192
|
+
import { useUser } from '@hooks/useUser';
|
|
1193
|
+
import type { User } from '@types/user';
|
|
1194
|
+
import { cn } from '@utils/cn';
|
|
1195
|
+
\`\`\`
|
|
1196
|
+
|
|
1197
|
+
\u274C Bad:
|
|
1198
|
+
\`\`\`typescript
|
|
1199
|
+
import { Button } from '../../../components/Button'; // relative path
|
|
1200
|
+
import Button from '@components/Button/Button'; // no file extension in alias
|
|
1201
|
+
\`\`\`
|
|
988
1202
|
`;
|
|
989
1203
|
};
|
|
990
1204
|
linterTemplate = (cart) => {
|
|
@@ -1016,6 +1230,7 @@ Use \`:root\` for runtime values (e.g., animation targets, JS-readable tokens) t
|
|
|
1016
1230
|
const hasLinter = cart.linter !== "NOT_USING";
|
|
1017
1231
|
const files = [
|
|
1018
1232
|
{ relativePath: ".github/copilot-instructions.md", content: generalTemplate(cart) },
|
|
1233
|
+
{ relativePath: ".github/instructions/imports.instructions.md", content: importsTemplate() },
|
|
1019
1234
|
{ relativePath: ".github/instructions/components.instructions.md", content: componentsTemplate(cart) },
|
|
1020
1235
|
{ relativePath: ".github/instructions/hooks.instructions.md", content: hooksTemplate(cart) }
|
|
1021
1236
|
];
|
|
@@ -1054,6 +1269,227 @@ Use \`:root\` for runtime values (e.g., animation targets, JS-readable tokens) t
|
|
|
1054
1269
|
}
|
|
1055
1270
|
});
|
|
1056
1271
|
|
|
1272
|
+
// src/scaffold/react-vite/templates/home-page.ts
|
|
1273
|
+
function buildBadges(o) {
|
|
1274
|
+
const badges = [
|
|
1275
|
+
{ label: "React 19", bg: "#1e3a8a", fg: "#bfdbfe", border: "#3b82f6" },
|
|
1276
|
+
{ label: "TypeScript", bg: "#1e3a8a", fg: "#bfdbfe", border: "#2563eb" },
|
|
1277
|
+
{ label: "Vite 6", bg: "#4c1d95", fg: "#ddd6fe", border: "#8b5cf6" },
|
|
1278
|
+
{ label: o.layout === "FSD" ? "FSD" : "Bulletproof React", bg: "#78350f", fg: "#fde68a", border: "#d97706" }
|
|
1279
|
+
];
|
|
1280
|
+
if (o.hasRouter) badges.push({ label: "TanStack Router", bg: "#7f1d1d", fg: "#fecaca", border: "#ef4444" });
|
|
1281
|
+
if (o.hasZustand) badges.push({ label: "Zustand", bg: "#14532d", fg: "#bbf7d0", border: "#22c55e" });
|
|
1282
|
+
if (o.hasQuery) badges.push({ label: "TanStack Query", bg: "#7c2d12", fg: "#fed7aa", border: "#f97316" });
|
|
1283
|
+
if (o.hasTailwind) badges.push({ label: "Tailwind CSS v4", bg: "#164e63", fg: "#a5f3fc", border: "#06b6d4" });
|
|
1284
|
+
if (o.linter === "BIOME") badges.push({ label: "Biome", bg: "#1e3a5f", fg: "#bae6fd", border: "#0284c7" });
|
|
1285
|
+
if (o.linter === "ESLINT") badges.push({ label: "ESLint", bg: "#312e81", fg: "#c7d2fe", border: "#6366f1" });
|
|
1286
|
+
return badges;
|
|
1287
|
+
}
|
|
1288
|
+
function buildHomePage(o) {
|
|
1289
|
+
const badges = buildBadges(o);
|
|
1290
|
+
const storeImport = o.hasZustand ? `import { useAppStore } from '${o.storeImportPath}';
|
|
1291
|
+
` : "";
|
|
1292
|
+
const queryImport = o.hasQuery ? `import { useQuery } from '@tanstack/react-query';
|
|
1293
|
+
` : "";
|
|
1294
|
+
const postType = o.hasQuery ? `
|
|
1295
|
+
type Post = { id: number; title: string; body: string };
|
|
1296
|
+
` : "";
|
|
1297
|
+
const exportKw = o.namedExport ? `export const ${o.componentName}` : `const ${o.componentName}`;
|
|
1298
|
+
const defaultExp = o.namedExport ? "" : `
|
|
1299
|
+
export default ${o.componentName};
|
|
1300
|
+
`;
|
|
1301
|
+
if (o.hasTailwind) {
|
|
1302
|
+
const badgesJsx = badges.map((b) => ` <span className="px-2.5 py-1 rounded-md text-xs font-medium" style={{ background: '${b.bg}80', color: '${b.fg}', border: '1px solid ${b.border}70' }}>${b.label}</span>`).join("\n");
|
|
1303
|
+
const counterCmp2 = o.hasZustand ? `
|
|
1304
|
+
const Counter = () => {
|
|
1305
|
+
const { count, increment, decrement, reset } = useAppStore();
|
|
1306
|
+
return (
|
|
1307
|
+
<div className="flex items-center gap-3">
|
|
1308
|
+
<button
|
|
1309
|
+
onClick={decrement}
|
|
1310
|
+
className="w-9 h-9 rounded-lg bg-surface-high hover:bg-surface-muted text-text text-xl font-bold transition-colors"
|
|
1311
|
+
>
|
|
1312
|
+
\u2212
|
|
1313
|
+
</button>
|
|
1314
|
+
<span className="text-3xl font-bold text-text w-14 text-center tabular-nums">{count}</span>
|
|
1315
|
+
<button
|
|
1316
|
+
onClick={increment}
|
|
1317
|
+
className="w-9 h-9 rounded-lg bg-surface-high hover:bg-surface-muted text-text text-xl font-bold transition-colors"
|
|
1318
|
+
>
|
|
1319
|
+
+
|
|
1320
|
+
</button>
|
|
1321
|
+
<button onClick={reset} className="ml-2 text-sm text-text-muted hover:text-text transition-colors">
|
|
1322
|
+
reset
|
|
1323
|
+
</button>
|
|
1324
|
+
</div>
|
|
1325
|
+
);
|
|
1326
|
+
};
|
|
1327
|
+
` : "";
|
|
1328
|
+
const queryCmp2 = o.hasQuery ? `
|
|
1329
|
+
const QueryDemo = () => {
|
|
1330
|
+
const { data, isLoading, isError } = useQuery<Post>({
|
|
1331
|
+
queryKey: ['demo-post'],
|
|
1332
|
+
queryFn: () => fetch('https://jsonplaceholder.typicode.com/posts/1').then(r => r.json()),
|
|
1333
|
+
});
|
|
1334
|
+
if (isLoading) return <p className="text-text-muted text-sm">Fetching post\u2026</p>;
|
|
1335
|
+
if (isError) return <p className="text-red-400 text-sm">Failed to fetch.</p>;
|
|
1336
|
+
return (
|
|
1337
|
+
<div className="space-y-1">
|
|
1338
|
+
<p className="text-text text-sm font-medium">{data?.title}</p>
|
|
1339
|
+
<p className="text-text-muted text-xs leading-relaxed">{data?.body}</p>
|
|
1340
|
+
</div>
|
|
1341
|
+
);
|
|
1342
|
+
};
|
|
1343
|
+
` : "";
|
|
1344
|
+
const zustandCard2 = o.hasZustand ? `
|
|
1345
|
+
<div className="rounded-xl p-5 mb-3 bg-surface-high/60 border border-border">
|
|
1346
|
+
<p className="text-xs font-semibold text-text-muted uppercase tracking-wider mb-3">Zustand \u2014 Counter</p>
|
|
1347
|
+
<Counter />
|
|
1348
|
+
</div>` : "";
|
|
1349
|
+
const queryCard2 = o.hasQuery ? `
|
|
1350
|
+
<div className="rounded-xl p-5 bg-surface-high/60 border border-border">
|
|
1351
|
+
<p className="text-xs font-semibold text-text-muted uppercase tracking-wider mb-3">TanStack Query \u2014 Fetched Post</p>
|
|
1352
|
+
<QueryDemo />
|
|
1353
|
+
</div>` : "";
|
|
1354
|
+
return `${storeImport}${queryImport}${postType}${counterCmp2}${queryCmp2}
|
|
1355
|
+
${exportKw} = () => {
|
|
1356
|
+
return (
|
|
1357
|
+
<div className="min-h-screen flex items-center justify-center p-6 bg-gradient-to-br from-surface to-surface-muted">
|
|
1358
|
+
<div className="w-full max-w-lg">
|
|
1359
|
+
<div className="text-center mb-8">
|
|
1360
|
+
<div className="text-5xl mb-3">\u{1F680}</div>
|
|
1361
|
+
<h1 className="text-4xl font-bold text-text mb-2">${o.projectName}</h1>
|
|
1362
|
+
<p className="text-text-muted">Your React + Vite starter is ready.</p>
|
|
1363
|
+
</div>
|
|
1364
|
+
|
|
1365
|
+
<div className="rounded-xl p-5 mb-3 bg-surface-high/60 border border-border">
|
|
1366
|
+
<p className="text-xs font-semibold text-text-muted uppercase tracking-wider mb-3">Stack</p>
|
|
1367
|
+
<div className="flex flex-wrap gap-2">
|
|
1368
|
+
${badgesJsx}
|
|
1369
|
+
</div>
|
|
1370
|
+
</div>${zustandCard2}${queryCard2}
|
|
1371
|
+
</div>
|
|
1372
|
+
</div>
|
|
1373
|
+
);
|
|
1374
|
+
};
|
|
1375
|
+
${defaultExp}`;
|
|
1376
|
+
}
|
|
1377
|
+
const badgeDefs = badges.map((b) => ` { label: '${b.label}', bg: '${b.bg}80', fg: '${b.fg}', border: '${b.border}70' }`).join(",\n");
|
|
1378
|
+
const counterCmp = o.hasZustand ? `
|
|
1379
|
+
const Counter = () => {
|
|
1380
|
+
const { count, increment, decrement, reset } = useAppStore();
|
|
1381
|
+
const btn: React.CSSProperties = {
|
|
1382
|
+
width: '2.25rem', height: '2.25rem', borderRadius: '0.5rem',
|
|
1383
|
+
background: '#334155', border: 'none', color: 'white',
|
|
1384
|
+
fontSize: '1.25rem', cursor: 'pointer', lineHeight: 1,
|
|
1385
|
+
};
|
|
1386
|
+
return (
|
|
1387
|
+
<div style={{ display: 'flex', alignItems: 'center', gap: '0.75rem' }}>
|
|
1388
|
+
<button style={btn} onClick={decrement}>\u2212</button>
|
|
1389
|
+
<span style={{ fontSize: '1.875rem', fontWeight: 700, color: 'white', width: '3.5rem', textAlign: 'center' }}>{count}</span>
|
|
1390
|
+
<button style={btn} onClick={increment}>+</button>
|
|
1391
|
+
<button
|
|
1392
|
+
onClick={reset}
|
|
1393
|
+
style={{ background: 'none', border: 'none', color: '#94a3b8', cursor: 'pointer', fontSize: '0.875rem', marginLeft: '0.5rem' }}
|
|
1394
|
+
>
|
|
1395
|
+
reset
|
|
1396
|
+
</button>
|
|
1397
|
+
</div>
|
|
1398
|
+
);
|
|
1399
|
+
};
|
|
1400
|
+
` : "";
|
|
1401
|
+
const queryCmp = o.hasQuery ? `
|
|
1402
|
+
const QueryDemo = () => {
|
|
1403
|
+
const { data, isLoading, isError } = useQuery<Post>({
|
|
1404
|
+
queryKey: ['demo-post'],
|
|
1405
|
+
queryFn: () => fetch('https://jsonplaceholder.typicode.com/posts/1').then(r => r.json()),
|
|
1406
|
+
});
|
|
1407
|
+
if (isLoading) return <p style={{ color: '#94a3b8', fontSize: '0.875rem' }}>Fetching post\u2026</p>;
|
|
1408
|
+
if (isError) return <p style={{ color: '#f87171', fontSize: '0.875rem' }}>Failed to fetch.</p>;
|
|
1409
|
+
return (
|
|
1410
|
+
<div>
|
|
1411
|
+
<p style={{ color: 'white', fontSize: '0.875rem', fontWeight: 500, margin: '0 0 0.25rem' }}>{data?.title}</p>
|
|
1412
|
+
<p style={{ color: '#94a3b8', fontSize: '0.75rem', lineHeight: 1.6, margin: 0 }}>{data?.body}</p>
|
|
1413
|
+
</div>
|
|
1414
|
+
);
|
|
1415
|
+
};
|
|
1416
|
+
` : "";
|
|
1417
|
+
const card = `{ background: 'rgba(30,41,59,0.6)', border: '1px solid rgba(71,85,105,0.5)', borderRadius: '0.75rem', padding: '1.25rem', marginBottom: '0.75rem' }`;
|
|
1418
|
+
const cardLabel = `{ fontSize: '0.65rem', fontWeight: 600, color: '#94a3b8', textTransform: 'uppercase' as const, letterSpacing: '0.1em', marginBottom: '0.75rem' }`;
|
|
1419
|
+
const zustandCard = o.hasZustand ? `
|
|
1420
|
+
<div style={${card}}>
|
|
1421
|
+
<p style={${cardLabel}}>Zustand \u2014 Counter</p>
|
|
1422
|
+
<Counter />
|
|
1423
|
+
</div>` : "";
|
|
1424
|
+
const queryCard = o.hasQuery ? `
|
|
1425
|
+
<div style={{ ...${card}, marginBottom: 0 }}>
|
|
1426
|
+
<p style={${cardLabel}}>TanStack Query \u2014 Fetched Post</p>
|
|
1427
|
+
<QueryDemo />
|
|
1428
|
+
</div>` : "";
|
|
1429
|
+
const reactImport = o.hasZustand ? `import type React from 'react';
|
|
1430
|
+
` : "";
|
|
1431
|
+
return `${reactImport}${storeImport}${queryImport}${postType}
|
|
1432
|
+
const stackBadges = [
|
|
1433
|
+
${badgeDefs},
|
|
1434
|
+
];
|
|
1435
|
+
${counterCmp}${queryCmp}
|
|
1436
|
+
${exportKw} = () => {
|
|
1437
|
+
return (
|
|
1438
|
+
<div style={{ minHeight: '100vh', display: 'flex', alignItems: 'center', justifyContent: 'center', padding: '1.5rem', background: 'linear-gradient(135deg, #020617 0%, #0f172a 100%)', fontFamily: 'system-ui, -apple-system, sans-serif' }}>
|
|
1439
|
+
<div style={{ width: '100%', maxWidth: '480px' }}>
|
|
1440
|
+
<div style={{ textAlign: 'center', marginBottom: '2rem' }}>
|
|
1441
|
+
<div style={{ fontSize: '3rem', marginBottom: '0.75rem' }}>\u{1F680}</div>
|
|
1442
|
+
<h1 style={{ fontSize: '2.25rem', fontWeight: 700, color: '#f8fafc', margin: '0 0 0.5rem' }}>${o.projectName}</h1>
|
|
1443
|
+
<p style={{ color: '#94a3b8', margin: 0 }}>Your React + Vite starter is ready.</p>
|
|
1444
|
+
</div>
|
|
1445
|
+
|
|
1446
|
+
<div style={${card}}>
|
|
1447
|
+
<p style={${cardLabel}}>Stack</p>
|
|
1448
|
+
<div style={{ display: 'flex', flexWrap: 'wrap', gap: '0.5rem' }}>
|
|
1449
|
+
{stackBadges.map(b => (
|
|
1450
|
+
<span key={b.label} style={{ background: b.bg, color: b.fg, border: \`1px solid \${b.border}\`, borderRadius: '0.375rem', padding: '0.25rem 0.625rem', fontSize: '0.75rem', fontWeight: 500 }}>
|
|
1451
|
+
{b.label}
|
|
1452
|
+
</span>
|
|
1453
|
+
))}
|
|
1454
|
+
</div>
|
|
1455
|
+
</div>${zustandCard}${queryCard}
|
|
1456
|
+
</div>
|
|
1457
|
+
</div>
|
|
1458
|
+
);
|
|
1459
|
+
};
|
|
1460
|
+
${defaultExp}`;
|
|
1461
|
+
}
|
|
1462
|
+
var homePageFsdTemplate, homePageBprTemplate;
|
|
1463
|
+
var init_home_page = __esm({
|
|
1464
|
+
"src/scaffold/react-vite/templates/home-page.ts"() {
|
|
1465
|
+
"use strict";
|
|
1466
|
+
homePageFsdTemplate = (cart) => buildHomePage({
|
|
1467
|
+
projectName: cart.projectName,
|
|
1468
|
+
layout: "FSD",
|
|
1469
|
+
hasTailwind: cart.css === "TAILWIND",
|
|
1470
|
+
hasZustand: cart.stateManagement === "ZUSTAND",
|
|
1471
|
+
hasQuery: cart.query === "TANSTACK_QUERY",
|
|
1472
|
+
hasRouter: cart.router === "TANSTACK_ROUTER",
|
|
1473
|
+
linter: cart.linter,
|
|
1474
|
+
storeImportPath: "@/shared/lib/store",
|
|
1475
|
+
componentName: "HomePage",
|
|
1476
|
+
namedExport: true
|
|
1477
|
+
});
|
|
1478
|
+
homePageBprTemplate = (cart) => buildHomePage({
|
|
1479
|
+
projectName: cart.projectName,
|
|
1480
|
+
layout: "BPR",
|
|
1481
|
+
hasTailwind: cart.css === "TAILWIND",
|
|
1482
|
+
hasZustand: cart.stateManagement === "ZUSTAND",
|
|
1483
|
+
hasQuery: cart.query === "TANSTACK_QUERY",
|
|
1484
|
+
hasRouter: cart.router === "TANSTACK_ROUTER",
|
|
1485
|
+
linter: cart.linter,
|
|
1486
|
+
storeImportPath: "@/stores/appStore",
|
|
1487
|
+
componentName: "Home",
|
|
1488
|
+
namedExport: false
|
|
1489
|
+
});
|
|
1490
|
+
}
|
|
1491
|
+
});
|
|
1492
|
+
|
|
1057
1493
|
// src/scaffold/react-vite/templates/fsd-layout.ts
|
|
1058
1494
|
var getFsdFileMap;
|
|
1059
1495
|
var init_fsd_layout = __esm({
|
|
@@ -1072,6 +1508,7 @@ var init_fsd_layout = __esm({
|
|
|
1072
1508
|
init_styles();
|
|
1073
1509
|
init_vite_env_d_ts();
|
|
1074
1510
|
init_copilot_instructions();
|
|
1511
|
+
init_home_page();
|
|
1075
1512
|
getFsdFileMap = (cart) => {
|
|
1076
1513
|
const hasRouter = cart.router === "TANSTACK_ROUTER";
|
|
1077
1514
|
const hasZustand = cart.stateManagement === "ZUSTAND";
|
|
@@ -1089,8 +1526,7 @@ var init_fsd_layout = __esm({
|
|
|
1089
1526
|
{ relativePath: "src/app/index.tsx", content: appTsxFsdTemplate(hasRouter, hasQuery) },
|
|
1090
1527
|
{
|
|
1091
1528
|
relativePath: "src/pages/home/ui/HomePage.tsx",
|
|
1092
|
-
content:
|
|
1093
|
-
`
|
|
1529
|
+
content: homePageFsdTemplate(cart)
|
|
1094
1530
|
},
|
|
1095
1531
|
{
|
|
1096
1532
|
relativePath: "src/pages/home/index.ts",
|
|
@@ -1108,7 +1544,7 @@ var init_fsd_layout = __esm({
|
|
|
1108
1544
|
if (hasRouter) {
|
|
1109
1545
|
files.push(
|
|
1110
1546
|
{ relativePath: "src/routes/__root.tsx", content: rootRouteTemplate() },
|
|
1111
|
-
{ relativePath: "src/routes/index.tsx", content:
|
|
1547
|
+
{ relativePath: "src/routes/index.tsx", content: indexRouteFsdTemplate() },
|
|
1112
1548
|
{ relativePath: "src/routes/routeTree.gen.ts", content: routeTreeGenTemplate() }
|
|
1113
1549
|
);
|
|
1114
1550
|
}
|
|
@@ -1176,6 +1612,7 @@ var init_bpr_layout = __esm({
|
|
|
1176
1612
|
init_styles();
|
|
1177
1613
|
init_vite_env_d_ts();
|
|
1178
1614
|
init_copilot_instructions();
|
|
1615
|
+
init_home_page();
|
|
1179
1616
|
getBprFileMap = (cart) => {
|
|
1180
1617
|
const hasRouter = cart.router === "TANSTACK_ROUTER";
|
|
1181
1618
|
const hasZustand = cart.stateManagement === "ZUSTAND";
|
|
@@ -1191,6 +1628,7 @@ var init_bpr_layout = __esm({
|
|
|
1191
1628
|
{ relativePath: "src/vite-env.d.ts", content: viteEnvDtsTemplate() },
|
|
1192
1629
|
{ relativePath: "src/main.tsx", content: mainTsxTemplate(cart) },
|
|
1193
1630
|
{ relativePath: "src/App.tsx", content: appTsxBprTemplate(hasRouter, hasQuery) },
|
|
1631
|
+
{ relativePath: "src/pages/Home.tsx", content: homePageBprTemplate(cart) },
|
|
1194
1632
|
{
|
|
1195
1633
|
relativePath: "src/providers/index.tsx",
|
|
1196
1634
|
content: hasQuery ? queryProviderBprTemplate() : simpleProviderBprTemplate()
|
|
@@ -1208,7 +1646,7 @@ var init_bpr_layout = __esm({
|
|
|
1208
1646
|
if (hasRouter) {
|
|
1209
1647
|
files.push(
|
|
1210
1648
|
{ relativePath: "src/routes/__root.tsx", content: rootRouteTemplate() },
|
|
1211
|
-
{ relativePath: "src/routes/index.tsx", content:
|
|
1649
|
+
{ relativePath: "src/routes/index.tsx", content: indexRouteBprTemplate() },
|
|
1212
1650
|
{ relativePath: "src/routes/routeTree.gen.ts", content: routeTreeGenTemplate() }
|
|
1213
1651
|
);
|
|
1214
1652
|
}
|
|
@@ -1537,12 +1975,23 @@ var init_vite_config2 = __esm({
|
|
|
1537
1975
|
const imports = [
|
|
1538
1976
|
`import { defineConfig } from 'vite';`,
|
|
1539
1977
|
`import react from '@vitejs/plugin-react';`,
|
|
1978
|
+
`import path from 'path';`,
|
|
1540
1979
|
hasTailwind ? `import tailwindcss from '@tailwindcss/vite';` : ""
|
|
1541
1980
|
].filter(Boolean).join("\n");
|
|
1542
1981
|
const plugins = ["react()", hasTailwind ? "tailwindcss()" : ""].filter(Boolean).join(",\n ");
|
|
1543
1982
|
return `${imports}
|
|
1544
1983
|
|
|
1545
1984
|
export default defineConfig({
|
|
1985
|
+
resolve: {
|
|
1986
|
+
alias: {
|
|
1987
|
+
'@': path.resolve(__dirname, './src'),
|
|
1988
|
+
'@components': path.resolve(__dirname, './src/components'),
|
|
1989
|
+
'@hooks': path.resolve(__dirname, './src/hooks'),
|
|
1990
|
+
'@lib': path.resolve(__dirname, './src/lib'),
|
|
1991
|
+
'@types': path.resolve(__dirname, './src/types'),
|
|
1992
|
+
'@utils': path.resolve(__dirname, './src/utils'),
|
|
1993
|
+
},
|
|
1994
|
+
},
|
|
1546
1995
|
plugins: [
|
|
1547
1996
|
${plugins},
|
|
1548
1997
|
],
|