@vertz/create-vertz-app 0.1.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/bin/create-vertz-app.ts +65 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +4 -0
- package/dist/prompts.d.ts +31 -0
- package/dist/prompts.d.ts.map +1 -0
- package/dist/prompts.js +122 -0
- package/dist/scaffold.d.ts +14 -0
- package/dist/scaffold.d.ts.map +1 -0
- package/dist/scaffold.js +74 -0
- package/dist/templates/__tests__/templates.test.d.ts +2 -0
- package/dist/templates/__tests__/templates.test.d.ts.map +1 -0
- package/dist/templates/__tests__/templates.test.js +242 -0
- package/dist/templates/index.d.ts +70 -0
- package/dist/templates/index.d.ts.map +1 -0
- package/dist/templates/index.js +326 -0
- package/dist/types.d.ts +27 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +1 -0
- package/package.json +44 -0
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { Command } from 'commander';
|
|
4
|
+
import { resolveOptions, scaffold } from '../src/index.js';
|
|
5
|
+
import type { Runtime } from '../src/types.js';
|
|
6
|
+
|
|
7
|
+
const program = new Command();
|
|
8
|
+
|
|
9
|
+
program
|
|
10
|
+
.name('create-vertz-app')
|
|
11
|
+
.description('Scaffold a new Vertz project')
|
|
12
|
+
.version('0.1.0')
|
|
13
|
+
.argument('[name]', 'Project name')
|
|
14
|
+
.option('-r, --runtime <runtime>', 'Runtime to use (bun, node, deno)', 'bun')
|
|
15
|
+
.option('-e, --example', 'Include example health module', undefined)
|
|
16
|
+
.option('--no-example', 'Exclude example health module')
|
|
17
|
+
.action(
|
|
18
|
+
async (
|
|
19
|
+
name: string | undefined,
|
|
20
|
+
options: {
|
|
21
|
+
runtime: string;
|
|
22
|
+
example?: boolean;
|
|
23
|
+
},
|
|
24
|
+
) => {
|
|
25
|
+
try {
|
|
26
|
+
// Convert runtime string to Runtime type
|
|
27
|
+
const runtime = options.runtime.toLowerCase() as Runtime;
|
|
28
|
+
|
|
29
|
+
// Handle --example / --no-example
|
|
30
|
+
let includeExample: boolean | undefined;
|
|
31
|
+
if (options.example === true) {
|
|
32
|
+
includeExample = true;
|
|
33
|
+
} else if (options.example === false) {
|
|
34
|
+
includeExample = false;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const cliOptions = {
|
|
38
|
+
projectName: name,
|
|
39
|
+
runtime,
|
|
40
|
+
includeExample,
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
const resolved = await resolveOptions(cliOptions);
|
|
44
|
+
|
|
45
|
+
console.log(`Creating Vertz app: ${resolved.projectName}`);
|
|
46
|
+
console.log(`Runtime: ${resolved.runtime}`);
|
|
47
|
+
console.log(`Include example: ${resolved.includeExample ? 'Yes' : 'No'}`);
|
|
48
|
+
|
|
49
|
+
// Create project in current directory
|
|
50
|
+
const targetDir = process.cwd();
|
|
51
|
+
await scaffold(targetDir, resolved);
|
|
52
|
+
|
|
53
|
+
console.log(`\n✓ Created ${resolved.projectName}`);
|
|
54
|
+
console.log(`\nNext steps:`);
|
|
55
|
+
console.log(` cd ${resolved.projectName}`);
|
|
56
|
+
console.log(` bun install`);
|
|
57
|
+
console.log(` bun run dev`);
|
|
58
|
+
} catch (error) {
|
|
59
|
+
console.error('Error:', error instanceof Error ? error.message : error);
|
|
60
|
+
process.exit(1);
|
|
61
|
+
}
|
|
62
|
+
},
|
|
63
|
+
);
|
|
64
|
+
|
|
65
|
+
program.parse();
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,cAAc,CAAC;AAC7B,cAAc,eAAe,CAAC;AAC9B,cAAc,sBAAsB,CAAC;AACrC,cAAc,YAAY,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { CliOptions, Runtime, ScaffoldOptions } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Error thrown in CI mode when project name is required but not provided
|
|
4
|
+
*/
|
|
5
|
+
export declare class ProjectNameRequiredError extends Error {
|
|
6
|
+
constructor();
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Error thrown when an invalid runtime is provided
|
|
10
|
+
*/
|
|
11
|
+
export declare class InvalidRuntimeError extends Error {
|
|
12
|
+
constructor(runtime: string);
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Prompts the user for project name
|
|
16
|
+
*/
|
|
17
|
+
export declare function promptForProjectName(): Promise<string>;
|
|
18
|
+
/**
|
|
19
|
+
* Prompts the user for runtime selection
|
|
20
|
+
*/
|
|
21
|
+
export declare function promptForRuntime(): Promise<Runtime>;
|
|
22
|
+
/**
|
|
23
|
+
* Prompts the user for example module inclusion
|
|
24
|
+
*/
|
|
25
|
+
export declare function promptForExample(): Promise<boolean>;
|
|
26
|
+
/**
|
|
27
|
+
* Resolves CLI options into complete scaffold options
|
|
28
|
+
* Handles both interactive and CI modes
|
|
29
|
+
*/
|
|
30
|
+
export declare function resolveOptions(cliOptions: Partial<CliOptions>): Promise<ScaffoldOptions>;
|
|
31
|
+
//# sourceMappingURL=prompts.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prompts.d.ts","sourceRoot":"","sources":["../src/prompts.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAIvE;;GAEG;AACH,qBAAa,wBAAyB,SAAQ,KAAK;;CAKlD;AAED;;GAEG;AACH,qBAAa,mBAAoB,SAAQ,KAAK;gBAChC,OAAO,EAAE,MAAM;CAI5B;AAED;;GAEG;AACH,wBAAsB,oBAAoB,IAAI,OAAO,CAAC,MAAM,CAAC,CAY5D;AAED;;GAEG;AACH,wBAAsB,gBAAgB,IAAI,OAAO,CAAC,OAAO,CAAC,CAiBzD;AAED;;GAEG;AACH,wBAAsB,gBAAgB,IAAI,OAAO,CAAC,OAAO,CAAC,CAkBzD;AAED;;;GAGG;AACH,wBAAsB,cAAc,CAAC,UAAU,EAAE,OAAO,CAAC,UAAU,CAAC,GAAG,OAAO,CAAC,eAAe,CAAC,CA0C9F"}
|
package/dist/prompts.js
ADDED
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { createInterface } from 'node:readline';
|
|
2
|
+
const VALID_RUNTIMES = ['bun', 'node', 'deno'];
|
|
3
|
+
/**
|
|
4
|
+
* Error thrown in CI mode when project name is required but not provided
|
|
5
|
+
*/
|
|
6
|
+
export class ProjectNameRequiredError extends Error {
|
|
7
|
+
constructor() {
|
|
8
|
+
super('Project name is required in CI mode. Use --name or pass as argument.');
|
|
9
|
+
this.name = 'ProjectNameRequiredError';
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Error thrown when an invalid runtime is provided
|
|
14
|
+
*/
|
|
15
|
+
export class InvalidRuntimeError extends Error {
|
|
16
|
+
constructor(runtime) {
|
|
17
|
+
super(`Invalid runtime: ${runtime}. Valid options are: ${VALID_RUNTIMES.join(', ')}`);
|
|
18
|
+
this.name = 'InvalidRuntimeError';
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Prompts the user for project name
|
|
23
|
+
*/
|
|
24
|
+
export async function promptForProjectName() {
|
|
25
|
+
return new Promise((resolve) => {
|
|
26
|
+
const rl = createInterface({
|
|
27
|
+
input: process.stdin,
|
|
28
|
+
output: process.stdout,
|
|
29
|
+
});
|
|
30
|
+
rl.question('Project name: ', (answer) => {
|
|
31
|
+
rl.close();
|
|
32
|
+
resolve(answer.trim() || 'my-vertz-app');
|
|
33
|
+
});
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Prompts the user for runtime selection
|
|
38
|
+
*/
|
|
39
|
+
export async function promptForRuntime() {
|
|
40
|
+
return new Promise((resolve) => {
|
|
41
|
+
const rl = createInterface({
|
|
42
|
+
input: process.stdin,
|
|
43
|
+
output: process.stdout,
|
|
44
|
+
});
|
|
45
|
+
rl.question('Runtime (bun/node/deno) [bun]: ', (answer) => {
|
|
46
|
+
rl.close();
|
|
47
|
+
const trimmed = answer.trim().toLowerCase();
|
|
48
|
+
if (VALID_RUNTIMES.includes(trimmed)) {
|
|
49
|
+
resolve(trimmed);
|
|
50
|
+
}
|
|
51
|
+
else {
|
|
52
|
+
resolve('bun');
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Prompts the user for example module inclusion
|
|
59
|
+
*/
|
|
60
|
+
export async function promptForExample() {
|
|
61
|
+
return new Promise((resolve) => {
|
|
62
|
+
const rl = createInterface({
|
|
63
|
+
input: process.stdin,
|
|
64
|
+
output: process.stdout,
|
|
65
|
+
});
|
|
66
|
+
rl.question('Include example health module? (Y/n): ', (answer) => {
|
|
67
|
+
rl.close();
|
|
68
|
+
const trimmed = answer.trim().toLowerCase();
|
|
69
|
+
// Default to yes
|
|
70
|
+
if (trimmed === 'n' || trimmed === 'no') {
|
|
71
|
+
resolve(false);
|
|
72
|
+
}
|
|
73
|
+
else {
|
|
74
|
+
resolve(true);
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Resolves CLI options into complete scaffold options
|
|
81
|
+
* Handles both interactive and CI modes
|
|
82
|
+
*/
|
|
83
|
+
export async function resolveOptions(cliOptions) {
|
|
84
|
+
const isCI = process.env.CI === 'true';
|
|
85
|
+
// Validate runtime if provided
|
|
86
|
+
if (cliOptions.runtime !== undefined && !VALID_RUNTIMES.includes(cliOptions.runtime)) {
|
|
87
|
+
throw new InvalidRuntimeError(cliOptions.runtime);
|
|
88
|
+
}
|
|
89
|
+
// Handle project name
|
|
90
|
+
let projectName = cliOptions.projectName;
|
|
91
|
+
if (!projectName) {
|
|
92
|
+
if (isCI) {
|
|
93
|
+
throw new ProjectNameRequiredError();
|
|
94
|
+
}
|
|
95
|
+
projectName = await promptForProjectName();
|
|
96
|
+
}
|
|
97
|
+
// Handle runtime
|
|
98
|
+
let runtime = cliOptions.runtime;
|
|
99
|
+
if (!runtime) {
|
|
100
|
+
if (isCI) {
|
|
101
|
+
runtime = 'bun'; // Default in CI mode
|
|
102
|
+
}
|
|
103
|
+
else {
|
|
104
|
+
runtime = await promptForRuntime();
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
// Handle example inclusion
|
|
108
|
+
let includeExample = cliOptions.includeExample;
|
|
109
|
+
if (includeExample === undefined) {
|
|
110
|
+
if (isCI) {
|
|
111
|
+
includeExample = true; // Default in CI mode
|
|
112
|
+
}
|
|
113
|
+
else {
|
|
114
|
+
includeExample = await promptForExample();
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
return {
|
|
118
|
+
projectName,
|
|
119
|
+
runtime,
|
|
120
|
+
includeExample,
|
|
121
|
+
};
|
|
122
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { ScaffoldOptions } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Error thrown when the project directory already exists
|
|
4
|
+
*/
|
|
5
|
+
export declare class DirectoryExistsError extends Error {
|
|
6
|
+
constructor(projectName: string);
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Scaffolds a new Vertz project
|
|
10
|
+
* @param parentDir - Parent directory where the project will be created
|
|
11
|
+
* @param options - Scaffold options
|
|
12
|
+
*/
|
|
13
|
+
export declare function scaffold(parentDir: string, options: ScaffoldOptions): Promise<void>;
|
|
14
|
+
//# sourceMappingURL=scaffold.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scaffold.d.ts","sourceRoot":"","sources":["../src/scaffold.ts"],"names":[],"mappings":"AAoBA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAElD;;GAEG;AACH,qBAAa,oBAAqB,SAAQ,KAAK;gBACjC,WAAW,EAAE,MAAM;CAIhC;AAED;;;;GAIG;AACH,wBAAsB,QAAQ,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC,CA6DzF"}
|
package/dist/scaffold.js
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { promises as fs } from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { appTemplate, denoConfigTemplate, envExampleTemplate, envSrcTemplate, envTemplate, gitignoreTemplate, healthCheckSchemaTemplate, healthModuleDefTemplate, healthModuleTemplate, healthRouterTemplate, healthServiceTemplate, mainTemplate, packageJsonTemplate, requestIdMiddlewareTemplate, tsconfigTemplate, vertzConfigTemplate, } from './templates/index.js';
|
|
4
|
+
/**
|
|
5
|
+
* Error thrown when the project directory already exists
|
|
6
|
+
*/
|
|
7
|
+
export class DirectoryExistsError extends Error {
|
|
8
|
+
constructor(projectName) {
|
|
9
|
+
super(`Directory "${projectName}" already exists`);
|
|
10
|
+
this.name = 'DirectoryExistsError';
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Scaffolds a new Vertz project
|
|
15
|
+
* @param parentDir - Parent directory where the project will be created
|
|
16
|
+
* @param options - Scaffold options
|
|
17
|
+
*/
|
|
18
|
+
export async function scaffold(parentDir, options) {
|
|
19
|
+
const { projectName, runtime, includeExample } = options;
|
|
20
|
+
const projectDir = path.join(parentDir, projectName);
|
|
21
|
+
// Check if directory already exists
|
|
22
|
+
try {
|
|
23
|
+
await fs.access(projectDir);
|
|
24
|
+
throw new DirectoryExistsError(projectName);
|
|
25
|
+
}
|
|
26
|
+
catch (err) {
|
|
27
|
+
if (err instanceof DirectoryExistsError) {
|
|
28
|
+
throw err;
|
|
29
|
+
}
|
|
30
|
+
// Directory doesn't exist, which is what we want
|
|
31
|
+
}
|
|
32
|
+
// Create project directory
|
|
33
|
+
await fs.mkdir(projectDir, { recursive: true });
|
|
34
|
+
// Create subdirectories
|
|
35
|
+
const srcDir = path.join(projectDir, 'src');
|
|
36
|
+
const modulesDir = path.join(srcDir, 'modules');
|
|
37
|
+
const middlewaresDir = path.join(srcDir, 'middlewares');
|
|
38
|
+
await fs.mkdir(srcDir, { recursive: true });
|
|
39
|
+
await fs.mkdir(modulesDir, { recursive: true });
|
|
40
|
+
await fs.mkdir(middlewaresDir, { recursive: true });
|
|
41
|
+
// Generate and write core config files
|
|
42
|
+
await writeFile(projectDir, 'package.json', packageJsonTemplate({ projectName, runtime, includeExample }));
|
|
43
|
+
await writeFile(projectDir, 'tsconfig.json', tsconfigTemplate(runtime));
|
|
44
|
+
await writeFile(projectDir, 'vertz.config.ts', vertzConfigTemplate());
|
|
45
|
+
await writeFile(projectDir, '.env', envTemplate());
|
|
46
|
+
await writeFile(projectDir, '.env.example', envExampleTemplate());
|
|
47
|
+
await writeFile(projectDir, '.gitignore', gitignoreTemplate());
|
|
48
|
+
// Generate Deno-specific config if needed
|
|
49
|
+
if (runtime === 'deno') {
|
|
50
|
+
await writeFile(projectDir, 'deno.json', denoConfigTemplate());
|
|
51
|
+
}
|
|
52
|
+
// Generate source files
|
|
53
|
+
await writeFile(srcDir, 'env.ts', envSrcTemplate());
|
|
54
|
+
await writeFile(srcDir, 'app.ts', appTemplate());
|
|
55
|
+
await writeFile(srcDir, 'main.ts', mainTemplate());
|
|
56
|
+
await writeFile(middlewaresDir, 'request-id.middleware.ts', requestIdMiddlewareTemplate());
|
|
57
|
+
// Generate example health module if requested
|
|
58
|
+
if (includeExample) {
|
|
59
|
+
const schemasDir = path.join(modulesDir, 'schemas');
|
|
60
|
+
await fs.mkdir(schemasDir, { recursive: true });
|
|
61
|
+
await writeFile(modulesDir, 'health.module-def.ts', healthModuleDefTemplate());
|
|
62
|
+
await writeFile(modulesDir, 'health.module.ts', healthModuleTemplate());
|
|
63
|
+
await writeFile(modulesDir, 'health.service.ts', healthServiceTemplate());
|
|
64
|
+
await writeFile(modulesDir, 'health.router.ts', healthRouterTemplate());
|
|
65
|
+
await writeFile(schemasDir, 'health-check.schema.ts', healthCheckSchemaTemplate());
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Helper to write a file with consistent formatting
|
|
70
|
+
*/
|
|
71
|
+
async function writeFile(dir, filename, content) {
|
|
72
|
+
const filePath = path.join(dir, filename);
|
|
73
|
+
await fs.writeFile(filePath, content, 'utf-8');
|
|
74
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"templates.test.d.ts","sourceRoot":"","sources":["../../../src/templates/__tests__/templates.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { appTemplate, denoConfigTemplate, envExampleTemplate, envSrcTemplate, envTemplate, gitignoreTemplate, healthCheckSchemaTemplate, healthModuleDefTemplate, healthModuleTemplate, healthRouterTemplate, healthServiceTemplate, mainTemplate, packageJsonTemplate, requestIdMiddlewareTemplate, tsconfigTemplate, vertzConfigTemplate, } from '../index.js';
|
|
3
|
+
describe('templates', () => {
|
|
4
|
+
describe('packageJsonTemplate', () => {
|
|
5
|
+
it('returns valid JSON', () => {
|
|
6
|
+
const result = packageJsonTemplate({ projectName: 'test-app', runtime: 'bun' });
|
|
7
|
+
expect(() => JSON.parse(result)).not.toThrow();
|
|
8
|
+
});
|
|
9
|
+
it('includes project name', () => {
|
|
10
|
+
const result = packageJsonTemplate({ projectName: 'my-app', runtime: 'bun' });
|
|
11
|
+
const pkg = JSON.parse(result);
|
|
12
|
+
expect(pkg.name).toBe('my-app');
|
|
13
|
+
});
|
|
14
|
+
it('includes @vertz/server dependency', () => {
|
|
15
|
+
const result = packageJsonTemplate({ projectName: 'test-app', runtime: 'bun' });
|
|
16
|
+
const pkg = JSON.parse(result);
|
|
17
|
+
expect(pkg.dependencies['@vertz/server']).toBeDefined();
|
|
18
|
+
});
|
|
19
|
+
it('includes @vertz/cli as dev dependency', () => {
|
|
20
|
+
const result = packageJsonTemplate({ projectName: 'test-app', runtime: 'bun' });
|
|
21
|
+
const pkg = JSON.parse(result);
|
|
22
|
+
expect(pkg.devDependencies['@vertz/cli']).toBeDefined();
|
|
23
|
+
});
|
|
24
|
+
it('includes required scripts', () => {
|
|
25
|
+
const result = packageJsonTemplate({ projectName: 'test-app', runtime: 'bun' });
|
|
26
|
+
const pkg = JSON.parse(result);
|
|
27
|
+
expect(pkg.scripts.dev).toBeDefined();
|
|
28
|
+
expect(pkg.scripts.build).toBeDefined();
|
|
29
|
+
expect(pkg.scripts.check).toBeDefined();
|
|
30
|
+
});
|
|
31
|
+
it('uses bun scripts for bun runtime', () => {
|
|
32
|
+
const result = packageJsonTemplate({ projectName: 'test-app', runtime: 'bun' });
|
|
33
|
+
const pkg = JSON.parse(result);
|
|
34
|
+
expect(pkg.scripts.dev).toContain('bun');
|
|
35
|
+
});
|
|
36
|
+
it('uses tsx for node runtime', () => {
|
|
37
|
+
const result = packageJsonTemplate({ projectName: 'test-app', runtime: 'node' });
|
|
38
|
+
const pkg = JSON.parse(result);
|
|
39
|
+
expect(pkg.scripts.dev).toContain('tsx');
|
|
40
|
+
expect(pkg.dependencies.tsx).toBeDefined();
|
|
41
|
+
});
|
|
42
|
+
// Tests for runtime-specific type dependencies
|
|
43
|
+
describe('runtime-specific type dependencies', () => {
|
|
44
|
+
it('adds bun-types to devDependencies for bun runtime', () => {
|
|
45
|
+
const result = packageJsonTemplate({ projectName: 'test-app', runtime: 'bun' });
|
|
46
|
+
const pkg = JSON.parse(result);
|
|
47
|
+
expect(pkg.devDependencies['bun-types']).toBeDefined();
|
|
48
|
+
expect(pkg.devDependencies['bun-types']).toMatch(/^\^1\./);
|
|
49
|
+
});
|
|
50
|
+
it('adds @types/node to devDependencies for node runtime', () => {
|
|
51
|
+
const result = packageJsonTemplate({ projectName: 'test-app', runtime: 'node' });
|
|
52
|
+
const pkg = JSON.parse(result);
|
|
53
|
+
expect(pkg.devDependencies['@types/node']).toBeDefined();
|
|
54
|
+
expect(pkg.devDependencies['@types/node']).toMatch(/^\^20\./);
|
|
55
|
+
});
|
|
56
|
+
it('does not add type packages for deno runtime', () => {
|
|
57
|
+
const result = packageJsonTemplate({ projectName: 'test-app', runtime: 'deno' });
|
|
58
|
+
const pkg = JSON.parse(result);
|
|
59
|
+
expect(pkg.devDependencies['bun-types']).toBeUndefined();
|
|
60
|
+
expect(pkg.devDependencies['@types/node']).toBeUndefined();
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
describe('tsconfigTemplate', () => {
|
|
65
|
+
it('returns valid JSON', () => {
|
|
66
|
+
const result = tsconfigTemplate('bun');
|
|
67
|
+
expect(() => JSON.parse(result)).not.toThrow();
|
|
68
|
+
});
|
|
69
|
+
it('has strict mode enabled', () => {
|
|
70
|
+
const result = tsconfigTemplate('bun');
|
|
71
|
+
const tsconfig = JSON.parse(result);
|
|
72
|
+
expect(tsconfig.compilerOptions.strict).toBe(true);
|
|
73
|
+
});
|
|
74
|
+
// Tests for runtime-specific types in tsconfig
|
|
75
|
+
describe('runtime-specific types', () => {
|
|
76
|
+
it('includes bun-types in tsconfig for bun runtime', () => {
|
|
77
|
+
const result = tsconfigTemplate('bun');
|
|
78
|
+
const tsconfig = JSON.parse(result);
|
|
79
|
+
expect(tsconfig.compilerOptions.types).toContain('bun-types');
|
|
80
|
+
});
|
|
81
|
+
it('includes node types in tsconfig for node runtime', () => {
|
|
82
|
+
const result = tsconfigTemplate('node');
|
|
83
|
+
const tsconfig = JSON.parse(result);
|
|
84
|
+
expect(tsconfig.compilerOptions.types).toContain('node');
|
|
85
|
+
expect(tsconfig.compilerOptions.types).not.toContain('bun-types');
|
|
86
|
+
});
|
|
87
|
+
it('has empty types array for deno runtime', () => {
|
|
88
|
+
const result = tsconfigTemplate('deno');
|
|
89
|
+
const tsconfig = JSON.parse(result);
|
|
90
|
+
// Deno has built-in types, so types should be empty or undefined
|
|
91
|
+
expect(tsconfig.compilerOptions.types).toEqual([]);
|
|
92
|
+
});
|
|
93
|
+
});
|
|
94
|
+
});
|
|
95
|
+
describe('vertzConfigTemplate', () => {
|
|
96
|
+
it('returns non-empty string', () => {
|
|
97
|
+
const result = vertzConfigTemplate();
|
|
98
|
+
expect(result.length).toBeGreaterThan(0);
|
|
99
|
+
});
|
|
100
|
+
it('exports a default config', () => {
|
|
101
|
+
const result = vertzConfigTemplate();
|
|
102
|
+
expect(result).toContain('export default');
|
|
103
|
+
});
|
|
104
|
+
});
|
|
105
|
+
describe('envTemplate', () => {
|
|
106
|
+
it('returns non-empty string', () => {
|
|
107
|
+
const result = envTemplate();
|
|
108
|
+
expect(result.length).toBeGreaterThan(0);
|
|
109
|
+
});
|
|
110
|
+
it('includes DATABASE_URL placeholder', () => {
|
|
111
|
+
const result = envTemplate();
|
|
112
|
+
expect(result).toContain('DATABASE_URL=');
|
|
113
|
+
});
|
|
114
|
+
});
|
|
115
|
+
describe('envExampleTemplate', () => {
|
|
116
|
+
it('returns non-empty string', () => {
|
|
117
|
+
const result = envExampleTemplate();
|
|
118
|
+
expect(result.length).toBeGreaterThan(0);
|
|
119
|
+
});
|
|
120
|
+
it('matches env template structure', () => {
|
|
121
|
+
const result = envExampleTemplate();
|
|
122
|
+
expect(result).toContain('DATABASE_URL=');
|
|
123
|
+
});
|
|
124
|
+
});
|
|
125
|
+
describe('gitignoreTemplate', () => {
|
|
126
|
+
it('returns non-empty string', () => {
|
|
127
|
+
const result = gitignoreTemplate();
|
|
128
|
+
expect(result.length).toBeGreaterThan(0);
|
|
129
|
+
});
|
|
130
|
+
it('includes node_modules', () => {
|
|
131
|
+
const result = gitignoreTemplate();
|
|
132
|
+
expect(result).toContain('node_modules');
|
|
133
|
+
});
|
|
134
|
+
it('includes dist/', () => {
|
|
135
|
+
const result = gitignoreTemplate();
|
|
136
|
+
expect(result).toContain('dist/');
|
|
137
|
+
});
|
|
138
|
+
});
|
|
139
|
+
describe('envSrcTemplate', () => {
|
|
140
|
+
it('returns non-empty string', () => {
|
|
141
|
+
const result = envSrcTemplate();
|
|
142
|
+
expect(result.length).toBeGreaterThan(0);
|
|
143
|
+
});
|
|
144
|
+
it('uses envsafe for validation', () => {
|
|
145
|
+
const result = envSrcTemplate();
|
|
146
|
+
expect(result).toContain('envsafe');
|
|
147
|
+
});
|
|
148
|
+
});
|
|
149
|
+
describe('appTemplate', () => {
|
|
150
|
+
it('returns non-empty string', () => {
|
|
151
|
+
const result = appTemplate();
|
|
152
|
+
expect(result.length).toBeGreaterThan(0);
|
|
153
|
+
});
|
|
154
|
+
it('exports createServer', () => {
|
|
155
|
+
const result = appTemplate();
|
|
156
|
+
expect(result).toContain('createServer');
|
|
157
|
+
});
|
|
158
|
+
});
|
|
159
|
+
describe('mainTemplate', () => {
|
|
160
|
+
it('returns non-empty string', () => {
|
|
161
|
+
const result = mainTemplate();
|
|
162
|
+
expect(result.length).toBeGreaterThan(0);
|
|
163
|
+
});
|
|
164
|
+
it('starts the app', () => {
|
|
165
|
+
const result = mainTemplate();
|
|
166
|
+
expect(result).toContain('app.start');
|
|
167
|
+
});
|
|
168
|
+
});
|
|
169
|
+
describe('requestIdMiddlewareTemplate', () => {
|
|
170
|
+
it('returns non-empty string', () => {
|
|
171
|
+
const result = requestIdMiddlewareTemplate();
|
|
172
|
+
expect(result.length).toBeGreaterThan(0);
|
|
173
|
+
});
|
|
174
|
+
it('handles requestId', () => {
|
|
175
|
+
const result = requestIdMiddlewareTemplate();
|
|
176
|
+
expect(result).toContain('requestId');
|
|
177
|
+
});
|
|
178
|
+
});
|
|
179
|
+
describe('health module templates', () => {
|
|
180
|
+
it('healthModuleDefTemplate returns non-empty string', () => {
|
|
181
|
+
const result = healthModuleDefTemplate();
|
|
182
|
+
expect(result.length).toBeGreaterThan(0);
|
|
183
|
+
});
|
|
184
|
+
it('healthModuleTemplate returns non-empty string', () => {
|
|
185
|
+
const result = healthModuleTemplate();
|
|
186
|
+
expect(result.length).toBeGreaterThan(0);
|
|
187
|
+
});
|
|
188
|
+
it('healthServiceTemplate returns non-empty string', () => {
|
|
189
|
+
const result = healthServiceTemplate();
|
|
190
|
+
expect(result.length).toBeGreaterThan(0);
|
|
191
|
+
});
|
|
192
|
+
it('healthRouterTemplate returns non-empty string', () => {
|
|
193
|
+
const result = healthRouterTemplate();
|
|
194
|
+
expect(result.length).toBeGreaterThan(0);
|
|
195
|
+
});
|
|
196
|
+
it('healthCheckSchemaTemplate returns non-empty string', () => {
|
|
197
|
+
const result = healthCheckSchemaTemplate();
|
|
198
|
+
expect(result.length).toBeGreaterThan(0);
|
|
199
|
+
});
|
|
200
|
+
it('templates include helpful comments for new users', () => {
|
|
201
|
+
const serviceResult = healthServiceTemplate();
|
|
202
|
+
expect(serviceResult).toContain('In a real app');
|
|
203
|
+
});
|
|
204
|
+
});
|
|
205
|
+
describe('denoConfigTemplate', () => {
|
|
206
|
+
it('returns valid JSON', () => {
|
|
207
|
+
const result = denoConfigTemplate();
|
|
208
|
+
expect(() => JSON.parse(result)).not.toThrow();
|
|
209
|
+
});
|
|
210
|
+
it('includes imports', () => {
|
|
211
|
+
const result = denoConfigTemplate();
|
|
212
|
+
const config = JSON.parse(result);
|
|
213
|
+
expect(config.imports).toBeDefined();
|
|
214
|
+
});
|
|
215
|
+
});
|
|
216
|
+
describe('all templates return non-empty strings', () => {
|
|
217
|
+
it('every template function returns a non-empty string', () => {
|
|
218
|
+
const templates = [
|
|
219
|
+
() => packageJsonTemplate({ projectName: 'test', runtime: 'bun' }),
|
|
220
|
+
() => tsconfigTemplate('bun'),
|
|
221
|
+
vertzConfigTemplate,
|
|
222
|
+
envTemplate,
|
|
223
|
+
envExampleTemplate,
|
|
224
|
+
gitignoreTemplate,
|
|
225
|
+
envSrcTemplate,
|
|
226
|
+
appTemplate,
|
|
227
|
+
mainTemplate,
|
|
228
|
+
requestIdMiddlewareTemplate,
|
|
229
|
+
healthModuleDefTemplate,
|
|
230
|
+
healthModuleTemplate,
|
|
231
|
+
healthServiceTemplate,
|
|
232
|
+
healthRouterTemplate,
|
|
233
|
+
healthCheckSchemaTemplate,
|
|
234
|
+
denoConfigTemplate,
|
|
235
|
+
];
|
|
236
|
+
for (const template of templates) {
|
|
237
|
+
const result = template();
|
|
238
|
+
expect(result.length).toBeGreaterThan(0);
|
|
239
|
+
}
|
|
240
|
+
});
|
|
241
|
+
});
|
|
242
|
+
});
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import type { Runtime } from '../types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Package.json template
|
|
4
|
+
*/
|
|
5
|
+
export declare function packageJsonTemplate({ projectName, runtime, }: {
|
|
6
|
+
projectName: string;
|
|
7
|
+
runtime: Runtime;
|
|
8
|
+
includeExample?: boolean;
|
|
9
|
+
}): string;
|
|
10
|
+
/**
|
|
11
|
+
* Tsconfig.json template - runtime-specific types
|
|
12
|
+
*/
|
|
13
|
+
export declare function tsconfigTemplate(runtime: Runtime): string;
|
|
14
|
+
/**
|
|
15
|
+
* vertz.config.ts template
|
|
16
|
+
*/
|
|
17
|
+
export declare function vertzConfigTemplate(): string;
|
|
18
|
+
/**
|
|
19
|
+
* .env template
|
|
20
|
+
*/
|
|
21
|
+
export declare function envTemplate(): string;
|
|
22
|
+
/**
|
|
23
|
+
* .env.example template
|
|
24
|
+
*/
|
|
25
|
+
export declare function envExampleTemplate(): string;
|
|
26
|
+
/**
|
|
27
|
+
* .gitignore template
|
|
28
|
+
*/
|
|
29
|
+
export declare function gitignoreTemplate(): string;
|
|
30
|
+
/**
|
|
31
|
+
* src/env.ts template
|
|
32
|
+
*/
|
|
33
|
+
export declare function envSrcTemplate(): string;
|
|
34
|
+
/**
|
|
35
|
+
* src/app.ts template
|
|
36
|
+
*/
|
|
37
|
+
export declare function appTemplate(): string;
|
|
38
|
+
/**
|
|
39
|
+
* src/main.ts template
|
|
40
|
+
*/
|
|
41
|
+
export declare function mainTemplate(): string;
|
|
42
|
+
/**
|
|
43
|
+
* src/middlewares/request-id.middleware.ts template
|
|
44
|
+
*/
|
|
45
|
+
export declare function requestIdMiddlewareTemplate(): string;
|
|
46
|
+
/**
|
|
47
|
+
* src/modules/health.module-def.ts template
|
|
48
|
+
*/
|
|
49
|
+
export declare function healthModuleDefTemplate(): string;
|
|
50
|
+
/**
|
|
51
|
+
* src/modules/health.module.ts template
|
|
52
|
+
*/
|
|
53
|
+
export declare function healthModuleTemplate(): string;
|
|
54
|
+
/**
|
|
55
|
+
* src/modules/health.service.ts template
|
|
56
|
+
*/
|
|
57
|
+
export declare function healthServiceTemplate(): string;
|
|
58
|
+
/**
|
|
59
|
+
* src/modules/health.router.ts template
|
|
60
|
+
*/
|
|
61
|
+
export declare function healthRouterTemplate(): string;
|
|
62
|
+
/**
|
|
63
|
+
* src/modules/schemas/health-check.schema.ts template
|
|
64
|
+
*/
|
|
65
|
+
export declare function healthCheckSchemaTemplate(): string;
|
|
66
|
+
/**
|
|
67
|
+
* deno.json template for Deno runtime
|
|
68
|
+
*/
|
|
69
|
+
export declare function denoConfigTemplate(): string;
|
|
70
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/templates/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAE3C;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,EAClC,WAAW,EACX,OAAO,GACR,EAAE;IACD,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,OAAO,CAAC;IACjB,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B,GAAG,MAAM,CA+CT;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,CA+BzD;AAED;;GAEG;AACH,wBAAgB,mBAAmB,IAAI,MAAM,CAQ5C;AAED;;GAEG;AACH,wBAAgB,WAAW,IAAI,MAAM,CAMpC;AAED;;GAEG;AACH,wBAAgB,kBAAkB,IAAI,MAAM,CAM3C;AAED;;GAEG;AACH,wBAAgB,iBAAiB,IAAI,MAAM,CAgC1C;AAED;;GAEG;AACH,wBAAgB,cAAc,IAAI,MAAM,CAavC;AAED;;GAEG;AACH,wBAAgB,WAAW,IAAI,MAAM,CAWpC;AAED;;GAEG;AACH,wBAAgB,YAAY,IAAI,MAAM,CAarC;AAED;;GAEG;AACH,wBAAgB,2BAA2B,IAAI,MAAM,CAmBpD;AAED;;GAEG;AACH,wBAAgB,uBAAuB,IAAI,MAAM,CAQhD;AAED;;GAEG;AACH,wBAAgB,oBAAoB,IAAI,MAAM,CAY7C;AAED;;GAEG;AACH,wBAAgB,qBAAqB,IAAI,MAAM,CAkB9C;AAED;;GAEG;AACH,wBAAgB,oBAAoB,IAAI,MAAM,CAqB7C;AAED;;GAEG;AACH,wBAAgB,yBAAyB,IAAI,MAAM,CAQlD;AAED;;GAEG;AACH,wBAAgB,kBAAkB,IAAI,MAAM,CAgB3C"}
|
|
@@ -0,0 +1,326 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Package.json template
|
|
3
|
+
*/
|
|
4
|
+
export function packageJsonTemplate({ projectName, runtime, }) {
|
|
5
|
+
const deps = {
|
|
6
|
+
'@vertz/server': '^0.1.0',
|
|
7
|
+
};
|
|
8
|
+
const devDeps = {
|
|
9
|
+
'@vertz/cli': '^0.1.0',
|
|
10
|
+
typescript: '^5.9.3',
|
|
11
|
+
};
|
|
12
|
+
// Add runtime-specific type dependencies
|
|
13
|
+
if (runtime === 'bun') {
|
|
14
|
+
devDeps['bun-types'] = '^1.0.0';
|
|
15
|
+
}
|
|
16
|
+
else if (runtime === 'node') {
|
|
17
|
+
devDeps['@types/node'] = '^20.0.0';
|
|
18
|
+
}
|
|
19
|
+
// deno: no additional types needed (built-in)
|
|
20
|
+
const scripts = {};
|
|
21
|
+
if (runtime === 'bun') {
|
|
22
|
+
scripts.dev = 'bun run src/main.ts';
|
|
23
|
+
scripts.build = 'bun run vertz:build';
|
|
24
|
+
scripts.check = 'bun run vertz:check';
|
|
25
|
+
}
|
|
26
|
+
else if (runtime === 'node') {
|
|
27
|
+
deps.tsx = '^4.19.0';
|
|
28
|
+
scripts.dev = 'tsx watch src/main.ts';
|
|
29
|
+
scripts.build = 'tsc && vertz build';
|
|
30
|
+
scripts.check = 'tsc --noEmit';
|
|
31
|
+
}
|
|
32
|
+
else if (runtime === 'deno') {
|
|
33
|
+
scripts.dev = 'deno run src/main.ts';
|
|
34
|
+
scripts.check = 'deno check src/main.ts';
|
|
35
|
+
}
|
|
36
|
+
scripts.start = 'bun run src/main.ts';
|
|
37
|
+
const pkg = {
|
|
38
|
+
name: projectName,
|
|
39
|
+
version: '0.1.0',
|
|
40
|
+
type: 'module',
|
|
41
|
+
license: 'MIT',
|
|
42
|
+
scripts,
|
|
43
|
+
dependencies: deps,
|
|
44
|
+
devDependencies: devDeps,
|
|
45
|
+
};
|
|
46
|
+
return JSON.stringify(pkg, null, 2);
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Tsconfig.json template - runtime-specific types
|
|
50
|
+
*/
|
|
51
|
+
export function tsconfigTemplate(runtime) {
|
|
52
|
+
let types = [];
|
|
53
|
+
if (runtime === 'bun') {
|
|
54
|
+
types = ['bun-types'];
|
|
55
|
+
}
|
|
56
|
+
else if (runtime === 'node') {
|
|
57
|
+
types = ['node'];
|
|
58
|
+
}
|
|
59
|
+
// deno: types = [] (deno has built-in types)
|
|
60
|
+
const tsconfig = {
|
|
61
|
+
compilerOptions: {
|
|
62
|
+
target: 'ES2022',
|
|
63
|
+
module: 'ESNext',
|
|
64
|
+
lib: ['ES2022'],
|
|
65
|
+
moduleResolution: 'bundler',
|
|
66
|
+
strict: true,
|
|
67
|
+
esModuleInterop: true,
|
|
68
|
+
skipLibCheck: true,
|
|
69
|
+
forceConsistentCasingInFileNames: true,
|
|
70
|
+
resolveJsonModule: true,
|
|
71
|
+
allowJs: true,
|
|
72
|
+
outDir: './dist',
|
|
73
|
+
rootDir: './src',
|
|
74
|
+
types,
|
|
75
|
+
},
|
|
76
|
+
include: ['src/**/*'],
|
|
77
|
+
exclude: ['node_modules', 'dist'],
|
|
78
|
+
};
|
|
79
|
+
return JSON.stringify(tsconfig, null, 2);
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* vertz.config.ts template
|
|
83
|
+
*/
|
|
84
|
+
export function vertzConfigTemplate() {
|
|
85
|
+
return `import { defineConfig } from '@vertz/server';
|
|
86
|
+
|
|
87
|
+
export default defineConfig({
|
|
88
|
+
modules: [],
|
|
89
|
+
middlewares: [],
|
|
90
|
+
});
|
|
91
|
+
`;
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* .env template
|
|
95
|
+
*/
|
|
96
|
+
export function envTemplate() {
|
|
97
|
+
return `# Database connection string
|
|
98
|
+
DATABASE_URL=
|
|
99
|
+
|
|
100
|
+
# Add more environment variables below
|
|
101
|
+
`;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* .env.example template
|
|
105
|
+
*/
|
|
106
|
+
export function envExampleTemplate() {
|
|
107
|
+
return `# Database connection string (leave blank in development)
|
|
108
|
+
DATABASE_URL=
|
|
109
|
+
|
|
110
|
+
# Add more environment variables below
|
|
111
|
+
`;
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* .gitignore template
|
|
115
|
+
*/
|
|
116
|
+
export function gitignoreTemplate() {
|
|
117
|
+
return `# Dependencies
|
|
118
|
+
node_modules/
|
|
119
|
+
.pnp/
|
|
120
|
+
.pnp.js
|
|
121
|
+
|
|
122
|
+
# Build outputs
|
|
123
|
+
dist/
|
|
124
|
+
.vertz/
|
|
125
|
+
.env
|
|
126
|
+
.env.local
|
|
127
|
+
.env.*.local
|
|
128
|
+
|
|
129
|
+
# IDE
|
|
130
|
+
.idea/
|
|
131
|
+
.vscode/
|
|
132
|
+
*.swp
|
|
133
|
+
*.swo
|
|
134
|
+
|
|
135
|
+
# OS
|
|
136
|
+
.DS_Store
|
|
137
|
+
Thumbs.db
|
|
138
|
+
|
|
139
|
+
# Logs
|
|
140
|
+
*.log
|
|
141
|
+
npm-debug.log*
|
|
142
|
+
yarn-debug.log*
|
|
143
|
+
yarn-error.log*
|
|
144
|
+
|
|
145
|
+
# Test coverage
|
|
146
|
+
coverage/
|
|
147
|
+
`;
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* src/env.ts template
|
|
151
|
+
*/
|
|
152
|
+
export function envSrcTemplate() {
|
|
153
|
+
return `import { envsafe, str, port } from 'envsafe';
|
|
154
|
+
|
|
155
|
+
export const env = envsafe({
|
|
156
|
+
DATABASE_URL: str({
|
|
157
|
+
default: '',
|
|
158
|
+
allowEmpty: true,
|
|
159
|
+
}),
|
|
160
|
+
PORT: port({
|
|
161
|
+
default: 3000,
|
|
162
|
+
}),
|
|
163
|
+
});
|
|
164
|
+
`;
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* src/app.ts template
|
|
168
|
+
*/
|
|
169
|
+
export function appTemplate() {
|
|
170
|
+
return `import { createServer } from '@vertz/server';
|
|
171
|
+
|
|
172
|
+
export const app = createServer({
|
|
173
|
+
name: 'vertz-app',
|
|
174
|
+
requestId: {
|
|
175
|
+
header: 'x-request-id',
|
|
176
|
+
attribute: 'requestId',
|
|
177
|
+
},
|
|
178
|
+
});
|
|
179
|
+
`;
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* src/main.ts template
|
|
183
|
+
*/
|
|
184
|
+
export function mainTemplate() {
|
|
185
|
+
return `import { app } from './app.js';
|
|
186
|
+
import { env } from './env.js';
|
|
187
|
+
|
|
188
|
+
async function main() {
|
|
189
|
+
const { PORT } = env;
|
|
190
|
+
|
|
191
|
+
await app.start({ port: PORT });
|
|
192
|
+
console.log(\`Server running at http://localhost:\${PORT}\`);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
main();
|
|
196
|
+
`;
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* src/middlewares/request-id.middleware.ts template
|
|
200
|
+
*/
|
|
201
|
+
export function requestIdMiddlewareTemplate() {
|
|
202
|
+
return `import type { Middleware } from '@vertz/server';
|
|
203
|
+
import { randomUUID } from 'crypto';
|
|
204
|
+
|
|
205
|
+
export const requestIdMiddleware: Middleware = {
|
|
206
|
+
name: 'requestId',
|
|
207
|
+
handler: async (req, context, next) => {
|
|
208
|
+
const requestId = req.headers.get('x-request-id') || randomUUID();
|
|
209
|
+
|
|
210
|
+
context.set('requestId', requestId);
|
|
211
|
+
|
|
212
|
+
const response = await next();
|
|
213
|
+
|
|
214
|
+
response.headers.set('x-request-id', requestId);
|
|
215
|
+
|
|
216
|
+
return response;
|
|
217
|
+
},
|
|
218
|
+
};
|
|
219
|
+
`;
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* src/modules/health.module-def.ts template
|
|
223
|
+
*/
|
|
224
|
+
export function healthModuleDefTemplate() {
|
|
225
|
+
return `import type { ModuleDefinition } from '@vertz/server';
|
|
226
|
+
|
|
227
|
+
export const healthModuleDef = {
|
|
228
|
+
name: 'health',
|
|
229
|
+
imports: [],
|
|
230
|
+
} satisfies ModuleDefinition;
|
|
231
|
+
`;
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* src/modules/health.module.ts template
|
|
235
|
+
*/
|
|
236
|
+
export function healthModuleTemplate() {
|
|
237
|
+
return `import type { Module } from '@vertz/server';
|
|
238
|
+
import { healthModuleDef } from './health.module-def.js';
|
|
239
|
+
import { healthRouter } from './health.router.js';
|
|
240
|
+
import { HealthService } from './health.service.js';
|
|
241
|
+
|
|
242
|
+
export const healthModule = {
|
|
243
|
+
definition: healthModuleDef,
|
|
244
|
+
routers: [healthRouter],
|
|
245
|
+
services: [HealthService],
|
|
246
|
+
} satisfies Module;
|
|
247
|
+
`;
|
|
248
|
+
}
|
|
249
|
+
/**
|
|
250
|
+
* src/modules/health.service.ts template
|
|
251
|
+
*/
|
|
252
|
+
export function healthServiceTemplate() {
|
|
253
|
+
return `import type { Service } from '@vertz/server';
|
|
254
|
+
import { HealthCheckSchema } from './schemas/health-check.schema.js';
|
|
255
|
+
|
|
256
|
+
export class HealthService implements Service {
|
|
257
|
+
readonly name = 'health';
|
|
258
|
+
|
|
259
|
+
async check() {
|
|
260
|
+
// In a real app, check database, external services, etc.
|
|
261
|
+
return {
|
|
262
|
+
status: 'ok',
|
|
263
|
+
timestamp: new Date().toISOString(),
|
|
264
|
+
};
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
export const healthService = new HealthService();
|
|
269
|
+
`;
|
|
270
|
+
}
|
|
271
|
+
/**
|
|
272
|
+
* src/modules/health.router.ts template
|
|
273
|
+
*/
|
|
274
|
+
export function healthRouterTemplate() {
|
|
275
|
+
return `import type { Router } from '@vertz/server';
|
|
276
|
+
import { healthService } from './health.service.js';
|
|
277
|
+
|
|
278
|
+
export const healthRouter: Router = {
|
|
279
|
+
routes: {
|
|
280
|
+
'GET /health': {
|
|
281
|
+
handler: async () => {
|
|
282
|
+
const health = await healthService.check();
|
|
283
|
+
return { data: health };
|
|
284
|
+
},
|
|
285
|
+
},
|
|
286
|
+
'GET /health/ready': {
|
|
287
|
+
handler: async () => {
|
|
288
|
+
// Additional readiness check
|
|
289
|
+
return { data: { ready: true } };
|
|
290
|
+
},
|
|
291
|
+
},
|
|
292
|
+
},
|
|
293
|
+
};
|
|
294
|
+
`;
|
|
295
|
+
}
|
|
296
|
+
/**
|
|
297
|
+
* src/modules/schemas/health-check.schema.ts template
|
|
298
|
+
*/
|
|
299
|
+
export function healthCheckSchemaTemplate() {
|
|
300
|
+
return `import { s } from '@vertz/schema';
|
|
301
|
+
|
|
302
|
+
export const HealthCheckSchema = s.object({
|
|
303
|
+
status: s.string(),
|
|
304
|
+
timestamp: s.string(),
|
|
305
|
+
});
|
|
306
|
+
`;
|
|
307
|
+
}
|
|
308
|
+
/**
|
|
309
|
+
* deno.json template for Deno runtime
|
|
310
|
+
*/
|
|
311
|
+
export function denoConfigTemplate() {
|
|
312
|
+
const config = {
|
|
313
|
+
imports: {
|
|
314
|
+
'@vertz/server': 'jsr:@vertz/server@^0.1.0',
|
|
315
|
+
'@vertz/schema': 'jsr:@vertz/schema@^0.1.0',
|
|
316
|
+
},
|
|
317
|
+
tasks: {
|
|
318
|
+
dev: 'deno run --watch src/main.ts',
|
|
319
|
+
check: 'deno check src/main.ts',
|
|
320
|
+
},
|
|
321
|
+
compilerOptions: {
|
|
322
|
+
strict: true,
|
|
323
|
+
},
|
|
324
|
+
};
|
|
325
|
+
return JSON.stringify(config, null, 2);
|
|
326
|
+
}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runtime options for the scaffolded project
|
|
3
|
+
*/
|
|
4
|
+
export type Runtime = 'bun' | 'node' | 'deno';
|
|
5
|
+
/**
|
|
6
|
+
* Options for the scaffold function
|
|
7
|
+
*/
|
|
8
|
+
export interface ScaffoldOptions {
|
|
9
|
+
/** Name of the project to create */
|
|
10
|
+
projectName: string;
|
|
11
|
+
/** Target runtime (bun, node, or deno) */
|
|
12
|
+
runtime: Runtime;
|
|
13
|
+
/** Whether to include example health module */
|
|
14
|
+
includeExample: boolean;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* CLI options parsed from command line flags
|
|
18
|
+
*/
|
|
19
|
+
export interface CliOptions {
|
|
20
|
+
/** Project name (positional argument or --name) */
|
|
21
|
+
projectName?: string;
|
|
22
|
+
/** Target runtime */
|
|
23
|
+
runtime?: Runtime;
|
|
24
|
+
/** Whether to include example module */
|
|
25
|
+
includeExample?: boolean;
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +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"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/package.json
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@vertz/create-vertz-app",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"description": "Create a new Vertz application",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "https://github.com/vertz-dev/vertz.git",
|
|
10
|
+
"directory": "packages/create-vertz-app"
|
|
11
|
+
},
|
|
12
|
+
"publishConfig": {
|
|
13
|
+
"access": "public",
|
|
14
|
+
"provenance": true
|
|
15
|
+
},
|
|
16
|
+
"bin": {
|
|
17
|
+
"create-vertz-app": "./bin/create-vertz-app.ts"
|
|
18
|
+
},
|
|
19
|
+
"main": "./dist/index.js",
|
|
20
|
+
"types": "./dist/index.d.ts",
|
|
21
|
+
"exports": {
|
|
22
|
+
".": {
|
|
23
|
+
"import": "./dist/index.js",
|
|
24
|
+
"types": "./dist/index.d.ts"
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
"files": [
|
|
28
|
+
"dist",
|
|
29
|
+
"bin"
|
|
30
|
+
],
|
|
31
|
+
"scripts": {
|
|
32
|
+
"build": "tsc"
|
|
33
|
+
},
|
|
34
|
+
"dependencies": {
|
|
35
|
+
"commander": "^14.0.0"
|
|
36
|
+
},
|
|
37
|
+
"devDependencies": {
|
|
38
|
+
"@types/node": "^25.2.3",
|
|
39
|
+
"typescript": "^5.9.3"
|
|
40
|
+
},
|
|
41
|
+
"engines": {
|
|
42
|
+
"node": ">=22"
|
|
43
|
+
}
|
|
44
|
+
}
|