zero-config-cli 1.0.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/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +245 -0
- package/dist/cli.js.map +1 -0
- package/dist/copier.d.ts +12 -0
- package/dist/copier.d.ts.map +1 -0
- package/dist/copier.js +45 -0
- package/dist/copier.js.map +1 -0
- package/dist/downloader.d.ts +15 -0
- package/dist/downloader.d.ts.map +1 -0
- package/dist/downloader.js +69 -0
- package/dist/downloader.js.map +1 -0
- package/dist/generator.d.ts +31 -0
- package/dist/generator.d.ts.map +1 -0
- package/dist/generator.js +91 -0
- package/dist/generator.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +9 -0
- package/dist/index.js.map +1 -0
- package/dist/installer.d.ts +10 -0
- package/dist/installer.d.ts.map +1 -0
- package/dist/installer.js +33 -0
- package/dist/installer.js.map +1 -0
- package/dist/prisma-swap.d.ts +16 -0
- package/dist/prisma-swap.d.ts.map +1 -0
- package/dist/prisma-swap.js +53 -0
- package/dist/prisma-swap.js.map +1 -0
- package/dist/registry.d.ts +40 -0
- package/dist/registry.d.ts.map +1 -0
- package/dist/registry.js +121 -0
- package/dist/registry.js.map +1 -0
- package/dist/resolver.d.ts +15 -0
- package/dist/resolver.d.ts.map +1 -0
- package/dist/resolver.js +40 -0
- package/dist/resolver.js.map +1 -0
- package/dist/types.d.ts +8 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +6 -0
- package/dist/types.js.map +1 -0
- package/package.json +53 -0
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AA8QpC,wBAAgB,SAAS,IAAI,OAAO,CA8BnC"}
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
// ---------------------------------------------------------------------------
|
|
2
|
+
// CLI interface — Commander setup + interactive prompts via @clack/prompts
|
|
3
|
+
// ---------------------------------------------------------------------------
|
|
4
|
+
import { Command } from 'commander';
|
|
5
|
+
import { intro, outro, select, text, spinner, note, cancel, isCancel } from '@clack/prompts';
|
|
6
|
+
import pc from 'picocolors';
|
|
7
|
+
import { templateData, FRONTEND_KEYS, BACKEND_KEYS, databaseOptions } from './registry.js';
|
|
8
|
+
import { generateProject } from './generator.js';
|
|
9
|
+
import { installDependencies } from './installer.js';
|
|
10
|
+
const PKG = { version: '1.0.0', name: 'zero-config-cli' };
|
|
11
|
+
/** Prompt user to select a frontend framework. */
|
|
12
|
+
async function promptFrontend() {
|
|
13
|
+
const choices = FRONTEND_KEYS.map((key) => {
|
|
14
|
+
const t = templateData[key];
|
|
15
|
+
return {
|
|
16
|
+
value: key,
|
|
17
|
+
label: `${t.icon} ${t.name} ${t.version}`,
|
|
18
|
+
hint: `Port ${t.port} · ${t.description}`,
|
|
19
|
+
};
|
|
20
|
+
});
|
|
21
|
+
const result = await select({
|
|
22
|
+
message: 'Select a frontend framework:',
|
|
23
|
+
options: choices,
|
|
24
|
+
});
|
|
25
|
+
if (isCancel(result)) {
|
|
26
|
+
cancel('Operation cancelled.');
|
|
27
|
+
process.exit(0);
|
|
28
|
+
}
|
|
29
|
+
return result;
|
|
30
|
+
}
|
|
31
|
+
/** Ask user for a custom folder name, with a default fallback. */
|
|
32
|
+
async function promptFolderName(defaultName, label) {
|
|
33
|
+
const result = await text({
|
|
34
|
+
message: `Enter folder name for ${label}:`,
|
|
35
|
+
placeholder: defaultName,
|
|
36
|
+
defaultValue: defaultName,
|
|
37
|
+
validate: (value) => {
|
|
38
|
+
if (!value || value.trim().length === 0)
|
|
39
|
+
return 'Folder name cannot be empty';
|
|
40
|
+
if (!/^[a-zA-Z0-9\-_]+$/.test(value.trim())) {
|
|
41
|
+
return 'Folder name must only contain letters, numbers, hyphens, and underscores';
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
});
|
|
45
|
+
if (isCancel(result)) {
|
|
46
|
+
cancel('Operation cancelled.');
|
|
47
|
+
process.exit(0);
|
|
48
|
+
}
|
|
49
|
+
return result.trim() || defaultName;
|
|
50
|
+
}
|
|
51
|
+
/** Prompt user to select a backend framework. */
|
|
52
|
+
async function promptBackend() {
|
|
53
|
+
const choices = BACKEND_KEYS.map((key) => {
|
|
54
|
+
const t = templateData[key];
|
|
55
|
+
return {
|
|
56
|
+
value: key,
|
|
57
|
+
label: `${t.icon} ${t.name} ${t.version}`,
|
|
58
|
+
hint: `Port ${t.port} · ${t.description}`,
|
|
59
|
+
};
|
|
60
|
+
});
|
|
61
|
+
const result = await select({
|
|
62
|
+
message: 'Select a backend framework:',
|
|
63
|
+
options: choices,
|
|
64
|
+
});
|
|
65
|
+
if (isCancel(result)) {
|
|
66
|
+
cancel('Operation cancelled.');
|
|
67
|
+
process.exit(0);
|
|
68
|
+
}
|
|
69
|
+
return result;
|
|
70
|
+
}
|
|
71
|
+
/** Prompt user to select a database. */
|
|
72
|
+
async function promptDatabase() {
|
|
73
|
+
const choices = databaseOptions.map((db) => ({
|
|
74
|
+
value: db.id,
|
|
75
|
+
label: `${db.icon} ${db.name}`,
|
|
76
|
+
hint: db.description,
|
|
77
|
+
}));
|
|
78
|
+
const result = await select({
|
|
79
|
+
message: 'Select a database:',
|
|
80
|
+
options: choices,
|
|
81
|
+
});
|
|
82
|
+
if (isCancel(result)) {
|
|
83
|
+
cancel('Operation cancelled.');
|
|
84
|
+
process.exit(0);
|
|
85
|
+
}
|
|
86
|
+
return result;
|
|
87
|
+
}
|
|
88
|
+
/** Prompt user for npm install scope. */
|
|
89
|
+
async function promptInstallScope() {
|
|
90
|
+
const result = await select({
|
|
91
|
+
message: 'Install dependencies?',
|
|
92
|
+
options: [
|
|
93
|
+
{ value: 'both', label: 'Yes (both frontend & backend)', hint: 'Recommended' },
|
|
94
|
+
{ value: 'backend', label: 'Backend only' },
|
|
95
|
+
{ value: 'frontend', label: 'Frontend only' },
|
|
96
|
+
{ value: 'skip', label: 'Skip' },
|
|
97
|
+
],
|
|
98
|
+
initialValue: 'both',
|
|
99
|
+
});
|
|
100
|
+
if (isCancel(result)) {
|
|
101
|
+
cancel('Operation cancelled.');
|
|
102
|
+
process.exit(0);
|
|
103
|
+
}
|
|
104
|
+
return result;
|
|
105
|
+
}
|
|
106
|
+
// ---------------------------------------------------------------------------
|
|
107
|
+
// Main interactive flow
|
|
108
|
+
// ---------------------------------------------------------------------------
|
|
109
|
+
async function runInteractive(outputDir, options) {
|
|
110
|
+
intro(pc.bold(pc.cyan('Zero-Config Starter Generator')));
|
|
111
|
+
// Step 1: Frontend
|
|
112
|
+
const frontend = options.frontend || (await promptFrontend());
|
|
113
|
+
const frontendFolder = options.frontendFolder || (await promptFolderName(frontend, 'frontend'));
|
|
114
|
+
// Step 2: Backend
|
|
115
|
+
const backend = options.backend || (await promptBackend());
|
|
116
|
+
const backendFolder = options.backendFolder || (await promptFolderName(backend, 'backend'));
|
|
117
|
+
// Step 3: Database
|
|
118
|
+
const database = options.database || (await promptDatabase());
|
|
119
|
+
// Generate
|
|
120
|
+
const genSpinner = spinner();
|
|
121
|
+
genSpinner.start('Generating your project...');
|
|
122
|
+
try {
|
|
123
|
+
const result = await generateProject({
|
|
124
|
+
frontend,
|
|
125
|
+
backend,
|
|
126
|
+
database,
|
|
127
|
+
outputDir,
|
|
128
|
+
frontendFolderName: frontendFolder,
|
|
129
|
+
backendFolderName: backendFolder,
|
|
130
|
+
templatesPath: options.templatesPath,
|
|
131
|
+
});
|
|
132
|
+
genSpinner.stop('Project generated successfully!');
|
|
133
|
+
note(`📁 ${pc.bold(outputDir)}/\n` +
|
|
134
|
+
` ${result.frontendFolder}/\n` +
|
|
135
|
+
` ${result.backendFolder}/\n` +
|
|
136
|
+
`🗄️ Database: ${databaseOptions.find((d) => d.id === database)?.name || database}`, 'Generated Structure');
|
|
137
|
+
// Step 4: npm install (skip with --no-install flag)
|
|
138
|
+
const installScope = options.install === false ? 'skip' : await promptInstallScope();
|
|
139
|
+
if (installScope !== 'skip') {
|
|
140
|
+
const installSpinner = spinner();
|
|
141
|
+
installSpinner.start('Installing dependencies...');
|
|
142
|
+
const { installed, failed } = await installDependencies(installScope, result.frontendPath, result.backendPath);
|
|
143
|
+
if (failed.length > 0) {
|
|
144
|
+
installSpinner.stop(`${pc.green(`Installed in: ${installed.join(', ') || 'none'}`)} ${pc.red(`Failed: ${failed.join(', ') || 'none'}`)}`);
|
|
145
|
+
}
|
|
146
|
+
else if (installed.length > 0) {
|
|
147
|
+
installSpinner.stop(`${pc.green(`Dependencies installed in ${installed.join(' & ')}`)}`);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
// Final message
|
|
151
|
+
const fe = templateData[frontend];
|
|
152
|
+
const be = templateData[backend];
|
|
153
|
+
const db = databaseOptions.find((d) => d.id === database);
|
|
154
|
+
outro(`${pc.green('✨')} Your stack is ready!\n\n` +
|
|
155
|
+
` ${fe.icon} Frontend: ${pc.cyan(`cd ${outputDir}/${result.frontendFolder} && npm run dev`)} → ${pc.underline(`http://localhost:${fe.port}`)}\n` +
|
|
156
|
+
` ${be.icon} Backend: ${pc.cyan(`cd ${outputDir}/${result.backendFolder} && npm run dev`)} → ${pc.underline(`http://localhost:${be.port}`)}\n` +
|
|
157
|
+
` ${db.icon} Database: ${db.name}\n\n` +
|
|
158
|
+
` ${pc.dim('Happy coding! 🚀')}`);
|
|
159
|
+
}
|
|
160
|
+
catch (err) {
|
|
161
|
+
genSpinner.stop('Generation failed');
|
|
162
|
+
const message = err instanceof Error ? err.message : 'Unknown error';
|
|
163
|
+
cancel(`${pc.red('Error:')} ${message}`);
|
|
164
|
+
process.exit(1);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
// ---------------------------------------------------------------------------
|
|
168
|
+
// Non-interactive / flag-based mode
|
|
169
|
+
// ---------------------------------------------------------------------------
|
|
170
|
+
async function runNonInteractive(outputDir, options) {
|
|
171
|
+
if (!options.frontend || !options.backend || !options.database) {
|
|
172
|
+
console.error(pc.red('Error:') +
|
|
173
|
+
' In non-interactive mode you must provide --frontend, --backend, and --database flags.\n' +
|
|
174
|
+
` Example: npx zero-config my-project -f react -b express -d postgresql`);
|
|
175
|
+
process.exit(1);
|
|
176
|
+
}
|
|
177
|
+
const frontendFolder = options.frontendFolder || options.frontend;
|
|
178
|
+
const backendFolder = options.backendFolder || options.backend;
|
|
179
|
+
try {
|
|
180
|
+
const result = await generateProject({
|
|
181
|
+
frontend: options.frontend,
|
|
182
|
+
backend: options.backend,
|
|
183
|
+
database: options.database,
|
|
184
|
+
outputDir,
|
|
185
|
+
frontendFolderName: frontendFolder,
|
|
186
|
+
backendFolderName: backendFolder,
|
|
187
|
+
templatesPath: options.templatesPath,
|
|
188
|
+
});
|
|
189
|
+
console.log(pc.green('✅ Project generated successfully!'));
|
|
190
|
+
console.log(` ${outputDir}/${result.frontendFolder}/`);
|
|
191
|
+
console.log(` ${outputDir}/${result.backendFolder}/`);
|
|
192
|
+
if (options.install !== false) {
|
|
193
|
+
console.log(pc.cyan('\nInstalling dependencies...'));
|
|
194
|
+
const { installed, failed } = await installDependencies('both', result.frontendPath, result.backendPath);
|
|
195
|
+
if (failed.length > 0) {
|
|
196
|
+
console.log(pc.yellow(`Installed: ${installed.join(', ') || 'none'} Failed: ${failed.join(', ') || 'none'}`));
|
|
197
|
+
}
|
|
198
|
+
else {
|
|
199
|
+
console.log(pc.green(`Dependencies installed in ${installed.join(' & ')}`));
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
const fe = templateData[options.frontend];
|
|
203
|
+
const be = templateData[options.backend];
|
|
204
|
+
console.log(`\n${pc.bold('Next steps:')}`);
|
|
205
|
+
console.log(` ${fe.icon} Frontend: cd ${outputDir}/${result.frontendFolder} && npm run dev`);
|
|
206
|
+
console.log(` ${be.icon} Backend: cd ${outputDir}/${result.backendFolder} && npm run dev`);
|
|
207
|
+
}
|
|
208
|
+
catch (err) {
|
|
209
|
+
const message = err instanceof Error ? err.message : 'Unknown error';
|
|
210
|
+
console.error(pc.red('Error:'), message);
|
|
211
|
+
process.exit(1);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
// ---------------------------------------------------------------------------
|
|
215
|
+
// Commander setup
|
|
216
|
+
// ---------------------------------------------------------------------------
|
|
217
|
+
export function createCLI() {
|
|
218
|
+
const program = new Command();
|
|
219
|
+
program
|
|
220
|
+
.name('zero-config')
|
|
221
|
+
.description('Generate production-ready full-stack starter projects with zero config')
|
|
222
|
+
.version(PKG.version);
|
|
223
|
+
program
|
|
224
|
+
.command('init', { isDefault: true })
|
|
225
|
+
.description('Generate a new full-stack starter project')
|
|
226
|
+
.argument('[outputDir]', 'Output directory name', 'my-project')
|
|
227
|
+
.option('-f, --frontend <name>', 'Frontend template (react, angular, vuejs, nextjs)')
|
|
228
|
+
.option('-b, --backend <name>', 'Backend template (express, nestjs, fastify)')
|
|
229
|
+
.option('-d, --database <name>', 'Database (postgresql, mysql, mariadb, sqlserver, sqlite, cockroachdb, mongodb)')
|
|
230
|
+
.option('--frontend-folder <name>', 'Custom folder name for frontend')
|
|
231
|
+
.option('--backend-folder <name>', 'Custom folder name for backend')
|
|
232
|
+
.option('--no-install', 'Skip npm install step')
|
|
233
|
+
.option('-t, --templates-path <path>', 'Path to templates folder or GitHub zip URL')
|
|
234
|
+
.action(async (outputDir, options) => {
|
|
235
|
+
const isInteractive = !options.frontend && !options.backend && !options.database;
|
|
236
|
+
if (isInteractive) {
|
|
237
|
+
await runInteractive(outputDir, options);
|
|
238
|
+
}
|
|
239
|
+
else {
|
|
240
|
+
await runNonInteractive(outputDir, options);
|
|
241
|
+
}
|
|
242
|
+
});
|
|
243
|
+
return program;
|
|
244
|
+
}
|
|
245
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
|
package/dist/copier.d.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { TemplateFile } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Recursively read a template directory and return all files with their paths.
|
|
4
|
+
* Mirror of the backend's `readLocalTemplateFolder()`.
|
|
5
|
+
*/
|
|
6
|
+
export declare function readLocalTemplateFolder(templateName: string, basePath: string): TemplateFile[];
|
|
7
|
+
/**
|
|
8
|
+
* Write template files to a target output directory.
|
|
9
|
+
* Creates directories as needed.
|
|
10
|
+
*/
|
|
11
|
+
export declare function writeTemplateFiles(files: TemplateFile[], outputDir: string): void;
|
|
12
|
+
//# sourceMappingURL=copier.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"copier.d.ts","sourceRoot":"","sources":["../src/copier.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE/C;;;GAGG;AACH,wBAAgB,uBAAuB,CACnC,YAAY,EAAE,MAAM,EACpB,QAAQ,EAAE,MAAM,GACjB,YAAY,EAAE,CAahB;AAiBD;;;GAGG;AACH,wBAAgB,kBAAkB,CAC9B,KAAK,EAAE,YAAY,EAAE,EACrB,SAAS,EAAE,MAAM,GAClB,IAAI,CAMN"}
|
package/dist/copier.js
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
// ---------------------------------------------------------------------------
|
|
2
|
+
// Local template copier — walks a directory and returns all files as buffers
|
|
3
|
+
// Ported from zero-config/Backend/src/app.service.ts — readLocalTemplateFolder()
|
|
4
|
+
// ---------------------------------------------------------------------------
|
|
5
|
+
import * as fs from 'node:fs';
|
|
6
|
+
import * as path from 'node:path';
|
|
7
|
+
/**
|
|
8
|
+
* Recursively read a template directory and return all files with their paths.
|
|
9
|
+
* Mirror of the backend's `readLocalTemplateFolder()`.
|
|
10
|
+
*/
|
|
11
|
+
export function readLocalTemplateFolder(templateName, basePath) {
|
|
12
|
+
const templateDir = path.join(basePath, templateName);
|
|
13
|
+
const files = [];
|
|
14
|
+
if (!fs.existsSync(templateDir)) {
|
|
15
|
+
throw new Error(`Template "${templateName}" not found at: ${templateDir}. ` +
|
|
16
|
+
'Make sure the templates path is correct.');
|
|
17
|
+
}
|
|
18
|
+
walkDir(templateDir, '', files);
|
|
19
|
+
return files;
|
|
20
|
+
}
|
|
21
|
+
function walkDir(dir, relativePrefix, files) {
|
|
22
|
+
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
23
|
+
for (const entry of entries) {
|
|
24
|
+
const fullPath = path.join(dir, entry.name);
|
|
25
|
+
const relPath = relativePrefix ? `${relativePrefix}/${entry.name}` : entry.name;
|
|
26
|
+
if (entry.isDirectory()) {
|
|
27
|
+
walkDir(fullPath, relPath, files);
|
|
28
|
+
}
|
|
29
|
+
else if (entry.isFile()) {
|
|
30
|
+
files.push({ path: relPath, content: fs.readFileSync(fullPath) });
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Write template files to a target output directory.
|
|
36
|
+
* Creates directories as needed.
|
|
37
|
+
*/
|
|
38
|
+
export function writeTemplateFiles(files, outputDir) {
|
|
39
|
+
for (const file of files) {
|
|
40
|
+
const absolutePath = path.resolve(outputDir, file.path);
|
|
41
|
+
fs.mkdirSync(path.dirname(absolutePath), { recursive: true });
|
|
42
|
+
fs.writeFileSync(absolutePath, file.content);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
//# sourceMappingURL=copier.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"copier.js","sourceRoot":"","sources":["../src/copier.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,6EAA6E;AAC7E,iFAAiF;AACjF,8EAA8E;AAE9E,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAGlC;;;GAGG;AACH,MAAM,UAAU,uBAAuB,CACnC,YAAoB,EACpB,QAAgB;IAEhB,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,YAAY,CAAC,CAAC;IACtD,MAAM,KAAK,GAAmB,EAAE,CAAC;IAEjC,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,WAAW,CAAC,EAAE,CAAC;QAC9B,MAAM,IAAI,KAAK,CACX,aAAa,YAAY,mBAAmB,WAAW,IAAI;YAC3D,0CAA0C,CAC7C,CAAC;IACN,CAAC;IAED,OAAO,CAAC,WAAW,EAAE,EAAE,EAAE,KAAK,CAAC,CAAC;IAChC,OAAO,KAAK,CAAC;AACjB,CAAC;AAED,SAAS,OAAO,CAAC,GAAW,EAAE,cAAsB,EAAE,KAAqB;IACvE,MAAM,OAAO,GAAG,EAAE,CAAC,WAAW,CAAC,GAAG,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;IAE7D,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;QAC1B,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;QAC5C,MAAM,OAAO,GAAG,cAAc,CAAC,CAAC,CAAC,GAAG,cAAc,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC;QAEhF,IAAI,KAAK,CAAC,WAAW,EAAE,EAAE,CAAC;YACtB,OAAO,CAAC,QAAQ,EAAE,OAAO,EAAE,KAAK,CAAC,CAAC;QACtC,CAAC;aAAM,IAAI,KAAK,CAAC,MAAM,EAAE,EAAE,CAAC;YACxB,KAAK,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,EAAE,CAAC,YAAY,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;QACtE,CAAC;IACL,CAAC;AACL,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,kBAAkB,CAC9B,KAAqB,EACrB,SAAiB;IAEjB,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACvB,MAAM,YAAY,GAAG,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC;QACxD,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,YAAY,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAC9D,EAAE,CAAC,aAAa,CAAC,YAAY,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;IACjD,CAAC;AACL,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { TemplateFile } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Fetch a zip archive from a URL with a timeout.
|
|
4
|
+
*/
|
|
5
|
+
export declare function fetchZip(url: string): Promise<Buffer>;
|
|
6
|
+
/**
|
|
7
|
+
* Extract a single template folder from a zip buffer.
|
|
8
|
+
* Ported from the backend's `extractTemplateFolder()`.
|
|
9
|
+
*/
|
|
10
|
+
export declare function extractTemplateFolder(zipBuffer: Buffer, templateName: string): Promise<TemplateFile[]>;
|
|
11
|
+
/**
|
|
12
|
+
* Download templates from URL, then extract a specific template folder.
|
|
13
|
+
*/
|
|
14
|
+
export declare function downloadAndExtract(url: string, templateName: string): Promise<TemplateFile[]>;
|
|
15
|
+
//# sourceMappingURL=downloader.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"downloader.d.ts","sourceRoot":"","sources":["../src/downloader.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAM/C;;GAEG;AACH,wBAAsB,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAuB3D;AAED;;;GAGG;AACH,wBAAsB,qBAAqB,CACvC,SAAS,EAAE,MAAM,EACjB,YAAY,EAAE,MAAM,GACrB,OAAO,CAAC,YAAY,EAAE,CAAC,CAyCzB;AAED;;GAEG;AACH,wBAAsB,kBAAkB,CACpC,GAAG,EAAE,MAAM,EACX,YAAY,EAAE,MAAM,GACrB,OAAO,CAAC,YAAY,EAAE,CAAC,CAGzB"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
// ---------------------------------------------------------------------------
|
|
2
|
+
// GitHub zip downloader + extractor
|
|
3
|
+
// Ported from zero-config/Backend/src/app.service.ts — extractTemplateFolder()
|
|
4
|
+
// ---------------------------------------------------------------------------
|
|
5
|
+
import unzipper from 'unzipper';
|
|
6
|
+
const FETCH_TIMEOUT = 30_000; // 30 seconds
|
|
7
|
+
const MAX_ZIP_SIZE = 50 * 1024 * 1024; // 50 MB
|
|
8
|
+
const MAX_EXTRACTED_SIZE = 100 * 1024 * 1024; // 100 MB
|
|
9
|
+
/**
|
|
10
|
+
* Fetch a zip archive from a URL with a timeout.
|
|
11
|
+
*/
|
|
12
|
+
export async function fetchZip(url) {
|
|
13
|
+
const controller = new AbortController();
|
|
14
|
+
const timer = setTimeout(() => controller.abort(), FETCH_TIMEOUT);
|
|
15
|
+
try {
|
|
16
|
+
const response = await fetch(url, { signal: controller.signal });
|
|
17
|
+
if (!response.ok) {
|
|
18
|
+
throw new Error(`Failed to fetch templates from ${url} (HTTP ${response.status})`);
|
|
19
|
+
}
|
|
20
|
+
const buffer = Buffer.from(await response.arrayBuffer());
|
|
21
|
+
if (buffer.length > MAX_ZIP_SIZE) {
|
|
22
|
+
throw new Error('Template archive exceeds maximum size (50MB)');
|
|
23
|
+
}
|
|
24
|
+
return buffer;
|
|
25
|
+
}
|
|
26
|
+
finally {
|
|
27
|
+
clearTimeout(timer);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Extract a single template folder from a zip buffer.
|
|
32
|
+
* Ported from the backend's `extractTemplateFolder()`.
|
|
33
|
+
*/
|
|
34
|
+
export async function extractTemplateFolder(zipBuffer, templateName) {
|
|
35
|
+
let totalExtractedSize = 0;
|
|
36
|
+
const directory = await unzipper.Open.buffer(zipBuffer);
|
|
37
|
+
const repoPrefix = directory.files[0]?.path.split('/')[0] || '';
|
|
38
|
+
const templatePath = `${repoPrefix}/${templateName}/`;
|
|
39
|
+
const templateFiles = directory.files.filter((file) => file.path.startsWith(templatePath));
|
|
40
|
+
if (templateFiles.length === 0) {
|
|
41
|
+
throw new Error(`Template "${templateName}" not found in the archive. ` +
|
|
42
|
+
'Make sure the template name is correct and the archive contains the expected folder.');
|
|
43
|
+
}
|
|
44
|
+
const files = [];
|
|
45
|
+
for (const file of templateFiles) {
|
|
46
|
+
if (file.type !== 'File')
|
|
47
|
+
continue;
|
|
48
|
+
const content = await file.buffer();
|
|
49
|
+
totalExtractedSize += content.length;
|
|
50
|
+
if (totalExtractedSize > MAX_EXTRACTED_SIZE) {
|
|
51
|
+
throw new Error('Total extracted size exceeds maximum (100MB)');
|
|
52
|
+
}
|
|
53
|
+
const relativePath = file.path.replace(templatePath, '');
|
|
54
|
+
// Prevent path traversal
|
|
55
|
+
if (relativePath.includes('..') || relativePath.startsWith('/')) {
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
files.push({ path: relativePath, content });
|
|
59
|
+
}
|
|
60
|
+
return files;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Download templates from URL, then extract a specific template folder.
|
|
64
|
+
*/
|
|
65
|
+
export async function downloadAndExtract(url, templateName) {
|
|
66
|
+
const zipBuffer = await fetchZip(url);
|
|
67
|
+
return extractTemplateFolder(zipBuffer, templateName);
|
|
68
|
+
}
|
|
69
|
+
//# sourceMappingURL=downloader.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"downloader.js","sourceRoot":"","sources":["../src/downloader.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export interface GenerateOptions {
|
|
2
|
+
/** Frontend template key (e.g. "react") */
|
|
3
|
+
frontend: string;
|
|
4
|
+
/** Backend template key (e.g. "express") */
|
|
5
|
+
backend: string;
|
|
6
|
+
/** Database ID (e.g. "postgresql") */
|
|
7
|
+
database: string;
|
|
8
|
+
/** Output directory — the parent where frontend/backend folders go */
|
|
9
|
+
outputDir: string;
|
|
10
|
+
/** Custom folder name for the frontend template (defaults to template key) */
|
|
11
|
+
frontendFolderName?: string;
|
|
12
|
+
/** Custom folder name for the backend template (defaults to template key) */
|
|
13
|
+
backendFolderName?: string;
|
|
14
|
+
/** Explicit templates path (local folder or GitHub zip URL) */
|
|
15
|
+
templatesPath?: string;
|
|
16
|
+
}
|
|
17
|
+
export interface GenerateResult {
|
|
18
|
+
frontendPath: string;
|
|
19
|
+
backendPath: string;
|
|
20
|
+
frontendFolder: string;
|
|
21
|
+
backendFolder: string;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Generate a full-stack project:
|
|
25
|
+
* 1. Resolve template source
|
|
26
|
+
* 2. Copy/extract frontend + backend templates
|
|
27
|
+
* 3. Swap Prisma provider in backend
|
|
28
|
+
* 4. Write to output directory
|
|
29
|
+
*/
|
|
30
|
+
export declare function generateProject(opts: GenerateOptions): Promise<GenerateResult>;
|
|
31
|
+
//# sourceMappingURL=generator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generator.d.ts","sourceRoot":"","sources":["../src/generator.ts"],"names":[],"mappings":"AAcA,MAAM,WAAW,eAAe;IAC5B,2CAA2C;IAC3C,QAAQ,EAAE,MAAM,CAAC;IACjB,4CAA4C;IAC5C,OAAO,EAAE,MAAM,CAAC;IAChB,sCAAsC;IACtC,QAAQ,EAAE,MAAM,CAAC;IACjB,sEAAsE;IACtE,SAAS,EAAE,MAAM,CAAC;IAClB,8EAA8E;IAC9E,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,6EAA6E;IAC7E,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,+DAA+D;IAC/D,aAAa,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,cAAc;IAC3B,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,EAAE,MAAM,CAAC;IACvB,aAAa,EAAE,MAAM,CAAC;CACzB;AA4BD;;;;;;GAMG;AACH,wBAAsB,eAAe,CAAC,IAAI,EAAE,eAAe,GAAG,OAAO,CAAC,cAAc,CAAC,CAgEpF"}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
// ---------------------------------------------------------------------------
|
|
2
|
+
// Generator — orchestrates template resolution, file copying, and Prisma swap
|
|
3
|
+
// Ported from zero-config/Backend/src/app.service.ts — generateCombinedTemplates()
|
|
4
|
+
// ---------------------------------------------------------------------------
|
|
5
|
+
import * as path from 'node:path';
|
|
6
|
+
import * as fs from 'node:fs';
|
|
7
|
+
import { ALLOWED_TEMPLATES, FRONTEND_KEYS, BACKEND_KEYS } from './registry.js';
|
|
8
|
+
import { resolveTemplateSource } from './resolver.js';
|
|
9
|
+
import { readLocalTemplateFolder, writeTemplateFiles } from './copier.js';
|
|
10
|
+
import { fetchZip, extractTemplateFolder } from './downloader.js';
|
|
11
|
+
import { replacePrismaProvider, replacePrismaTestProvider } from './prisma-swap.js';
|
|
12
|
+
/**
|
|
13
|
+
* Validate that the selected templates are allowed and correctly paired.
|
|
14
|
+
*/
|
|
15
|
+
function validateOptions(opts) {
|
|
16
|
+
if (!ALLOWED_TEMPLATES.has(opts.frontend)) {
|
|
17
|
+
throw new Error(`Invalid frontend: "${opts.frontend}". Allowed: ${FRONTEND_KEYS.join(', ')}`);
|
|
18
|
+
}
|
|
19
|
+
if (!ALLOWED_TEMPLATES.has(opts.backend)) {
|
|
20
|
+
throw new Error(`Invalid backend: "${opts.backend}". Allowed: ${BACKEND_KEYS.join(', ')}`);
|
|
21
|
+
}
|
|
22
|
+
if (!FRONTEND_KEYS.includes(opts.frontend)) {
|
|
23
|
+
throw new Error(`"${opts.frontend}" is not a frontend template. Expected one of: ${FRONTEND_KEYS.join(', ')}`);
|
|
24
|
+
}
|
|
25
|
+
if (!BACKEND_KEYS.includes(opts.backend)) {
|
|
26
|
+
throw new Error(`"${opts.backend}" is not a backend template. Expected one of: ${BACKEND_KEYS.join(', ')}`);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Generate a full-stack project:
|
|
31
|
+
* 1. Resolve template source
|
|
32
|
+
* 2. Copy/extract frontend + backend templates
|
|
33
|
+
* 3. Swap Prisma provider in backend
|
|
34
|
+
* 4. Write to output directory
|
|
35
|
+
*/
|
|
36
|
+
export async function generateProject(opts) {
|
|
37
|
+
validateOptions(opts);
|
|
38
|
+
const source = resolveTemplateSource(opts.templatesPath);
|
|
39
|
+
const frontendFolder = opts.frontendFolderName || opts.frontend;
|
|
40
|
+
const backendFolder = opts.backendFolderName || opts.backend;
|
|
41
|
+
// Create output directory
|
|
42
|
+
const outputDir = path.resolve(process.cwd(), opts.outputDir);
|
|
43
|
+
fs.mkdirSync(outputDir, { recursive: true });
|
|
44
|
+
// --- Resolve templates ---
|
|
45
|
+
let frontendFiles;
|
|
46
|
+
let backendFiles;
|
|
47
|
+
if (source.type === 'local') {
|
|
48
|
+
frontendFiles = readLocalTemplateFolder(opts.frontend, source.path);
|
|
49
|
+
backendFiles = readLocalTemplateFolder(opts.backend, source.path);
|
|
50
|
+
}
|
|
51
|
+
else {
|
|
52
|
+
// Download once, extract both templates from the same zip
|
|
53
|
+
let zipBuffer;
|
|
54
|
+
try {
|
|
55
|
+
zipBuffer = await fetchZip(source.path);
|
|
56
|
+
}
|
|
57
|
+
catch (err) {
|
|
58
|
+
throw new Error(`Could not download templates from ${source.path}. ` +
|
|
59
|
+
'Check your internet connection or set ZERO_CONFIG_TEMPLATES_PATH to a local folder.');
|
|
60
|
+
}
|
|
61
|
+
try {
|
|
62
|
+
[frontendFiles, backendFiles] = await Promise.all([
|
|
63
|
+
extractTemplateFolder(zipBuffer, opts.frontend),
|
|
64
|
+
extractTemplateFolder(zipBuffer, opts.backend),
|
|
65
|
+
]);
|
|
66
|
+
}
|
|
67
|
+
catch (err) {
|
|
68
|
+
throw new Error(`Failed to extract templates from the archive: ${err instanceof Error ? err.message : 'Unknown error'}`);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
// --- Swap Prisma provider in backend ---
|
|
72
|
+
replacePrismaProvider(backendFiles, opts.database);
|
|
73
|
+
replacePrismaTestProvider(backendFiles, opts.database);
|
|
74
|
+
// --- Prefix paths with folder names ---
|
|
75
|
+
for (const file of frontendFiles) {
|
|
76
|
+
file.path = `${frontendFolder}/${file.path}`;
|
|
77
|
+
}
|
|
78
|
+
for (const file of backendFiles) {
|
|
79
|
+
file.path = `${backendFolder}/${file.path}`;
|
|
80
|
+
}
|
|
81
|
+
// --- Write to disk ---
|
|
82
|
+
const allFiles = [...frontendFiles, ...backendFiles];
|
|
83
|
+
writeTemplateFiles(allFiles, outputDir);
|
|
84
|
+
return {
|
|
85
|
+
frontendPath: path.resolve(outputDir, frontendFolder),
|
|
86
|
+
backendPath: path.resolve(outputDir, backendFolder),
|
|
87
|
+
frontendFolder,
|
|
88
|
+
backendFolder,
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
//# sourceMappingURL=generator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generator.js","sourceRoot":"","sources":["../src/generator.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// ---------------------------------------------------------------------------
|
|
3
|
+
// Entry point for the zero-config CLI.
|
|
4
|
+
// Bootstraps Commander and parses command-line arguments.
|
|
5
|
+
// ---------------------------------------------------------------------------
|
|
6
|
+
import { createCLI } from './cli.js';
|
|
7
|
+
const program = createCLI();
|
|
8
|
+
program.parse(process.argv);
|
|
9
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA,8EAA8E;AAC9E,uCAAuC;AACvC,0DAA0D;AAC1D,8EAA8E;AAE9E,OAAO,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAErC,MAAM,OAAO,GAAG,SAAS,EAAE,CAAC;AAE5B,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export type InstallScope = 'both' | 'backend' | 'frontend' | 'skip';
|
|
2
|
+
/**
|
|
3
|
+
* Run npm install in one or both generated project directories.
|
|
4
|
+
* Returns a summary of what was installed.
|
|
5
|
+
*/
|
|
6
|
+
export declare function installDependencies(scope: InstallScope, frontendPath: string, backendPath: string): Promise<{
|
|
7
|
+
installed: string[];
|
|
8
|
+
failed: string[];
|
|
9
|
+
}>;
|
|
10
|
+
//# sourceMappingURL=installer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"installer.d.ts","sourceRoot":"","sources":["../src/installer.ts"],"names":[],"mappings":"AAMA,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,SAAS,GAAG,UAAU,GAAG,MAAM,CAAC;AAEpE;;;GAGG;AACH,wBAAsB,mBAAmB,CACrC,KAAK,EAAE,YAAY,EACnB,YAAY,EAAE,MAAM,EACpB,WAAW,EAAE,MAAM,GACpB,OAAO,CAAC;IAAE,SAAS,EAAE,MAAM,EAAE,CAAC;IAAC,MAAM,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC,CAyBpD"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
// ---------------------------------------------------------------------------
|
|
2
|
+
// npm install runner — installs dependencies in generated project folders
|
|
3
|
+
// ---------------------------------------------------------------------------
|
|
4
|
+
import { execSync } from 'node:child_process';
|
|
5
|
+
/**
|
|
6
|
+
* Run npm install in one or both generated project directories.
|
|
7
|
+
* Returns a summary of what was installed.
|
|
8
|
+
*/
|
|
9
|
+
export async function installDependencies(scope, frontendPath, backendPath) {
|
|
10
|
+
const installed = [];
|
|
11
|
+
const failed = [];
|
|
12
|
+
const tasks = [
|
|
13
|
+
{ label: 'Frontend', path: frontendPath, shouldRun: scope === 'both' || scope === 'frontend' },
|
|
14
|
+
{ label: 'Backend', path: backendPath, shouldRun: scope === 'both' || scope === 'backend' },
|
|
15
|
+
];
|
|
16
|
+
for (const task of tasks) {
|
|
17
|
+
if (!task.shouldRun)
|
|
18
|
+
continue;
|
|
19
|
+
try {
|
|
20
|
+
execSync('npm install', {
|
|
21
|
+
cwd: task.path,
|
|
22
|
+
stdio: 'pipe',
|
|
23
|
+
timeout: 120_000, // 2 minutes per folder
|
|
24
|
+
});
|
|
25
|
+
installed.push(task.label);
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
failed.push(task.label);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
return { installed, failed };
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=installer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"installer.js","sourceRoot":"","sources":["../src/installer.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,0EAA0E;AAC1E,8EAA8E;AAE9E,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAI9C;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,mBAAmB,CACrC,KAAmB,EACnB,YAAoB,EACpB,WAAmB;IAEnB,MAAM,SAAS,GAAa,EAAE,CAAC;IAC/B,MAAM,MAAM,GAAa,EAAE,CAAC;IAE5B,MAAM,KAAK,GAA0D;QACjE,EAAE,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,YAAY,EAAE,SAAS,EAAE,KAAK,KAAK,MAAM,IAAI,KAAK,KAAK,UAAU,EAAE;QAC9F,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,WAAW,EAAE,SAAS,EAAE,KAAK,KAAK,MAAM,IAAI,KAAK,KAAK,SAAS,EAAE;KAC9F,CAAC;IAEF,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACvB,IAAI,CAAC,IAAI,CAAC,SAAS;YAAE,SAAS;QAE9B,IAAI,CAAC;YACD,QAAQ,CAAC,aAAa,EAAE;gBACpB,GAAG,EAAE,IAAI,CAAC,IAAI;gBACd,KAAK,EAAE,MAAM;gBACb,OAAO,EAAE,OAAO,EAAE,uBAAuB;aAC5C,CAAC,CAAC;YACH,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAC/B,CAAC;QAAC,MAAM,CAAC;YACL,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAC5B,CAAC;IACL,CAAC;IAED,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,CAAC;AACjC,CAAC"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { TemplateFile } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Replace the Prisma datasource provider in all schema.prisma files.
|
|
4
|
+
*
|
|
5
|
+
* Targets only the `datasource db { ... }` block's provider line,
|
|
6
|
+
* not the `generator client { ... }` block.
|
|
7
|
+
*
|
|
8
|
+
* Ported from the backend's `replacePrismaProvider()`.
|
|
9
|
+
*/
|
|
10
|
+
export declare function replacePrismaProvider(files: TemplateFile[], database: string | undefined): void;
|
|
11
|
+
/**
|
|
12
|
+
* Replace the test Prisma schema provider (schema.test.prisma) as well.
|
|
13
|
+
* Some templates have a separate test database schema.
|
|
14
|
+
*/
|
|
15
|
+
export declare function replacePrismaTestProvider(files: TemplateFile[], database: string | undefined): void;
|
|
16
|
+
//# sourceMappingURL=prisma-swap.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prisma-swap.d.ts","sourceRoot":"","sources":["../src/prisma-swap.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAG/C;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CACjC,KAAK,EAAE,YAAY,EAAE,EACrB,QAAQ,EAAE,MAAM,GAAG,SAAS,GAC7B,IAAI,CAwBN;AAED;;;GAGG;AACH,wBAAgB,yBAAyB,CACrC,KAAK,EAAE,YAAY,EAAE,EACrB,QAAQ,EAAE,MAAM,GAAG,SAAS,GAC7B,IAAI,CAmBN"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
// ---------------------------------------------------------------------------
|
|
2
|
+
// Prisma provider replacer
|
|
3
|
+
// Ported from zero-config/Backend/src/app.service.ts — replacePrismaProvider()
|
|
4
|
+
// ---------------------------------------------------------------------------
|
|
5
|
+
import { PROVIDER_MAP } from './registry.js';
|
|
6
|
+
/**
|
|
7
|
+
* Replace the Prisma datasource provider in all schema.prisma files.
|
|
8
|
+
*
|
|
9
|
+
* Targets only the `datasource db { ... }` block's provider line,
|
|
10
|
+
* not the `generator client { ... }` block.
|
|
11
|
+
*
|
|
12
|
+
* Ported from the backend's `replacePrismaProvider()`.
|
|
13
|
+
*/
|
|
14
|
+
export function replacePrismaProvider(files, database) {
|
|
15
|
+
if (!database)
|
|
16
|
+
return;
|
|
17
|
+
const provider = PROVIDER_MAP[database];
|
|
18
|
+
if (!provider) {
|
|
19
|
+
console.warn(`[warn] Unknown database "${database}", skipping Prisma provider replacement`);
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
for (const file of files) {
|
|
23
|
+
if (!file.path.endsWith('prisma/schema.prisma'))
|
|
24
|
+
continue;
|
|
25
|
+
const content = file.content.toString('utf-8');
|
|
26
|
+
// Match: datasource db { ... provider = "current_provider" ... }
|
|
27
|
+
const updated = content.replace(/(datasource db\s*\{[\s\S]*?provider\s*=\s*")[a-z0-9\-_]+(")/, `$1${provider}$2`);
|
|
28
|
+
if (updated !== content) {
|
|
29
|
+
file.content = Buffer.from(updated, 'utf-8');
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Replace the test Prisma schema provider (schema.test.prisma) as well.
|
|
35
|
+
* Some templates have a separate test database schema.
|
|
36
|
+
*/
|
|
37
|
+
export function replacePrismaTestProvider(files, database) {
|
|
38
|
+
if (!database)
|
|
39
|
+
return;
|
|
40
|
+
const provider = PROVIDER_MAP[database];
|
|
41
|
+
if (!provider)
|
|
42
|
+
return;
|
|
43
|
+
for (const file of files) {
|
|
44
|
+
if (!file.path.endsWith('prisma/schema.test.prisma'))
|
|
45
|
+
continue;
|
|
46
|
+
const content = file.content.toString('utf-8');
|
|
47
|
+
const updated = content.replace(/(datasource db\s*\{[\s\S]*?provider\s*=\s*")[a-z0-9\-_]+(")/, `$1${provider}$2`);
|
|
48
|
+
if (updated !== content) {
|
|
49
|
+
file.content = Buffer.from(updated, 'utf-8');
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=prisma-swap.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prisma-swap.js","sourceRoot":"","sources":["../src/prisma-swap.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,2BAA2B;AAC3B,+EAA+E;AAC/E,8EAA8E;AAG9E,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAE7C;;;;;;;GAOG;AACH,MAAM,UAAU,qBAAqB,CACjC,KAAqB,EACrB,QAA4B;IAE5B,IAAI,CAAC,QAAQ;QAAE,OAAO;IAEtB,MAAM,QAAQ,GAAG,YAAY,CAAC,QAAQ,CAAC,CAAC;IACxC,IAAI,CAAC,QAAQ,EAAE,CAAC;QACZ,OAAO,CAAC,IAAI,CAAC,4BAA4B,QAAQ,yCAAyC,CAAC,CAAC;QAC5F,OAAO;IACX,CAAC;IAED,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACvB,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,sBAAsB,CAAC;YAAE,SAAS;QAE1D,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;QAE/C,iEAAiE;QACjE,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,CAC3B,6DAA6D,EAC7D,KAAK,QAAQ,IAAI,CACpB,CAAC;QAEF,IAAI,OAAO,KAAK,OAAO,EAAE,CAAC;YACtB,IAAI,CAAC,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QACjD,CAAC;IACL,CAAC;AACL,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,yBAAyB,CACrC,KAAqB,EACrB,QAA4B;IAE5B,IAAI,CAAC,QAAQ;QAAE,OAAO;IAEtB,MAAM,QAAQ,GAAG,YAAY,CAAC,QAAQ,CAAC,CAAC;IACxC,IAAI,CAAC,QAAQ;QAAE,OAAO;IAEtB,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACvB,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,2BAA2B,CAAC;YAAE,SAAS;QAE/D,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;QAC/C,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,CAC3B,6DAA6D,EAC7D,KAAK,QAAQ,IAAI,CACpB,CAAC;QAEF,IAAI,OAAO,KAAK,OAAO,EAAE,CAAC;YACtB,IAAI,CAAC,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QACjD,CAAC;IACL,CAAC;AACL,CAAC"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
export type TemplateType = 'frontend' | 'backend';
|
|
2
|
+
export interface FrontendTemplate {
|
|
3
|
+
name: string;
|
|
4
|
+
fullName: string;
|
|
5
|
+
icon: string;
|
|
6
|
+
type: 'frontend';
|
|
7
|
+
version: string;
|
|
8
|
+
port: number;
|
|
9
|
+
description: string;
|
|
10
|
+
technologies: string;
|
|
11
|
+
}
|
|
12
|
+
export interface BackendTemplate {
|
|
13
|
+
name: string;
|
|
14
|
+
fullName: string;
|
|
15
|
+
icon: string;
|
|
16
|
+
type: 'backend';
|
|
17
|
+
version: string;
|
|
18
|
+
port: number;
|
|
19
|
+
description: string;
|
|
20
|
+
technologies: string;
|
|
21
|
+
database: string;
|
|
22
|
+
databaseIcon: string;
|
|
23
|
+
orm: string;
|
|
24
|
+
}
|
|
25
|
+
export type Template = FrontendTemplate | BackendTemplate;
|
|
26
|
+
export interface DatabaseOption {
|
|
27
|
+
id: string;
|
|
28
|
+
name: string;
|
|
29
|
+
icon: string;
|
|
30
|
+
description: string;
|
|
31
|
+
defaultOrm: string;
|
|
32
|
+
}
|
|
33
|
+
export declare const templateData: Record<string, Template>;
|
|
34
|
+
export declare const ALL_TEMPLATE_KEYS: string[];
|
|
35
|
+
export declare const FRONTEND_KEYS: string[];
|
|
36
|
+
export declare const BACKEND_KEYS: string[];
|
|
37
|
+
export declare const databaseOptions: DatabaseOption[];
|
|
38
|
+
export declare const PROVIDER_MAP: Record<string, string>;
|
|
39
|
+
export declare const ALLOWED_TEMPLATES: ReadonlySet<string>;
|
|
40
|
+
//# sourceMappingURL=registry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../src/registry.ts"],"names":[],"mappings":"AAQA,MAAM,MAAM,YAAY,GAAG,UAAU,GAAG,SAAS,CAAC;AAElD,MAAM,WAAW,gBAAgB;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,UAAU,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,eAAe;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,SAAS,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,GAAG,EAAE,MAAM,CAAC;CACf;AAED,MAAM,MAAM,QAAQ,GAAG,gBAAgB,GAAG,eAAe,CAAC;AAE1D,MAAM,WAAW,cAAc;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;CACtB;AAID,eAAO,MAAM,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,QAAQ,CAgFjD,CAAC;AAIF,eAAO,MAAM,iBAAiB,UAA4B,CAAC;AAE3D,eAAO,MAAM,aAAa,UAEF,CAAC;AAEzB,eAAO,MAAM,YAAY,UAED,CAAC;AAIzB,eAAO,MAAM,eAAe,EAAE,cAAc,EAQ3C,CAAC;AAMF,eAAO,MAAM,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAQ/C,CAAC;AAKF,eAAO,MAAM,iBAAiB,EAAE,WAAW,CAAC,MAAM,CAA8B,CAAC"}
|
package/dist/registry.js
ADDED
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
// ---------------------------------------------------------------------------
|
|
2
|
+
// Registry — single source of truth for all templates, databases, and mappings
|
|
3
|
+
// Ported from zero-config/frontend/app/data/templates.ts and
|
|
4
|
+
// zero-config/frontend/app/types/templates.ts
|
|
5
|
+
// ---------------------------------------------------------------------------
|
|
6
|
+
// --- Template Data --------------------------------------------------------
|
|
7
|
+
export const templateData = {
|
|
8
|
+
react: {
|
|
9
|
+
name: 'React',
|
|
10
|
+
fullName: 'React + Vite',
|
|
11
|
+
icon: '⚛️',
|
|
12
|
+
type: 'frontend',
|
|
13
|
+
version: 'v19',
|
|
14
|
+
port: 5173,
|
|
15
|
+
description: 'Auth context, Protected routes, Token refresh',
|
|
16
|
+
technologies: 'React 19, Vite 7.2, React Router v6, TypeScript 5.9, Tailwind CSS',
|
|
17
|
+
},
|
|
18
|
+
angular: {
|
|
19
|
+
name: 'Angular',
|
|
20
|
+
fullName: 'Angular + SSR',
|
|
21
|
+
icon: '🅰️',
|
|
22
|
+
type: 'frontend',
|
|
23
|
+
version: 'v21',
|
|
24
|
+
port: 4200,
|
|
25
|
+
description: 'Auth guards, Signals, Tailwind CSS 4',
|
|
26
|
+
technologies: 'Angular 21, SSR, Signals, Tailwind CSS 4.x, Vitest, RxJS',
|
|
27
|
+
},
|
|
28
|
+
vuejs: {
|
|
29
|
+
name: 'Vue.js',
|
|
30
|
+
fullName: 'Vue.js + Vite',
|
|
31
|
+
icon: '💚',
|
|
32
|
+
type: 'frontend',
|
|
33
|
+
version: 'v3',
|
|
34
|
+
port: 5173,
|
|
35
|
+
description: 'Pinia store, Composition API, Oxlint',
|
|
36
|
+
technologies: 'Vue 3.5, Pinia 3.0, Vue Router 4.6, Vite 7.3, Oxlint, Tailwind CSS',
|
|
37
|
+
},
|
|
38
|
+
nextjs: {
|
|
39
|
+
name: 'Next.js',
|
|
40
|
+
fullName: 'Next.js App Router',
|
|
41
|
+
icon: '▲',
|
|
42
|
+
type: 'frontend',
|
|
43
|
+
version: 'v16',
|
|
44
|
+
port: 3000,
|
|
45
|
+
description: 'SQLite auth, Server Actions, Full CRUD',
|
|
46
|
+
technologies: 'Next.js 16, SQLite, Server Actions, Tailwind CSS 4',
|
|
47
|
+
},
|
|
48
|
+
express: {
|
|
49
|
+
name: 'Express',
|
|
50
|
+
fullName: 'Express.js',
|
|
51
|
+
icon: '🚀',
|
|
52
|
+
type: 'backend',
|
|
53
|
+
version: 'v4.18',
|
|
54
|
+
port: 5000,
|
|
55
|
+
description: 'Minimalist, unopinionated, Auto-migration',
|
|
56
|
+
technologies: 'Express 4.18, Prisma 6, JWT, bcrypt, express-validator',
|
|
57
|
+
database: 'PostgreSQL',
|
|
58
|
+
databaseIcon: '🐘',
|
|
59
|
+
orm: 'Prisma',
|
|
60
|
+
},
|
|
61
|
+
nestjs: {
|
|
62
|
+
name: 'NestJS',
|
|
63
|
+
fullName: 'NestJS',
|
|
64
|
+
icon: '🐱',
|
|
65
|
+
type: 'backend',
|
|
66
|
+
version: 'v11',
|
|
67
|
+
port: 5000,
|
|
68
|
+
description: 'Modular architecture, Passport.js',
|
|
69
|
+
technologies: 'NestJS 11, Prisma 6.2, Passport.js, JWT, class-validator',
|
|
70
|
+
database: 'PostgreSQL',
|
|
71
|
+
databaseIcon: '🐘',
|
|
72
|
+
orm: 'Prisma',
|
|
73
|
+
},
|
|
74
|
+
fastify: {
|
|
75
|
+
name: 'Fastify',
|
|
76
|
+
fullName: 'Fastify',
|
|
77
|
+
icon: '⚡',
|
|
78
|
+
type: 'backend',
|
|
79
|
+
version: 'v5',
|
|
80
|
+
port: 5000,
|
|
81
|
+
description: 'High-performance, low overhead',
|
|
82
|
+
technologies: 'Fastify 5, Prisma, JWT, Swagger, TypeScript',
|
|
83
|
+
database: 'PostgreSQL',
|
|
84
|
+
databaseIcon: '🐘',
|
|
85
|
+
orm: 'Prisma',
|
|
86
|
+
},
|
|
87
|
+
};
|
|
88
|
+
// --- Lists ----------------------------------------------------------------
|
|
89
|
+
export const ALL_TEMPLATE_KEYS = Object.keys(templateData);
|
|
90
|
+
export const FRONTEND_KEYS = Object.entries(templateData)
|
|
91
|
+
.filter(([, t]) => t.type === 'frontend')
|
|
92
|
+
.map(([key]) => key);
|
|
93
|
+
export const BACKEND_KEYS = Object.entries(templateData)
|
|
94
|
+
.filter(([, t]) => t.type === 'backend')
|
|
95
|
+
.map(([key]) => key);
|
|
96
|
+
// --- Database Options -----------------------------------------------------
|
|
97
|
+
export const databaseOptions = [
|
|
98
|
+
{ id: 'postgresql', name: 'PostgreSQL', icon: '🐘', description: 'Relational SQL database', defaultOrm: 'Prisma' },
|
|
99
|
+
{ id: 'mysql', name: 'MySQL', icon: '🐬', description: 'Popular open-source RDBMS', defaultOrm: 'Prisma' },
|
|
100
|
+
{ id: 'mariadb', name: 'MariaDB', icon: '🪶', description: 'MySQL-compatible fork', defaultOrm: 'Prisma' },
|
|
101
|
+
{ id: 'sqlserver', name: 'SQL Server', icon: '🟦', description: 'Microsoft enterprise database', defaultOrm: 'Prisma' },
|
|
102
|
+
{ id: 'sqlite', name: 'SQLite', icon: '📁', description: 'Embedded file-based database', defaultOrm: 'Prisma' },
|
|
103
|
+
{ id: 'cockroachdb', name: 'CockroachDB', icon: '🪳', description: 'Cloud-native distributed SQL', defaultOrm: 'Prisma' },
|
|
104
|
+
{ id: 'mongodb', name: 'MongoDB', icon: '🍃', description: 'NoSQL document database', defaultOrm: 'Prisma' },
|
|
105
|
+
];
|
|
106
|
+
// --- Provider Map ---------------------------------------------------------
|
|
107
|
+
// Maps user-facing database IDs to Prisma provider strings.
|
|
108
|
+
// Ported from zero-config/Backend/src/app.service.ts
|
|
109
|
+
export const PROVIDER_MAP = {
|
|
110
|
+
postgresql: 'postgresql',
|
|
111
|
+
mysql: 'mysql',
|
|
112
|
+
mariadb: 'mysql',
|
|
113
|
+
sqlserver: 'sqlserver',
|
|
114
|
+
sqlite: 'sqlite',
|
|
115
|
+
cockroachdb: 'cockroachdb',
|
|
116
|
+
mongodb: 'mongodb',
|
|
117
|
+
};
|
|
118
|
+
// --- Allowed Template Whitelist -------------------------------------------
|
|
119
|
+
// Ported from zero-config/Backend/src/app.service.ts
|
|
120
|
+
export const ALLOWED_TEMPLATES = new Set(ALL_TEMPLATE_KEYS);
|
|
121
|
+
//# sourceMappingURL=registry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.js","sourceRoot":"","sources":["../src/registry.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export type SourceType = 'local' | 'url';
|
|
2
|
+
export interface ResolvedSource {
|
|
3
|
+
type: SourceType;
|
|
4
|
+
path: string;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Resolve where templates live:
|
|
8
|
+
* 1. If `TEMPLATES_PATH` env var is set, use it (local path or URL)
|
|
9
|
+
* 2. Auto-detect sibling `zero-config-templates/` folder relative to CWD
|
|
10
|
+
* 3. Fall back to the GitHub zip URL
|
|
11
|
+
*
|
|
12
|
+
* Ported from the backend's `resolveTemplateSource()`.
|
|
13
|
+
*/
|
|
14
|
+
export declare function resolveTemplateSource(templatesPath?: string): ResolvedSource;
|
|
15
|
+
//# sourceMappingURL=resolver.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resolver.d.ts","sourceRoot":"","sources":["../src/resolver.ts"],"names":[],"mappings":"AAQA,MAAM,MAAM,UAAU,GAAG,OAAO,GAAG,KAAK,CAAC;AAEzC,MAAM,WAAW,cAAc;IAC3B,IAAI,EAAE,UAAU,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;CAChB;AAED;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CAAC,aAAa,CAAC,EAAE,MAAM,GAAG,cAAc,CA4B5E"}
|
package/dist/resolver.js
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
// ---------------------------------------------------------------------------
|
|
2
|
+
// Template source resolver
|
|
3
|
+
// Ported from zero-config/Backend/src/app.service.ts — resolveTemplateSource()
|
|
4
|
+
// ---------------------------------------------------------------------------
|
|
5
|
+
import * as fs from 'node:fs';
|
|
6
|
+
import * as path from 'node:path';
|
|
7
|
+
/**
|
|
8
|
+
* Resolve where templates live:
|
|
9
|
+
* 1. If `TEMPLATES_PATH` env var is set, use it (local path or URL)
|
|
10
|
+
* 2. Auto-detect sibling `zero-config-templates/` folder relative to CWD
|
|
11
|
+
* 3. Fall back to the GitHub zip URL
|
|
12
|
+
*
|
|
13
|
+
* Ported from the backend's `resolveTemplateSource()`.
|
|
14
|
+
*/
|
|
15
|
+
export function resolveTemplateSource(templatesPath) {
|
|
16
|
+
const envPath = templatesPath || process.env.ZERO_CONFIG_TEMPLATES_PATH || '';
|
|
17
|
+
if (envPath) {
|
|
18
|
+
return {
|
|
19
|
+
type: envPath.startsWith('http://') || envPath.startsWith('https://') ? 'url' : 'local',
|
|
20
|
+
path: envPath,
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
// Auto-detect: check CWD, then parent, then grandparent
|
|
24
|
+
const candidates = [
|
|
25
|
+
path.resolve(process.cwd(), 'zero-config-templates'),
|
|
26
|
+
path.resolve(process.cwd(), '..', 'zero-config-templates'),
|
|
27
|
+
path.resolve(process.cwd(), '..', '..', 'zero-config-templates'),
|
|
28
|
+
];
|
|
29
|
+
for (const candidate of candidates) {
|
|
30
|
+
if (fs.existsSync(candidate)) {
|
|
31
|
+
return { type: 'local', path: candidate };
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
// Fallback — GitHub default branch zip
|
|
35
|
+
return {
|
|
36
|
+
type: 'url',
|
|
37
|
+
path: 'https://github.com/dhuruvandb/zero-config-templates/archive/refs/heads/main.zip',
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=resolver.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resolver.js","sourceRoot":"","sources":["../src/resolver.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,2BAA2B;AAC3B,+EAA+E;AAC/E,8EAA8E;AAE9E,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AASlC;;;;;;;GAOG;AACH,MAAM,UAAU,qBAAqB,CAAC,aAAsB;IACxD,MAAM,OAAO,GAAG,aAAa,IAAI,OAAO,CAAC,GAAG,CAAC,0BAA0B,IAAI,EAAE,CAAC;IAE9E,IAAI,OAAO,EAAE,CAAC;QACV,OAAO;YACH,IAAI,EAAE,OAAO,CAAC,UAAU,CAAC,SAAS,CAAC,IAAI,OAAO,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO;YACvF,IAAI,EAAE,OAAO;SAChB,CAAC;IACN,CAAC;IAED,wDAAwD;IACxD,MAAM,UAAU,GAAG;QACf,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,uBAAuB,CAAC;QACpD,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,IAAI,EAAE,uBAAuB,CAAC;QAC1D,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,uBAAuB,CAAC;KACnE,CAAC;IAEF,KAAK,MAAM,SAAS,IAAI,UAAU,EAAE,CAAC;QACjC,IAAI,EAAE,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;YAC3B,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC;QAC9C,CAAC;IACL,CAAC;IAED,uCAAuC;IACvC,OAAO;QACH,IAAI,EAAE,KAAK;QACX,IAAI,EAAE,iFAAiF;KAC1F,CAAC;AACN,CAAC"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAKA,uDAAuD;AACvD,MAAM,WAAW,YAAY;IACzB,8DAA8D;IAC9D,IAAI,EAAE,MAAM,CAAC;IACb,uBAAuB;IACvB,OAAO,EAAE,MAAM,CAAC;CACnB"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
// ---------------------------------------------------------------------------
|
|
2
|
+
// Shared types for template file handling
|
|
3
|
+
// Ported from zero-config/Backend/src/app.service.ts (TemplateFile interface)
|
|
4
|
+
// ---------------------------------------------------------------------------
|
|
5
|
+
export {};
|
|
6
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,0CAA0C;AAC1C,8EAA8E;AAC9E,8EAA8E"}
|
package/package.json
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "zero-config-cli",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Generate production-ready full-stack projects from the terminal — zero config required.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": "dist/index.js",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": "./dist/index.js"
|
|
9
|
+
},
|
|
10
|
+
"engines": {
|
|
11
|
+
"node": ">=18"
|
|
12
|
+
},
|
|
13
|
+
"scripts": {
|
|
14
|
+
"dev": "tsx src/index.ts",
|
|
15
|
+
"build": "tsc",
|
|
16
|
+
"start": "node dist/index.js",
|
|
17
|
+
"test": "vitest run",
|
|
18
|
+
"test:watch": "vitest",
|
|
19
|
+
"prepublishOnly": "npm run build"
|
|
20
|
+
},
|
|
21
|
+
"files": [
|
|
22
|
+
"dist",
|
|
23
|
+
"README.md",
|
|
24
|
+
"LICENSE"
|
|
25
|
+
],
|
|
26
|
+
"keywords": [
|
|
27
|
+
"zero-config",
|
|
28
|
+
"starter",
|
|
29
|
+
"generator",
|
|
30
|
+
"full-stack",
|
|
31
|
+
"react",
|
|
32
|
+
"angular",
|
|
33
|
+
"vue",
|
|
34
|
+
"nextjs",
|
|
35
|
+
"express",
|
|
36
|
+
"nestjs",
|
|
37
|
+
"fastify"
|
|
38
|
+
],
|
|
39
|
+
"license": "MIT",
|
|
40
|
+
"dependencies": {
|
|
41
|
+
"@clack/prompts": "^0.10.1",
|
|
42
|
+
"commander": "^13.1.0",
|
|
43
|
+
"picocolors": "^1.1.1",
|
|
44
|
+
"unzipper": "^0.12.3"
|
|
45
|
+
},
|
|
46
|
+
"devDependencies": {
|
|
47
|
+
"@types/node": "^22.10.7",
|
|
48
|
+
"@types/unzipper": "^0.10.10",
|
|
49
|
+
"tsx": "^4.19.2",
|
|
50
|
+
"typescript": "^5.7.3",
|
|
51
|
+
"vitest": "^3.0.0"
|
|
52
|
+
}
|
|
53
|
+
}
|