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 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
- const extFlags = args.filter(a => a.startsWith("--ext="));
6
- const extModules = extFlags.flatMap(f => f.replace("--ext=", "").split(",")).filter(Boolean);
7
- if (extModules.length > 0) {
8
- const { loadExtensions } = await import("./extensions.js");
9
- await loadExtensions(extModules);
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();
@@ -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 => { totalEntities += (a.entities?.length || 0); totalPages += (a.pages?.length || 0); });
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.apps[0]?.meta || {}).map(([k,v]) => \`
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 ? \`<div class="stat-label">Entity: \${comp.entityRef}</div>\` : ''}
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];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "irgen",
3
- "version": "0.3.1",
3
+ "version": "0.3.2",
4
4
  "private": false,
5
5
  "license": "MIT",
6
6
  "description": "a policy-driven, IR-based code generation toolchain for backend, frontend, and beyond.",