irgen 0.3.1 → 0.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +10 -0
- package/README.md +3 -0
- package/dist/cli/check.js +16 -6
- package/dist/cli/init.js +23 -5
- package/dist/cli/studio.js +37 -8
- package/dist/cli.js +11 -10
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,16 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to the `irgen` project will be documented in this file.
|
|
4
4
|
|
|
5
|
+
## [0.3.2] - 2026-02-04
|
|
6
|
+
|
|
7
|
+
### Enhancements
|
|
8
|
+
- **CLI Execution Order**: Refactored the core entry point to ensure TypeScript support (`tsx`) and extensions (`--ext`) are fully loaded before subcommands (`init`, `studio`, `check`) are executed.
|
|
9
|
+
- **Detailed CLI Help**: Added specialized help messages for `init`, `studio`, and `check` commands, providing better guidance on parameters and extension usage.
|
|
10
|
+
- **Resilient Studio Dashboard**: Updated the Studio UI to be more robust when visualizing complex Hybrid applications (Backend + Frontend) and added better error handling for malformed IR.
|
|
11
|
+
|
|
12
|
+
### Bug Fixes
|
|
13
|
+
- **Studio Data Loading**: Fixed an issue where the Studio dashboard would appear empty when using extension-specific DSLs due to late extension loading.
|
|
14
|
+
|
|
5
15
|
## [0.3.1] - 2026-02-03
|
|
6
16
|
|
|
7
17
|
### New Features
|
package/README.md
CHANGED
|
@@ -92,6 +92,9 @@ npx irgen examples/app.dsl.ts --targets=backend,frontend --outDir=generated/full
|
|
|
92
92
|
# Static-site (HTML-first)
|
|
93
93
|
npx irgen examples/docs.dsl.ts --targets=static-site --outDir=generated/static-docs
|
|
94
94
|
|
|
95
|
+
> [!TIP]
|
|
96
|
+
> **Working with multiple DSL files?** Instead of passing multiple files to the CLI, use a **single entry point** and `import` your other DSL files. This ensures your project structure remains clean and prevents CLI ambiguity.
|
|
97
|
+
|
|
95
98
|
## Specialized Commands
|
|
96
99
|
|
|
97
100
|
### Project Scaffolding
|
package/dist/cli/check.js
CHANGED
|
@@ -1,6 +1,22 @@
|
|
|
1
1
|
import { aggregateDecls } from "../dsl/aggregator.js";
|
|
2
2
|
import { validateSemantics } from "../dsl/validator.js";
|
|
3
3
|
export async function runCheck(args) {
|
|
4
|
+
if (args.includes("--help") || args.includes("-h")) {
|
|
5
|
+
console.log(`
|
|
6
|
+
irgen check — Semantic validation for irgen DSL files
|
|
7
|
+
|
|
8
|
+
Usage:
|
|
9
|
+
irgen check <dsl-file>... [options]
|
|
10
|
+
|
|
11
|
+
Options:
|
|
12
|
+
--ext=<path> Load extensions (for extension-specific validation)
|
|
13
|
+
--help, -h Show this help message
|
|
14
|
+
|
|
15
|
+
Example:
|
|
16
|
+
npx irgen check app.dsl.ts ui.dsl.ts --ext=irgen-ext-php-shared-hosting
|
|
17
|
+
`);
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
4
20
|
const dslFiles = args.filter(a => a.endsWith(".dsl.ts"));
|
|
5
21
|
if (dslFiles.length === 0) {
|
|
6
22
|
console.error("Usage: irgen check <dsl-file>...");
|
|
@@ -8,12 +24,6 @@ export async function runCheck(args) {
|
|
|
8
24
|
}
|
|
9
25
|
console.log(`Checking semantic integrity for: ${dslFiles.join(", ")}...`);
|
|
10
26
|
try {
|
|
11
|
-
const extFlags = args.filter(a => a.startsWith("--ext="));
|
|
12
|
-
const extModules = extFlags.flatMap(f => f.replace("--ext=", "").split(",")).filter(Boolean);
|
|
13
|
-
if (extModules.length > 0) {
|
|
14
|
-
const { loadExtensions } = await import("./extensions.js");
|
|
15
|
-
await loadExtensions(extModules);
|
|
16
|
-
}
|
|
17
27
|
const decl = await aggregateDecls(dslFiles);
|
|
18
28
|
const messages = validateSemantics(decl);
|
|
19
29
|
if (messages.length === 0) {
|
package/dist/cli/init.js
CHANGED
|
@@ -2,11 +2,29 @@ import prompts from "prompts";
|
|
|
2
2
|
import fs from "node:fs";
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
export async function runInit(args) {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
5
|
+
if (args.includes("--help") || args.includes("-h")) {
|
|
6
|
+
const { templateRegistry } = await import("./template-registry.js");
|
|
7
|
+
const extTemplates = templateRegistry.getTemplates();
|
|
8
|
+
console.log(`
|
|
9
|
+
irgen init — Scaffold a new irgen project
|
|
10
|
+
|
|
11
|
+
Usage:
|
|
12
|
+
irgen init <projectName> [options]
|
|
13
|
+
|
|
14
|
+
Options:
|
|
15
|
+
--ext=<path> Load extensions (provides extra templates)
|
|
16
|
+
--help, -h Show this help message
|
|
17
|
+
|
|
18
|
+
Available Templates:
|
|
19
|
+
- fullstack Standard Backend + Frontend (React)
|
|
20
|
+
- backend Node.js/Prisma backend only
|
|
21
|
+
- frontend React/Vite frontend only
|
|
22
|
+
${extTemplates.map(t => ` - ${t.id.padEnd(18)} ${t.title}`).join("\n")}
|
|
23
|
+
|
|
24
|
+
Example:
|
|
25
|
+
npx irgen init my-blog --ext=irgen-ext-php-shared-hosting --template=php-blog
|
|
26
|
+
`);
|
|
27
|
+
return;
|
|
10
28
|
}
|
|
11
29
|
const { templateRegistry } = await import("./template-registry.js");
|
|
12
30
|
const extTemplates = templateRegistry.getTemplates();
|
package/dist/cli/studio.js
CHANGED
|
@@ -4,6 +4,25 @@ import { aggregateDecls } from "../dsl/aggregator.js";
|
|
|
4
4
|
import path from "node:path";
|
|
5
5
|
import fs from "node:fs";
|
|
6
6
|
export async function runStudio(args) {
|
|
7
|
+
if (args.includes("--help") || args.includes("-h")) {
|
|
8
|
+
console.log(`
|
|
9
|
+
irgen studio — Interactive DSL Dashboard
|
|
10
|
+
|
|
11
|
+
Usage:
|
|
12
|
+
irgen studio <dsl-file>... [options]
|
|
13
|
+
|
|
14
|
+
Options:
|
|
15
|
+
--ext=<path> Load extensions (to visualize custom mappers/emitters)
|
|
16
|
+
--help, -h Show this help message
|
|
17
|
+
|
|
18
|
+
The Studio provides a real-time visual representation of your system's
|
|
19
|
+
entities, pages, and components as defined in your DSL.
|
|
20
|
+
|
|
21
|
+
Example:
|
|
22
|
+
npx irgen studio app.dsl.ts ui.dsl.ts --ext=irgen-ext-php-shared-hosting
|
|
23
|
+
`);
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
7
26
|
const dslFiles = args.filter(a => a.endsWith(".dsl.ts"));
|
|
8
27
|
if (dslFiles.length === 0) {
|
|
9
28
|
console.error("Usage: irgen studio <dsl-file>...");
|
|
@@ -40,7 +59,6 @@ export async function runStudio(args) {
|
|
|
40
59
|
res.json(currentIR);
|
|
41
60
|
});
|
|
42
61
|
// Static UI
|
|
43
|
-
// For now, let's embed a simple but beautiful HTML
|
|
44
62
|
app.get("/", (req, res) => {
|
|
45
63
|
res.send(getStudioHtml());
|
|
46
64
|
});
|
|
@@ -278,14 +296,17 @@ function getStudioHtml() {
|
|
|
278
296
|
document.getElementById('header-content').innerHTML = '<h2>Project Overview</h2><p>Summary of discovered resources</p>';
|
|
279
297
|
let totalEntities = 0;
|
|
280
298
|
let totalPages = 0;
|
|
281
|
-
ir.apps.forEach(a => {
|
|
299
|
+
ir.apps.forEach(a => {
|
|
300
|
+
if (a.entities) totalEntities += a.entities.length;
|
|
301
|
+
if (a.pages) totalPages += a.pages.length;
|
|
302
|
+
});
|
|
282
303
|
|
|
283
304
|
const content = document.getElementById('content');
|
|
284
305
|
content.innerHTML = \`
|
|
285
306
|
<div class="stats-row">
|
|
286
307
|
<div class="stat-card">
|
|
287
308
|
<div class="stat-value">\${ir.apps.length}</div>
|
|
288
|
-
<div class="stat-label">Applications</div>
|
|
309
|
+
<div class="stat-label">Applications / Modules</div>
|
|
289
310
|
</div>
|
|
290
311
|
<div class="stat-card">
|
|
291
312
|
<div class="stat-value">\${totalEntities}</div>
|
|
@@ -297,14 +318,22 @@ function getStudioHtml() {
|
|
|
297
318
|
</div>
|
|
298
319
|
</div>
|
|
299
320
|
<div class="card">
|
|
300
|
-
<h3>Metadata</h3>
|
|
321
|
+
<h3>Metadata & Configuration</h3>
|
|
301
322
|
<table class="mono">
|
|
302
|
-
\${Object.entries(ir.
|
|
323
|
+
\${Object.entries(ir.meta || {}).map(([k,v]) => \`
|
|
303
324
|
<tr>
|
|
304
|
-
<td style="color: var(--accent)">\${k}</td>
|
|
305
|
-
<td>\${JSON.stringify(v)}</td>
|
|
325
|
+
<td style="color: var(--accent); width: 200px">\${k}</td>
|
|
326
|
+
<td><pre style="margin:0; white-space: pre-wrap">\${JSON.stringify(v, null, 2)}</pre></td>
|
|
306
327
|
</tr>
|
|
307
328
|
\`).join('')}
|
|
329
|
+
\${ir.apps.map(app =>
|
|
330
|
+
Object.entries(app.meta || {}).map(([k,v]) => \`
|
|
331
|
+
<tr>
|
|
332
|
+
<td style="color: var(--text-dim)">[\${app.name}] \${k}</td>
|
|
333
|
+
<td><pre style="margin:0; white-space: pre-wrap">\${JSON.stringify(v, null, 2)}</pre></td>
|
|
334
|
+
</tr>
|
|
335
|
+
\`).join('')
|
|
336
|
+
).join('')}
|
|
308
337
|
</table>
|
|
309
338
|
</div>
|
|
310
339
|
\`;
|
|
@@ -352,7 +381,7 @@ function getStudioHtml() {
|
|
|
352
381
|
<strong>\${comp.name}</strong>
|
|
353
382
|
\${comp.entityRef ? '<span class="badge">Bound</span>' : ''}
|
|
354
383
|
</div>
|
|
355
|
-
\${comp.entityRef ?
|
|
384
|
+
\${comp.entityRef ? \\\`<div class="stat-label">Entity: \\\${comp.entityRef}</div>\\\` : ''}
|
|
356
385
|
</div>
|
|
357
386
|
\`).join('')}
|
|
358
387
|
</div>
|
package/dist/cli.js
CHANGED
|
@@ -44,6 +44,17 @@ Examples:
|
|
|
44
44
|
console.log("irgen 0.1.0");
|
|
45
45
|
process.exit(0);
|
|
46
46
|
}
|
|
47
|
+
try {
|
|
48
|
+
const { register } = await import("tsx/esm/api");
|
|
49
|
+
register();
|
|
50
|
+
}
|
|
51
|
+
catch (err) {
|
|
52
|
+
console.warn("tsx loader unavailable; falling back to manual TS transpile where supported.", err instanceof Error ? err.message : err);
|
|
53
|
+
}
|
|
54
|
+
const rawArgs = process.argv.slice(2);
|
|
55
|
+
const extFlags = rawArgs.filter(a => a.startsWith("--ext="));
|
|
56
|
+
const extModules = extFlags.flatMap(f => f.replace("--ext=", "").split(",")).filter(Boolean);
|
|
57
|
+
await loadExtensions(extModules);
|
|
47
58
|
if (process.argv[2] === "check") {
|
|
48
59
|
const { runCheck } = await import("./cli/check.js");
|
|
49
60
|
await runCheck(process.argv.slice(3));
|
|
@@ -59,13 +70,6 @@ Examples:
|
|
|
59
70
|
await runInit(process.argv.slice(3));
|
|
60
71
|
process.exit(0);
|
|
61
72
|
}
|
|
62
|
-
try {
|
|
63
|
-
const { register } = await import("tsx/esm/api");
|
|
64
|
-
register();
|
|
65
|
-
}
|
|
66
|
-
catch (err) {
|
|
67
|
-
console.warn("tsx loader unavailable; falling back to manual TS transpile where supported.", err instanceof Error ? err.message : err);
|
|
68
|
-
}
|
|
69
73
|
const modeFlag = process.argv.find(a => a.startsWith("--mode=")) ?? "--mode=backend";
|
|
70
74
|
const mode = modeFlag.split("=")[1];
|
|
71
75
|
// Additional flags: --emitters (list emitters), --emitter=<name> (run one emitter)
|
|
@@ -73,9 +77,6 @@ Examples:
|
|
|
73
77
|
const emitterFlag = process.argv.find(a => a.startsWith("--emitter=")) ?? null;
|
|
74
78
|
const emitterName = emitterFlag ? emitterFlag.split("=")[1] : null;
|
|
75
79
|
// parse positional args: first DSL entry and optional outDir
|
|
76
|
-
const rawArgs = process.argv.slice(2);
|
|
77
|
-
const extFlags = rawArgs.filter(a => a.startsWith("--ext="));
|
|
78
|
-
const extModules = extFlags.flatMap(f => f.replace("--ext=", "").split(",")).filter(Boolean);
|
|
79
80
|
const outDirFlag = process.argv.find(a => a.startsWith("--outDir=")) ?? null;
|
|
80
81
|
const entries = rawArgs.filter(a => a.endsWith(".dsl.ts"));
|
|
81
82
|
const entry = entries[0];
|