create-velox-app 0.7.0 → 0.7.2

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/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # create-velox-app
2
2
 
3
+ ## 0.7.2
4
+
5
+ ### Patch Changes
6
+
7
+ - chore(auth,core,create,cli,client,orm,mcp,router,validation,web): simplify code for clarity and maintainability
8
+
9
+ ## 0.7.1
10
+
11
+ ### Patch Changes
12
+
13
+ - security audit, bumps dependency packages
14
+
3
15
  ## 0.7.0
4
16
 
5
17
  ### Minor Changes
@@ -11,72 +11,17 @@ import { compileTemplate } from './compiler.js';
11
11
  import { AUTH_CONFIG, applyDatabaseDependencies } from './placeholders.js';
12
12
  import { generateRootFiles, generateWebBaseFiles, generateWebStyleFiles } from './shared/index.js';
13
13
  // ============================================================================
14
- // API Template Compilation
14
+ // Helpers
15
15
  // ============================================================================
16
+ /** Shorthand: compile a static auth API template (no user-specific config needed) */
17
+ function auth(sourcePath) {
18
+ return compileTemplate(sourcePath, AUTH_CONFIG);
19
+ }
20
+ /** Compile API package.json and swap database dependencies based on config */
16
21
  function generateApiPackageJson(config) {
17
22
  const content = compileTemplate('api/package.auth.json', config);
18
23
  return applyDatabaseDependencies(content, config);
19
24
  }
20
- function generateApiTsConfig() {
21
- return compileTemplate('api/tsconfig.json', AUTH_CONFIG);
22
- }
23
- function generateApiTsupConfig() {
24
- return compileTemplate('api/tsup.config.ts', AUTH_CONFIG);
25
- }
26
- function generateEnvExample(config) {
27
- return compileTemplate('api/env.auth', config);
28
- }
29
- function generatePrismaSchema(config) {
30
- return compileTemplate('api/prisma/schema.auth.prisma', config);
31
- }
32
- function generatePrismaConfig() {
33
- return compileTemplate('api/prisma.config.ts', AUTH_CONFIG);
34
- }
35
- function generateAuthConfig() {
36
- return compileTemplate('api/config/auth.ts', AUTH_CONFIG);
37
- }
38
- function generateConfigApp(config) {
39
- return compileTemplate('api/config/app.ts', config);
40
- }
41
- function generateAuthProcedures() {
42
- return compileTemplate('api/procedures/auth.ts', AUTH_CONFIG);
43
- }
44
- function generateUserProceduresWithAuth() {
45
- return compileTemplate('api/procedures/users.auth.ts', AUTH_CONFIG);
46
- }
47
- function generateIndexTs() {
48
- return compileTemplate('api/index.auth.ts', AUTH_CONFIG);
49
- }
50
- function generateRouterTs() {
51
- return compileTemplate('api/router.auth.ts', AUTH_CONFIG);
52
- }
53
- function generateConfigDatabase(config) {
54
- return compileTemplate('api/config/database.ts', config);
55
- }
56
- function generateProfileProcedures() {
57
- return compileTemplate('api/procedures/profiles.auth.ts', AUTH_CONFIG);
58
- }
59
- function generateHealthProcedures() {
60
- return compileTemplate('api/procedures/health.ts', AUTH_CONFIG);
61
- }
62
- function generateUserSchema() {
63
- return compileTemplate('api/schemas/user.ts', AUTH_CONFIG);
64
- }
65
- function generateAuthSchema() {
66
- return compileTemplate('api/schemas/auth.ts', AUTH_CONFIG);
67
- }
68
- function generateHealthSchema() {
69
- return compileTemplate('api/schemas/health.ts', AUTH_CONFIG);
70
- }
71
- function generateApiTypesTs() {
72
- return compileTemplate('api/types.auth.ts', AUTH_CONFIG);
73
- }
74
- function generateAuthUtils() {
75
- return compileTemplate('api/utils/auth.ts', AUTH_CONFIG);
76
- }
77
- function generateDockerCompose(config) {
78
- return compileTemplate('api/docker-compose.yml', config);
79
- }
80
25
  // ============================================================================
81
26
  // Auth Template Generator
82
27
  // ============================================================================
