archetype-engine 2.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/LICENSE +21 -0
- package/README.md +241 -0
- package/dist/src/ai/adapters/anthropic.d.ts +31 -0
- package/dist/src/ai/adapters/anthropic.d.ts.map +1 -0
- package/dist/src/ai/adapters/anthropic.js +75 -0
- package/dist/src/ai/adapters/openai.d.ts +33 -0
- package/dist/src/ai/adapters/openai.d.ts.map +1 -0
- package/dist/src/ai/adapters/openai.js +120 -0
- package/dist/src/ai/adapters/vercel.d.ts +434 -0
- package/dist/src/ai/adapters/vercel.d.ts.map +1 -0
- package/dist/src/ai/adapters/vercel.js +162 -0
- package/dist/src/ai/index.d.ts +492 -0
- package/dist/src/ai/index.d.ts.map +1 -0
- package/dist/src/ai/index.js +71 -0
- package/dist/src/ai/state.d.ts +13 -0
- package/dist/src/ai/state.d.ts.map +1 -0
- package/dist/src/ai/state.js +215 -0
- package/dist/src/ai/tools.d.ts +13 -0
- package/dist/src/ai/tools.d.ts.map +1 -0
- package/dist/src/ai/tools.js +257 -0
- package/dist/src/ai/types.d.ts +196 -0
- package/dist/src/ai/types.d.ts.map +1 -0
- package/dist/src/ai/types.js +9 -0
- package/dist/src/cli.d.ts +3 -0
- package/dist/src/cli.d.ts.map +1 -0
- package/dist/src/cli.js +540 -0
- package/dist/src/core/utils.d.ts +27 -0
- package/dist/src/core/utils.d.ts.map +1 -0
- package/dist/src/core/utils.js +56 -0
- package/dist/src/entity.d.ts +165 -0
- package/dist/src/entity.d.ts.map +1 -0
- package/dist/src/entity.js +108 -0
- package/dist/src/fields.d.ts +207 -0
- package/dist/src/fields.d.ts.map +1 -0
- package/dist/src/fields.js +291 -0
- package/dist/src/generators/erd-ir.d.ts +10 -0
- package/dist/src/generators/erd-ir.d.ts.map +1 -0
- package/dist/src/generators/erd-ir.js +119 -0
- package/dist/src/index.d.ts +51 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +101 -0
- package/dist/src/init/dependencies.d.ts +31 -0
- package/dist/src/init/dependencies.d.ts.map +1 -0
- package/dist/src/init/dependencies.js +101 -0
- package/dist/src/init/entity-templates.d.ts +42 -0
- package/dist/src/init/entity-templates.d.ts.map +1 -0
- package/dist/src/init/entity-templates.js +367 -0
- package/dist/src/init/index.d.ts +10 -0
- package/dist/src/init/index.d.ts.map +1 -0
- package/dist/src/init/index.js +250 -0
- package/dist/src/init/prompts.d.ts +11 -0
- package/dist/src/init/prompts.d.ts.map +1 -0
- package/dist/src/init/prompts.js +275 -0
- package/dist/src/init/templates.d.ts +24 -0
- package/dist/src/init/templates.d.ts.map +1 -0
- package/dist/src/init/templates.js +587 -0
- package/dist/src/json/index.d.ts +11 -0
- package/dist/src/json/index.d.ts.map +1 -0
- package/dist/src/json/index.js +26 -0
- package/dist/src/json/parser.d.ts +61 -0
- package/dist/src/json/parser.d.ts.map +1 -0
- package/dist/src/json/parser.js +309 -0
- package/dist/src/json/types.d.ts +275 -0
- package/dist/src/json/types.d.ts.map +1 -0
- package/dist/src/json/types.js +10 -0
- package/dist/src/manifest.d.ts +147 -0
- package/dist/src/manifest.d.ts.map +1 -0
- package/dist/src/manifest.js +104 -0
- package/dist/src/relations.d.ts +96 -0
- package/dist/src/relations.d.ts.map +1 -0
- package/dist/src/relations.js +108 -0
- package/dist/src/source.d.ts +93 -0
- package/dist/src/source.d.ts.map +1 -0
- package/dist/src/source.js +89 -0
- package/dist/src/template/context.d.ts +34 -0
- package/dist/src/template/context.d.ts.map +1 -0
- package/dist/src/template/context.js +31 -0
- package/dist/src/template/index.d.ts +6 -0
- package/dist/src/template/index.d.ts.map +1 -0
- package/dist/src/template/index.js +12 -0
- package/dist/src/template/registry.d.ts +18 -0
- package/dist/src/template/registry.d.ts.map +1 -0
- package/dist/src/template/registry.js +89 -0
- package/dist/src/template/runner.d.ts +9 -0
- package/dist/src/template/runner.d.ts.map +1 -0
- package/dist/src/template/runner.js +125 -0
- package/dist/src/template/types.d.ts +73 -0
- package/dist/src/template/types.d.ts.map +1 -0
- package/dist/src/template/types.js +3 -0
- package/dist/src/templates/nextjs-drizzle-trpc/generators/api.d.ts +22 -0
- package/dist/src/templates/nextjs-drizzle-trpc/generators/api.d.ts.map +1 -0
- package/dist/src/templates/nextjs-drizzle-trpc/generators/api.js +866 -0
- package/dist/src/templates/nextjs-drizzle-trpc/generators/auth.d.ts +20 -0
- package/dist/src/templates/nextjs-drizzle-trpc/generators/auth.d.ts.map +1 -0
- package/dist/src/templates/nextjs-drizzle-trpc/generators/auth.js +273 -0
- package/dist/src/templates/nextjs-drizzle-trpc/generators/crud-hooks.d.ts +22 -0
- package/dist/src/templates/nextjs-drizzle-trpc/generators/crud-hooks.d.ts.map +1 -0
- package/dist/src/templates/nextjs-drizzle-trpc/generators/crud-hooks.js +237 -0
- package/dist/src/templates/nextjs-drizzle-trpc/generators/hooks.d.ts +30 -0
- package/dist/src/templates/nextjs-drizzle-trpc/generators/hooks.d.ts.map +1 -0
- package/dist/src/templates/nextjs-drizzle-trpc/generators/hooks.js +345 -0
- package/dist/src/templates/nextjs-drizzle-trpc/generators/i18n.d.ts +25 -0
- package/dist/src/templates/nextjs-drizzle-trpc/generators/i18n.d.ts.map +1 -0
- package/dist/src/templates/nextjs-drizzle-trpc/generators/i18n.js +199 -0
- package/dist/src/templates/nextjs-drizzle-trpc/generators/index.d.ts +8 -0
- package/dist/src/templates/nextjs-drizzle-trpc/generators/index.d.ts.map +1 -0
- package/dist/src/templates/nextjs-drizzle-trpc/generators/index.js +18 -0
- package/dist/src/templates/nextjs-drizzle-trpc/generators/schema.d.ts +22 -0
- package/dist/src/templates/nextjs-drizzle-trpc/generators/schema.d.ts.map +1 -0
- package/dist/src/templates/nextjs-drizzle-trpc/generators/schema.js +270 -0
- package/dist/src/templates/nextjs-drizzle-trpc/generators/service.d.ts +23 -0
- package/dist/src/templates/nextjs-drizzle-trpc/generators/service.d.ts.map +1 -0
- package/dist/src/templates/nextjs-drizzle-trpc/generators/service.js +304 -0
- package/dist/src/templates/nextjs-drizzle-trpc/generators/validation.d.ts +21 -0
- package/dist/src/templates/nextjs-drizzle-trpc/generators/validation.d.ts.map +1 -0
- package/dist/src/templates/nextjs-drizzle-trpc/generators/validation.js +248 -0
- package/dist/src/templates/nextjs-drizzle-trpc/index.d.ts +30 -0
- package/dist/src/templates/nextjs-drizzle-trpc/index.d.ts.map +1 -0
- package/dist/src/templates/nextjs-drizzle-trpc/index.js +71 -0
- package/dist/src/validation/index.d.ts +71 -0
- package/dist/src/validation/index.d.ts.map +1 -0
- package/dist/src/validation/index.js +314 -0
- package/package.json +86 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../../src/cli.ts"],"names":[],"mappings":""}
|
package/dist/src/cli.js
ADDED
|
@@ -0,0 +1,540 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
4
|
+
if (k2 === undefined) k2 = k;
|
|
5
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
6
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
7
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
8
|
+
}
|
|
9
|
+
Object.defineProperty(o, k2, desc);
|
|
10
|
+
}) : (function(o, m, k, k2) {
|
|
11
|
+
if (k2 === undefined) k2 = k;
|
|
12
|
+
o[k2] = m[k];
|
|
13
|
+
}));
|
|
14
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
15
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
16
|
+
}) : function(o, v) {
|
|
17
|
+
o["default"] = v;
|
|
18
|
+
});
|
|
19
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
20
|
+
var ownKeys = function(o) {
|
|
21
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
22
|
+
var ar = [];
|
|
23
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
24
|
+
return ar;
|
|
25
|
+
};
|
|
26
|
+
return ownKeys(o);
|
|
27
|
+
};
|
|
28
|
+
return function (mod) {
|
|
29
|
+
if (mod && mod.__esModule) return mod;
|
|
30
|
+
var result = {};
|
|
31
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
32
|
+
__setModuleDefault(result, mod);
|
|
33
|
+
return result;
|
|
34
|
+
};
|
|
35
|
+
})();
|
|
36
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
37
|
+
const fs = __importStar(require("fs"));
|
|
38
|
+
const path = __importStar(require("path"));
|
|
39
|
+
const http = __importStar(require("http"));
|
|
40
|
+
const child_process_1 = require("child_process");
|
|
41
|
+
const module_1 = require("module");
|
|
42
|
+
const erd_ir_1 = require("./generators/erd-ir");
|
|
43
|
+
const init_1 = require("./init");
|
|
44
|
+
const template_1 = require("./template");
|
|
45
|
+
const parser_1 = require("./json/parser");
|
|
46
|
+
const validation_1 = require("./validation");
|
|
47
|
+
const command = process.argv[2];
|
|
48
|
+
// Find config path - first non-flag argument after command
|
|
49
|
+
const args = process.argv.slice(2);
|
|
50
|
+
const configPath = args.find(a => a !== command && !a.startsWith('-')) || 'archetype.config.ts';
|
|
51
|
+
// Parse CLI flags
|
|
52
|
+
const templateArg = args.find(a => a.startsWith('--template='));
|
|
53
|
+
const templateOverride = templateArg?.split('=')[1];
|
|
54
|
+
const yesFlag = args.includes('--yes') || args.includes('-y');
|
|
55
|
+
const headlessFlag = args.includes('--headless');
|
|
56
|
+
const jsonOutputFlag = args.includes('--json') || args.includes('--output=json');
|
|
57
|
+
const stdinFlag = args.includes('--stdin');
|
|
58
|
+
/**
|
|
59
|
+
* Output helper - formats output based on --json flag
|
|
60
|
+
*/
|
|
61
|
+
function output(data) {
|
|
62
|
+
if (jsonOutputFlag) {
|
|
63
|
+
console.log(JSON.stringify(data, null, 2));
|
|
64
|
+
}
|
|
65
|
+
else if (typeof data === 'string') {
|
|
66
|
+
console.log(data);
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
console.log(data);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Error output helper
|
|
74
|
+
*/
|
|
75
|
+
function errorOutput(message, data) {
|
|
76
|
+
if (jsonOutputFlag) {
|
|
77
|
+
console.log(JSON.stringify({ success: false, error: message, ...data }, null, 2));
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
console.error(message);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Read JSON manifest from stdin
|
|
85
|
+
*/
|
|
86
|
+
async function readStdin() {
|
|
87
|
+
return new Promise((resolve, reject) => {
|
|
88
|
+
let data = '';
|
|
89
|
+
process.stdin.setEncoding('utf8');
|
|
90
|
+
process.stdin.on('data', chunk => { data += chunk; });
|
|
91
|
+
process.stdin.on('end', () => resolve(data));
|
|
92
|
+
process.stdin.on('error', reject);
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Load manifest from JSON file or string
|
|
97
|
+
*/
|
|
98
|
+
function loadManifestFromJSON(jsonContent) {
|
|
99
|
+
try {
|
|
100
|
+
return (0, parser_1.parseManifestJSON)(jsonContent);
|
|
101
|
+
}
|
|
102
|
+
catch (err) {
|
|
103
|
+
const message = err instanceof Error ? err.message : 'Unknown error';
|
|
104
|
+
if (jsonOutputFlag) {
|
|
105
|
+
errorOutput('Failed to parse JSON manifest', { parseError: message });
|
|
106
|
+
process.exit(1);
|
|
107
|
+
}
|
|
108
|
+
console.error('Failed to parse JSON manifest:', message);
|
|
109
|
+
process.exit(1);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Load manifest from TypeScript config file or JSON file
|
|
114
|
+
*/
|
|
115
|
+
function loadManifest(configFile) {
|
|
116
|
+
const absolutePath = path.resolve(configFile);
|
|
117
|
+
// Check if file exists
|
|
118
|
+
if (!fs.existsSync(absolutePath)) {
|
|
119
|
+
// Try common alternatives
|
|
120
|
+
const alternatives = [
|
|
121
|
+
'archetype.config.ts',
|
|
122
|
+
'archetype.config.js',
|
|
123
|
+
'manifest.json',
|
|
124
|
+
'archetype.json',
|
|
125
|
+
'archetype/index.ts', // Legacy
|
|
126
|
+
'archetype/index.js', // Legacy
|
|
127
|
+
];
|
|
128
|
+
for (const alt of alternatives) {
|
|
129
|
+
const altPath = path.resolve(alt);
|
|
130
|
+
if (fs.existsSync(altPath)) {
|
|
131
|
+
if (!jsonOutputFlag)
|
|
132
|
+
console.log(`Using config: ${alt}`);
|
|
133
|
+
return loadManifest(alt);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
if (jsonOutputFlag) {
|
|
137
|
+
errorOutput(`Config file not found: ${configFile}`);
|
|
138
|
+
process.exit(1);
|
|
139
|
+
}
|
|
140
|
+
console.error(`Config file not found: ${configFile}`);
|
|
141
|
+
console.error('Run "archetype init" to create one, or create manually:');
|
|
142
|
+
console.error(' - archetype.config.ts (TypeScript)');
|
|
143
|
+
console.error(' - manifest.json (JSON for AI agents)');
|
|
144
|
+
process.exit(1);
|
|
145
|
+
}
|
|
146
|
+
// If it's a JSON file, use JSON parser
|
|
147
|
+
if (absolutePath.endsWith('.json')) {
|
|
148
|
+
const content = fs.readFileSync(absolutePath, 'utf-8');
|
|
149
|
+
return loadManifestFromJSON(content);
|
|
150
|
+
}
|
|
151
|
+
// Get the config file's directory for module resolution
|
|
152
|
+
const configDir = path.dirname(absolutePath);
|
|
153
|
+
// Create a require function rooted in the project directory
|
|
154
|
+
// This ensures npm link and local dependencies are resolved correctly
|
|
155
|
+
const projectRequire = (0, module_1.createRequire)(path.join(configDir, 'package.json'));
|
|
156
|
+
// Clear require cache for fresh load
|
|
157
|
+
delete projectRequire.cache[absolutePath];
|
|
158
|
+
// Register TypeScript if needed
|
|
159
|
+
if (absolutePath.endsWith('.ts')) {
|
|
160
|
+
try {
|
|
161
|
+
// Use the project's ts-node if available, fall back to ours
|
|
162
|
+
let tsNode;
|
|
163
|
+
try {
|
|
164
|
+
tsNode = projectRequire('ts-node');
|
|
165
|
+
}
|
|
166
|
+
catch {
|
|
167
|
+
tsNode = require('ts-node');
|
|
168
|
+
}
|
|
169
|
+
tsNode.register({
|
|
170
|
+
transpileOnly: true,
|
|
171
|
+
cwd: configDir,
|
|
172
|
+
compilerOptions: {
|
|
173
|
+
module: 'commonjs',
|
|
174
|
+
moduleResolution: 'node',
|
|
175
|
+
},
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
catch {
|
|
179
|
+
// ts-node should already be registered if running via ts-node
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
// Load the module using the project-rooted require
|
|
183
|
+
const loadedModule = projectRequire(absolutePath);
|
|
184
|
+
const manifest = loadedModule.default || loadedModule;
|
|
185
|
+
if (!manifest.entities) {
|
|
186
|
+
console.error('Invalid manifest: missing "entities" property');
|
|
187
|
+
console.error('Make sure your config exports a manifest created with defineManifest()');
|
|
188
|
+
process.exit(1);
|
|
189
|
+
}
|
|
190
|
+
return manifest;
|
|
191
|
+
}
|
|
192
|
+
function openBrowser(url) {
|
|
193
|
+
const cmd = process.platform === 'darwin'
|
|
194
|
+
? 'open'
|
|
195
|
+
: process.platform === 'win32'
|
|
196
|
+
? 'start'
|
|
197
|
+
: 'xdg-open';
|
|
198
|
+
(0, child_process_1.exec)(`${cmd} "${url}"`);
|
|
199
|
+
}
|
|
200
|
+
function serveERD(mermaidCode, port = 3333, maxAttempts = 10) {
|
|
201
|
+
const html = buildHTML(mermaidCode);
|
|
202
|
+
const server = http.createServer((_, res) => {
|
|
203
|
+
res.writeHead(200, { 'Content-Type': 'text/html' });
|
|
204
|
+
res.end(html);
|
|
205
|
+
});
|
|
206
|
+
server.on('error', (err) => {
|
|
207
|
+
if (err.code === 'EADDRINUSE') {
|
|
208
|
+
const nextPort = port + 1;
|
|
209
|
+
if (nextPort - 3333 < maxAttempts) {
|
|
210
|
+
console.log(`Port ${port} is in use, trying ${nextPort}...`);
|
|
211
|
+
serveERD(mermaidCode, nextPort, maxAttempts);
|
|
212
|
+
}
|
|
213
|
+
else {
|
|
214
|
+
console.error(`Could not find an available port (tried ${3333}-${port})`);
|
|
215
|
+
console.error('Try killing the process using the port:');
|
|
216
|
+
console.error(` lsof -i :3333 | grep LISTEN | awk '{print $2}' | xargs kill -9`);
|
|
217
|
+
process.exit(1);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
else {
|
|
221
|
+
console.error('Server error:', err.message);
|
|
222
|
+
process.exit(1);
|
|
223
|
+
}
|
|
224
|
+
});
|
|
225
|
+
server.listen(port, () => {
|
|
226
|
+
console.log(`http://localhost:${port}`);
|
|
227
|
+
console.log('Ctrl+C to stop');
|
|
228
|
+
openBrowser(`http://localhost:${port}`);
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
function buildHTML(mermaidCode) {
|
|
232
|
+
return `<!DOCTYPE html>
|
|
233
|
+
<html lang="en">
|
|
234
|
+
<head>
|
|
235
|
+
<meta charset="UTF-8">
|
|
236
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
237
|
+
<title>ERD Viewer</title>
|
|
238
|
+
<script src="https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.min.js"></script>
|
|
239
|
+
<script src="https://cdn.jsdelivr.net/npm/panzoom@9/dist/panzoom.min.js"></script>
|
|
240
|
+
<style>
|
|
241
|
+
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
242
|
+
body {
|
|
243
|
+
background: #0d1117;
|
|
244
|
+
width: 100vw;
|
|
245
|
+
height: 100vh;
|
|
246
|
+
overflow: hidden;
|
|
247
|
+
}
|
|
248
|
+
#container {
|
|
249
|
+
width: 100%;
|
|
250
|
+
height: 100%;
|
|
251
|
+
}
|
|
252
|
+
#diagram {
|
|
253
|
+
display: inline-block;
|
|
254
|
+
padding: 48px;
|
|
255
|
+
}
|
|
256
|
+
#diagram svg {
|
|
257
|
+
display: block;
|
|
258
|
+
}
|
|
259
|
+
</style>
|
|
260
|
+
</head>
|
|
261
|
+
<body>
|
|
262
|
+
<div id="container">
|
|
263
|
+
<div id="diagram">
|
|
264
|
+
<pre class="mermaid">${mermaidCode}</pre>
|
|
265
|
+
</div>
|
|
266
|
+
</div>
|
|
267
|
+
<script>
|
|
268
|
+
mermaid.initialize({
|
|
269
|
+
startOnLoad: true,
|
|
270
|
+
theme: 'dark',
|
|
271
|
+
er: { useMaxWidth: false }
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
window.addEventListener('load', () => {
|
|
275
|
+
setTimeout(() => {
|
|
276
|
+
const diagram = document.getElementById('diagram');
|
|
277
|
+
const rect = diagram.getBoundingClientRect();
|
|
278
|
+
|
|
279
|
+
const padding = 64;
|
|
280
|
+
const scaleX = (window.innerWidth - padding * 2) / rect.width;
|
|
281
|
+
const scaleY = (window.innerHeight - padding * 2) / rect.height;
|
|
282
|
+
const scale = Math.min(scaleX, scaleY, 1);
|
|
283
|
+
|
|
284
|
+
const instance = panzoom(diagram, {
|
|
285
|
+
maxZoom: 5,
|
|
286
|
+
minZoom: 0.05,
|
|
287
|
+
bounds: false
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
instance.zoomAbs(0, 0, scale);
|
|
291
|
+
|
|
292
|
+
setTimeout(() => {
|
|
293
|
+
const newRect = diagram.getBoundingClientRect();
|
|
294
|
+
const x = (window.innerWidth - newRect.width) / 2;
|
|
295
|
+
const y = (window.innerHeight - newRect.height) / 2;
|
|
296
|
+
instance.moveTo(x, y);
|
|
297
|
+
}, 50);
|
|
298
|
+
}, 300);
|
|
299
|
+
});
|
|
300
|
+
</script>
|
|
301
|
+
</body>
|
|
302
|
+
</html>`;
|
|
303
|
+
}
|
|
304
|
+
async function runGenerate(manifest) {
|
|
305
|
+
// Determine template: CLI flag > config > error
|
|
306
|
+
const templateId = templateOverride || manifest.template;
|
|
307
|
+
if (!templateId) {
|
|
308
|
+
if (jsonOutputFlag) {
|
|
309
|
+
errorOutput('No template specified', {
|
|
310
|
+
suggestion: 'Add template to config or use --template flag',
|
|
311
|
+
availableTemplates: (0, template_1.listTemplates)().map(t => t.id),
|
|
312
|
+
});
|
|
313
|
+
process.exit(1);
|
|
314
|
+
}
|
|
315
|
+
console.error('No template specified.');
|
|
316
|
+
console.error('');
|
|
317
|
+
console.error('Either:');
|
|
318
|
+
console.error(' 1. Set "template" in archetype.config.ts');
|
|
319
|
+
console.error(' 2. Use --template flag: archetype generate --template=nextjs-drizzle-trpc');
|
|
320
|
+
console.error('');
|
|
321
|
+
console.error('Available templates:');
|
|
322
|
+
for (const t of (0, template_1.listTemplates)()) {
|
|
323
|
+
console.error(` - ${t.id}: ${t.description}`);
|
|
324
|
+
}
|
|
325
|
+
process.exit(1);
|
|
326
|
+
}
|
|
327
|
+
if (!jsonOutputFlag) {
|
|
328
|
+
console.log(`Generating with template: ${templateId}`);
|
|
329
|
+
console.log(`Entities: ${manifest.entities.map(e => e.name).join(', ')}`);
|
|
330
|
+
console.log('');
|
|
331
|
+
}
|
|
332
|
+
// Get template from registry
|
|
333
|
+
const template = await (0, template_1.getTemplate)(templateId);
|
|
334
|
+
if (!template) {
|
|
335
|
+
if (jsonOutputFlag) {
|
|
336
|
+
errorOutput(`Template not found: ${templateId}`, {
|
|
337
|
+
availableTemplates: (0, template_1.listTemplates)().map(t => t.id),
|
|
338
|
+
});
|
|
339
|
+
process.exit(1);
|
|
340
|
+
}
|
|
341
|
+
console.error(`Template not found: ${templateId}`);
|
|
342
|
+
console.error('');
|
|
343
|
+
console.error('Available templates:');
|
|
344
|
+
for (const t of (0, template_1.listTemplates)()) {
|
|
345
|
+
console.error(` - ${t.id}: ${t.description}`);
|
|
346
|
+
}
|
|
347
|
+
process.exit(1);
|
|
348
|
+
}
|
|
349
|
+
// Run template and collect generated files
|
|
350
|
+
const generatedFiles = await (0, template_1.runTemplate)(template, manifest, { dryRun: false });
|
|
351
|
+
// Generate ERD (always generate regardless of template)
|
|
352
|
+
const outputDir = template.defaultConfig.outputDir;
|
|
353
|
+
(0, erd_ir_1.saveERDFromIR)(manifest, `${outputDir}/erd.md`);
|
|
354
|
+
if (jsonOutputFlag) {
|
|
355
|
+
// Output structured JSON result
|
|
356
|
+
const files = generatedFiles.map(f => f.path);
|
|
357
|
+
files.push(`${outputDir}/erd.md`);
|
|
358
|
+
output({
|
|
359
|
+
success: true,
|
|
360
|
+
template: templateId,
|
|
361
|
+
entities: manifest.entities.map(e => e.name),
|
|
362
|
+
files,
|
|
363
|
+
});
|
|
364
|
+
}
|
|
365
|
+
else {
|
|
366
|
+
console.log(` Created: ${outputDir}/erd.md`);
|
|
367
|
+
console.log('');
|
|
368
|
+
console.log('Generation complete!');
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
/**
|
|
372
|
+
* Run validation command
|
|
373
|
+
*/
|
|
374
|
+
async function runValidate(manifest) {
|
|
375
|
+
const result = (0, validation_1.validateManifest)(manifest);
|
|
376
|
+
if (jsonOutputFlag) {
|
|
377
|
+
output(result);
|
|
378
|
+
}
|
|
379
|
+
else {
|
|
380
|
+
if (result.valid) {
|
|
381
|
+
console.log('Manifest is valid!');
|
|
382
|
+
if (result.warnings.length > 0) {
|
|
383
|
+
console.log('');
|
|
384
|
+
console.log('Warnings:');
|
|
385
|
+
for (const w of result.warnings) {
|
|
386
|
+
console.log(` - [${w.code}] ${w.path}: ${w.message}`);
|
|
387
|
+
if (w.suggestion)
|
|
388
|
+
console.log(` Fix: ${w.suggestion}`);
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
else {
|
|
393
|
+
console.error('Validation failed!');
|
|
394
|
+
console.error('');
|
|
395
|
+
console.error('Errors:');
|
|
396
|
+
for (const e of result.errors) {
|
|
397
|
+
console.error(` - [${e.code}] ${e.path}: ${e.message}`);
|
|
398
|
+
if (e.suggestion)
|
|
399
|
+
console.error(` Fix: ${e.suggestion}`);
|
|
400
|
+
}
|
|
401
|
+
if (result.warnings.length > 0) {
|
|
402
|
+
console.error('');
|
|
403
|
+
console.error('Warnings:');
|
|
404
|
+
for (const w of result.warnings) {
|
|
405
|
+
console.error(` - [${w.code}] ${w.path}: ${w.message}`);
|
|
406
|
+
if (w.suggestion)
|
|
407
|
+
console.error(` Fix: ${w.suggestion}`);
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
process.exit(result.valid ? 0 : 1);
|
|
413
|
+
}
|
|
414
|
+
/**
|
|
415
|
+
* Load JSON manifest for validation (doesn't convert to IR yet)
|
|
416
|
+
*/
|
|
417
|
+
function loadJSONManifest(configFile) {
|
|
418
|
+
const absolutePath = path.resolve(configFile);
|
|
419
|
+
if (!fs.existsSync(absolutePath)) {
|
|
420
|
+
if (jsonOutputFlag) {
|
|
421
|
+
errorOutput(`File not found: ${configFile}`);
|
|
422
|
+
process.exit(1);
|
|
423
|
+
}
|
|
424
|
+
console.error(`File not found: ${configFile}`);
|
|
425
|
+
process.exit(1);
|
|
426
|
+
}
|
|
427
|
+
// For JSON files, parse and return
|
|
428
|
+
if (absolutePath.endsWith('.json')) {
|
|
429
|
+
const content = fs.readFileSync(absolutePath, 'utf-8');
|
|
430
|
+
try {
|
|
431
|
+
return JSON.parse(content);
|
|
432
|
+
}
|
|
433
|
+
catch (err) {
|
|
434
|
+
const message = err instanceof Error ? err.message : 'Unknown error';
|
|
435
|
+
if (jsonOutputFlag) {
|
|
436
|
+
errorOutput('Failed to parse JSON', { parseError: message });
|
|
437
|
+
process.exit(1);
|
|
438
|
+
}
|
|
439
|
+
console.error('Failed to parse JSON:', message);
|
|
440
|
+
process.exit(1);
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
// For TypeScript files, load and convert to JSON-like structure
|
|
444
|
+
// (validation works on the JSON structure, not the IR)
|
|
445
|
+
const manifest = loadManifest(configFile);
|
|
446
|
+
// Convert IR back to JSON-like structure for validation
|
|
447
|
+
return {
|
|
448
|
+
entities: manifest.entities.map(e => ({
|
|
449
|
+
name: e.name,
|
|
450
|
+
fields: Object.fromEntries(Object.entries(e.fields).map(([name, field]) => [
|
|
451
|
+
name,
|
|
452
|
+
{ type: field.type, required: field.required, unique: field.unique }
|
|
453
|
+
])),
|
|
454
|
+
relations: Object.fromEntries(Object.entries(e.relations).map(([name, rel]) => [
|
|
455
|
+
name,
|
|
456
|
+
{ type: rel.type, entity: rel.entity, field: rel.field }
|
|
457
|
+
])),
|
|
458
|
+
behaviors: e.behaviors,
|
|
459
|
+
auth: e.auth,
|
|
460
|
+
protected: e.protected,
|
|
461
|
+
})),
|
|
462
|
+
database: manifest.database,
|
|
463
|
+
mode: manifest.mode.type,
|
|
464
|
+
auth: manifest.auth,
|
|
465
|
+
template: manifest.template,
|
|
466
|
+
};
|
|
467
|
+
}
|
|
468
|
+
// Main CLI logic
|
|
469
|
+
async function main() {
|
|
470
|
+
if (command === 'generate') {
|
|
471
|
+
let manifest;
|
|
472
|
+
if (stdinFlag) {
|
|
473
|
+
// Read from stdin
|
|
474
|
+
const jsonContent = await readStdin();
|
|
475
|
+
manifest = loadManifestFromJSON(jsonContent);
|
|
476
|
+
}
|
|
477
|
+
else {
|
|
478
|
+
manifest = loadManifest(configPath);
|
|
479
|
+
}
|
|
480
|
+
await runGenerate(manifest);
|
|
481
|
+
}
|
|
482
|
+
else if (command === 'validate') {
|
|
483
|
+
let manifestJSON;
|
|
484
|
+
if (stdinFlag) {
|
|
485
|
+
// Read from stdin
|
|
486
|
+
const jsonContent = await readStdin();
|
|
487
|
+
try {
|
|
488
|
+
manifestJSON = JSON.parse(jsonContent);
|
|
489
|
+
}
|
|
490
|
+
catch (err) {
|
|
491
|
+
const message = err instanceof Error ? err.message : 'Unknown error';
|
|
492
|
+
errorOutput('Failed to parse JSON from stdin', { parseError: message });
|
|
493
|
+
process.exit(1);
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
else {
|
|
497
|
+
manifestJSON = loadJSONManifest(configPath);
|
|
498
|
+
}
|
|
499
|
+
await runValidate(manifestJSON);
|
|
500
|
+
}
|
|
501
|
+
else if (command === 'view') {
|
|
502
|
+
const manifest = loadManifest(configPath);
|
|
503
|
+
const erd = (0, erd_ir_1.generateERDFromIR)(manifest);
|
|
504
|
+
serveERD(erd);
|
|
505
|
+
}
|
|
506
|
+
else if (command === 'init') {
|
|
507
|
+
// Run the TUI init flow
|
|
508
|
+
await (0, init_1.init)({ yes: yesFlag, headless: headlessFlag });
|
|
509
|
+
}
|
|
510
|
+
else {
|
|
511
|
+
console.log('Usage:');
|
|
512
|
+
console.log(' archetype init - Interactive setup with prompts');
|
|
513
|
+
console.log(' archetype init --yes - Quick setup with defaults (full mode)');
|
|
514
|
+
console.log(' archetype init --headless - Quick setup for headless mode (no database)');
|
|
515
|
+
console.log(' archetype generate [config] - Generate code from entities');
|
|
516
|
+
console.log(' archetype validate [config] - Validate manifest without generating');
|
|
517
|
+
console.log(' archetype view [config] - View ERD diagram in browser');
|
|
518
|
+
console.log('');
|
|
519
|
+
console.log('Flags:');
|
|
520
|
+
console.log(' --json - Output as JSON (for AI agents)');
|
|
521
|
+
console.log(' --stdin - Read JSON manifest from stdin');
|
|
522
|
+
console.log(' --template=<id> - Override template');
|
|
523
|
+
console.log('');
|
|
524
|
+
console.log('Config defaults to archetype.config.ts or manifest.json');
|
|
525
|
+
console.log('');
|
|
526
|
+
console.log('Available templates:');
|
|
527
|
+
for (const t of (0, template_1.listTemplates)()) {
|
|
528
|
+
console.log(` - ${t.id}: ${t.description}`);
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
main().catch(err => {
|
|
533
|
+
if (jsonOutputFlag) {
|
|
534
|
+
errorOutput(err.message || 'Unknown error');
|
|
535
|
+
}
|
|
536
|
+
else {
|
|
537
|
+
console.error('Error:', err.message || err);
|
|
538
|
+
}
|
|
539
|
+
process.exit(1);
|
|
540
|
+
});
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Convert camelCase or PascalCase to snake_case
|
|
3
|
+
*/
|
|
4
|
+
export declare function toSnakeCase(str: string): string;
|
|
5
|
+
/**
|
|
6
|
+
* Simple pluralization rules
|
|
7
|
+
*/
|
|
8
|
+
export declare function pluralize(name: string): string;
|
|
9
|
+
/**
|
|
10
|
+
* Convert to camelCase (first letter lowercase)
|
|
11
|
+
*/
|
|
12
|
+
export declare function toCamelCase(str: string): string;
|
|
13
|
+
/**
|
|
14
|
+
* Convert to PascalCase (first letter uppercase)
|
|
15
|
+
*/
|
|
16
|
+
export declare function toPascalCase(str: string): string;
|
|
17
|
+
/**
|
|
18
|
+
* Get database table name from entity name
|
|
19
|
+
* Example: "User" -> "users", "Category" -> "categories"
|
|
20
|
+
*/
|
|
21
|
+
export declare function getTableName(entityName: string): string;
|
|
22
|
+
/**
|
|
23
|
+
* Get database column name from field name
|
|
24
|
+
* Example: "firstName" -> "first_name"
|
|
25
|
+
*/
|
|
26
|
+
export declare function getColumnName(fieldName: string): string;
|
|
27
|
+
//# sourceMappingURL=utils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../../src/core/utils.ts"],"names":[],"mappings":"AAEA;;GAEG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAI/C;AAED;;GAEG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAS9C;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAE/C;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAEhD;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAEvD;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAEvD"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Shared utilities for code generation
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.toSnakeCase = toSnakeCase;
|
|
5
|
+
exports.pluralize = pluralize;
|
|
6
|
+
exports.toCamelCase = toCamelCase;
|
|
7
|
+
exports.toPascalCase = toPascalCase;
|
|
8
|
+
exports.getTableName = getTableName;
|
|
9
|
+
exports.getColumnName = getColumnName;
|
|
10
|
+
/**
|
|
11
|
+
* Convert camelCase or PascalCase to snake_case
|
|
12
|
+
*/
|
|
13
|
+
function toSnakeCase(str) {
|
|
14
|
+
return str
|
|
15
|
+
.replace(/[A-Z]/g, letter => `_${letter.toLowerCase()}`)
|
|
16
|
+
.replace(/^_/, '');
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Simple pluralization rules
|
|
20
|
+
*/
|
|
21
|
+
function pluralize(name) {
|
|
22
|
+
if (name.endsWith('y')) {
|
|
23
|
+
return name.slice(0, -1) + 'ies';
|
|
24
|
+
}
|
|
25
|
+
if (name.endsWith('s') || name.endsWith('x') ||
|
|
26
|
+
name.endsWith('ch') || name.endsWith('sh')) {
|
|
27
|
+
return name + 'es';
|
|
28
|
+
}
|
|
29
|
+
return name + 's';
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Convert to camelCase (first letter lowercase)
|
|
33
|
+
*/
|
|
34
|
+
function toCamelCase(str) {
|
|
35
|
+
return str.charAt(0).toLowerCase() + str.slice(1);
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Convert to PascalCase (first letter uppercase)
|
|
39
|
+
*/
|
|
40
|
+
function toPascalCase(str) {
|
|
41
|
+
return str.charAt(0).toUpperCase() + str.slice(1);
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Get database table name from entity name
|
|
45
|
+
* Example: "User" -> "users", "Category" -> "categories"
|
|
46
|
+
*/
|
|
47
|
+
function getTableName(entityName) {
|
|
48
|
+
return toSnakeCase(pluralize(entityName));
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Get database column name from field name
|
|
52
|
+
* Example: "firstName" -> "first_name"
|
|
53
|
+
*/
|
|
54
|
+
function getColumnName(fieldName) {
|
|
55
|
+
return toSnakeCase(fieldName);
|
|
56
|
+
}
|