@react-spa-scaffold/mcp 2.1.1 → 2.3.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/README.md +2 -1
- package/dist/constants.d.ts +4 -0
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +4 -0
- package/dist/constants.js.map +1 -1
- package/dist/features/definitions/auth.d.ts +3 -0
- package/dist/features/definitions/auth.d.ts.map +1 -0
- package/dist/features/definitions/auth.js +17 -0
- package/dist/features/definitions/auth.js.map +1 -0
- package/dist/features/definitions/core.d.ts.map +1 -1
- package/dist/features/definitions/core.js +16 -1
- package/dist/features/definitions/core.js.map +1 -1
- package/dist/features/definitions/database.d.ts +3 -0
- package/dist/features/definitions/database.d.ts.map +1 -0
- package/dist/features/definitions/database.js +45 -0
- package/dist/features/definitions/database.js.map +1 -0
- package/dist/features/definitions/deployment.d.ts +3 -0
- package/dist/features/definitions/deployment.d.ts.map +1 -0
- package/dist/features/definitions/deployment.js +14 -0
- package/dist/features/definitions/deployment.js.map +1 -0
- package/dist/features/definitions/forms.d.ts.map +1 -1
- package/dist/features/definitions/forms.js +4 -0
- package/dist/features/definitions/forms.js.map +1 -1
- package/dist/features/definitions/index.d.ts +3 -0
- package/dist/features/definitions/index.d.ts.map +1 -1
- package/dist/features/definitions/index.js +3 -0
- package/dist/features/definitions/index.js.map +1 -1
- package/dist/features/definitions/mobile.d.ts.map +1 -1
- package/dist/features/definitions/mobile.js +11 -2
- package/dist/features/definitions/mobile.js.map +1 -1
- package/dist/features/definitions/observability.js +1 -1
- package/dist/features/definitions/observability.js.map +1 -1
- package/dist/features/definitions/routing.d.ts.map +1 -1
- package/dist/features/definitions/routing.js +2 -1
- package/dist/features/definitions/routing.js.map +1 -1
- package/dist/features/definitions/state.d.ts.map +1 -1
- package/dist/features/definitions/state.js +9 -2
- package/dist/features/definitions/state.js.map +1 -1
- package/dist/features/definitions/testing.d.ts.map +1 -1
- package/dist/features/definitions/testing.js +4 -2
- package/dist/features/definitions/testing.js.map +1 -1
- package/dist/features/registry.d.ts.map +1 -1
- package/dist/features/registry.js +4 -1
- package/dist/features/registry.js.map +1 -1
- package/dist/features/types.test.js +6 -2
- package/dist/features/types.test.js.map +1 -1
- package/dist/resources/docs.d.ts.map +1 -1
- package/dist/resources/docs.js +5 -0
- package/dist/resources/docs.js.map +1 -1
- package/dist/tools/add-features.js +1 -1
- package/dist/tools/add-features.js.map +1 -1
- package/dist/utils/docs.d.ts.map +1 -1
- package/dist/utils/docs.js +2 -0
- package/dist/utils/docs.js.map +1 -1
- package/dist/utils/scaffold/claude-md/index.d.ts.map +1 -1
- package/dist/utils/scaffold/claude-md/index.js +3 -1
- package/dist/utils/scaffold/claude-md/index.js.map +1 -1
- package/dist/utils/scaffold/claude-md/sections.d.ts +2 -0
- package/dist/utils/scaffold/claude-md/sections.d.ts.map +1 -1
- package/dist/utils/scaffold/claude-md/sections.js +132 -2
- package/dist/utils/scaffold/claude-md/sections.js.map +1 -1
- package/dist/utils/scaffold/compute.js +1 -1
- package/dist/utils/scaffold/compute.js.map +1 -1
- package/dist/utils/scaffold/generators.d.ts +2 -2
- package/dist/utils/scaffold/generators.d.ts.map +1 -1
- package/dist/utils/scaffold/generators.js +64 -22
- package/dist/utils/scaffold/generators.js.map +1 -1
- package/package.json +1 -1
- package/templates/.env.example +44 -10
- package/templates/.github/workflows/ci.yml +12 -4
- package/templates/.github/workflows/deploy.yml +59 -0
- package/templates/CLAUDE.md +251 -2
- package/templates/docs/ARCHITECTURE.md +13 -12
- package/templates/docs/AUTHENTICATION.md +325 -0
- package/templates/docs/CODING_STANDARDS.md +65 -0
- package/templates/docs/DEPLOYMENT.md +268 -0
- package/templates/docs/E2E_TESTING.md +133 -11
- package/templates/docs/SUPABASE_INTEGRATION.md +310 -0
- package/templates/docs/TESTING.md +195 -77
- package/templates/e2e/auth/auth.setup.ts +60 -0
- package/templates/e2e/fixtures/index.ts +24 -2
- package/templates/e2e/tests/profile.auth.spec.ts +103 -0
- package/templates/e2e/tests/profile.spec.ts +64 -0
- package/templates/e2e/tests/register-form.spec.ts +38 -0
- package/templates/gitignore +5 -0
- package/templates/package.json +15 -3
- package/templates/playwright.config.ts +39 -4
- package/templates/src/App.tsx +32 -19
- package/templates/src/components/layout/Header.test.tsx +17 -1
- package/templates/src/components/layout/Header.tsx +13 -1
- package/templates/src/components/shared/AccountButton/AccountButton.test.tsx +30 -0
- package/templates/src/components/shared/AccountButton/AccountButton.tsx +38 -0
- package/templates/src/components/shared/AccountButton/index.ts +1 -0
- package/templates/src/components/shared/ErrorBoundary/ErrorBoundary.test.tsx +4 -4
- package/templates/src/components/shared/ErrorBoundary/ErrorBoundary.tsx +55 -53
- package/templates/src/components/shared/ProfileSync/ProfileSync.test.tsx +44 -0
- package/templates/src/components/shared/ProfileSync/ProfileSync.tsx +104 -0
- package/templates/src/components/shared/ProfileSync/index.ts +1 -0
- package/templates/src/components/shared/ProtectedRoute/ProtectedRoute.test.tsx +43 -0
- package/templates/src/components/shared/ProtectedRoute/ProtectedRoute.tsx +35 -0
- package/templates/src/components/shared/ProtectedRoute/index.ts +1 -0
- package/templates/src/components/shared/index.ts +5 -2
- package/templates/src/contexts/clerkContext.tsx +45 -0
- package/templates/src/contexts/performanceContext.tsx +3 -3
- package/templates/src/contexts/supabaseContext.test.tsx +59 -0
- package/templates/src/contexts/supabaseContext.tsx +87 -0
- package/templates/src/hooks/index.ts +40 -2
- package/templates/src/hooks/supabase/index.ts +12 -0
- package/templates/src/hooks/supabase/useProfiles.test.tsx +207 -0
- package/templates/src/hooks/supabase/useProfiles.ts +213 -0
- package/templates/src/hooks/supabase/useSupabaseQuery.test.tsx +150 -0
- package/templates/src/hooks/supabase/useSupabaseQuery.ts +91 -0
- package/templates/src/hooks/useCopyFeedback.test.ts +129 -0
- package/templates/src/hooks/useCopyFeedback.ts +41 -0
- package/templates/src/hooks/useDebouncedCallback.test.ts +164 -0
- package/templates/src/hooks/useDebouncedCallback.ts +47 -0
- package/templates/src/hooks/useDocumentTitle.test.ts +59 -0
- package/templates/src/hooks/useDocumentTitle.ts +31 -0
- package/templates/src/hooks/useIOSViewportReset.test.ts +58 -0
- package/templates/src/hooks/useIOSViewportReset.ts +18 -0
- package/templates/src/hooks/useKeyboardShortcut.test.ts +86 -0
- package/templates/src/hooks/useKeyboardShortcuts.ts +44 -0
- package/templates/src/hooks/useLocalStorage.test.ts +111 -0
- package/templates/src/hooks/useLocalStorage.ts +77 -0
- package/templates/src/hooks/useSyncedFormData.test.ts +75 -0
- package/templates/src/hooks/useSyncedFormData.ts +21 -0
- package/templates/src/hooks/useSyncedState.test.ts +119 -0
- package/templates/src/hooks/useSyncedState.ts +30 -0
- package/templates/src/index.css +1 -0
- package/templates/src/lib/api.test.ts +30 -38
- package/templates/src/lib/api.ts +1 -7
- package/templates/src/lib/config.ts +54 -4
- package/templates/src/lib/constants.ts +10 -0
- package/templates/src/lib/createSelectors.test.ts +136 -0
- package/templates/src/lib/createSelectors.ts +31 -0
- package/templates/src/lib/env.ts +36 -14
- package/templates/src/lib/index.ts +5 -2
- package/templates/src/lib/routes.ts +1 -0
- package/templates/src/lib/sentry.ts +58 -0
- package/templates/src/lib/storage.ts +6 -2
- package/templates/src/lib/supabase/client.ts +58 -0
- package/templates/src/lib/supabase/index.ts +5 -0
- package/templates/src/main.tsx +19 -31
- package/templates/src/mocks/constants.ts +31 -0
- package/templates/src/mocks/fixtures/index.ts +3 -1
- package/templates/src/mocks/fixtures/profiles.ts +55 -0
- package/templates/src/mocks/fixtures/users.ts +91 -0
- package/templates/src/mocks/handlers/index.ts +2 -1
- package/templates/src/mocks/handlers/supabase.ts +64 -0
- package/templates/src/mocks/handlers/todos.ts +1 -1
- package/templates/src/mocks/index.ts +6 -0
- package/templates/src/pages/Profile.test.tsx +263 -0
- package/templates/src/pages/Profile.tsx +171 -0
- package/templates/src/pages/index.ts +1 -0
- package/templates/src/stores/preferencesStore.ts +35 -9
- package/templates/src/test/clerkMock.tsx +137 -0
- package/templates/src/test/fetchMock.ts +58 -0
- package/templates/src/test/index.ts +51 -2
- package/templates/src/test/mocks.ts +128 -1
- package/templates/src/test/providers.tsx +10 -4
- package/templates/src/test/supabaseMock.ts +112 -0
- package/templates/src/test-setup.ts +42 -2
- package/templates/src/types/database.ts +46 -0
- package/templates/src/types/index.ts +1 -0
- package/templates/src/types/supabase.ts +167 -0
- package/templates/src/vite-env.d.ts +6 -0
- package/templates/supabase/migrations/20260104000000_create_profiles_table.sql +67 -0
- package/templates/vitest.config.ts +9 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/utils/scaffold/claude-md/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,4BAA4B,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/utils/scaffold/claude-md/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,4BAA4B,CAAC;AAiB5D,8DAA8D;AAC9D,wBAAgB,gBAAgB,CAC9B,UAAU,EAAE,SAAS,EAAE,EACvB,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC9B,MAAM,CAkBR"}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* CLAUDE.md generator - composes sections based on selected features.
|
|
3
3
|
*/
|
|
4
4
|
import { FEATURE } from '../../../constants.js';
|
|
5
|
-
import { generateHeader, generateCommandsSection, generateStructureSection, generateCodePatternsSection, generateUiSection, generateMobileSection, generateThemingSection, generateMcpSection, generateI18nSection, generateTestingSection, generateGotchasSection, } from './sections.js';
|
|
5
|
+
import { generateHeader, generateCommandsSection, generateStructureSection, generateCodePatternsSection, generateUiSection, generateMobileSection, generateThemingSection, generateAuthSection, generateDatabaseSection, generateMcpSection, generateI18nSection, generateTestingSection, generateGotchasSection, } from './sections.js';
|
|
6
6
|
/** Generates CLAUDE.md content based on selected features. */
|
|
7
7
|
export function generateClaudeMd(featureIds, projectName, scripts) {
|
|
8
8
|
const sections = [
|
|
@@ -13,6 +13,8 @@ export function generateClaudeMd(featureIds, projectName, scripts) {
|
|
|
13
13
|
featureIds.includes(FEATURE.UI) && generateUiSection(),
|
|
14
14
|
featureIds.includes(FEATURE.MOBILE) && generateMobileSection(),
|
|
15
15
|
featureIds.includes(FEATURE.THEMING) && generateThemingSection(),
|
|
16
|
+
featureIds.includes(FEATURE.AUTH) && generateAuthSection(),
|
|
17
|
+
featureIds.includes(FEATURE.DATABASE) && generateDatabaseSection(),
|
|
16
18
|
generateMcpSection(featureIds),
|
|
17
19
|
featureIds.includes(FEATURE.I18N) && generateI18nSection(),
|
|
18
20
|
featureIds.includes(FEATURE.TESTING) && generateTestingSection(),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/utils/scaffold/claude-md/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AAEhD,OAAO,EACL,cAAc,EACd,uBAAuB,EACvB,wBAAwB,EACxB,2BAA2B,EAC3B,iBAAiB,EACjB,qBAAqB,EACrB,sBAAsB,EACtB,kBAAkB,EAClB,mBAAmB,EACnB,sBAAsB,EACtB,sBAAsB,GACvB,MAAM,eAAe,CAAC;AAEvB,8DAA8D;AAC9D,MAAM,UAAU,gBAAgB,CAC9B,UAAuB,EACvB,WAAmB,EACnB,OAA+B;IAE/B,MAAM,QAAQ,GAAG;QACf,cAAc,CAAC,WAAW,CAAC;QAC3B,uBAAuB,CAAC,OAAO,CAAC;QAChC,wBAAwB,CAAC,UAAU,CAAC;QACpC,2BAA2B,CAAC,UAAU,CAAC;QACvC,UAAU,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC,IAAI,iBAAiB,EAAE;QACtD,UAAU,CAAC,QAAQ,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,qBAAqB,EAAE;QAC9D,UAAU,CAAC,QAAQ,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,sBAAsB,EAAE;QAChE,kBAAkB,CAAC,UAAU,CAAC;QAC9B,UAAU,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,mBAAmB,EAAE;QAC1D,UAAU,CAAC,QAAQ,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,sBAAsB,EAAE;QAChE,sBAAsB,CAAC,UAAU,CAAC;KACnC,CAAC;IAEF,OAAO,QAAQ,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC7C,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/utils/scaffold/claude-md/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AAEhD,OAAO,EACL,cAAc,EACd,uBAAuB,EACvB,wBAAwB,EACxB,2BAA2B,EAC3B,iBAAiB,EACjB,qBAAqB,EACrB,sBAAsB,EACtB,mBAAmB,EACnB,uBAAuB,EACvB,kBAAkB,EAClB,mBAAmB,EACnB,sBAAsB,EACtB,sBAAsB,GACvB,MAAM,eAAe,CAAC;AAEvB,8DAA8D;AAC9D,MAAM,UAAU,gBAAgB,CAC9B,UAAuB,EACvB,WAAmB,EACnB,OAA+B;IAE/B,MAAM,QAAQ,GAAG;QACf,cAAc,CAAC,WAAW,CAAC;QAC3B,uBAAuB,CAAC,OAAO,CAAC;QAChC,wBAAwB,CAAC,UAAU,CAAC;QACpC,2BAA2B,CAAC,UAAU,CAAC;QACvC,UAAU,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC,IAAI,iBAAiB,EAAE;QACtD,UAAU,CAAC,QAAQ,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,qBAAqB,EAAE;QAC9D,UAAU,CAAC,QAAQ,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,sBAAsB,EAAE;QAChE,UAAU,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,mBAAmB,EAAE;QAC1D,UAAU,CAAC,QAAQ,CAAC,OAAO,CAAC,QAAQ,CAAC,IAAI,uBAAuB,EAAE;QAClE,kBAAkB,CAAC,UAAU,CAAC;QAC9B,UAAU,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,mBAAmB,EAAE;QAC1D,UAAU,CAAC,QAAQ,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,sBAAsB,EAAE;QAChE,sBAAsB,CAAC,UAAU,CAAC;KACnC,CAAC;IAEF,OAAO,QAAQ,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC7C,CAAC"}
|
|
@@ -9,6 +9,8 @@ export declare function generateCodePatternsSection(featureIds: FeatureId[]): st
|
|
|
9
9
|
export declare function generateUiSection(): string;
|
|
10
10
|
export declare function generateMobileSection(): string;
|
|
11
11
|
export declare function generateThemingSection(): string;
|
|
12
|
+
export declare function generateAuthSection(): string;
|
|
13
|
+
export declare function generateDatabaseSection(): string;
|
|
12
14
|
export declare function generateMcpSection(featureIds: FeatureId[]): string;
|
|
13
15
|
export declare function generateI18nSection(): string;
|
|
14
16
|
export declare function generateTestingSection(): string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sections.d.ts","sourceRoot":"","sources":["../../../../src/utils/scaffold/claude-md/sections.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,4BAA4B,CAAC;
|
|
1
|
+
{"version":3,"file":"sections.d.ts","sourceRoot":"","sources":["../../../../src/utils/scaffold/claude-md/sections.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,4BAA4B,CAAC;AAyB5D,wBAAgB,cAAc,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAI1D;AAED,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,MAAM,CAe/E;AAED,wBAAgB,wBAAwB,CAAC,UAAU,EAAE,SAAS,EAAE,GAAG,MAAM,CA6CxE;AAED,wBAAgB,2BAA2B,CAAC,UAAU,EAAE,SAAS,EAAE,GAAG,MAAM,CAkB3E;AAED,wBAAgB,iBAAiB,IAAI,MAAM,CAmB1C;AAED,wBAAgB,qBAAqB,IAAI,MAAM,CAoC9C;AAED,wBAAgB,sBAAsB,IAAI,MAAM,CAuB/C;AAED,wBAAgB,mBAAmB,IAAI,MAAM,CAsC5C;AAED,wBAAgB,uBAAuB,IAAI,MAAM,CA0EhD;AAED,wBAAgB,kBAAkB,CAAC,UAAU,EAAE,SAAS,EAAE,GAAG,MAAM,CAwClE;AAED,wBAAgB,mBAAmB,IAAI,MAAM,CAY5C;AAED,wBAAgB,sBAAsB,IAAI,MAAM,CAe/C;AAED,wBAAgB,sBAAsB,CAAC,UAAU,EAAE,SAAS,EAAE,GAAG,MAAM,CA2BtE"}
|
|
@@ -19,6 +19,10 @@ const SCRIPT_DESCRIPTIONS = {
|
|
|
19
19
|
'e2e:ui': 'Playwright UI mode',
|
|
20
20
|
'i18n:extract': 'Extract translations to .po',
|
|
21
21
|
prepare: 'Initialize Husky hooks',
|
|
22
|
+
'db:types': 'Generate Supabase TypeScript types',
|
|
23
|
+
'db:push': 'Push database migrations',
|
|
24
|
+
'db:reset': 'Reset database (destructive)',
|
|
25
|
+
'db:studio': 'Open Supabase Studio',
|
|
22
26
|
};
|
|
23
27
|
export function generateHeader(projectName) {
|
|
24
28
|
return `# CLAUDE.md
|
|
@@ -42,7 +46,10 @@ ${commandLines.join('\n')}
|
|
|
42
46
|
}
|
|
43
47
|
export function generateStructureSection(featureIds) {
|
|
44
48
|
const parts = ['src/', '├── components/ # ui/ (primitives), layout/, shared/ (features)'];
|
|
45
|
-
if (featureIds.includes(FEATURE.API) ||
|
|
49
|
+
if (featureIds.includes(FEATURE.API) ||
|
|
50
|
+
featureIds.includes(FEATURE.I18N) ||
|
|
51
|
+
featureIds.includes(FEATURE.MOBILE) ||
|
|
52
|
+
featureIds.includes(FEATURE.DATABASE)) {
|
|
46
53
|
parts.push('├── contexts/ # React Context providers');
|
|
47
54
|
}
|
|
48
55
|
parts.push('├── hooks/ # Custom hooks');
|
|
@@ -53,6 +60,8 @@ export function generateStructureSection(featureIds) {
|
|
|
53
60
|
libParts.push('routes');
|
|
54
61
|
if (featureIds.includes(FEATURE.STATE))
|
|
55
62
|
libParts.push('storage');
|
|
63
|
+
if (featureIds.includes(FEATURE.DATABASE))
|
|
64
|
+
libParts.push('supabase');
|
|
56
65
|
parts.push(`├── lib/ # ${libParts.join(', ')}`);
|
|
57
66
|
if (featureIds.includes(FEATURE.ROUTING))
|
|
58
67
|
parts.push('├── pages/ # Lazy-loaded route components');
|
|
@@ -180,6 +189,120 @@ const getResolvedTheme = usePreferencesStore((s) => s.getResolvedTheme);
|
|
|
180
189
|
The \`useThemeEffect\` hook automatically applies the \`.dark\` class to the document.
|
|
181
190
|
The ThemeToggle component provides a UI for switching between light, dark, and system themes.`;
|
|
182
191
|
}
|
|
192
|
+
export function generateAuthSection() {
|
|
193
|
+
return `
|
|
194
|
+
## Authentication (Clerk)
|
|
195
|
+
|
|
196
|
+
Clerk provides authentication with modal-based sign-in.
|
|
197
|
+
|
|
198
|
+
### Setup
|
|
199
|
+
|
|
200
|
+
1. Create an account at [clerk.com](https://clerk.com)
|
|
201
|
+
2. Get your Publishable Key from the dashboard
|
|
202
|
+
3. Set \`VITE_CLERK_PUBLISHABLE_KEY\` in \`.env\`
|
|
203
|
+
|
|
204
|
+
### Usage
|
|
205
|
+
|
|
206
|
+
\`\`\`tsx
|
|
207
|
+
import { SignedIn, SignedOut, UserButton, SignInButton } from '@clerk/react-router';
|
|
208
|
+
import { ProtectedRoute } from '@/components/shared';
|
|
209
|
+
|
|
210
|
+
// Conditional rendering
|
|
211
|
+
<SignedIn><UserButton /></SignedIn>
|
|
212
|
+
<SignedOut><SignInButton mode="modal"><Button>Sign In</Button></SignInButton></SignedOut>
|
|
213
|
+
|
|
214
|
+
// Protected routes
|
|
215
|
+
<Route path="/dashboard" element={<ProtectedRoute><DashboardPage /></ProtectedRoute>} />
|
|
216
|
+
\`\`\`
|
|
217
|
+
|
|
218
|
+
### Testing
|
|
219
|
+
|
|
220
|
+
\`\`\`tsx
|
|
221
|
+
import { setMockClerkSignedIn, resetClerkMocks } from '@/test';
|
|
222
|
+
|
|
223
|
+
beforeEach(() => resetClerkMocks());
|
|
224
|
+
|
|
225
|
+
it('shows sign-in when not authenticated', () => {
|
|
226
|
+
setMockClerkSignedIn(false);
|
|
227
|
+
// ...
|
|
228
|
+
});
|
|
229
|
+
\`\`\``;
|
|
230
|
+
}
|
|
231
|
+
export function generateDatabaseSection() {
|
|
232
|
+
return `
|
|
233
|
+
## Database (Supabase)
|
|
234
|
+
|
|
235
|
+
Supabase provides PostgreSQL with Row Level Security (RLS), integrated with Clerk authentication.
|
|
236
|
+
|
|
237
|
+
### Setup
|
|
238
|
+
|
|
239
|
+
1. Create a project at [supabase.com](https://supabase.com)
|
|
240
|
+
2. Configure Clerk as third-party auth provider in Supabase Dashboard
|
|
241
|
+
3. Enable Supabase integration in Clerk Dashboard → Integrations
|
|
242
|
+
4. Set environment variables in \`.env\`:
|
|
243
|
+
- \`VITE_SUPABASE_DATABASE_URL\` - Your project URL
|
|
244
|
+
- \`VITE_SUPABASE_ANON_KEY\` - Your anon/public key
|
|
245
|
+
- \`SUPABASE_PROJECT_ID\` - Project ID for CLI (subdomain from your URL)
|
|
246
|
+
|
|
247
|
+
### Usage
|
|
248
|
+
|
|
249
|
+
\`\`\`tsx
|
|
250
|
+
import { useSupabase, useSupabaseQuery, useProfile } from '@/hooks';
|
|
251
|
+
|
|
252
|
+
// Direct client access
|
|
253
|
+
const supabase = useSupabase();
|
|
254
|
+
const { data } = await supabase.from('profiles').select();
|
|
255
|
+
|
|
256
|
+
// TanStack Query wrapper
|
|
257
|
+
const { data, isLoading } = useSupabaseQuery({
|
|
258
|
+
table: 'profiles',
|
|
259
|
+
queryKey: ['current'],
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
// Current user's profile
|
|
263
|
+
const { profile, isLoading, exists } = useProfile();
|
|
264
|
+
\`\`\`
|
|
265
|
+
|
|
266
|
+
### Profile Mutations
|
|
267
|
+
|
|
268
|
+
\`\`\`tsx
|
|
269
|
+
import { useUpsertProfile, useUpdateProfile, useDeleteProfile } from '@/hooks';
|
|
270
|
+
|
|
271
|
+
const upsertProfile = useUpsertProfile();
|
|
272
|
+
await upsertProfile.mutateAsync({ id: userId, email: 'user@example.com' });
|
|
273
|
+
\`\`\`
|
|
274
|
+
|
|
275
|
+
### Auto-Sync
|
|
276
|
+
|
|
277
|
+
\`\`\`tsx
|
|
278
|
+
import { ProfileSync } from '@/components/shared';
|
|
279
|
+
|
|
280
|
+
// Add to app to auto-sync Clerk user to Supabase
|
|
281
|
+
<ProfileSync />
|
|
282
|
+
\`\`\`
|
|
283
|
+
|
|
284
|
+
### Testing
|
|
285
|
+
|
|
286
|
+
\`\`\`tsx
|
|
287
|
+
import { render, setMockSupabaseData, setMockSupabaseError, createProfile, resetSupabaseMocks } from '@/test';
|
|
288
|
+
|
|
289
|
+
beforeEach(() => resetSupabaseMocks());
|
|
290
|
+
|
|
291
|
+
it('displays profile data', async () => {
|
|
292
|
+
setMockSupabaseData([createProfile({ full_name: 'Test User' })]);
|
|
293
|
+
render(<ProfileCard />);
|
|
294
|
+
// Assert profile is displayed
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
it('handles error', async () => {
|
|
298
|
+
setMockSupabaseError({ message: 'Failed', code: 'ERROR' });
|
|
299
|
+
render(<ProfileCard />);
|
|
300
|
+
// Assert error state
|
|
301
|
+
});
|
|
302
|
+
\`\`\`
|
|
303
|
+
|
|
304
|
+
See [docs/SUPABASE_INTEGRATION.md](docs/SUPABASE_INTEGRATION.md) for full details.`;
|
|
305
|
+
}
|
|
183
306
|
export function generateMcpSection(featureIds) {
|
|
184
307
|
let section = `
|
|
185
308
|
## MCP Servers (PREFER OVER WebSearch)
|
|
@@ -254,12 +377,19 @@ export function generateGotchasSection(featureIds) {
|
|
|
254
377
|
gotchas.push('**Conventional commits** enforced by commitlint');
|
|
255
378
|
}
|
|
256
379
|
if (featureIds.includes(FEATURE.MOBILE)) {
|
|
257
|
-
gotchas.push('**Context hooks throw** outside provider (e.g., `useMobileContext()`)');
|
|
380
|
+
gotchas.push('**Context hooks throw** outside provider (e.g., `useMobileContext()`, `useSupabase()`)');
|
|
258
381
|
}
|
|
259
382
|
gotchas.push('**Barrel exports** in each directory via `index.ts`');
|
|
260
383
|
if (featureIds.includes(FEATURE.UI)) {
|
|
261
384
|
gotchas.push('**UI components** import directly: `@/components/ui/button` (no barrel)');
|
|
262
385
|
}
|
|
386
|
+
if (featureIds.includes(FEATURE.AUTH)) {
|
|
387
|
+
gotchas.push('**Clerk auth required** - set `VITE_CLERK_PUBLISHABLE_KEY` in `.env`');
|
|
388
|
+
}
|
|
389
|
+
if (featureIds.includes(FEATURE.DATABASE)) {
|
|
390
|
+
gotchas.push('**Supabase requires Clerk** - SupabaseProvider must be inside ClerkProvider');
|
|
391
|
+
gotchas.push('**RLS policies required** - All Supabase tables should have Row Level Security enabled');
|
|
392
|
+
}
|
|
263
393
|
return `
|
|
264
394
|
## Common Gotchas
|
|
265
395
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sections.js","sourceRoot":"","sources":["../../../../src/utils/scaffold/claude-md/sections.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AAGhD,gDAAgD;AAChD,MAAM,mBAAmB,GAA2B;IAClD,GAAG,EAAE,8BAA8B;IACnC,KAAK,EAAE,uCAAuC;IAC9C,OAAO,EAAE,0BAA0B;IACnC,SAAS,EAAE,iBAAiB;IAC5B,IAAI,EAAE,cAAc;IACpB,UAAU,EAAE,iBAAiB;IAC7B,MAAM,EAAE,iBAAiB;IACzB,cAAc,EAAE,gBAAgB;IAChC,IAAI,EAAE,aAAa;IACnB,YAAY,EAAE,mBAAmB;IACjC,eAAe,EAAE,0BAA0B;IAC3C,GAAG,EAAE,gBAAgB;IACrB,QAAQ,EAAE,oBAAoB;IAC9B,cAAc,EAAE,6BAA6B;IAC7C,OAAO,EAAE,wBAAwB;
|
|
1
|
+
{"version":3,"file":"sections.js","sourceRoot":"","sources":["../../../../src/utils/scaffold/claude-md/sections.ts"],"names":[],"mappings":"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"}
|
|
@@ -49,7 +49,7 @@ export async function computeScaffold(selectedFeatures, projectName = 'my-app')
|
|
|
49
49
|
const claudeMd = generateClaudeMd(resolvedFeatures, projectName, scripts);
|
|
50
50
|
const viteEnvDts = generateViteEnvDts(resolvedFeatures);
|
|
51
51
|
const envTs = generateEnvTs(resolvedFeatures);
|
|
52
|
-
const routesTs = resolvedFeatures.includes(FEATURE.ROUTING) ? generateRoutesTs() : undefined;
|
|
52
|
+
const routesTs = resolvedFeatures.includes(FEATURE.ROUTING) ? generateRoutesTs(resolvedFeatures) : undefined;
|
|
53
53
|
return {
|
|
54
54
|
packageJson: { name: projectName, dependencies, devDependencies, scripts, engines },
|
|
55
55
|
structure,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"compute.js","sourceRoot":"","sources":["../../../src/utils/scaffold/compute.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAEvC,OAAO,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAE7C,OAAO,EAAE,sBAAsB,EAAE,MAAM,YAAY,CAAC;AACpD,OAAO,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAElD,OAAO,EAAE,0BAA0B,EAAE,iBAAiB,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAChG,OAAO,EAAE,oBAAoB,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAC3E,OAAO,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AACjD,OAAO,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAExG,KAAK,UAAU,qBAAqB;IAClC,MAAM,IAAI,GAAG,mBAAmB,CAAC,cAAc,CAAC,CAAC;IACjD,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IAC9C,OAAO,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;AAC7B,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,gBAA0B,EAC1B,cAAsB,QAAQ;IAE9B,kCAAkC;IAClC,MAAM,gBAAgB,GAAG,0BAA0B,CAAC,gBAAgB,CAAC,CAAC;IAEtE,2DAA2D;IAC3D,MAAM,CAAC,iBAAiB,EAAE,UAAU,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;QACxD,qBAAqB,EAAE;QACvB,iBAAiB,CAAC,gBAAgB,CAAC;KACpC,CAAC,CAAC;IAEH,MAAM,OAAO,GAAI,iBAAiB,CAAC,OAAkC,IAAI,EAAE,CAAC;IAC5E,MAAM,EAAE,YAAY,EAAE,eAAe,EAAE,QAAQ,EAAE,GAAG,UAAU,CAAC;IAE/D,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACxB,OAAO,CAAC,KAAK,CAAC,4BAA4B,EAAE,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;IACnE,CAAC;IAED,kBAAkB;IAClB,MAAM,OAAO,GAAG,YAAY,CAAC,gBAAgB,CAAC,CAAC;IAC/C,MAAM,IAAI,GAAG,sBAAsB,CAAC,gBAAgB,CAAC,CAAC;IACtD,MAAM,aAAa,GAAG,gBAAgB,CAAC,gBAAgB,CAAC,CAAC;IAEzD,0CAA0C;IAC1C,MAAM,WAAW,GAAG,cAAc,CAAC,gBAAgB,CAAC,CAAC;IAErD,oEAAoE;IACpE,MAAM,SAAS,GAAG,IAAI,GAAG,CAAC,WAAW,CAAC,CAAC;IACvC,MAAM,SAAS,GAAG,oBAAoB,CAAC,gBAAgB,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;IAE1F,0BAA0B;IAC1B,MAAM,QAAQ,GAAG,gBAAgB,CAAC,gBAAgB,EAAE,WAAW,EAAE,OAAO,CAAC,CAAC;IAC1E,MAAM,UAAU,GAAG,kBAAkB,CAAC,gBAAgB,CAAC,CAAC;IACxD,MAAM,KAAK,GAAG,aAAa,CAAC,gBAAgB,CAAC,CAAC;IAC9C,MAAM,QAAQ,GAAG,gBAAgB,CAAC,QAAQ,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,gBAAgB,
|
|
1
|
+
{"version":3,"file":"compute.js","sourceRoot":"","sources":["../../../src/utils/scaffold/compute.ts"],"names":[],"mappings":"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"}
|
|
@@ -4,6 +4,6 @@ export { generateClaudeMd } from './claude-md/index.js';
|
|
|
4
4
|
export declare function generateViteEnvDts(featureIds: FeatureId[]): string;
|
|
5
5
|
/** Generate env.ts content based on selected features. */
|
|
6
6
|
export declare function generateEnvTs(featureIds: FeatureId[]): string;
|
|
7
|
-
/** Generates routes.ts content. */
|
|
8
|
-
export declare function generateRoutesTs(): string;
|
|
7
|
+
/** Generates routes.ts content based on selected features. */
|
|
8
|
+
export declare function generateRoutesTs(featureIds: FeatureId[]): string;
|
|
9
9
|
//# sourceMappingURL=generators.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"generators.d.ts","sourceRoot":"","sources":["../../../src/utils/scaffold/generators.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC;AAEzD,OAAO,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAExD,iEAAiE;AACjE,wBAAgB,kBAAkB,CAAC,UAAU,EAAE,SAAS,EAAE,GAAG,MAAM,
|
|
1
|
+
{"version":3,"file":"generators.d.ts","sourceRoot":"","sources":["../../../src/utils/scaffold/generators.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC;AAEzD,OAAO,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAExD,iEAAiE;AACjE,wBAAgB,kBAAkB,CAAC,UAAU,EAAE,SAAS,EAAE,GAAG,MAAM,CAqDlE;AAED,0DAA0D;AAC1D,wBAAgB,aAAa,CAAC,UAAU,EAAE,SAAS,EAAE,GAAG,MAAM,CA2G7D;AAED,8DAA8D;AAC9D,wBAAgB,gBAAgB,CAAC,UAAU,EAAE,SAAS,EAAE,GAAG,MAAM,CAqBhE"}
|
|
@@ -27,6 +27,13 @@ export function generateViteEnvDts(featureIds) {
|
|
|
27
27
|
if (featureIds.includes(FEATURE.PERFORMANCE)) {
|
|
28
28
|
envVars.push(' readonly VITE_PERF_TEST: string;');
|
|
29
29
|
}
|
|
30
|
+
if (featureIds.includes(FEATURE.AUTH)) {
|
|
31
|
+
envVars.push(' readonly VITE_CLERK_PUBLISHABLE_KEY: string;');
|
|
32
|
+
}
|
|
33
|
+
if (featureIds.includes(FEATURE.DATABASE)) {
|
|
34
|
+
envVars.push(' readonly VITE_SUPABASE_DATABASE_URL: string;');
|
|
35
|
+
envVars.push(' readonly VITE_SUPABASE_ANON_KEY: string;');
|
|
36
|
+
}
|
|
30
37
|
// Vite built-in env vars (always required for TypeScript)
|
|
31
38
|
envVars.push(" readonly MODE: 'development' | 'production' | 'test';");
|
|
32
39
|
envVars.push(' readonly DEV: boolean;');
|
|
@@ -45,38 +52,66 @@ interface ImportMeta {
|
|
|
45
52
|
}
|
|
46
53
|
/** Generate env.ts content based on selected features. */
|
|
47
54
|
export function generateEnvTs(featureIds) {
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
];
|
|
55
|
+
// Check if we need the booleanEnv helper
|
|
56
|
+
const needsBooleanEnv = featureIds.includes(FEATURE.OBSERVABILITY) || featureIds.includes(FEATURE.PERFORMANCE);
|
|
57
|
+
const schemaFields = [' VITE_APP_NAME: z.string().min(1),', ' VITE_APP_URL: z.string().url(),'];
|
|
52
58
|
const envFields = [
|
|
53
59
|
' VITE_APP_NAME: import.meta.env.VITE_APP_NAME,',
|
|
54
60
|
' VITE_APP_URL: import.meta.env.VITE_APP_URL,',
|
|
55
61
|
];
|
|
56
62
|
if (featureIds.includes(FEATURE.API)) {
|
|
57
|
-
schemaFields.push(' VITE_API_URL: z.string().url()
|
|
63
|
+
schemaFields.push(' VITE_API_URL: z.string().url(),');
|
|
58
64
|
envFields.push(' VITE_API_URL: import.meta.env.VITE_API_URL,');
|
|
59
65
|
}
|
|
60
66
|
if (featureIds.includes(FEATURE.OBSERVABILITY)) {
|
|
61
|
-
schemaFields.push(' VITE_SENTRY_DSN: z.string().url()
|
|
62
|
-
schemaFields.push(' VITE_SENTRY_ENABLED:
|
|
67
|
+
schemaFields.push(' VITE_SENTRY_DSN: z.string().url(),');
|
|
68
|
+
schemaFields.push(' VITE_SENTRY_ENABLED: booleanEnv,');
|
|
63
69
|
envFields.push(' VITE_SENTRY_DSN: import.meta.env.VITE_SENTRY_DSN,');
|
|
64
70
|
envFields.push(' VITE_SENTRY_ENABLED: import.meta.env.VITE_SENTRY_ENABLED,');
|
|
65
71
|
}
|
|
72
|
+
if (featureIds.includes(FEATURE.AUTH)) {
|
|
73
|
+
schemaFields.push(' VITE_CLERK_PUBLISHABLE_KEY: z.string().min(1),');
|
|
74
|
+
envFields.push(' VITE_CLERK_PUBLISHABLE_KEY: import.meta.env.VITE_CLERK_PUBLISHABLE_KEY,');
|
|
75
|
+
}
|
|
76
|
+
if (featureIds.includes(FEATURE.DATABASE)) {
|
|
77
|
+
schemaFields.push(' VITE_SUPABASE_DATABASE_URL: z.string().url(),');
|
|
78
|
+
schemaFields.push(' VITE_SUPABASE_ANON_KEY: z.string().min(1),');
|
|
79
|
+
envFields.push(' VITE_SUPABASE_DATABASE_URL: import.meta.env.VITE_SUPABASE_DATABASE_URL,');
|
|
80
|
+
envFields.push(' VITE_SUPABASE_ANON_KEY: import.meta.env.VITE_SUPABASE_ANON_KEY,');
|
|
81
|
+
}
|
|
82
|
+
if (featureIds.includes(FEATURE.PERFORMANCE)) {
|
|
83
|
+
schemaFields.push(' VITE_PERF_TEST: booleanEnv,');
|
|
84
|
+
envFields.push(' VITE_PERF_TEST: import.meta.env.VITE_PERF_TEST,');
|
|
85
|
+
}
|
|
66
86
|
// Vite built-in env vars (always included)
|
|
67
|
-
schemaFields.push(" MODE: z.enum(['development', 'production', 'test'])
|
|
68
|
-
schemaFields.push(' DEV: z.boolean()
|
|
69
|
-
schemaFields.push(' PROD: z.boolean()
|
|
87
|
+
schemaFields.push(" MODE: z.enum(['development', 'production', 'test']),");
|
|
88
|
+
schemaFields.push(' DEV: z.boolean(),');
|
|
89
|
+
schemaFields.push(' PROD: z.boolean(),');
|
|
70
90
|
envFields.push(' MODE: import.meta.env.MODE,');
|
|
71
91
|
envFields.push(' DEV: import.meta.env.DEV,');
|
|
72
92
|
envFields.push(' PROD: import.meta.env.PROD,');
|
|
93
|
+
// Build the booleanEnv helper if needed
|
|
94
|
+
const booleanEnvHelper = needsBooleanEnv
|
|
95
|
+
? `
|
|
96
|
+
/**
|
|
97
|
+
* Transforms string env var to boolean.
|
|
98
|
+
* - 'true', '1' → true
|
|
99
|
+
* - 'false', '0' → false
|
|
100
|
+
*/
|
|
101
|
+
const booleanEnv = z.enum(['true', 'false', '1', '0']).transform((val) => val === 'true' || val === '1');
|
|
102
|
+
|
|
103
|
+
`
|
|
104
|
+
: '';
|
|
73
105
|
return `/**
|
|
74
106
|
* Environment variable validation using Zod.
|
|
75
107
|
* Validates at runtime to catch missing/invalid env vars early.
|
|
108
|
+
*
|
|
109
|
+
* All env vars are REQUIRED. The MCP scaffold tool strips unused vars
|
|
110
|
+
* when scaffolding builds without certain features.
|
|
76
111
|
*/
|
|
77
112
|
|
|
78
113
|
import { z } from 'zod';
|
|
79
|
-
|
|
114
|
+
${booleanEnvHelper}
|
|
80
115
|
const envSchema = z.object({
|
|
81
116
|
${schemaFields.join('\n')}
|
|
82
117
|
});
|
|
@@ -85,7 +120,7 @@ export type Env = z.infer<typeof envSchema>;
|
|
|
85
120
|
|
|
86
121
|
/**
|
|
87
122
|
* Validate environment variables and return typed env object.
|
|
88
|
-
* Throws if
|
|
123
|
+
* Throws if any required env var is missing or invalid.
|
|
89
124
|
*/
|
|
90
125
|
export function validateEnv(): Env {
|
|
91
126
|
const env = {
|
|
@@ -95,15 +130,17 @@ ${envFields.join('\n')}
|
|
|
95
130
|
const result = envSchema.safeParse(env);
|
|
96
131
|
|
|
97
132
|
if (!result.success) {
|
|
98
|
-
const errors = result.error.
|
|
99
|
-
|
|
133
|
+
const errors = result.error.flatten();
|
|
134
|
+
const fieldErrors = Object.entries(errors.fieldErrors)
|
|
135
|
+
.map(([key, msgs]) => \`\${key}: \${(msgs as string[]).join(', ')}\`)
|
|
136
|
+
.join('; ');
|
|
137
|
+
const formErrors = errors.formErrors.join('; ');
|
|
138
|
+
const allErrors = [fieldErrors, formErrors].filter(Boolean).join('; ');
|
|
100
139
|
|
|
101
|
-
|
|
102
|
-
throw new Error('Invalid environment configuration');
|
|
103
|
-
}
|
|
140
|
+
throw new Error(\`Environment validation failed: \${allErrors}\`);
|
|
104
141
|
}
|
|
105
142
|
|
|
106
|
-
return result.data
|
|
143
|
+
return result.data;
|
|
107
144
|
}
|
|
108
145
|
|
|
109
146
|
/**
|
|
@@ -113,16 +150,21 @@ ${envFields.join('\n')}
|
|
|
113
150
|
export const env = validateEnv();
|
|
114
151
|
`;
|
|
115
152
|
}
|
|
116
|
-
/** Generates routes.ts content. */
|
|
117
|
-
export function generateRoutesTs() {
|
|
153
|
+
/** Generates routes.ts content based on selected features. */
|
|
154
|
+
export function generateRoutesTs(featureIds) {
|
|
155
|
+
const routes = [" HOME: '/',"];
|
|
156
|
+
// Add PROFILE route when database feature is selected
|
|
157
|
+
if (featureIds.includes(FEATURE.DATABASE)) {
|
|
158
|
+
routes.push(" PROFILE: '/profile',");
|
|
159
|
+
}
|
|
160
|
+
routes.push(" NOT_FOUND: '*',");
|
|
118
161
|
return `/**
|
|
119
162
|
* Typed route constants.
|
|
120
163
|
* Use these instead of hardcoded strings for type-safe navigation.
|
|
121
164
|
*/
|
|
122
165
|
|
|
123
166
|
export const ROUTES = {
|
|
124
|
-
|
|
125
|
-
NOT_FOUND: '*',
|
|
167
|
+
${routes.join('\n')}
|
|
126
168
|
} as const;
|
|
127
169
|
|
|
128
170
|
export type AppRoute = (typeof ROUTES)[keyof typeof ROUTES];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"generators.js","sourceRoot":"","sources":["../../../src/utils/scaffold/generators.ts"],"names":[],"mappings":"AAAA,qCAAqC;AAErC;;;GAGG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAG7C,OAAO,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAExD,iEAAiE;AACjE,MAAM,UAAU,kBAAkB,CAAC,UAAuB;IACxD,MAAM,QAAQ,GAAa,EAAE,CAAC;IAE9B,wCAAwC;IACxC,IAAI,UAAU,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;QACtC,QAAQ,CAAC,IAAI,CAAC;;;EAGhB,CAAC,CAAC;IACF,CAAC;IAED,mCAAmC;IACnC,MAAM,OAAO,GAAa,CAAC,mCAAmC,EAAE,kCAAkC,CAAC,CAAC;IAEpG,IAAI,UAAU,CAAC,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;QACrC,OAAO,CAAC,IAAI,CAAC,kCAAkC,CAAC,CAAC;IACnD,CAAC;IAED,IAAI,UAAU,CAAC,QAAQ,CAAC,OAAO,CAAC,aAAa,CAAC,EAAE,CAAC;QAC/C,OAAO,CAAC,IAAI,CAAC,qCAAqC,CAAC,CAAC;QACpD,OAAO,CAAC,IAAI,CAAC,yCAAyC,CAAC,CAAC;IAC1D,CAAC;IAED,IAAI,UAAU,CAAC,QAAQ,CAAC,OAAO,CAAC,WAAW,CAAC,EAAE,CAAC;QAC7C,OAAO,CAAC,IAAI,CAAC,oCAAoC,CAAC,CAAC;IACrD,CAAC;IAED,0DAA0D;IAC1D,OAAO,CAAC,IAAI,CAAC,yDAAyD,CAAC,CAAC;IACxE,OAAO,CAAC,IAAI,CAAC,0BAA0B,CAAC,CAAC;IACzC,OAAO,CAAC,IAAI,CAAC,2BAA2B,CAAC,CAAC;IAE1C,QAAQ,CAAC,IAAI,CAAC;;;EAGd,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC;;;;;;EAMlB,CAAC,CAAC;IAEF,OAAO,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,IAAI,CAAC;AACtC,CAAC;AAED,0DAA0D;AAC1D,MAAM,UAAU,aAAa,CAAC,UAAuB;IACnD,MAAM,
|
|
1
|
+
{"version":3,"file":"generators.js","sourceRoot":"","sources":["../../../src/utils/scaffold/generators.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
package/templates/.env.example
CHANGED
|
@@ -1,21 +1,55 @@
|
|
|
1
|
-
#
|
|
1
|
+
# ─────────────────────────────────────────────────────────────
|
|
2
|
+
# Application (required)
|
|
3
|
+
# ─────────────────────────────────────────────────────────────
|
|
2
4
|
VITE_APP_NAME="My App"
|
|
3
5
|
VITE_APP_URL=http://localhost:5173
|
|
4
|
-
|
|
5
|
-
# Optional: Base URL for deployment subdirectory
|
|
6
|
-
# VITE_BASE_URL=/
|
|
6
|
+
VITE_API_URL=https://jsonplaceholder.typicode.com
|
|
7
7
|
|
|
8
8
|
# ─────────────────────────────────────────────────────────────
|
|
9
|
-
# Sentry Error Tracking (
|
|
9
|
+
# Sentry Error Tracking (required)
|
|
10
10
|
# ─────────────────────────────────────────────────────────────
|
|
11
|
-
# Set to 'false' to disable Sentry entirely (opt-out)
|
|
12
|
-
# Enabled by default when DSN is provided
|
|
13
|
-
# VITE_SENTRY_ENABLED=true
|
|
14
|
-
|
|
15
11
|
# Runtime DSN for error reporting (client-side, safe to expose)
|
|
16
|
-
#
|
|
12
|
+
# Get from: https://sentry.io/settings/projects/YOUR_PROJECT/keys/
|
|
13
|
+
VITE_SENTRY_DSN=https://xxxxx@o123456.ingest.sentry.io/789
|
|
14
|
+
VITE_SENTRY_ENABLED=true
|
|
17
15
|
|
|
18
16
|
# CI/CD secrets for source map upload (set in GitHub Secrets):
|
|
19
17
|
# - SENTRY_AUTH_TOKEN: API token for uploading source maps
|
|
20
18
|
# - SENTRY_ORG: Sentry organization slug
|
|
21
19
|
# - SENTRY_PROJECT: Sentry project slug
|
|
20
|
+
|
|
21
|
+
# ─────────────────────────────────────────────────────────────
|
|
22
|
+
# Clerk Authentication (required)
|
|
23
|
+
# ─────────────────────────────────────────────────────────────
|
|
24
|
+
# Get your Publishable Key from: https://dashboard.clerk.com/~/api-keys
|
|
25
|
+
VITE_CLERK_PUBLISHABLE_KEY=pk_test_xxxxx
|
|
26
|
+
|
|
27
|
+
# ─────────────────────────────────────────────────────────────
|
|
28
|
+
# Supabase Database (required)
|
|
29
|
+
# ─────────────────────────────────────────────────────────────
|
|
30
|
+
# Get your Project URL and API Key from:
|
|
31
|
+
# https://supabase.com/dashboard/project/YOUR_PROJECT/settings/api
|
|
32
|
+
#
|
|
33
|
+
# For Netlify deployments, these can be auto-configured via the
|
|
34
|
+
# Netlify Supabase extension: Extensions > Supabase > Connect
|
|
35
|
+
VITE_SUPABASE_DATABASE_URL=https://your-project.supabase.co
|
|
36
|
+
VITE_SUPABASE_ANON_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
|
|
37
|
+
|
|
38
|
+
# ─────────────────────────────────────────────────────────────
|
|
39
|
+
# Performance Testing (required)
|
|
40
|
+
# ─────────────────────────────────────────────────────────────
|
|
41
|
+
VITE_PERF_TEST=false
|
|
42
|
+
|
|
43
|
+
# Supabase CLI (required for npm run db:types)
|
|
44
|
+
# Project ID is the subdomain from your Supabase URL (e.g., abc123xyz from https://abc123xyz.supabase.co)
|
|
45
|
+
SUPABASE_PROJECT_ID=your-project-id
|
|
46
|
+
|
|
47
|
+
# ─────────────────────────────────────────────────────────────
|
|
48
|
+
# E2E Testing (optional - for authenticated Playwright tests)
|
|
49
|
+
# ─────────────────────────────────────────────────────────────
|
|
50
|
+
# Create a test user in Clerk and provide credentials here
|
|
51
|
+
# Required for running: npx playwright test --project=authenticated
|
|
52
|
+
# E2E_CLERK_USER_USERNAME=test@example.com
|
|
53
|
+
# E2E_CLERK_USER_PASSWORD=your-test-password
|
|
54
|
+
# CLERK_SECRET_KEY=sk_test_xxxxx
|
|
55
|
+
|
|
@@ -84,14 +84,19 @@ jobs:
|
|
|
84
84
|
fail-fast: false
|
|
85
85
|
matrix:
|
|
86
86
|
include:
|
|
87
|
-
- type:
|
|
88
|
-
project:
|
|
89
|
-
command: npx playwright test --project=
|
|
87
|
+
- type: desktop
|
|
88
|
+
project: desktop
|
|
89
|
+
command: npx playwright test --project=desktop
|
|
90
90
|
report-name: playwright-report
|
|
91
91
|
upload-on: failure
|
|
92
|
+
- type: mobile
|
|
93
|
+
project: mobile
|
|
94
|
+
command: npx playwright test --project=mobile
|
|
95
|
+
report-name: playwright-report-mobile
|
|
96
|
+
upload-on: failure
|
|
92
97
|
- type: performance
|
|
93
98
|
project: performance
|
|
94
|
-
command: PERF_TEST=true npx playwright test --project=performance
|
|
99
|
+
command: PERF_TEST=true PERF_CI=true npx playwright test --project=performance
|
|
95
100
|
report-name: performance-report
|
|
96
101
|
upload-on: always
|
|
97
102
|
steps:
|
|
@@ -101,6 +106,9 @@ jobs:
|
|
|
101
106
|
with:
|
|
102
107
|
name: dist
|
|
103
108
|
path: dist/
|
|
109
|
+
- name: Rebuild with performance tracking
|
|
110
|
+
if: matrix.type == 'performance'
|
|
111
|
+
run: VITE_PERF_TEST=true npm run build
|
|
104
112
|
- name: Get Playwright version
|
|
105
113
|
id: playwright-version
|
|
106
114
|
run: echo "version=$(npm ls @playwright/test --json | jq -r '.dependencies["@playwright/test"].version')" >> $GITHUB_OUTPUT
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
name: Deploy
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main, master]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [main, master]
|
|
8
|
+
workflow_dispatch:
|
|
9
|
+
|
|
10
|
+
permissions:
|
|
11
|
+
contents: read
|
|
12
|
+
pull-requests: write
|
|
13
|
+
deployments: write
|
|
14
|
+
|
|
15
|
+
concurrency:
|
|
16
|
+
group: deploy-${{ github.ref }}
|
|
17
|
+
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
|
|
18
|
+
|
|
19
|
+
jobs:
|
|
20
|
+
deploy:
|
|
21
|
+
name: Deploy
|
|
22
|
+
runs-on: ubuntu-latest
|
|
23
|
+
timeout-minutes: 15
|
|
24
|
+
steps:
|
|
25
|
+
- uses: actions/checkout@v6
|
|
26
|
+
|
|
27
|
+
- uses: ./.github/actions/setup-node-deps
|
|
28
|
+
|
|
29
|
+
- name: Build
|
|
30
|
+
run: npm run build
|
|
31
|
+
|
|
32
|
+
- name: Deploy Preview
|
|
33
|
+
if: github.event_name == 'pull_request'
|
|
34
|
+
uses: nwtgck/actions-netlify@v3
|
|
35
|
+
with:
|
|
36
|
+
publish-dir: './dist'
|
|
37
|
+
production-deploy: false
|
|
38
|
+
github-token: ${{ secrets.GITHUB_TOKEN }}
|
|
39
|
+
deploy-message: 'Preview deploy from PR #${{ github.event.number }}'
|
|
40
|
+
enable-pull-request-comment: true
|
|
41
|
+
enable-commit-comment: false
|
|
42
|
+
overwrites-pull-request-comment: true
|
|
43
|
+
alias: pr-${{ github.event.number }}
|
|
44
|
+
env:
|
|
45
|
+
NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }}
|
|
46
|
+
NETLIFY_SITE_ID: ${{ secrets.NETLIFY_SITE_ID }}
|
|
47
|
+
|
|
48
|
+
- name: Deploy Production
|
|
49
|
+
if: github.event_name == 'push' || github.event_name == 'workflow_dispatch'
|
|
50
|
+
uses: nwtgck/actions-netlify@v3
|
|
51
|
+
with:
|
|
52
|
+
publish-dir: './dist'
|
|
53
|
+
production-deploy: true
|
|
54
|
+
github-token: ${{ secrets.GITHUB_TOKEN }}
|
|
55
|
+
deploy-message: 'Production deploy from ${{ github.sha }}'
|
|
56
|
+
enable-commit-comment: true
|
|
57
|
+
env:
|
|
58
|
+
NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }}
|
|
59
|
+
NETLIFY_SITE_ID: ${{ secrets.NETLIFY_SITE_ID }}
|