@vertz/create-vertz-app 0.2.1 → 0.2.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,115 +1,89 @@
1
+ // ── Config file templates ──────────────────────────────────
1
2
  /**
2
- * Package.json template
3
+ * Package.json template — full-stack deps + #generated imports map
3
4
  */
4
- export function packageJsonTemplate({ projectName, runtime, }) {
5
- const deps = {
6
- '@vertz/core': '^0.1.0',
7
- '@vertz/schema': '^0.1.0',
8
- envsafe: '^2.0.3',
9
- };
10
- const devDeps = {
11
- '@vertz/cli': '^0.1.0',
12
- typescript: '^5.9.3',
13
- };
14
- // Add runtime-specific type dependencies
15
- if (runtime === 'bun') {
16
- devDeps['bun-types'] = '^1.0.0';
17
- }
18
- else if (runtime === 'node') {
19
- devDeps['@types/node'] = '^20.0.0';
20
- }
21
- // deno: no additional types needed (built-in)
22
- const scripts = {};
23
- if (runtime === 'bun') {
24
- scripts.dev = 'bun run src/main.ts';
25
- scripts.build = 'vertz build';
26
- scripts.check = 'vertz check';
27
- }
28
- else if (runtime === 'node') {
29
- deps.tsx = '^4.19.0';
30
- scripts.dev = 'tsx watch src/main.ts';
31
- scripts.build = 'vertz build';
32
- scripts.check = 'vertz check';
33
- }
34
- else if (runtime === 'deno') {
35
- scripts.dev = 'deno run --watch src/main.ts';
36
- scripts.build = 'vertz build';
37
- scripts.check = 'deno check src/main.ts';
38
- }
39
- scripts.start = scripts.dev;
5
+ export function packageJsonTemplate(projectName) {
40
6
  const pkg = {
41
7
  name: projectName,
42
8
  version: '0.1.0',
43
9
  type: 'module',
44
10
  license: 'MIT',
45
- scripts,
46
- dependencies: deps,
47
- devDependencies: devDeps,
11
+ scripts: {
12
+ dev: 'vertz dev',
13
+ build: 'vertz build',
14
+ codegen: 'vertz codegen',
15
+ },
16
+ imports: {
17
+ '#generated': './.vertz/generated/client.ts',
18
+ '#generated/types': './.vertz/generated/types/index.ts',
19
+ },
20
+ dependencies: {
21
+ '@vertz/db': '^0.2.0',
22
+ '@vertz/server': '^0.2.0',
23
+ '@vertz/theme-shadcn': '^0.2.0',
24
+ '@vertz/ui': '^0.2.0',
25
+ },
26
+ devDependencies: {
27
+ '@vertz/cli': '^0.2.0',
28
+ '@vertz/ui-compiler': '^0.2.0',
29
+ 'bun-types': '^1.0.0',
30
+ typescript: '^5.8.0',
31
+ },
48
32
  };
49
33
  return JSON.stringify(pkg, null, 2);
50
34
  }
51
35
  /**
52
- * Tsconfig.json template - runtime-specific types
36
+ * Tsconfig.json template JSX config for @vertz/ui
53
37
  */
54
- export function tsconfigTemplate(runtime) {
55
- let types = [];
56
- if (runtime === 'bun') {
57
- types = ['bun-types'];
58
- }
59
- else if (runtime === 'node') {
60
- types = ['node'];
61
- }
62
- // deno: types = [] (deno has built-in types)
38
+ export function tsconfigTemplate() {
63
39
  const tsconfig = {
64
40
  compilerOptions: {
65
- target: 'ES2022',
41
+ declaration: true,
42
+ esModuleInterop: true,
43
+ jsx: 'react-jsx',
44
+ jsxImportSource: '@vertz/ui',
66
45
  module: 'ESNext',
67
- lib: ['ES2022'],
68
46
  moduleResolution: 'bundler',
69
- strict: true,
70
- esModuleInterop: true,
47
+ outDir: 'dist',
48
+ rootDir: 'src',
71
49
  skipLibCheck: true,
72
- forceConsistentCasingInFileNames: true,
73
- resolveJsonModule: true,
74
- allowJs: true,
75
- outDir: './dist',
76
- rootDir: './src',
77
- types,
50
+ strict: true,
51
+ target: 'ES2022',
52
+ types: ['bun-types'],
78
53
  },
79
- include: ['src/**/*'],
80
- exclude: ['node_modules', 'dist'],
54
+ include: ['src'],
81
55
  };
82
56
  return JSON.stringify(tsconfig, null, 2);
83
57
  }
84
58
  /**
85
- * vertz.config.ts template
59
+ * vertz.config.ts template — compiler entry + codegen config
86
60
  */
87
61
  export function vertzConfigTemplate() {
88
- return `import { defineConfig } from '@vertz/core';
62
+ return `/** @type {import('@vertz/compiler').VertzConfig} */
63
+ export default {
64
+ compiler: {
65
+ entryFile: 'src/api/server.ts',
66
+ },
67
+ };
89
68
 
90
- export default defineConfig({
91
- entry: './src/app.ts',
92
- });
69
+ /** @type {import('@vertz/codegen').CodegenConfig} */
70
+ export const codegen = {
71
+ generators: ['typescript'],
72
+ };
93
73
  `;
94
74
  }
95
75
  /**
96
76
  * .env template
97
77
  */
98
78
  export function envTemplate() {
99
- return `# Database connection string
100
- DATABASE_URL=
101
-
102
- # Add more environment variables below
79
+ return `PORT=3000
103
80
  `;
104
81
  }
105
82
  /**
106
83
  * .env.example template
107
84
  */
108
85
  export function envExampleTemplate() {
109
- return `# Database connection string (leave blank in development)
110
- DATABASE_URL=
111
-
112
- # Add more environment variables below
86
+ return `PORT=3000
113
87
  `;
114
88
  }
115
89
  /**
@@ -118,16 +92,19 @@ DATABASE_URL=
118
92
  export function gitignoreTemplate() {
119
93
  return `# Dependencies
120
94
  node_modules/
121
- .pnp/
122
- .pnp.js
123
95
 
124
96
  # Build outputs
125
97
  dist/
126
98
  .vertz/
99
+
100
+ # Environment
127
101
  .env
128
102
  .env.local
129
103
  .env.*.local
130
104
 
105
+ # Database
106
+ *.db
107
+
131
108
  # IDE
132
109
  .idea/
133
110
  .vscode/
@@ -140,179 +117,281 @@ Thumbs.db
140
117
 
141
118
  # Logs
142
119
  *.log
143
- npm-debug.log*
144
- yarn-debug.log*
145
- yarn-error.log*
146
-
147
- # Test coverage
148
- coverage/
149
120
  `;
150
121
  }
122
+ // ── Source file templates ───────────────────────────────────
151
123
  /**
152
- * src/env.ts template
124
+ * src/api/server.ts — createServer with entities + db
153
125
  */
154
- export function envSrcTemplate() {
155
- return `import { envsafe, str, port } from 'envsafe';
126
+ export function serverTemplate() {
127
+ return `import { createServer } from '@vertz/server';
128
+ import { db } from './db';
129
+ import { tasks } from './entities/tasks.entity';
156
130
 
157
- export const env = envsafe({
158
- DATABASE_URL: str({
159
- default: '',
160
- allowEmpty: true,
161
- }),
162
- PORT: port({
163
- default: 3000,
164
- }),
131
+ const app = createServer({
132
+ basePath: '/api',
133
+ entities: [tasks],
134
+ db,
165
135
  });
136
+
137
+ export default app;
138
+
139
+ if (import.meta.main) {
140
+ const PORT = Number(process.env.PORT) || 3000;
141
+ app.listen(PORT).then((handle) => {
142
+ console.log(\`Server running at http://localhost:\${handle.port}/api\`);
143
+ });
144
+ }
166
145
  `;
167
146
  }
168
147
  /**
169
- * src/app.ts template
148
+ * src/api/schema.ts — tasks table + model
170
149
  */
171
- export function appTemplate(includeExample) {
172
- const imports = includeExample
173
- ? `import { vertz } from '@vertz/core';
174
- import { healthModule } from './modules/health.module.js';`
175
- : `import { vertz } from '@vertz/core';`;
176
- const registerModules = includeExample
177
- ? `
178
- .register(healthModule)`
179
- : '';
180
- return `${imports}
150
+ export function schemaTemplate() {
151
+ return `import { d } from '@vertz/db';
181
152
 
182
- const app = vertz
183
- .app({ basePath: '/api' })${registerModules};
153
+ export const tasksTable = d.table('tasks', {
154
+ id: d.uuid().primary({ generate: 'uuid' }),
155
+ title: d.text(),
156
+ completed: d.boolean().default(false),
157
+ createdAt: d.timestamp().default('now').readOnly(),
158
+ updatedAt: d.timestamp().autoUpdate().readOnly(),
159
+ });
184
160
 
185
- export { app };
161
+ export const tasksModel = d.model(tasksTable);
186
162
  `;
187
163
  }
188
164
  /**
189
- * src/main.ts template
165
+ * src/api/db.ts — createSqliteAdapter with autoApply migrations
190
166
  */
191
- export function mainTemplate() {
192
- return `import { app } from './app.js';
193
- import { env } from './env.js';
167
+ export function dbTemplate() {
168
+ return `import { createSqliteAdapter } from '@vertz/db/sqlite';
169
+ import { tasksTable } from './schema';
194
170
 
195
- app.listen(env.PORT).then((handle) => {
196
- console.log(\`✓ Server running at http://localhost:\${handle.port}/api\`);
171
+ export const db = await createSqliteAdapter({
172
+ schema: tasksTable,
173
+ migrations: { autoApply: true },
197
174
  });
198
175
  `;
199
176
  }
200
177
  /**
201
- * src/middlewares/request-id.middleware.ts template
178
+ * src/api/entities/tasks.entity.ts — entity with CRUD access
202
179
  */
203
- export function requestIdMiddlewareTemplate() {
204
- return `import type { Middleware } from '@vertz/server';
205
- import { randomUUID } from 'crypto';
180
+ export function tasksEntityTemplate() {
181
+ return `import { entity } from '@vertz/server';
182
+ import { tasksModel } from '../schema';
206
183
 
207
- export const requestIdMiddleware: Middleware = {
208
- name: 'requestId',
209
- handler: async (req, context, next) => {
210
- const requestId = req.headers.get('x-request-id') || randomUUID();
211
-
212
- context.set('requestId', requestId);
213
-
214
- const response = await next();
215
-
216
- response.headers.set('x-request-id', requestId);
217
-
218
- return response;
184
+ export const tasks = entity('tasks', {
185
+ model: tasksModel,
186
+ access: {
187
+ list: () => true,
188
+ get: () => true,
189
+ create: () => true,
190
+ update: () => true,
191
+ delete: () => true,
219
192
  },
220
- };
193
+ });
221
194
  `;
222
195
  }
223
196
  /**
224
- * src/modules/health.module-def.ts template
197
+ * src/client.ts — #generated imports + type re-export
225
198
  */
226
- export function healthModuleDefTemplate() {
227
- return `import { vertz } from '@vertz/core';
199
+ export function clientTemplate() {
200
+ return `import { createClient } from '#generated';
201
+
202
+ export type * from '#generated/types';
228
203
 
229
- export const healthDef = vertz.moduleDef({ name: 'health' });
204
+ export const api = createClient();
230
205
  `;
231
206
  }
232
207
  /**
233
- * src/modules/health.module.ts template
208
+ * src/app.tsx — SSR module exports + ThemeProvider + render HomePage
234
209
  */
235
- export function healthModuleTemplate() {
236
- return `import { vertz } from '@vertz/core';
237
- import { healthDef } from './health.module-def.js';
238
- import { healthRouter } from './health.router.js';
239
- import { healthService } from './health.service.js';
210
+ export function appComponentTemplate() {
211
+ return `import { css, getInjectedCSS, globalCss, ThemeProvider } from '@vertz/ui';
212
+ import { HomePage } from './pages/home';
213
+ import { appTheme, themeGlobals } from './styles/theme';
240
214
 
241
- export const healthModule = vertz.module(healthDef, {
242
- services: [healthService],
243
- routers: [healthRouter],
215
+ const appGlobals = globalCss({
216
+ a: {
217
+ textDecoration: 'none',
218
+ color: 'inherit',
219
+ },
244
220
  });
221
+
222
+ const styles = css({
223
+ shell: ['min-h:screen', 'bg:background', 'text:foreground'],
224
+ header: [
225
+ 'flex',
226
+ 'items:center',
227
+ 'justify:between',
228
+ 'px:6',
229
+ 'py:4',
230
+ 'border-b:1',
231
+ 'border:border',
232
+ ],
233
+ title: ['font:lg', 'font:bold', 'text:foreground'],
234
+ main: ['max-w:2xl', 'mx:auto', 'px:6', 'py:8'],
235
+ });
236
+
237
+ export { getInjectedCSS };
238
+ export const theme = appTheme;
239
+ export const globalStyles = [themeGlobals.css, appGlobals.css];
240
+
241
+ export function App() {
242
+ return (
243
+ <div data-testid="app-root">
244
+ <ThemeProvider theme="light">
245
+ <div class={styles.shell}>
246
+ <header class={styles.header}>
247
+ <div class={styles.title}>My Vertz App</div>
248
+ </header>
249
+ <main class={styles.main}>
250
+ <HomePage />
251
+ </main>
252
+ </div>
253
+ </ThemeProvider>
254
+ </div>
255
+ );
256
+ }
245
257
  `;
246
258
  }
247
259
  /**
248
- * src/modules/health.service.ts template
260
+ * src/entry-client.ts — mount + HMR self-accept
249
261
  */
250
- export function healthServiceTemplate() {
251
- return `import { healthDef } from './health.module-def.js';
262
+ export function entryClientTemplate() {
263
+ return `import { mount } from '@vertz/ui';
264
+ import { App, globalStyles, theme } from './app';
265
+
266
+ import.meta.hot.accept();
252
267
 
253
- export const healthService = healthDef.service({
254
- methods: () => ({
255
- check: () => ({
256
- status: 'ok',
257
- timestamp: new Date().toISOString(),
258
- }),
259
- }),
268
+ mount(App, '#app', {
269
+ theme,
270
+ styles: globalStyles,
260
271
  });
261
272
  `;
262
273
  }
263
274
  /**
264
- * src/modules/health.router.ts template
275
+ * src/styles/theme.ts — configureTheme from @vertz/theme-shadcn
265
276
  */
266
- export function healthRouterTemplate() {
267
- return `import { s } from '@vertz/schema';
268
- import { healthDef } from './health.module-def.js';
269
- import { healthService } from './health.service.js';
277
+ export function themeTemplate() {
278
+ return `import { configureTheme } from '@vertz/theme-shadcn';
270
279
 
271
- const HealthResponseSchema = s.object({
272
- status: s.string(),
273
- timestamp: s.string(),
280
+ const { theme, globals } = configureTheme({
281
+ palette: 'zinc',
282
+ radius: 'md',
274
283
  });
275
284
 
276
- export const healthRouter = healthDef
277
- .router({ prefix: '/health', inject: { healthService } })
278
- .get('/', {
279
- response: HealthResponseSchema,
280
- handler: (ctx) => ctx.healthService.check(),
281
- })
282
- .get('/ready', {
283
- response: s.object({ ready: s.boolean() }),
284
- handler: () => ({ ready: true }),
285
- });
285
+ export const appTheme = theme;
286
+ export const themeGlobals = globals;
286
287
  `;
287
288
  }
288
289
  /**
289
- * src/modules/schemas/health-check.schema.ts template
290
+ * src/pages/home.tsx — task list + create form with query + css
290
291
  */
291
- export function healthCheckSchemaTemplate() {
292
- return `import { s } from '@vertz/schema';
292
+ export function homePageTemplate() {
293
+ return `import { css, query, queryMatch } from '@vertz/ui';
294
+ import { api } from '../client';
293
295
 
294
- export const HealthCheckSchema = s.object({
295
- status: s.string(),
296
- timestamp: s.string(),
296
+ const pageStyles = css({
297
+ container: ['py:2', 'w:full'],
298
+ heading: ['font:xl', 'font:bold', 'text:foreground', 'mb:4'],
299
+ form: ['flex', 'gap:2', 'mb:6'],
300
+ input: [
301
+ 'flex-1',
302
+ 'px:3',
303
+ 'py:2',
304
+ 'rounded:md',
305
+ 'border:1',
306
+ 'border:border',
307
+ 'bg:background',
308
+ 'text:foreground',
309
+ ],
310
+ button: [
311
+ 'px:4',
312
+ 'py:2',
313
+ 'rounded:md',
314
+ 'bg:primary.600',
315
+ 'text:white',
316
+ 'font:medium',
317
+ 'cursor:pointer',
318
+ ],
319
+ list: ['flex', 'flex-col', 'gap:2'],
320
+ item: [
321
+ 'flex',
322
+ 'items:center',
323
+ 'gap:3',
324
+ 'px:4',
325
+ 'py:3',
326
+ 'rounded:md',
327
+ 'border:1',
328
+ 'border:border',
329
+ 'bg:card',
330
+ ],
331
+ loading: ['text:muted-foreground'],
332
+ error: ['text:destructive'],
333
+ empty: ['text:muted-foreground', 'text:center', 'py:8'],
297
334
  });
298
- `;
335
+
336
+ export function HomePage() {
337
+ const tasksQuery = query(api.tasks.list());
338
+
339
+ const handleSubmit = async (e: SubmitEvent) => {
340
+ e.preventDefault();
341
+ const form = e.target as HTMLFormElement;
342
+ const data = new FormData(form);
343
+ const title = data.get('title') as string;
344
+ if (!title.trim()) return;
345
+
346
+ await api.tasks.create({ title });
347
+ form.reset();
348
+ tasksQuery.refetch();
349
+ };
350
+
351
+ return (
352
+ <div class={pageStyles.container} data-testid="home-page">
353
+ <h1 class={pageStyles.heading}>Tasks</h1>
354
+
355
+ <form class={pageStyles.form} onSubmit={handleSubmit}>
356
+ <input
357
+ name="title"
358
+ class={pageStyles.input}
359
+ placeholder="What needs to be done?"
360
+ required
361
+ />
362
+ <button type="submit" class={pageStyles.button}>
363
+ Add
364
+ </button>
365
+ </form>
366
+
367
+ {queryMatch(tasksQuery, {
368
+ loading: () => (
369
+ <div class={pageStyles.loading}>Loading tasks...</div>
370
+ ),
371
+ error: (err) => (
372
+ <div class={pageStyles.error}>
373
+ {err instanceof Error ? err.message : String(err)}
374
+ </div>
375
+ ),
376
+ data: (response) => (
377
+ <>
378
+ {response.items.length === 0 && (
379
+ <div class={pageStyles.empty}>
380
+ No tasks yet. Add one above!
381
+ </div>
382
+ )}
383
+ <div data-testid="task-list" class={pageStyles.list}>
384
+ {response.items.map((task) => (
385
+ <div key={task.id} class={pageStyles.item}>
386
+ <span>{task.title}</span>
387
+ </div>
388
+ ))}
389
+ </div>
390
+ </>
391
+ ),
392
+ })}
393
+ </div>
394
+ );
299
395
  }
300
- /**
301
- * deno.json template for Deno runtime
302
- */
303
- export function denoConfigTemplate() {
304
- const config = {
305
- imports: {
306
- '@vertz/server': 'jsr:@vertz/server@^0.1.0',
307
- '@vertz/schema': 'jsr:@vertz/schema@^0.1.0',
308
- },
309
- tasks: {
310
- dev: 'deno run --watch src/main.ts',
311
- check: 'deno check src/main.ts',
312
- },
313
- compilerOptions: {
314
- strict: true,
315
- },
316
- };
317
- return JSON.stringify(config, null, 2);
396
+ `;
318
397
  }
package/dist/types.d.ts CHANGED
@@ -1,17 +1,9 @@
1
- /**
2
- * Runtime options for the scaffolded project
3
- */
4
- export type Runtime = 'bun' | 'node' | 'deno';
5
1
  /**
6
2
  * Options for the scaffold function
7
3
  */
8
4
  export interface ScaffoldOptions {
9
5
  /** Name of the project to create */
10
6
  projectName: string;
11
- /** Target runtime (bun, node, or deno) */
12
- runtime: Runtime;
13
- /** Whether to include example health module */
14
- includeExample: boolean;
15
7
  }
16
8
  /**
17
9
  * CLI options parsed from command line flags
@@ -19,9 +11,5 @@ export interface ScaffoldOptions {
19
11
  export interface CliOptions {
20
12
  /** Project name (positional argument or --name) */
21
13
  projectName?: string;
22
- /** Target runtime */
23
- runtime?: Runtime;
24
- /** Whether to include example module */
25
- includeExample?: boolean;
26
14
  }
27
15
  //# sourceMappingURL=types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,MAAM,OAAO,GAAG,KAAK,GAAG,MAAM,GAAG,MAAM,CAAC;AAE9C;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,oCAAoC;IACpC,WAAW,EAAE,MAAM,CAAC;IACpB,0CAA0C;IAC1C,OAAO,EAAE,OAAO,CAAC;IACjB,+CAA+C;IAC/C,cAAc,EAAE,OAAO,CAAC;CACzB;AAED;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB,mDAAmD;IACnD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,qBAAqB;IACrB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,wCAAwC;IACxC,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,oCAAoC;IACpC,WAAW,EAAE,MAAM,CAAC;CACrB;AAED;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB,mDAAmD;IACnD,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vertz/create-vertz-app",
3
- "version": "0.2.1",
3
+ "version": "0.2.5",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
6
  "description": "Create a new Vertz application",