@@ -84,34 +29,43 @@ export function generateAuthTemplate(config) {
84
29
  const files = [
85
30
  // API package files
86
31
  { path: 'apps/api/package.json', content: generateApiPackageJson(config) },
87
- { path: 'apps/api/tsconfig.json', content: generateApiTsConfig() },
88
- { path: 'apps/api/tsup.config.ts', content: generateApiTsupConfig() },
89
- { path: 'apps/api/prisma.config.ts', content: generatePrismaConfig() },
90
- { path: 'apps/api/.env.example', content: generateEnvExample(config) },
91
- { path: 'apps/api/.env', content: generateEnvExample(config) },
32
+ { path: 'apps/api/tsconfig.json', content: auth('api/tsconfig.json') },
33
+ { path: 'apps/api/tsup.config.ts', content: auth('api/tsup.config.ts') },
34
+ { path: 'apps/api/prisma.config.ts', content: auth('api/prisma.config.ts') },
35
+ { path: 'apps/api/.env.example', content: compileTemplate('api/env.auth', config) },
36
+ { path: 'apps/api/.env', content: compileTemplate('api/env.auth', config) },
92
37
  // Prisma
93
- { path: 'apps/api/prisma/schema.prisma', content: generatePrismaSchema(config) },
38
+ {
39
+ path: 'apps/api/prisma/schema.prisma',
40
+ content: compileTemplate('api/prisma/schema.auth.prisma', config),
41
+ },
94
42
  // API Source files
95
- { path: 'apps/api/src/router.ts', content: generateRouterTs() },
96
- { path: 'apps/api/src/index.ts', content: generateIndexTs() },
97
- { path: 'apps/api/src/config/app.ts', content: generateConfigApp(config) },
98
- { path: 'apps/api/src/config/auth.ts', content: generateAuthConfig() },
99
- { path: 'apps/api/src/config/database.ts', content: generateConfigDatabase(config) },
100
- { path: 'apps/api/src/procedures/health.ts', content: generateHealthProcedures() },
101
- { path: 'apps/api/src/procedures/auth.ts', content: generateAuthProcedures() },
102
- { path: 'apps/api/src/procedures/users.ts', content: generateUserProceduresWithAuth() },
103
- { path: 'apps/api/src/procedures/profiles.ts', content: generateProfileProcedures() },
104
- { path: 'apps/api/src/schemas/user.ts', content: generateUserSchema() },
105
- { path: 'apps/api/src/schemas/auth.ts', content: generateAuthSchema() },
106
- { path: 'apps/api/src/schemas/health.ts', content: generateHealthSchema() },
107
- { path: 'apps/api/src/types.ts', content: generateApiTypesTs() },
108
- { path: 'apps/api/src/utils/auth.ts', content: generateAuthUtils() },
43
+ { path: 'apps/api/src/router.ts', content: auth('api/router.auth.ts') },
44
+ { path: 'apps/api/src/index.ts', content: auth('api/index.auth.ts') },
45
+ { path: 'apps/api/src/config/app.ts', content: compileTemplate('api/config/app.ts', config) },
46
+ { path: 'apps/api/src/config/auth.ts', content: auth('api/config/auth.ts') },
47
+ {
48
+ path: 'apps/api/src/config/database.ts',
49
+ content: compileTemplate('api/config/database.ts', config),
50
+ },
51
+ { path: 'apps/api/src/procedures/health.ts', content: auth('api/procedures/health.ts') },
52
+ { path: 'apps/api/src/procedures/auth.ts', content: auth('api/procedures/auth.ts') },
53
+ { path: 'apps/api/src/procedures/users.ts', content: auth('api/procedures/users.auth.ts') },
54
+ {
55
+ path: 'apps/api/src/procedures/profiles.ts',
56
+ content: auth('api/procedures/profiles.auth.ts'),
57
+ },
58
+ { path: 'apps/api/src/schemas/user.ts', content: auth('api/schemas/user.ts') },
59
+ { path: 'apps/api/src/schemas/auth.ts', content: auth('api/schemas/auth.ts') },
60
+ { path: 'apps/api/src/schemas/health.ts', content: auth('api/schemas/health.ts') },
61
+ { path: 'apps/api/src/types.ts', content: auth('api/types.auth.ts') },
62
+ { path: 'apps/api/src/utils/auth.ts', content: auth('api/utils/auth.ts') },
109
63
  ];
110
64
  // Add docker-compose for PostgreSQL
111
65
  if (config.database === 'postgresql') {
112
66
  files.push({
113
67
  path: 'apps/api/docker-compose.yml',
114
- content: generateDockerCompose(config),
68
+ content: compileTemplate('api/docker-compose.yml', config),
115
69
  });
116
70
  }
117
71
  // Add root workspace files
@@ -65,3 +65,14 @@ export declare function sourceFileExists(relativePath: string): boolean;
65
65
  * @returns Array of file paths relative to source/
66
66
  */
67
67
  export declare function listSourceFiles(relativeDir: string): string[];
68
+ /**
69
+ * Read a shared script from templates/source/shared/scripts/.
70
+ *
71
+ * Used by RSC templates to include utility scripts (e.g., check-client-imports.sh).
72
+ * Resolves paths for both production (dist/) and development (src/) layouts.
73
+ *
74
+ * @param scriptName - Script filename (e.g., 'check-client-imports.sh')
75
+ * @returns Script file content as string
76
+ * @throws Error if script not found in any expected location
77
+ */
78
+ export declare function readSharedScript(scriptName: string): string;
@@ -146,3 +146,24 @@ export function listSourceFiles(relativeDir) {
146
146
  walkDir(dirPath, relativeDir);
147
147
  return files;
148
148
  }
149
+ // ============================================================================
150
+ // Shared Script Reading
151
+ // ============================================================================
152
+ /**
153
+ * Read a shared script from templates/source/shared/scripts/.
154
+ *
155
+ * Used by RSC templates to include utility scripts (e.g., check-client-imports.sh).
156
+ * Resolves paths for both production (dist/) and development (src/) layouts.
157
+ *
158
+ * @param scriptName - Script filename (e.g., 'check-client-imports.sh')
159
+ * @returns Script file content as string
160
+ * @throws Error if script not found in any expected location
161
+ */
162
+ export function readSharedScript(scriptName) {
163
+ const sourceDir = getSourceDir();
164
+ const scriptPath = path.join(sourceDir, 'shared', 'scripts', scriptName);
165
+ if (fs.existsSync(scriptPath)) {
166
+ return fs.readFileSync(scriptPath, 'utf-8');
167
+ }
168
+ throw new Error(`Shared script not found: ${scriptName}. Checked:\n - ${scriptPath}`);
169
+ }
@@ -35,26 +35,13 @@ export declare const PLACEHOLDERS: {
35
35
  /** Display-friendly database name (SQLite, PostgreSQL) */
36
36
  readonly DATABASE_DISPLAY: "__DATABASE_DISPLAY__";
37
37
  };
38
- /**
39
- * Default template configuration for templates that don't need real values.
40
- * Used when compiling templates that only need placeholder markers stripped,
41
- * not actual user-provided values (e.g., shared templates, route files).
42
- */
38
+ /** Default (SPA) template configuration for shared/generic templates */
43
39
  export declare const DEFAULT_CONFIG: TemplateConfig;
44
- /**
45
- * Auth template configuration for auth-specific templates.
46
- * Same as DEFAULT_CONFIG but with template set to 'auth'.
47
- */
40
+ /** Auth template configuration for auth-specific conditional processing */
48
41
  export declare const AUTH_CONFIG: TemplateConfig;
49
- /**
50
- * tRPC template configuration for tRPC-specific templates.
51
- * Same as DEFAULT_CONFIG but with template set to 'trpc'.
52
- */
42
+ /** tRPC template configuration for tRPC-specific conditional processing */
53
43
  export declare const TRPC_CONFIG: TemplateConfig;
54
- /**
55
- * RSC template configuration for RSC + Vinxi projects.
56
- * Uses React Server Components with file-based routing.
57
- */
44
+ /** RSC template configuration for RSC-specific conditional processing */
58
45
  export declare const RSC_CONFIG: TemplateConfig;
59
46
  /**
60
47
  * Apply placeholder replacements to template content.
@@ -39,46 +39,28 @@ export const PLACEHOLDERS = {
39
39
  DATABASE_DISPLAY: '__DATABASE_DISPLAY__',
40
40
  };
41
41
  /**
42
- * Default template configuration for templates that don't need real values.
43
- * Used when compiling templates that only need placeholder markers stripped,
44
- * not actual user-provided values (e.g., shared templates, route files).
45
- */
46
- export const DEFAULT_CONFIG = {
47
- projectName: '',
48
- packageManager: 'pnpm',
49
- template: 'spa',
50
- database: 'sqlite',
51
- };
52
- /**
53
- * Auth template configuration for auth-specific templates.
54
- * Same as DEFAULT_CONFIG but with template set to 'auth'.
55
- */
56
- export const AUTH_CONFIG = {
57
- projectName: '',
58
- packageManager: 'pnpm',
59
- template: 'auth',
60
- database: 'sqlite',
61
- };
62
- /**
63
- * tRPC template configuration for tRPC-specific templates.
64
- * Same as DEFAULT_CONFIG but with template set to 'trpc'.
65
- */
66
- export const TRPC_CONFIG = {
67
- projectName: '',
68
- packageManager: 'pnpm',
69
- template: 'trpc',
70
- database: 'sqlite',
71
- };
72
- /**
73
- * RSC template configuration for RSC + Vinxi projects.
74
- * Uses React Server Components with file-based routing.
42
+ * Create a minimal template configuration for a given template type.
43
+ *
44
+ * Used when compiling templates that only need conditional processing
45
+ * (e.g., keeping/removing @if auth blocks) and placeholder markers stripped,
46
+ * not actual user-provided values.
75
47
  */
76
- export const RSC_CONFIG = {
77
- projectName: '',
78
- packageManager: 'pnpm',
79
- template: 'rsc',
80
- database: 'sqlite',
81
- };
48
+ function createTemplateConfig(template) {
49
+ return {
50
+ projectName: '',
51
+ packageManager: 'pnpm',
52
+ template,
53
+ database: 'sqlite',
54
+ };
55
+ }
56
+ /** Default (SPA) template configuration for shared/generic templates */
57
+ export const DEFAULT_CONFIG = createTemplateConfig('spa');
58
+ /** Auth template configuration for auth-specific conditional processing */
59
+ export const AUTH_CONFIG = createTemplateConfig('auth');
60
+ /** tRPC template configuration for tRPC-specific conditional processing */
61
+ export const TRPC_CONFIG = createTemplateConfig('trpc');
62
+ /** RSC template configuration for RSC-specific conditional processing */
63
+ export const RSC_CONFIG = createTemplateConfig('rsc');
82
64
  // ============================================================================
83
65
  // Placeholder Replacement
84
66
  // ============================================================================
@@ -8,143 +8,19 @@
8
8
  * - File-based routing with auth pages
9
9
  * - Embedded Fastify API at /api/*
10
10
  */
11
- import fs from 'node:fs';
12
- import path from 'node:path';
13
- import { fileURLToPath } from 'node:url';
14
- import { compileTemplate } from './compiler.js';
11
+ import { compileTemplate, readSharedScript } from './compiler.js';
15
12
  import { applyDatabaseDependencies, RSC_CONFIG } from './placeholders.js';
16
- const __filename = fileURLToPath(import.meta.url);
17
- const __dirname = path.dirname(__filename);
18
- /**
19
- * Read shared script from templates/source/shared/scripts/
20
- */
21
- function readSharedScript(scriptName) {
22
- // Try dist path first (production), then src path (development)
23
- const distPath = path.join(__dirname, '..', '..', 'src', 'templates', 'source', 'shared', 'scripts', scriptName);
24
- const srcPath = path.join(__dirname, 'source', 'shared', 'scripts', scriptName);
25
- for (const scriptPath of [distPath, srcPath]) {
26
- if (fs.existsSync(scriptPath)) {
27
- return fs.readFileSync(scriptPath, 'utf-8');
28
- }
29
- }
30
- throw new Error(`Shared script not found: ${scriptName}. Checked:\n - ${distPath}\n - ${srcPath}`);
31
- }
32
13
  // ============================================================================
33
- // Template Compilation
14
+ // Helpers (only for functions with non-trivial logic)
34
15
  // ============================================================================
16
+ /** Compile package.json and swap database dependencies based on config */
35
17
  function generatePackageJson(config) {
36
18
  const content = compileTemplate('rsc-auth/package.json', config);
37
19
  return applyDatabaseDependencies(content, config);
38
20
  }
39
- function generateAppConfig() {
40
- return compileTemplate('rsc-auth/app.config.ts', RSC_CONFIG);
41
- }
42
- function generateTsConfig() {
43
- return compileTemplate('rsc-auth/tsconfig.json', RSC_CONFIG);
44
- }
45
- function generateEnvExample(config) {
46
- return compileTemplate('rsc-auth/env.example', config);
47
- }
48
- function generateGitignore() {
49
- return compileTemplate('rsc-auth/gitignore', RSC_CONFIG);
50
- }
51
- function generateClaudeMd(config) {
52
- return compileTemplate('rsc-auth/CLAUDE.md', config);
53
- }
54
- // Prisma
55
- function generatePrismaSchema(config) {
56
- return compileTemplate('rsc-auth/prisma/schema.prisma', config);
57
- }
58
- function generatePrismaConfig() {
59
- return compileTemplate('rsc-auth/prisma.config.ts', RSC_CONFIG);
60
- }
61
- // App layer (RSC) - Pages
62
- function generateHomePage() {
63
- return compileTemplate('rsc-auth/app/pages/index.tsx', RSC_CONFIG);
64
- }
65
- function generateUsersPage() {
66
- return compileTemplate('rsc-auth/app/pages/users.tsx', RSC_CONFIG);
67
- }
68
- function generateNotFoundPage() {
69
- return compileTemplate('rsc-auth/app/pages/_not-found.tsx', RSC_CONFIG);
70
- }
71
- // Auth pages
72
- function generateLoginPage() {
73
- return compileTemplate('rsc-auth/app/pages/auth/login.tsx', RSC_CONFIG);
74
- }
75
- function generateRegisterPage() {
76
- return compileTemplate('rsc-auth/app/pages/auth/register.tsx', RSC_CONFIG);
77
- }
78
- // Dashboard pages
79
- function generateDashboardPage() {
80
- return compileTemplate('rsc-auth/app/pages/dashboard/index.tsx', RSC_CONFIG);
81
- }
82
- // Layouts
83
- function generateRootLayout() {
84
- return compileTemplate('rsc-auth/app/layouts/root.tsx', RSC_CONFIG);
85
- }
86
- function generateMarketingLayout() {
87
- return compileTemplate('rsc-auth/app/layouts/marketing.tsx', RSC_CONFIG);
88
- }
89
- function generateMinimalLayout() {
90
- return compileTemplate('rsc-auth/app/layouts/minimal.tsx', RSC_CONFIG);
91
- }
92
- function generateMinimalContentLayout() {
93
- return compileTemplate('rsc-auth/app/layouts/minimal-content.tsx', RSC_CONFIG);
94
- }
95
- function generateDashboardLayout() {
96
- return compileTemplate('rsc-auth/app/layouts/dashboard.tsx', RSC_CONFIG);
97
- }
98
- // Actions
99
- function generateUserActions() {
100
- return compileTemplate('rsc-auth/app/actions/users.ts', RSC_CONFIG);
101
- }
102
- function generateAuthActions() {
103
- return compileTemplate('rsc-auth/app/actions/auth.ts', RSC_CONFIG);
104
- }
105
- // Source layer - Entry points
106
- function generateEntryClient() {
107
- return compileTemplate('rsc-auth/src/entry.client.tsx', RSC_CONFIG);
108
- }
109
- function generateEntryServer() {
110
- return compileTemplate('rsc-auth/src/entry.server.tsx', RSC_CONFIG);
111
- }
112
- // Source layer - API
113
- function generateApiHandler() {
114
- return compileTemplate('rsc-auth/src/api/handler.ts', RSC_CONFIG);
115
- }
116
- function generateDatabase(config) {
117
- return compileTemplate('rsc-auth/src/api/database.ts', config);
118
- }
119
- function generateHealthProcedures() {
120
- return compileTemplate('rsc-auth/src/api/procedures/health.ts', RSC_CONFIG);
121
- }
122
- function generateUserProcedures() {
123
- return compileTemplate('rsc-auth/src/api/procedures/users.ts', RSC_CONFIG);
124
- }
125
- function generateAuthProcedures() {
126
- return compileTemplate('rsc-auth/src/api/procedures/auth.ts', RSC_CONFIG);
127
- }
128
- function generateProfileProcedures() {
129
- return compileTemplate('rsc-auth/src/api/procedures/profiles.ts', RSC_CONFIG);
130
- }
131
- // Schemas
132
- function generateUserSchemas() {
133
- return compileTemplate('rsc-auth/src/api/schemas/user.ts', RSC_CONFIG);
134
- }
135
- function generateAuthSchemas() {
136
- return compileTemplate('rsc-auth/src/api/schemas/auth.ts', RSC_CONFIG);
137
- }
138
- // Utils
139
- function generateAuthUtils() {
140
- return compileTemplate('rsc-auth/src/api/utils/auth.ts', RSC_CONFIG);
141
- }
142
- // Public assets
143
- function generateFavicon() {
144
- return compileTemplate('rsc-auth/public/favicon.svg', RSC_CONFIG);
145
- }
146
- function generateDockerCompose(config) {
147
- return compileTemplate('rsc-auth/docker-compose.yml', config);
21
+ /** Shorthand: compile a static RSC-Auth template (no user-specific config needed) */
22
+ function rscAuth(sourcePath) {
23
+ return compileTemplate(sourcePath, RSC_CONFIG);
148
24
  }
149
25
  // ============================================================================
150
26
  // RSC Auth Template Generator
@@ -153,48 +29,75 @@ export function generateRscAuthTemplate(config) {
153
29
  const files = [
154
30
  // Root configuration
155
31
  { path: 'package.json', content: generatePackageJson(config) },
156
- { path: 'app.config.ts', content: generateAppConfig() },
157
- { path: 'tsconfig.json', content: generateTsConfig() },
158
- { path: '.env.example', content: generateEnvExample(config) },
159
- { path: '.env', content: generateEnvExample(config) },
160
- { path: '.gitignore', content: generateGitignore() },
161
- { path: 'CLAUDE.md', content: generateClaudeMd(config) },
32
+ { path: 'app.config.ts', content: rscAuth('rsc-auth/app.config.ts') },
33
+ { path: 'tsconfig.json', content: rscAuth('rsc-auth/tsconfig.json') },
34
+ { path: '.env.example', content: compileTemplate('rsc-auth/env.example', config) },
35
+ { path: '.env', content: compileTemplate('rsc-auth/env.example', config) },
36
+ { path: '.gitignore', content: rscAuth('rsc-auth/gitignore') },
37
+ { path: 'CLAUDE.md', content: compileTemplate('rsc-auth/CLAUDE.md', config) },
162
38
  // Prisma
163
- { path: 'prisma/schema.prisma', content: generatePrismaSchema(config) },
164
- { path: 'prisma.config.ts', content: generatePrismaConfig() },
39
+ {
40
+ path: 'prisma/schema.prisma',
41
+ content: compileTemplate('rsc-auth/prisma/schema.prisma', config),
42
+ },
43
+ { path: 'prisma.config.ts', content: rscAuth('rsc-auth/prisma.config.ts') },
165
44
  // App layer - Pages
166
- { path: 'app/pages/index.tsx', content: generateHomePage() },
167
- { path: 'app/pages/users.tsx', content: generateUsersPage() },
168
- { path: 'app/pages/_not-found.tsx', content: generateNotFoundPage() },
45
+ { path: 'app/pages/index.tsx', content: rscAuth('rsc-auth/app/pages/index.tsx') },
46
+ { path: 'app/pages/users.tsx', content: rscAuth('rsc-auth/app/pages/users.tsx') },
47
+ { path: 'app/pages/_not-found.tsx', content: rscAuth('rsc-auth/app/pages/_not-found.tsx') },
169
48
  // App layer - Auth pages
170
- { path: 'app/pages/auth/login.tsx', content: generateLoginPage() },
171
- { path: 'app/pages/auth/register.tsx', content: generateRegisterPage() },
49
+ { path: 'app/pages/auth/login.tsx', content: rscAuth('rsc-auth/app/pages/auth/login.tsx') },
50
+ {
51
+ path: 'app/pages/auth/register.tsx',
52
+ content: rscAuth('rsc-auth/app/pages/auth/register.tsx'),
53
+ },
172
54
  // App layer - Dashboard
173
- { path: 'app/pages/dashboard/index.tsx', content: generateDashboardPage() },
55
+ {
56
+ path: 'app/pages/dashboard/index.tsx',
57
+ content: rscAuth('rsc-auth/app/pages/dashboard/index.tsx'),
58
+ },
174
59
  // App layer - Layouts
175
- { path: 'app/layouts/root.tsx', content: generateRootLayout() },
176
- { path: 'app/layouts/marketing.tsx', content: generateMarketingLayout() },
177
- { path: 'app/layouts/minimal.tsx', content: generateMinimalLayout() },
178
- { path: 'app/layouts/minimal-content.tsx', content: generateMinimalContentLayout() },
179
- { path: 'app/layouts/dashboard.tsx', content: generateDashboardLayout() },
60
+ { path: 'app/layouts/root.tsx', content: rscAuth('rsc-auth/app/layouts/root.tsx') },
61
+ { path: 'app/layouts/marketing.tsx', content: rscAuth('rsc-auth/app/layouts/marketing.tsx') },
62
+ { path: 'app/layouts/minimal.tsx', content: rscAuth('rsc-auth/app/layouts/minimal.tsx') },
63
+ {
64
+ path: 'app/layouts/minimal-content.tsx',
65
+ content: rscAuth('rsc-auth/app/layouts/minimal-content.tsx'),
66
+ },
67
+ { path: 'app/layouts/dashboard.tsx', content: rscAuth('rsc-auth/app/layouts/dashboard.tsx') },
180
68
  // App layer - Actions
181
- { path: 'app/actions/users.ts', content: generateUserActions() },
182
- { path: 'app/actions/auth.ts', content: generateAuthActions() },
69
+ { path: 'app/actions/users.ts', content: rscAuth('rsc-auth/app/actions/users.ts') },
70
+ { path: 'app/actions/auth.ts', content: rscAuth('rsc-auth/app/actions/auth.ts') },
183
71
  // Source layer - Entry points
184
- { path: 'src/entry.client.tsx', content: generateEntryClient() },
185
- { path: 'src/entry.server.tsx', content: generateEntryServer() },
72
+ { path: 'src/entry.client.tsx', content: rscAuth('rsc-auth/src/entry.client.tsx') },
73
+ { path: 'src/entry.server.tsx', content: rscAuth('rsc-auth/src/entry.server.tsx') },
186
74
  // Source layer - API
187
- { path: 'src/api/handler.ts', content: generateApiHandler() },
188
- { path: 'src/api/database.ts', content: generateDatabase(config) },
189
- { path: 'src/api/procedures/health.ts', content: generateHealthProcedures() },
190
- { path: 'src/api/procedures/users.ts', content: generateUserProcedures() },
191
- { path: 'src/api/procedures/auth.ts', content: generateAuthProcedures() },
192
- { path: 'src/api/procedures/profiles.ts', content: generateProfileProcedures() },
193
- { path: 'src/api/schemas/user.ts', content: generateUserSchemas() },
194
- { path: 'src/api/schemas/auth.ts', content: generateAuthSchemas() },
195
- { path: 'src/api/utils/auth.ts', content: generateAuthUtils() },
75
+ { path: 'src/api/handler.ts', content: rscAuth('rsc-auth/src/api/handler.ts') },
76
+ {
77
+ path: 'src/api/database.ts',
78
+ content: compileTemplate('rsc-auth/src/api/database.ts', config),
79
+ },
80
+ {
81
+ path: 'src/api/procedures/health.ts',
82
+ content: rscAuth('rsc-auth/src/api/procedures/health.ts'),
83
+ },
84
+ {
85
+ path: 'src/api/procedures/users.ts',
86
+ content: rscAuth('rsc-auth/src/api/procedures/users.ts'),
87
+ },
88
+ {
89
+ path: 'src/api/procedures/auth.ts',
90
+ content: rscAuth('rsc-auth/src/api/procedures/auth.ts'),
91
+ },
92
+ {
93
+ path: 'src/api/procedures/profiles.ts',
94
+ content: rscAuth('rsc-auth/src/api/procedures/profiles.ts'),
95
+ },
96
+ { path: 'src/api/schemas/user.ts', content: rscAuth('rsc-auth/src/api/schemas/user.ts') },
97
+ { path: 'src/api/schemas/auth.ts', content: rscAuth('rsc-auth/src/api/schemas/auth.ts') },
98
+ { path: 'src/api/utils/auth.ts', content: rscAuth('rsc-auth/src/api/utils/auth.ts') },
196
99
  // Public assets
197
- { path: 'public/favicon.svg', content: generateFavicon() },
100
+ { path: 'public/favicon.svg', content: rscAuth('rsc-auth/public/favicon.svg') },
198
101
  // Scripts
199
102
  {
200
103
  path: 'scripts/check-client-imports.sh',
@@ -205,7 +108,7 @@ export function generateRscAuthTemplate(config) {
205
108
  if (config.database === 'postgresql') {
206
109
  files.push({
207
110
  path: 'docker-compose.yml',
208
- content: generateDockerCompose(config),
111
+ content: compileTemplate('rsc-auth/docker-compose.yml', config),
209
112
  });
210
113
  }
211
114
  return files;
@@ -10,154 +10,19 @@
10
10
  *
11
11
  * This is a single-package structure (not a monorepo workspace).
12
12
  */
13
- import fs from 'node:fs';
14
- import path from 'node:path';
15
- import { fileURLToPath } from 'node:url';
16
- import { compileTemplate } from './compiler.js';
13
+ import { compileTemplate, readSharedScript } from './compiler.js';
17
14
  import { applyDatabaseDependencies, RSC_CONFIG } from './placeholders.js';
18
- const __filename = fileURLToPath(import.meta.url);
19
- const __dirname = path.dirname(__filename);
20
- /**
21
- * Read shared script from templates/source/shared/scripts/
22
- */
23
- function readSharedScript(scriptName) {
24
- // Try dist path first (production), then src path (development)
25
- const distPath = path.join(__dirname, '..', '..', 'src', 'templates', 'source', 'shared', 'scripts', scriptName);
26
- const srcPath = path.join(__dirname, 'source', 'shared', 'scripts', scriptName);
27
- for (const scriptPath of [distPath, srcPath]) {
28
- if (fs.existsSync(scriptPath)) {
29
- return fs.readFileSync(scriptPath, 'utf-8');
30
- }
31
- }
32
- throw new Error(`Shared script not found: ${scriptName}. Checked:\n - ${distPath}\n - ${srcPath}`);
33
- }
34
15
  // ============================================================================
35
- // Template Compilation
16
+ // Helpers (only for functions with non-trivial logic)
36
17
  // ============================================================================
18
+ /** Compile package.json and swap database dependencies based on config */
37
19
  function generatePackageJson(config) {
38
20
  const content = compileTemplate('rsc/package.json', config);
39
21
  return applyDatabaseDependencies(content, config);
40
22
  }
41
- function generateAppConfig() {
42
- return compileTemplate('rsc/app.config.ts', RSC_CONFIG);
43
- }
44
- function generateTsConfig() {
45
- return compileTemplate('rsc/tsconfig.json', RSC_CONFIG);
46
- }
47
- function generateEnvExample(config) {
48
- return compileTemplate('rsc/env.example', config);
49
- }
50
- function generateGitignore() {
51
- return compileTemplate('rsc/gitignore', RSC_CONFIG);
52
- }
53
- function generateClaudeMd(config) {
54
- return compileTemplate('rsc/CLAUDE.md', config);
55
- }
56
- // Prisma
57
- function generatePrismaSchema(config) {
58
- return compileTemplate('rsc/prisma/schema.prisma', config);
59
- }
60
- function generatePrismaConfig() {
61
- return compileTemplate('rsc/prisma.config.ts', RSC_CONFIG);
62
- }
63
- // App layer (RSC)
64
- function generateHomePage() {
65
- return compileTemplate('rsc/app/pages/index.tsx', RSC_CONFIG);
66
- }
67
- function generateUsersPage() {
68
- return compileTemplate('rsc/app/pages/users.tsx', RSC_CONFIG);
69
- }
70
- function generateUserDetailPage() {
71
- return compileTemplate('rsc/app/pages/users/[id].tsx', RSC_CONFIG);
72
- }
73
- function generateRootLayout() {
74
- return compileTemplate('rsc/app/layouts/root.tsx', RSC_CONFIG);
75
- }
76
- function generateMarketingLayout() {
77
- return compileTemplate('rsc/app/layouts/marketing.tsx', RSC_CONFIG);
78
- }
79
- function generateMinimalLayout() {
80
- return compileTemplate('rsc/app/layouts/minimal.tsx', RSC_CONFIG);
81
- }
82
- function generateMinimalContentLayout() {
83
- return compileTemplate('rsc/app/layouts/minimal-content.tsx', RSC_CONFIG);
84
- }
85
- function generateAboutPage() {
86
- return compileTemplate('rsc/app/pages/(marketing)/about.tsx', RSC_CONFIG);
87
- }
88
- function generatePrintPage() {
89
- return compileTemplate('rsc/app/pages/print.tsx', RSC_CONFIG);
90
- }
91
- function generateNotFoundPage() {
92
- return compileTemplate('rsc/app/pages/_not-found.tsx', RSC_CONFIG);
93
- }
94
- function generateUserActions() {
95
- return compileTemplate('rsc/app/actions/users.ts', RSC_CONFIG);
96
- }
97
- function generatePostActions() {
98
- return compileTemplate('rsc/app/actions/posts.ts', RSC_CONFIG);
99
- }
100
- // Route group pages
101
- function generateSettingsPage() {
102
- return compileTemplate('rsc/app/pages/(dashboard)/settings.tsx', RSC_CONFIG);
103
- }
104
- function generateProfilePage() {
105
- return compileTemplate('rsc/app/pages/(dashboard)/profile.tsx', RSC_CONFIG);
106
- }
107
- // Nested dynamic route pages
108
- function generateUserPostsPage() {
109
- return compileTemplate('rsc/app/pages/users/[id]/posts/index.tsx', RSC_CONFIG);
110
- }
111
- function generatePostDetailPage() {
112
- return compileTemplate('rsc/app/pages/users/[id]/posts/[postId].tsx', RSC_CONFIG);
113
- }
114
- function generateNewPostPage() {
115
- return compileTemplate('rsc/app/pages/users/[id]/posts/new.tsx', RSC_CONFIG);
116
- }
117
- // Catch-all page
118
- function generateDocsPage() {
119
- return compileTemplate('rsc/app/pages/docs/[...slug].tsx', RSC_CONFIG);
120
- }
121
- // Additional layouts
122
- function generateDashboardLayout() {
123
- return compileTemplate('rsc/app/layouts/dashboard.tsx', RSC_CONFIG);
124
- }
125
- function generateUsersLayout() {
126
- return compileTemplate('rsc/app/pages/users/_layout.tsx', RSC_CONFIG);
127
- }
128
- // Source layer
129
- function generateEntryClient() {
130
- return compileTemplate('rsc/src/entry.client.tsx', RSC_CONFIG);
131
- }
132
- function generateEntryServer() {
133
- return compileTemplate('rsc/src/entry.server.tsx', RSC_CONFIG);
134
- }
135
- function generateApiHandler() {
136
- return compileTemplate('rsc/src/api/handler.ts', RSC_CONFIG);
137
- }
138
- function generateDatabase(config) {
139
- return compileTemplate('rsc/src/api/database.ts', config);
140
- }
141
- function generateHealthProcedures() {
142
- return compileTemplate('rsc/src/api/procedures/health.ts', RSC_CONFIG);
143
- }
144
- function generateUserProcedures() {
145
- return compileTemplate('rsc/src/api/procedures/users.ts', RSC_CONFIG);
146
- }
147
- function generatePostProcedures() {
148
- return compileTemplate('rsc/src/api/procedures/posts.ts', RSC_CONFIG);
149
- }
150
- function generateUserSchemas() {
151
- return compileTemplate('rsc/src/api/schemas/user.ts', RSC_CONFIG);
152
- }
153
- function generatePostSchemas() {
154
- return compileTemplate('rsc/src/api/schemas/post.ts', RSC_CONFIG);
155
- }
156
- function generateFavicon() {
157
- return compileTemplate('rsc/public/favicon.svg', RSC_CONFIG);
158
- }
159
- function generateDockerCompose(config) {
160
- return compileTemplate('rsc/docker-compose.yml', config);
23
+ /** Shorthand: compile a static RSC template (no user-specific config needed) */
24
+ function rsc(sourcePath) {
25
+ return compileTemplate(sourcePath, RSC_CONFIG);
161
26
  }
162
27
  // ============================================================================
163
28
  // RSC Template Generator
@@ -166,54 +31,75 @@ export function generateRscTemplate(config) {
166
31
  const files = [
167
32
  // Root configuration files
168
33
  { path: 'package.json', content: generatePackageJson(config) },
169
- { path: 'app.config.ts', content: generateAppConfig() },
170
- { path: 'tsconfig.json', content: generateTsConfig() },
171
- { path: '.env.example', content: generateEnvExample(config) },
172
- { path: '.env', content: generateEnvExample(config) },
173
- { path: '.gitignore', content: generateGitignore() },
174
- { path: 'CLAUDE.md', content: generateClaudeMd(config) },
34
+ { path: 'app.config.ts', content: rsc('rsc/app.config.ts') },
35
+ { path: 'tsconfig.json', content: rsc('rsc/tsconfig.json') },
36
+ { path: '.env.example', content: compileTemplate('rsc/env.example', config) },
37
+ { path: '.env', content: compileTemplate('rsc/env.example', config) },
38
+ { path: '.gitignore', content: rsc('rsc/gitignore') },
39
+ { path: 'CLAUDE.md', content: compileTemplate('rsc/CLAUDE.md', config) },
175
40
  // Prisma
176
- { path: 'prisma/schema.prisma', content: generatePrismaSchema(config) },
177
- { path: 'prisma.config.ts', content: generatePrismaConfig() },
41
+ { path: 'prisma/schema.prisma', content: compileTemplate('rsc/prisma/schema.prisma', config) },
42
+ { path: 'prisma.config.ts', content: rsc('rsc/prisma.config.ts') },
178
43
  // App layer (RSC) - Basic pages
179
- { path: 'app/pages/index.tsx', content: generateHomePage() },
180
- { path: 'app/pages/users.tsx', content: generateUsersPage() },
181
- { path: 'app/pages/print.tsx', content: generatePrintPage() },
182
- { path: 'app/pages/_not-found.tsx', content: generateNotFoundPage() },
44
+ { path: 'app/pages/index.tsx', content: rsc('rsc/app/pages/index.tsx') },
45
+ { path: 'app/pages/users.tsx', content: rsc('rsc/app/pages/users.tsx') },
46
+ { path: 'app/pages/print.tsx', content: rsc('rsc/app/pages/print.tsx') },
47
+ { path: 'app/pages/_not-found.tsx', content: rsc('rsc/app/pages/_not-found.tsx') },
183
48
  // App layer (RSC) - Nested dynamic routes (users/[id]/*)
184
- { path: 'app/pages/users/[id].tsx', content: generateUserDetailPage() },
185
- { path: 'app/pages/users/[id]/posts/index.tsx', content: generateUserPostsPage() },
186
- { path: 'app/pages/users/[id]/posts/[postId].tsx', content: generatePostDetailPage() },
187
- { path: 'app/pages/users/[id]/posts/new.tsx', content: generateNewPostPage() },
49
+ { path: 'app/pages/users/[id].tsx', content: rsc('rsc/app/pages/users/[id].tsx') },
50
+ {
51
+ path: 'app/pages/users/[id]/posts/index.tsx',
52
+ content: rsc('rsc/app/pages/users/[id]/posts/index.tsx'),
53
+ },
54
+ {
55
+ path: 'app/pages/users/[id]/posts/[postId].tsx',
56
+ content: rsc('rsc/app/pages/users/[id]/posts/[postId].tsx'),
57
+ },
58
+ {
59
+ path: 'app/pages/users/[id]/posts/new.tsx',
60
+ content: rsc('rsc/app/pages/users/[id]/posts/new.tsx'),
61
+ },
188
62
  // App layer (RSC) - Route groups
189
- { path: 'app/pages/(marketing)/about.tsx', content: generateAboutPage() },
190
- { path: 'app/pages/(dashboard)/settings.tsx', content: generateSettingsPage() },
191
- { path: 'app/pages/(dashboard)/profile.tsx', content: generateProfilePage() },
63
+ {
64
+ path: 'app/pages/(marketing)/about.tsx',
65
+ content: rsc('rsc/app/pages/(marketing)/about.tsx'),
66
+ },
67
+ {
68
+ path: 'app/pages/(dashboard)/settings.tsx',
69
+ content: rsc('rsc/app/pages/(dashboard)/settings.tsx'),
70
+ },
71
+ {
72
+ path: 'app/pages/(dashboard)/profile.tsx',
73
+ content: rsc('rsc/app/pages/(dashboard)/profile.tsx'),
74
+ },
192
75
  // App layer (RSC) - Catch-all routes
193
- { path: 'app/pages/docs/[...slug].tsx', content: generateDocsPage() },
76
+ { path: 'app/pages/docs/[...slug].tsx', content: rsc('rsc/app/pages/docs/[...slug].tsx') },
194
77
  // App layer (RSC) - Layouts
195
- { path: 'app/layouts/root.tsx', content: generateRootLayout() },
196
- { path: 'app/layouts/marketing.tsx', content: generateMarketingLayout() },
197
- { path: 'app/layouts/minimal.tsx', content: generateMinimalLayout() },
198
- { path: 'app/layouts/minimal-content.tsx', content: generateMinimalContentLayout() },
199
- { path: 'app/layouts/dashboard.tsx', content: generateDashboardLayout() },
200
- { path: 'app/pages/users/_layout.tsx', content: generateUsersLayout() },
78
+ { path: 'app/layouts/root.tsx', content: rsc('rsc/app/layouts/root.tsx') },
79
+ { path: 'app/layouts/marketing.tsx', content: rsc('rsc/app/layouts/marketing.tsx') },
80
+ { path: 'app/layouts/minimal.tsx', content: rsc('rsc/app/layouts/minimal.tsx') },
81
+ {
82
+ path: 'app/layouts/minimal-content.tsx',
83
+ content: rsc('rsc/app/layouts/minimal-content.tsx'),
84
+ },
85
+ { path: 'app/layouts/dashboard.tsx', content: rsc('rsc/app/layouts/dashboard.tsx') },
86
+ { path: 'app/pages/users/_layout.tsx', content: rsc('rsc/app/pages/users/_layout.tsx') },
201
87
  // App layer (RSC) - Server actions
202
- { path: 'app/actions/users.ts', content: generateUserActions() },
203
- { path: 'app/actions/posts.ts', content: generatePostActions() },
88
+ { path: 'app/actions/users.ts', content: rsc('rsc/app/actions/users.ts') },
89
+ { path: 'app/actions/posts.ts', content: rsc('rsc/app/actions/posts.ts') },
204
90
  // Source layer - Entry points
205
- { path: 'src/entry.client.tsx', content: generateEntryClient() },
206
- { path: 'src/entry.server.tsx', content: generateEntryServer() },
91
+ { path: 'src/entry.client.tsx', content: rsc('rsc/src/entry.client.tsx') },
92
+ { path: 'src/entry.server.tsx', content: rsc('rsc/src/entry.server.tsx') },
207
93
  // Source layer - API
208
- { path: 'src/api/handler.ts', content: generateApiHandler() },
209
- { path: 'src/api/database.ts', content: generateDatabase(config) },
210
- { path: 'src/api/procedures/health.ts', content: generateHealthProcedures() },
211
- { path: 'src/api/procedures/users.ts', content: generateUserProcedures() },
212
- { path: 'src/api/procedures/posts.ts', content: generatePostProcedures() },
213
- { path: 'src/api/schemas/user.ts', content: generateUserSchemas() },
214
- { path: 'src/api/schemas/post.ts', content: generatePostSchemas() },
94
+ { path: 'src/api/handler.ts', content: rsc('rsc/src/api/handler.ts') },
95
+ { path: 'src/api/database.ts', content: compileTemplate('rsc/src/api/database.ts', config) },
96
+ { path: 'src/api/procedures/health.ts', content: rsc('rsc/src/api/procedures/health.ts') },
97
+ { path: 'src/api/procedures/users.ts', content: rsc('rsc/src/api/procedures/users.ts') },
98
+ { path: 'src/api/procedures/posts.ts', content: rsc('rsc/src/api/procedures/posts.ts') },
99
+ { path: 'src/api/schemas/user.ts', content: rsc('rsc/src/api/schemas/user.ts') },
100
+ { path: 'src/api/schemas/post.ts', content: rsc('rsc/src/api/schemas/post.ts') },
215
101
  // Public assets
216
- { path: 'public/favicon.svg', content: generateFavicon() },
102
+ { path: 'public/favicon.svg', content: rsc('rsc/public/favicon.svg') },
217
103
  // Scripts
218
104
  {
219
105
  path: 'scripts/check-client-imports.sh',
@@ -224,7 +110,7 @@ export function generateRscTemplate(config) {
224
110
  if (config.database === 'postgresql') {
225
111
  files.push({
226
112
  path: 'docker-compose.yml',
227
- content: generateDockerCompose(config),
113
+ content: compileTemplate('rsc/docker-compose.yml', config),
228
114
  });
229
115
  }
230
116
  return files;
@@ -11,57 +11,17 @@ import { compileTemplate } from './compiler.js';
11
11
  import { applyDatabaseDependencies, DEFAULT_CONFIG } from './placeholders.js';
12
12
  import { generateRootFiles, generateWebBaseFiles, generateWebStyleFiles } from './shared/index.js';
13
13
  // ============================================================================
14
- // API Template Compilation
14
+ // Helpers
15
15
  // ============================================================================
16
+ /** Shorthand: compile a static API template (no user-specific config needed) */
17
+ function api(sourcePath) {
18
+ return compileTemplate(sourcePath, DEFAULT_CONFIG);
19
+ }
20
+ /** Compile API package.json and swap database dependencies based on config */
16
21
  function generateApiPackageJson(config) {
17
22
  const content = compileTemplate('api/package.default.json', config);
18
23
  return applyDatabaseDependencies(content, config);
19
24
  }
20
- function generateApiTsConfig() {
21
- return compileTemplate('api/tsconfig.json', DEFAULT_CONFIG);
22
- }
23
- function generateApiTsupConfig() {
24
- return compileTemplate('api/tsup.config.ts', DEFAULT_CONFIG);
25
- }
26
- function generateEnvExample(config) {
27
- return compileTemplate('api/env.default', config);
28
- }
29
- function generatePrismaSchema(config) {
30
- return compileTemplate('api/prisma/schema.default.prisma', config);
31
- }
32
- function generatePrismaConfig() {
33
- return compileTemplate('api/prisma.config.ts', DEFAULT_CONFIG);
34
- }
35
- function generateRouterTs() {
36
- return compileTemplate('api/router.default.ts', DEFAULT_CONFIG);
37
- }
38
- function generateIndexTs() {
39
- return compileTemplate('api/index.default.ts', DEFAULT_CONFIG);
40
- }
41
- function generateConfigApp(config) {
42
- return compileTemplate('api/config/app.ts', config);
43
- }
44
- function generateConfigDatabase(config) {
45
- return compileTemplate('api/config/database.ts', config);
46
- }
47
- function generateHealthProcedures() {
48
- return compileTemplate('api/procedures/health.ts', DEFAULT_CONFIG);
49
- }
50
- function generateUserProcedures() {
51
- return compileTemplate('api/procedures/users.default.ts', DEFAULT_CONFIG);
52
- }
53
- function generateUserSchema() {
54
- return compileTemplate('api/schemas/user.ts', DEFAULT_CONFIG);
55
- }
56
- function generateHealthSchema() {
57
- return compileTemplate('api/schemas/health.ts', DEFAULT_CONFIG);
58
- }
59
- function generateApiTypesTs() {
60
- return compileTemplate('api/types.default.ts', DEFAULT_CONFIG);
61
- }
62
- function generateDockerCompose(config) {
63
- return compileTemplate('api/docker-compose.yml', config);
64
- }
65
25
  // ============================================================================
66
26
  // SPA Template Generator
67
27
  // ============================================================================
@@ -69,29 +29,35 @@ export function generateSpaTemplate(config) {
69
29
  const files = [
70
30
  // API package files
71
31
  { path: 'apps/api/package.json', content: generateApiPackageJson(config) },
72
- { path: 'apps/api/tsconfig.json', content: generateApiTsConfig() },
73
- { path: 'apps/api/tsup.config.ts', content: generateApiTsupConfig() },
74
- { path: 'apps/api/prisma.config.ts', content: generatePrismaConfig() },
75
- { path: 'apps/api/.env.example', content: generateEnvExample(config) },
76
- { path: 'apps/api/.env', content: generateEnvExample(config) },
32
+ { path: 'apps/api/tsconfig.json', content: api('api/tsconfig.json') },
33
+ { path: 'apps/api/tsup.config.ts', content: api('api/tsup.config.ts') },
34
+ { path: 'apps/api/prisma.config.ts', content: api('api/prisma.config.ts') },
35
+ { path: 'apps/api/.env.example', content: compileTemplate('api/env.default', config) },
36
+ { path: 'apps/api/.env', content: compileTemplate('api/env.default', config) },
77
37
  // Prisma
78
- { path: 'apps/api/prisma/schema.prisma', content: generatePrismaSchema(config) },
38
+ {
39
+ path: 'apps/api/prisma/schema.prisma',
40
+ content: compileTemplate('api/prisma/schema.default.prisma', config),
41
+ },
79
42
  // API Source files
80
- { path: 'apps/api/src/router.ts', content: generateRouterTs() },
81
- { path: 'apps/api/src/index.ts', content: generateIndexTs() },
82
- { path: 'apps/api/src/config/app.ts', content: generateConfigApp(config) },
83
- { path: 'apps/api/src/config/database.ts', content: generateConfigDatabase(config) },
84
- { path: 'apps/api/src/procedures/health.ts', content: generateHealthProcedures() },
85
- { path: 'apps/api/src/procedures/users.ts', content: generateUserProcedures() },
86
- { path: 'apps/api/src/schemas/user.ts', content: generateUserSchema() },
87
- { path: 'apps/api/src/schemas/health.ts', content: generateHealthSchema() },
88
- { path: 'apps/api/src/types.ts', content: generateApiTypesTs() },
43
+ { path: 'apps/api/src/router.ts', content: api('api/router.default.ts') },
44
+ { path: 'apps/api/src/index.ts', content: api('api/index.default.ts') },
45
+ { path: 'apps/api/src/config/app.ts', content: compileTemplate('api/config/app.ts', config) },
46
+ {
47
+ path: 'apps/api/src/config/database.ts',
48
+ content: compileTemplate('api/config/database.ts', config),
49
+ },
50
+ { path: 'apps/api/src/procedures/health.ts', content: api('api/procedures/health.ts') },
51
+ { path: 'apps/api/src/procedures/users.ts', content: api('api/procedures/users.default.ts') },
52
+ { path: 'apps/api/src/schemas/user.ts', content: api('api/schemas/user.ts') },
53
+ { path: 'apps/api/src/schemas/health.ts', content: api('api/schemas/health.ts') },
54
+ { path: 'apps/api/src/types.ts', content: api('api/types.default.ts') },
89
55
  ];
90
56
  // Add docker-compose for PostgreSQL
91
57
  if (config.database === 'postgresql') {
92
58
  files.push({
93
59
  path: 'apps/api/docker-compose.yml',
94
- content: generateDockerCompose(config),
60
+ content: compileTemplate('api/docker-compose.yml', config),
95
61
  });
96
62
  }
97
63
  // Add root workspace files
@@ -16,62 +16,21 @@ import { compileTemplate } from './compiler.js';
16
16
  import { applyDatabaseDependencies, DEFAULT_CONFIG, TRPC_CONFIG } from './placeholders.js';
17
17
  import { generateRootFiles, generateWebBaseFiles, generateWebStyleFiles } from './shared/index.js';
18
18
  // ============================================================================
19
- // API Template Compilation
19
+ // Helpers
20
20
  // ============================================================================
21
+ /** Shorthand: compile a static API template (no user-specific config needed) */
22
+ function api(sourcePath) {
23
+ return compileTemplate(sourcePath, DEFAULT_CONFIG);
24
+ }
25
+ /** Shorthand: compile a tRPC-specific template */
26
+ function trpc(sourcePath) {
27
+ return compileTemplate(sourcePath, TRPC_CONFIG);
28
+ }
29
+ /** Compile API package.json with tRPC deps and swap database dependencies */
21
30
  function generateApiPackageJson(config) {
22
- // Use tRPC package.json with @trpc/server for TypeScript type portability
23
31
  const content = compileTemplate('api/package.trpc.json', config);
24
32
  return applyDatabaseDependencies(content, config);
25
33
  }
26
- function generateApiTsConfig() {
27
- return compileTemplate('api/tsconfig.json', DEFAULT_CONFIG);
28
- }
29
- function generateApiTsupConfig() {
30
- return compileTemplate('api/tsup.config.ts', DEFAULT_CONFIG);
31
- }
32
- function generateEnvExample(config) {
33
- // Use tRPC-specific env (no API_PREFIX since tRPC uses /trpc)
34
- return compileTemplate('api/env.trpc', config);
35
- }
36
- function generatePrismaSchema(config) {
37
- // Reuse default schema - same data model
38
- return compileTemplate('api/prisma/schema.default.prisma', config);
39
- }
40
- function generatePrismaConfig() {
41
- return compileTemplate('api/prisma.config.ts', DEFAULT_CONFIG);
42
- }
43
- function generateRouterTs() {
44
- return compileTemplate('api/router.trpc.ts', TRPC_CONFIG);
45
- }
46
- function generateIndexTs() {
47
- // Use tRPC-specific entry point
48
- return compileTemplate('api/index.trpc.ts', TRPC_CONFIG);
49
- }
50
- function generateConfigApp(config) {
51
- return compileTemplate('api/config/app.ts', config);
52
- }
53
- function generateConfigDatabase(config) {
54
- return compileTemplate('api/config/database.ts', config);
55
- }
56
- function generateHealthProcedures() {
57
- return compileTemplate('api/procedures/health.ts', DEFAULT_CONFIG);
58
- }
59
- function generateUserProcedures() {
60
- // Reuse default user procedures - same CRUD operations
61
- return compileTemplate('api/procedures/users.default.ts', DEFAULT_CONFIG);
62
- }
63
- function generateUserSchema() {
64
- return compileTemplate('api/schemas/user.ts', DEFAULT_CONFIG);
65
- }
66
- function generateHealthSchema() {
67
- return compileTemplate('api/schemas/health.ts', DEFAULT_CONFIG);
68
- }
69
- function generateApiTypesTs() {
70
- return compileTemplate('api/types.default.ts', DEFAULT_CONFIG);
71
- }
72
- function generateDockerCompose(config) {
73
- return compileTemplate('api/docker-compose.yml', config);
74
- }
75
34
  // ============================================================================
76
35
  // tRPC Template Generator
77
36
  // ============================================================================
@@ -79,34 +38,40 @@ export function generateTrpcTemplate(config) {
79
38
  const files = [
80
39
  // API package files
81
40
  { path: 'apps/api/package.json', content: generateApiPackageJson(config) },
82
- { path: 'apps/api/tsconfig.json', content: generateApiTsConfig() },
83
- { path: 'apps/api/tsup.config.ts', content: generateApiTsupConfig() },
84
- { path: 'apps/api/prisma.config.ts', content: generatePrismaConfig() },
85
- { path: 'apps/api/.env.example', content: generateEnvExample(config) },
86
- { path: 'apps/api/.env', content: generateEnvExample(config) },
87
- // Prisma
88
- { path: 'apps/api/prisma/schema.prisma', content: generatePrismaSchema(config) },
41
+ { path: 'apps/api/tsconfig.json', content: api('api/tsconfig.json') },
42
+ { path: 'apps/api/tsup.config.ts', content: api('api/tsup.config.ts') },
43
+ { path: 'apps/api/prisma.config.ts', content: api('api/prisma.config.ts') },
44
+ { path: 'apps/api/.env.example', content: compileTemplate('api/env.trpc', config) },
45
+ { path: 'apps/api/.env', content: compileTemplate('api/env.trpc', config) },
46
+ // Prisma (reuses default schema - same data model)
47
+ {
48
+ path: 'apps/api/prisma/schema.prisma',
49
+ content: compileTemplate('api/prisma/schema.default.prisma', config),
50
+ },
89
51
  // API Source files
90
- { path: 'apps/api/src/router.ts', content: generateRouterTs() },
91
- { path: 'apps/api/src/index.ts', content: generateIndexTs() },
92
- { path: 'apps/api/src/config/app.ts', content: generateConfigApp(config) },
93
- { path: 'apps/api/src/config/database.ts', content: generateConfigDatabase(config) },
94
- { path: 'apps/api/src/procedures/health.ts', content: generateHealthProcedures() },
95
- { path: 'apps/api/src/procedures/users.ts', content: generateUserProcedures() },
96
- { path: 'apps/api/src/schemas/user.ts', content: generateUserSchema() },
97
- { path: 'apps/api/src/schemas/health.ts', content: generateHealthSchema() },
98
- { path: 'apps/api/src/types.ts', content: generateApiTypesTs() },
52
+ { path: 'apps/api/src/router.ts', content: trpc('api/router.trpc.ts') },
53
+ { path: 'apps/api/src/index.ts', content: trpc('api/index.trpc.ts') },
54
+ { path: 'apps/api/src/config/app.ts', content: compileTemplate('api/config/app.ts', config) },
55
+ {
56
+ path: 'apps/api/src/config/database.ts',
57
+ content: compileTemplate('api/config/database.ts', config),
58
+ },
59
+ { path: 'apps/api/src/procedures/health.ts', content: api('api/procedures/health.ts') },
60
+ { path: 'apps/api/src/procedures/users.ts', content: api('api/procedures/users.default.ts') },
61
+ { path: 'apps/api/src/schemas/user.ts', content: api('api/schemas/user.ts') },
62
+ { path: 'apps/api/src/schemas/health.ts', content: api('api/schemas/health.ts') },
63
+ { path: 'apps/api/src/types.ts', content: api('api/types.default.ts') },
99
64
  ];
100
65
  // Add docker-compose for PostgreSQL
101
66
  if (config.database === 'postgresql') {
102
67
  files.push({
103
68
  path: 'apps/api/docker-compose.yml',
104
- content: generateDockerCompose(config),
69
+ content: compileTemplate('api/docker-compose.yml', config),
105
70
  });
106
71
  }
107
72
  // Add root workspace files (use 'trpc' variant for tRPC-specific CLAUDE.md)
108
73
  const rootFiles = generateRootFiles(config, 'trpc');
109
- // Add web package files (use false for isAuthTemplate)
74
+ // Add web package files
110
75
  const webBaseFiles = generateWebBaseFiles(config, false);
111
76
  const webStyleFiles = generateWebStyleFiles();
112
77
  return [...files, ...rootFiles, ...webBaseFiles, ...webStyleFiles];
@@ -68,15 +68,6 @@ export interface TemplateConfig {
68
68
  template: TemplateType;
69
69
  database: DatabaseType;
70
70
  }
71
- /**
72
- * Interface for template generators
73
- */
74
- export interface TemplateGenerator {
75
- /**
76
- * Generate all files for this template
77
- */
78
- generateFiles(config: TemplateConfig): TemplateFile[];
79
- }
80
71
  /**
81
72
  * A single template file to be written
82
73
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-velox-app",
3
- "version": "0.7.0",
3
+ "version": "0.7.2",
4
4
  "description": "Project scaffolder for VeloxTS framework",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -23,13 +23,13 @@
23
23
  "CHANGELOG.md"
24
24
  ],
25
25
  "dependencies": {
26
- "@clack/prompts": "1.0.0",
27
- "ora": "9.1.0",
26
+ "@clack/prompts": "1.0.1",
27
+ "ora": "9.3.0",
28
28
  "picocolors": "1.1.1"
29
29
  },
30
30
  "devDependencies": {
31
- "@playwright/test": "1.52.0",
32
- "@types/node": "25.1.0",
31
+ "@playwright/test": "1.58.2",
32
+ "@types/node": "25.2.3",
33
33
  "@vitest/coverage-v8": "4.0.18",
34
34
  "typescript": "5.9.3",
35
35
  "vitest": "4.0.18"
@@ -21,7 +21,7 @@
21
21
  "@veloxts/velox": "__VELOXTS_VERSION__"
22
22
  },
23
23
  "devDependencies": {
24
- "@types/node": "25.1.0",
24
+ "@types/node": "25.2.3",
25
25
  "@veloxts/cli": "__VELOXTS_VERSION__",
26
26
  "@veloxts/mcp": "__VELOXTS_VERSION__",
27
27
  "tsx": "4.21.0",
@@ -27,15 +27,15 @@
27
27
  "@veloxts/validation": "__VELOXTS_VERSION__",
28
28
  "@veloxts/web": "__VELOXTS_VERSION__",
29
29
  "@vinxi/server-functions": "0.5.1",
30
- "dotenv": "17.2.3",
30
+ "dotenv": "17.3.1",
31
31
  "react": "19.2.4",
32
32
  "react-dom": "19.2.4",
33
33
  "vinxi": "0.5.11",
34
34
  "zod": "4.3.6"
35
35
  },
36
36
  "devDependencies": {
37
- "@types/node": "25.1.0",
38
- "@types/react": "19.2.10",
37
+ "@types/node": "25.2.3",
38
+ "@types/react": "19.2.14",
39
39
  "@types/react-dom": "19.2.3",
40
40
  "@veloxts/cli": "__VELOXTS_VERSION__",
41
41
  "@veloxts/mcp": "__VELOXTS_VERSION__",
@@ -29,7 +29,7 @@
29
29
  "@veloxts/web": "__VELOXTS_VERSION__",
30
30
  "@vinxi/server-functions": "0.5.1",
31
31
  "bcrypt": "6.0.0",
32
- "dotenv": "17.2.3",
32
+ "dotenv": "17.3.1",
33
33
  "react": "19.2.4",
34
34
  "react-dom": "19.2.4",
35
35
  "vinxi": "0.5.11",
@@ -37,8 +37,8 @@
37
37
  },
38
38
  "devDependencies": {
39
39
  "@types/bcrypt": "6.0.0",
40
- "@types/node": "25.1.0",
41
- "@types/react": "19.2.10",
40
+ "@types/node": "25.2.3",
41
+ "@types/react": "19.2.14",
42
42
  "@types/react-dom": "19.2.3",
43
43
  "@veloxts/cli": "__VELOXTS_VERSION__",
44
44
  "@veloxts/mcp": "__VELOXTS_VERSION__",
@@ -10,19 +10,19 @@
10
10
  "type-check": "tsc --noEmit"
11
11
  },
12
12
  "dependencies": {
13
- "@tanstack/react-query": "5.90.20",
14
- "@tanstack/react-router": "1.157.18",
13
+ "@tanstack/react-query": "5.90.21",
14
+ "@tanstack/react-router": "1.159.10",
15
15
  "@veloxts/client": "__VELOXTS_VERSION__",
16
16
  "react": "19.2.4",
17
17
  "react-dom": "19.2.4",
18
- "react-error-boundary": "6.1.0"
18
+ "react-error-boundary": "6.1.1"
19
19
  },
20
20
  "devDependencies": {
21
- "@tanstack/router-plugin": "1.157.18",
22
- "@types/node": "25.1.0",
23
- "@types/react": "19.2.10",
21
+ "@tanstack/router-plugin": "1.159.12",
22
+ "@types/node": "25.2.3",
23
+ "@types/react": "19.2.14",
24
24
  "@types/react-dom": "19.2.3",
25
- "@vitejs/plugin-react": "5.1.2",
25
+ "@vitejs/plugin-react": "5.1.4",
26
26
  "typescript": "5.9.3",
27
27
  "vite": "7.3.1"
28
28
  }