beaver-build 1.0.2 → 1.0.4
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 +452 -31
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -268,12 +268,25 @@ var init_vite_config = __esm({
|
|
|
268
268
|
const tailwindPlugin = hasTailwind ? `
|
|
269
269
|
tailwindcss(),` : "";
|
|
270
270
|
const tanstackPlugin = hasTanstack ? `
|
|
271
|
-
TanStackRouterVite({ routesDirectory: './src/routes' }),` : "";
|
|
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,10 +494,45 @@ export default App;
|
|
|
473
494
|
});
|
|
474
495
|
|
|
475
496
|
// src/scaffold/react-vite/templates/router.ts
|
|
476
|
-
var 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";
|
|
501
|
+
routeTreeGenTemplate = () => `/* eslint-disable */
|
|
502
|
+
|
|
503
|
+
// @ts-nocheck
|
|
504
|
+
|
|
505
|
+
// noinspection JSUnusedGlobalSymbols
|
|
506
|
+
|
|
507
|
+
// This file was automatically generated by TanStack Router.
|
|
508
|
+
// You should NOT make direct changes to this file.
|
|
509
|
+
// Any changes made to this file will be overwritten the next time the route tree is regenerated.
|
|
510
|
+
|
|
511
|
+
import { Route as rootRouteImport } from './__root'
|
|
512
|
+
import { Route as IndexRouteImport } from './index'
|
|
513
|
+
|
|
514
|
+
const rootRouteChildren = {
|
|
515
|
+
IndexRoute: IndexRouteImport,
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
export const routeTree = rootRouteImport._addFileChildren(rootRouteChildren)
|
|
519
|
+
|
|
520
|
+
/* ROUTE_MANIFEST_START
|
|
521
|
+
{
|
|
522
|
+
"routes": {
|
|
523
|
+
"__root__": {
|
|
524
|
+
"filePath": "__root.tsx",
|
|
525
|
+
"children": [
|
|
526
|
+
"/"
|
|
527
|
+
]
|
|
528
|
+
},
|
|
529
|
+
"/": {
|
|
530
|
+
"filePath": "index.tsx"
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
ROUTE_MANIFEST_END */
|
|
535
|
+
`;
|
|
480
536
|
rootRouteTemplate = () => `import { createRootRoute, Outlet } from '@tanstack/react-router';
|
|
481
537
|
import { TanStackRouterDevtools } from '@tanstack/router-devtools';
|
|
482
538
|
|
|
@@ -489,19 +545,19 @@ export const Route = createRootRoute({
|
|
|
489
545
|
),
|
|
490
546
|
});
|
|
491
547
|
`;
|
|
492
|
-
|
|
548
|
+
indexRouteFsdTemplate = () => `import { createFileRoute } from '@tanstack/react-router';
|
|
549
|
+
import { HomePage } from '@/pages/home';
|
|
493
550
|
|
|
494
551
|
export const Route = createFileRoute('/')({
|
|
495
|
-
component:
|
|
552
|
+
component: HomePage,
|
|
496
553
|
});
|
|
554
|
+
`;
|
|
555
|
+
indexRouteBprTemplate = () => `import { createFileRoute } from '@tanstack/react-router';
|
|
556
|
+
import Home from '@/pages/Home';
|
|
497
557
|
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
<h3>Welcome Home!</h3>
|
|
502
|
-
</div>
|
|
503
|
-
);
|
|
504
|
-
}
|
|
558
|
+
export const Route = createFileRoute('/')({
|
|
559
|
+
component: Home,
|
|
560
|
+
});
|
|
505
561
|
`;
|
|
506
562
|
}
|
|
507
563
|
});
|
|
@@ -620,6 +676,25 @@ var init_styles = __esm({
|
|
|
620
676
|
"src/scaffold/react-vite/templates/styles.ts"() {
|
|
621
677
|
"use strict";
|
|
622
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
|
+
}
|
|
623
698
|
`;
|
|
624
699
|
}
|
|
625
700
|
});
|
|
@@ -640,7 +715,7 @@ declare module '*.css' {
|
|
|
640
715
|
});
|
|
641
716
|
|
|
642
717
|
// src/scaffold/react-vite/templates/copilot-instructions.ts
|
|
643
|
-
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;
|
|
644
719
|
var init_copilot_instructions = __esm({
|
|
645
720
|
"src/scaffold/react-vite/templates/copilot-instructions.ts"() {
|
|
646
721
|
"use strict";
|
|
@@ -723,6 +798,21 @@ Path-specific rules live in \`.github/instructions/*.instructions.md\` \u2014 th
|
|
|
723
798
|
## Stack
|
|
724
799
|
${stackLines}
|
|
725
800
|
|
|
801
|
+
## Import aliases
|
|
802
|
+
|
|
803
|
+
Always use path aliases instead of relative imports. Project-wide aliases are pre-configured in \`tsconfig.json\` and \`vite.config.ts\`:
|
|
804
|
+
|
|
805
|
+
- \`@/*\` \u2192 \`./src/\` \u2014 use for any import from src, e.g. \`import Button from '@/components/Button'\`.
|
|
806
|
+
- \`@components/*\` \u2192 \`./src/components/\` \u2014 e.g. \`import { Modal } from '@components/Modal'\`.
|
|
807
|
+
- \`@pages/*\` \u2192 \`./src/pages/\` \u2014 e.g. \`import { HomePage } from '@pages/Home'\`.
|
|
808
|
+
- \`@utils/*\` \u2192 \`./src/utils/\` \u2014 e.g. \`import { cn } from '@utils/cn'\`.
|
|
809
|
+
- \`@types/*\` \u2192 \`./src/types/\` \u2014 e.g. \`import type { User } from '@types/user'\`.
|
|
810
|
+
- \`@hooks/*\` \u2192 \`./src/hooks/\` \u2014 e.g. \`import { useAuth } from '@hooks/useAuth'\`.
|
|
811
|
+
- \`@layouts/*\` \u2192 \`./src/layouts/\` \u2014 e.g. \`import { MainLayout } from '@layouts/MainLayout'\`.
|
|
812
|
+
- \`@assets/*\` \u2192 \`./src/assets/\` \u2014 e.g. \`import logo from '@assets/logo.svg'\`.
|
|
813
|
+
|
|
814
|
+
**Never use relative imports** like \`import Foo from '../../../components/Foo'\`. Always resolve through an alias.
|
|
815
|
+
|
|
726
816
|
## Naming conventions
|
|
727
817
|
|
|
728
818
|
- **Components**: \`PascalCase.tsx\`, one component per file, named export (no default).
|
|
@@ -950,6 +1040,100 @@ Use \`:root\` for runtime values (e.g., animation targets, JS-readable tokens) t
|
|
|
950
1040
|
}
|
|
951
1041
|
}
|
|
952
1042
|
\`\`\`
|
|
1043
|
+
|
|
1044
|
+
### Color format \u2014 prefer oklch
|
|
1045
|
+
|
|
1046
|
+
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:
|
|
1047
|
+
|
|
1048
|
+
\`\`\`css
|
|
1049
|
+
@theme {
|
|
1050
|
+
--color-primary: oklch(0.55 0.22 265); /* L C H */
|
|
1051
|
+
--color-muted: oklch(0.62 0.01 265);
|
|
1052
|
+
}
|
|
1053
|
+
\`\`\`
|
|
1054
|
+
|
|
1055
|
+
Avoid hex or \`rgb()\` unless matching a fixed brand value.
|
|
1056
|
+
|
|
1057
|
+
### Theme variables are also CSS custom properties
|
|
1058
|
+
|
|
1059
|
+
Every \`@theme\` variable is a real CSS custom property \u2014 read it anywhere:
|
|
1060
|
+
|
|
1061
|
+
\`\`\`css
|
|
1062
|
+
.my-element {
|
|
1063
|
+
color: var(--color-primary); /* same value as the text-primary utility */
|
|
1064
|
+
}
|
|
1065
|
+
\`\`\`
|
|
1066
|
+
|
|
1067
|
+
Use \`var()\` for CSS that Tailwind utilities cannot express (e.g. complex \`box-shadow\`, SVG \`fill\`, pseudo-element content).
|
|
1068
|
+
|
|
1069
|
+
### Semantic token pattern \u2014 naming convention
|
|
1070
|
+
|
|
1071
|
+
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:
|
|
1072
|
+
|
|
1073
|
+
\`\`\`css
|
|
1074
|
+
@theme {
|
|
1075
|
+
/* Brand */
|
|
1076
|
+
--color-primary: oklch(0.55 0.22 265);
|
|
1077
|
+
--color-primary-fg: oklch(0.97 0.01 265);
|
|
1078
|
+
|
|
1079
|
+
/* Surfaces (dark-first) */
|
|
1080
|
+
--color-surface: oklch(0.11 0.015 265);
|
|
1081
|
+
--color-surface-muted: oklch(0.16 0.012 265);
|
|
1082
|
+
--color-surface-high: oklch(0.21 0.010 265);
|
|
1083
|
+
|
|
1084
|
+
/* Text */
|
|
1085
|
+
--color-text: oklch(0.97 0.000 0);
|
|
1086
|
+
--color-text-muted: oklch(0.62 0.010 265);
|
|
1087
|
+
--color-text-subtle: oklch(0.42 0.010 265);
|
|
1088
|
+
|
|
1089
|
+
/* Border */
|
|
1090
|
+
--color-border: oklch(0.28 0.010 265);
|
|
1091
|
+
}
|
|
1092
|
+
\`\`\`
|
|
1093
|
+
|
|
1094
|
+
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.
|
|
1095
|
+
`;
|
|
1096
|
+
};
|
|
1097
|
+
importsTemplate = () => {
|
|
1098
|
+
return frontmatter("**") + `# Import aliases and code organization
|
|
1099
|
+
|
|
1100
|
+
All imports use path aliases defined in \`tsconfig.json\` and \`vite.config.ts\`. This avoids fragile relative paths and makes moving files safe.
|
|
1101
|
+
|
|
1102
|
+
## Available aliases
|
|
1103
|
+
|
|
1104
|
+
- \`@/*\` \u2192 \`./src/\` \u2014 shortest form, use when unambiguous
|
|
1105
|
+
- \`@components/*\` \u2014 components in \`./src/components/\`
|
|
1106
|
+
- \`@pages/*\` \u2014 pages in \`./src/pages/\`
|
|
1107
|
+
- \`@utils/*\` \u2014 utilities in \`./src/utils/\`
|
|
1108
|
+
- \`@types/*\` \u2014 types in \`./src/types/\`
|
|
1109
|
+
- \`@hooks/*\` \u2014 hooks in \`./src/hooks/\`
|
|
1110
|
+
- \`@layouts/*\` \u2014 layouts in \`./src/layouts/\`
|
|
1111
|
+
- \`@assets/*\` \u2014 assets in \`./src/assets/\`
|
|
1112
|
+
|
|
1113
|
+
## Rules
|
|
1114
|
+
|
|
1115
|
+
- **Never use relative imports** (\`../../../\`). Always use an alias.
|
|
1116
|
+
- **Group imports**: standard library, third-party, then project aliases.
|
|
1117
|
+
- **Organize by type within a file**: imports, types, constants, then code.
|
|
1118
|
+
|
|
1119
|
+
### Examples
|
|
1120
|
+
|
|
1121
|
+
\u2705 Good:
|
|
1122
|
+
\`\`\`typescript
|
|
1123
|
+
import { ReactNode } from 'react';
|
|
1124
|
+
import { QueryClient } from '@tanstack/react-query';
|
|
1125
|
+
|
|
1126
|
+
import { Button } from '@components/Button';
|
|
1127
|
+
import { useUser } from '@hooks/useUser';
|
|
1128
|
+
import type { User } from '@types/user';
|
|
1129
|
+
import { cn } from '@utils/cn';
|
|
1130
|
+
\`\`\`
|
|
1131
|
+
|
|
1132
|
+
\u274C Bad:
|
|
1133
|
+
\`\`\`typescript
|
|
1134
|
+
import { Button } from '../../../components/Button'; // relative path
|
|
1135
|
+
import Button from '@components/Button/Button'; // no file extension in alias
|
|
1136
|
+
\`\`\`
|
|
953
1137
|
`;
|
|
954
1138
|
};
|
|
955
1139
|
linterTemplate = (cart) => {
|
|
@@ -981,6 +1165,7 @@ Use \`:root\` for runtime values (e.g., animation targets, JS-readable tokens) t
|
|
|
981
1165
|
const hasLinter = cart.linter !== "NOT_USING";
|
|
982
1166
|
const files = [
|
|
983
1167
|
{ relativePath: ".github/copilot-instructions.md", content: generalTemplate(cart) },
|
|
1168
|
+
{ relativePath: ".github/instructions/imports.instructions.md", content: importsTemplate() },
|
|
984
1169
|
{ relativePath: ".github/instructions/components.instructions.md", content: componentsTemplate(cart) },
|
|
985
1170
|
{ relativePath: ".github/instructions/hooks.instructions.md", content: hooksTemplate(cart) }
|
|
986
1171
|
];
|
|
@@ -1019,6 +1204,227 @@ Use \`:root\` for runtime values (e.g., animation targets, JS-readable tokens) t
|
|
|
1019
1204
|
}
|
|
1020
1205
|
});
|
|
1021
1206
|
|
|
1207
|
+
// src/scaffold/react-vite/templates/home-page.ts
|
|
1208
|
+
function buildBadges(o) {
|
|
1209
|
+
const badges = [
|
|
1210
|
+
{ label: "React 19", bg: "#1e3a8a", fg: "#bfdbfe", border: "#3b82f6" },
|
|
1211
|
+
{ label: "TypeScript", bg: "#1e3a8a", fg: "#bfdbfe", border: "#2563eb" },
|
|
1212
|
+
{ label: "Vite 6", bg: "#4c1d95", fg: "#ddd6fe", border: "#8b5cf6" },
|
|
1213
|
+
{ label: o.layout === "FSD" ? "FSD" : "Bulletproof React", bg: "#78350f", fg: "#fde68a", border: "#d97706" }
|
|
1214
|
+
];
|
|
1215
|
+
if (o.hasRouter) badges.push({ label: "TanStack Router", bg: "#7f1d1d", fg: "#fecaca", border: "#ef4444" });
|
|
1216
|
+
if (o.hasZustand) badges.push({ label: "Zustand", bg: "#14532d", fg: "#bbf7d0", border: "#22c55e" });
|
|
1217
|
+
if (o.hasQuery) badges.push({ label: "TanStack Query", bg: "#7c2d12", fg: "#fed7aa", border: "#f97316" });
|
|
1218
|
+
if (o.hasTailwind) badges.push({ label: "Tailwind CSS v4", bg: "#164e63", fg: "#a5f3fc", border: "#06b6d4" });
|
|
1219
|
+
if (o.linter === "BIOME") badges.push({ label: "Biome", bg: "#1e3a5f", fg: "#bae6fd", border: "#0284c7" });
|
|
1220
|
+
if (o.linter === "ESLINT") badges.push({ label: "ESLint", bg: "#312e81", fg: "#c7d2fe", border: "#6366f1" });
|
|
1221
|
+
return badges;
|
|
1222
|
+
}
|
|
1223
|
+
function buildHomePage(o) {
|
|
1224
|
+
const badges = buildBadges(o);
|
|
1225
|
+
const storeImport = o.hasZustand ? `import { useAppStore } from '${o.storeImportPath}';
|
|
1226
|
+
` : "";
|
|
1227
|
+
const queryImport = o.hasQuery ? `import { useQuery } from '@tanstack/react-query';
|
|
1228
|
+
` : "";
|
|
1229
|
+
const postType = o.hasQuery ? `
|
|
1230
|
+
type Post = { id: number; title: string; body: string };
|
|
1231
|
+
` : "";
|
|
1232
|
+
const exportKw = o.namedExport ? `export const ${o.componentName}` : `const ${o.componentName}`;
|
|
1233
|
+
const defaultExp = o.namedExport ? "" : `
|
|
1234
|
+
export default ${o.componentName};
|
|
1235
|
+
`;
|
|
1236
|
+
if (o.hasTailwind) {
|
|
1237
|
+
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");
|
|
1238
|
+
const counterCmp2 = o.hasZustand ? `
|
|
1239
|
+
const Counter = () => {
|
|
1240
|
+
const { count, increment, decrement, reset } = useAppStore();
|
|
1241
|
+
return (
|
|
1242
|
+
<div className="flex items-center gap-3">
|
|
1243
|
+
<button
|
|
1244
|
+
onClick={decrement}
|
|
1245
|
+
className="w-9 h-9 rounded-lg bg-surface-high hover:bg-surface-muted text-text text-xl font-bold transition-colors"
|
|
1246
|
+
>
|
|
1247
|
+
\u2212
|
|
1248
|
+
</button>
|
|
1249
|
+
<span className="text-3xl font-bold text-text w-14 text-center tabular-nums">{count}</span>
|
|
1250
|
+
<button
|
|
1251
|
+
onClick={increment}
|
|
1252
|
+
className="w-9 h-9 rounded-lg bg-surface-high hover:bg-surface-muted text-text text-xl font-bold transition-colors"
|
|
1253
|
+
>
|
|
1254
|
+
+
|
|
1255
|
+
</button>
|
|
1256
|
+
<button onClick={reset} className="ml-2 text-sm text-text-muted hover:text-text transition-colors">
|
|
1257
|
+
reset
|
|
1258
|
+
</button>
|
|
1259
|
+
</div>
|
|
1260
|
+
);
|
|
1261
|
+
};
|
|
1262
|
+
` : "";
|
|
1263
|
+
const queryCmp2 = o.hasQuery ? `
|
|
1264
|
+
const QueryDemo = () => {
|
|
1265
|
+
const { data, isLoading, isError } = useQuery<Post>({
|
|
1266
|
+
queryKey: ['demo-post'],
|
|
1267
|
+
queryFn: () => fetch('https://jsonplaceholder.typicode.com/posts/1').then(r => r.json()),
|
|
1268
|
+
});
|
|
1269
|
+
if (isLoading) return <p className="text-text-muted text-sm">Fetching post\u2026</p>;
|
|
1270
|
+
if (isError) return <p className="text-red-400 text-sm">Failed to fetch.</p>;
|
|
1271
|
+
return (
|
|
1272
|
+
<div className="space-y-1">
|
|
1273
|
+
<p className="text-text text-sm font-medium">{data?.title}</p>
|
|
1274
|
+
<p className="text-text-muted text-xs leading-relaxed">{data?.body}</p>
|
|
1275
|
+
</div>
|
|
1276
|
+
);
|
|
1277
|
+
};
|
|
1278
|
+
` : "";
|
|
1279
|
+
const zustandCard2 = o.hasZustand ? `
|
|
1280
|
+
<div className="rounded-xl p-5 mb-3 bg-surface-high/60 border border-border">
|
|
1281
|
+
<p className="text-xs font-semibold text-text-muted uppercase tracking-wider mb-3">Zustand \u2014 Counter</p>
|
|
1282
|
+
<Counter />
|
|
1283
|
+
</div>` : "";
|
|
1284
|
+
const queryCard2 = o.hasQuery ? `
|
|
1285
|
+
<div className="rounded-xl p-5 bg-surface-high/60 border border-border">
|
|
1286
|
+
<p className="text-xs font-semibold text-text-muted uppercase tracking-wider mb-3">TanStack Query \u2014 Fetched Post</p>
|
|
1287
|
+
<QueryDemo />
|
|
1288
|
+
</div>` : "";
|
|
1289
|
+
return `${storeImport}${queryImport}${postType}${counterCmp2}${queryCmp2}
|
|
1290
|
+
${exportKw} = () => {
|
|
1291
|
+
return (
|
|
1292
|
+
<div className="min-h-screen flex items-center justify-center p-6 bg-gradient-to-br from-surface to-surface-muted">
|
|
1293
|
+
<div className="w-full max-w-lg">
|
|
1294
|
+
<div className="text-center mb-8">
|
|
1295
|
+
<div className="text-5xl mb-3">\u{1F680}</div>
|
|
1296
|
+
<h1 className="text-4xl font-bold text-text mb-2">${o.projectName}</h1>
|
|
1297
|
+
<p className="text-text-muted">Your React + Vite starter is ready.</p>
|
|
1298
|
+
</div>
|
|
1299
|
+
|
|
1300
|
+
<div className="rounded-xl p-5 mb-3 bg-surface-high/60 border border-border">
|
|
1301
|
+
<p className="text-xs font-semibold text-text-muted uppercase tracking-wider mb-3">Stack</p>
|
|
1302
|
+
<div className="flex flex-wrap gap-2">
|
|
1303
|
+
${badgesJsx}
|
|
1304
|
+
</div>
|
|
1305
|
+
</div>${zustandCard2}${queryCard2}
|
|
1306
|
+
</div>
|
|
1307
|
+
</div>
|
|
1308
|
+
);
|
|
1309
|
+
};
|
|
1310
|
+
${defaultExp}`;
|
|
1311
|
+
}
|
|
1312
|
+
const badgeDefs = badges.map((b) => ` { label: '${b.label}', bg: '${b.bg}80', fg: '${b.fg}', border: '${b.border}70' }`).join(",\n");
|
|
1313
|
+
const counterCmp = o.hasZustand ? `
|
|
1314
|
+
const Counter = () => {
|
|
1315
|
+
const { count, increment, decrement, reset } = useAppStore();
|
|
1316
|
+
const btn: React.CSSProperties = {
|
|
1317
|
+
width: '2.25rem', height: '2.25rem', borderRadius: '0.5rem',
|
|
1318
|
+
background: '#334155', border: 'none', color: 'white',
|
|
1319
|
+
fontSize: '1.25rem', cursor: 'pointer', lineHeight: 1,
|
|
1320
|
+
};
|
|
1321
|
+
return (
|
|
1322
|
+
<div style={{ display: 'flex', alignItems: 'center', gap: '0.75rem' }}>
|
|
1323
|
+
<button style={btn} onClick={decrement}>\u2212</button>
|
|
1324
|
+
<span style={{ fontSize: '1.875rem', fontWeight: 700, color: 'white', width: '3.5rem', textAlign: 'center' }}>{count}</span>
|
|
1325
|
+
<button style={btn} onClick={increment}>+</button>
|
|
1326
|
+
<button
|
|
1327
|
+
onClick={reset}
|
|
1328
|
+
style={{ background: 'none', border: 'none', color: '#94a3b8', cursor: 'pointer', fontSize: '0.875rem', marginLeft: '0.5rem' }}
|
|
1329
|
+
>
|
|
1330
|
+
reset
|
|
1331
|
+
</button>
|
|
1332
|
+
</div>
|
|
1333
|
+
);
|
|
1334
|
+
};
|
|
1335
|
+
` : "";
|
|
1336
|
+
const queryCmp = o.hasQuery ? `
|
|
1337
|
+
const QueryDemo = () => {
|
|
1338
|
+
const { data, isLoading, isError } = useQuery<Post>({
|
|
1339
|
+
queryKey: ['demo-post'],
|
|
1340
|
+
queryFn: () => fetch('https://jsonplaceholder.typicode.com/posts/1').then(r => r.json()),
|
|
1341
|
+
});
|
|
1342
|
+
if (isLoading) return <p style={{ color: '#94a3b8', fontSize: '0.875rem' }}>Fetching post\u2026</p>;
|
|
1343
|
+
if (isError) return <p style={{ color: '#f87171', fontSize: '0.875rem' }}>Failed to fetch.</p>;
|
|
1344
|
+
return (
|
|
1345
|
+
<div>
|
|
1346
|
+
<p style={{ color: 'white', fontSize: '0.875rem', fontWeight: 500, margin: '0 0 0.25rem' }}>{data?.title}</p>
|
|
1347
|
+
<p style={{ color: '#94a3b8', fontSize: '0.75rem', lineHeight: 1.6, margin: 0 }}>{data?.body}</p>
|
|
1348
|
+
</div>
|
|
1349
|
+
);
|
|
1350
|
+
};
|
|
1351
|
+
` : "";
|
|
1352
|
+
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' }`;
|
|
1353
|
+
const cardLabel = `{ fontSize: '0.65rem', fontWeight: 600, color: '#94a3b8', textTransform: 'uppercase' as const, letterSpacing: '0.1em', marginBottom: '0.75rem' }`;
|
|
1354
|
+
const zustandCard = o.hasZustand ? `
|
|
1355
|
+
<div style={${card}}>
|
|
1356
|
+
<p style={${cardLabel}}>Zustand \u2014 Counter</p>
|
|
1357
|
+
<Counter />
|
|
1358
|
+
</div>` : "";
|
|
1359
|
+
const queryCard = o.hasQuery ? `
|
|
1360
|
+
<div style={{ ...${card}, marginBottom: 0 }}>
|
|
1361
|
+
<p style={${cardLabel}}>TanStack Query \u2014 Fetched Post</p>
|
|
1362
|
+
<QueryDemo />
|
|
1363
|
+
</div>` : "";
|
|
1364
|
+
const reactImport = o.hasZustand ? `import type React from 'react';
|
|
1365
|
+
` : "";
|
|
1366
|
+
return `${reactImport}${storeImport}${queryImport}${postType}
|
|
1367
|
+
const stackBadges = [
|
|
1368
|
+
${badgeDefs},
|
|
1369
|
+
];
|
|
1370
|
+
${counterCmp}${queryCmp}
|
|
1371
|
+
${exportKw} = () => {
|
|
1372
|
+
return (
|
|
1373
|
+
<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' }}>
|
|
1374
|
+
<div style={{ width: '100%', maxWidth: '480px' }}>
|
|
1375
|
+
<div style={{ textAlign: 'center', marginBottom: '2rem' }}>
|
|
1376
|
+
<div style={{ fontSize: '3rem', marginBottom: '0.75rem' }}>\u{1F680}</div>
|
|
1377
|
+
<h1 style={{ fontSize: '2.25rem', fontWeight: 700, color: '#f8fafc', margin: '0 0 0.5rem' }}>${o.projectName}</h1>
|
|
1378
|
+
<p style={{ color: '#94a3b8', margin: 0 }}>Your React + Vite starter is ready.</p>
|
|
1379
|
+
</div>
|
|
1380
|
+
|
|
1381
|
+
<div style={${card}}>
|
|
1382
|
+
<p style={${cardLabel}}>Stack</p>
|
|
1383
|
+
<div style={{ display: 'flex', flexWrap: 'wrap', gap: '0.5rem' }}>
|
|
1384
|
+
{stackBadges.map(b => (
|
|
1385
|
+
<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 }}>
|
|
1386
|
+
{b.label}
|
|
1387
|
+
</span>
|
|
1388
|
+
))}
|
|
1389
|
+
</div>
|
|
1390
|
+
</div>${zustandCard}${queryCard}
|
|
1391
|
+
</div>
|
|
1392
|
+
</div>
|
|
1393
|
+
);
|
|
1394
|
+
};
|
|
1395
|
+
${defaultExp}`;
|
|
1396
|
+
}
|
|
1397
|
+
var homePageFsdTemplate, homePageBprTemplate;
|
|
1398
|
+
var init_home_page = __esm({
|
|
1399
|
+
"src/scaffold/react-vite/templates/home-page.ts"() {
|
|
1400
|
+
"use strict";
|
|
1401
|
+
homePageFsdTemplate = (cart) => buildHomePage({
|
|
1402
|
+
projectName: cart.projectName,
|
|
1403
|
+
layout: "FSD",
|
|
1404
|
+
hasTailwind: cart.css === "TAILWIND",
|
|
1405
|
+
hasZustand: cart.stateManagement === "ZUSTAND",
|
|
1406
|
+
hasQuery: cart.query === "TANSTACK_QUERY",
|
|
1407
|
+
hasRouter: cart.router === "TANSTACK_ROUTER",
|
|
1408
|
+
linter: cart.linter,
|
|
1409
|
+
storeImportPath: "@/shared/lib/store",
|
|
1410
|
+
componentName: "HomePage",
|
|
1411
|
+
namedExport: true
|
|
1412
|
+
});
|
|
1413
|
+
homePageBprTemplate = (cart) => buildHomePage({
|
|
1414
|
+
projectName: cart.projectName,
|
|
1415
|
+
layout: "BPR",
|
|
1416
|
+
hasTailwind: cart.css === "TAILWIND",
|
|
1417
|
+
hasZustand: cart.stateManagement === "ZUSTAND",
|
|
1418
|
+
hasQuery: cart.query === "TANSTACK_QUERY",
|
|
1419
|
+
hasRouter: cart.router === "TANSTACK_ROUTER",
|
|
1420
|
+
linter: cart.linter,
|
|
1421
|
+
storeImportPath: "@/stores/appStore",
|
|
1422
|
+
componentName: "Home",
|
|
1423
|
+
namedExport: false
|
|
1424
|
+
});
|
|
1425
|
+
}
|
|
1426
|
+
});
|
|
1427
|
+
|
|
1022
1428
|
// src/scaffold/react-vite/templates/fsd-layout.ts
|
|
1023
1429
|
var getFsdFileMap;
|
|
1024
1430
|
var init_fsd_layout = __esm({
|
|
@@ -1037,6 +1443,7 @@ var init_fsd_layout = __esm({
|
|
|
1037
1443
|
init_styles();
|
|
1038
1444
|
init_vite_env_d_ts();
|
|
1039
1445
|
init_copilot_instructions();
|
|
1446
|
+
init_home_page();
|
|
1040
1447
|
getFsdFileMap = (cart) => {
|
|
1041
1448
|
const hasRouter = cart.router === "TANSTACK_ROUTER";
|
|
1042
1449
|
const hasZustand = cart.stateManagement === "ZUSTAND";
|
|
@@ -1054,8 +1461,7 @@ var init_fsd_layout = __esm({
|
|
|
1054
1461
|
{ relativePath: "src/app/index.tsx", content: appTsxFsdTemplate(hasRouter, hasQuery) },
|
|
1055
1462
|
{
|
|
1056
1463
|
relativePath: "src/pages/home/ui/HomePage.tsx",
|
|
1057
|
-
content:
|
|
1058
|
-
`
|
|
1464
|
+
content: homePageFsdTemplate(cart)
|
|
1059
1465
|
},
|
|
1060
1466
|
{
|
|
1061
1467
|
relativePath: "src/pages/home/index.ts",
|
|
@@ -1073,7 +1479,8 @@ var init_fsd_layout = __esm({
|
|
|
1073
1479
|
if (hasRouter) {
|
|
1074
1480
|
files.push(
|
|
1075
1481
|
{ relativePath: "src/routes/__root.tsx", content: rootRouteTemplate() },
|
|
1076
|
-
{ relativePath: "src/routes/index.tsx", content:
|
|
1482
|
+
{ relativePath: "src/routes/index.tsx", content: indexRouteFsdTemplate() },
|
|
1483
|
+
{ relativePath: "src/routes/routeTree.gen.ts", content: routeTreeGenTemplate() }
|
|
1077
1484
|
);
|
|
1078
1485
|
}
|
|
1079
1486
|
if (hasZustand) {
|
|
@@ -1140,6 +1547,7 @@ var init_bpr_layout = __esm({
|
|
|
1140
1547
|
init_styles();
|
|
1141
1548
|
init_vite_env_d_ts();
|
|
1142
1549
|
init_copilot_instructions();
|
|
1550
|
+
init_home_page();
|
|
1143
1551
|
getBprFileMap = (cart) => {
|
|
1144
1552
|
const hasRouter = cart.router === "TANSTACK_ROUTER";
|
|
1145
1553
|
const hasZustand = cart.stateManagement === "ZUSTAND";
|
|
@@ -1155,6 +1563,7 @@ var init_bpr_layout = __esm({
|
|
|
1155
1563
|
{ relativePath: "src/vite-env.d.ts", content: viteEnvDtsTemplate() },
|
|
1156
1564
|
{ relativePath: "src/main.tsx", content: mainTsxTemplate(cart) },
|
|
1157
1565
|
{ relativePath: "src/App.tsx", content: appTsxBprTemplate(hasRouter, hasQuery) },
|
|
1566
|
+
{ relativePath: "src/pages/Home.tsx", content: homePageBprTemplate(cart) },
|
|
1158
1567
|
{
|
|
1159
1568
|
relativePath: "src/providers/index.tsx",
|
|
1160
1569
|
content: hasQuery ? queryProviderBprTemplate() : simpleProviderBprTemplate()
|
|
@@ -1172,7 +1581,8 @@ var init_bpr_layout = __esm({
|
|
|
1172
1581
|
if (hasRouter) {
|
|
1173
1582
|
files.push(
|
|
1174
1583
|
{ relativePath: "src/routes/__root.tsx", content: rootRouteTemplate() },
|
|
1175
|
-
{ relativePath: "src/routes/index.tsx", content:
|
|
1584
|
+
{ relativePath: "src/routes/index.tsx", content: indexRouteBprTemplate() },
|
|
1585
|
+
{ relativePath: "src/routes/routeTree.gen.ts", content: routeTreeGenTemplate() }
|
|
1176
1586
|
);
|
|
1177
1587
|
}
|
|
1178
1588
|
if (hasZustand) {
|
|
@@ -1500,12 +1910,23 @@ var init_vite_config2 = __esm({
|
|
|
1500
1910
|
const imports = [
|
|
1501
1911
|
`import { defineConfig } from 'vite';`,
|
|
1502
1912
|
`import react from '@vitejs/plugin-react';`,
|
|
1913
|
+
`import path from 'path';`,
|
|
1503
1914
|
hasTailwind ? `import tailwindcss from '@tailwindcss/vite';` : ""
|
|
1504
1915
|
].filter(Boolean).join("\n");
|
|
1505
1916
|
const plugins = ["react()", hasTailwind ? "tailwindcss()" : ""].filter(Boolean).join(",\n ");
|
|
1506
1917
|
return `${imports}
|
|
1507
1918
|
|
|
1508
1919
|
export default defineConfig({
|
|
1920
|
+
resolve: {
|
|
1921
|
+
alias: {
|
|
1922
|
+
'@': path.resolve(__dirname, './src'),
|
|
1923
|
+
'@components': path.resolve(__dirname, './src/components'),
|
|
1924
|
+
'@hooks': path.resolve(__dirname, './src/hooks'),
|
|
1925
|
+
'@lib': path.resolve(__dirname, './src/lib'),
|
|
1926
|
+
'@types': path.resolve(__dirname, './src/types'),
|
|
1927
|
+
'@utils': path.resolve(__dirname, './src/utils'),
|
|
1928
|
+
},
|
|
1929
|
+
},
|
|
1509
1930
|
plugins: [
|
|
1510
1931
|
${plugins},
|
|
1511
1932
|
],
|