@bonsae/nrg 0.1.0 → 0.2.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.
@@ -661,13 +661,24 @@ function NodeRef(nodeClass, options) {
661
661
  };
662
662
  }
663
663
  function TypedInput(options) {
664
+ const { types, ...rest } = options ?? {};
664
665
  return {
665
666
  ...TypedInputSchema,
666
- ...options,
667
+ ...rest,
668
+ ...types ? { "x-typed-types": types } : {},
667
669
  [import_typebox.Kind]: "TypedInput"
668
670
  };
669
671
  }
672
+ var _OriginalString = import_typebox.Type.String.bind(import_typebox.Type);
673
+ function StringWithLang(options) {
674
+ const { lang, ...rest } = options ?? {};
675
+ return _OriginalString({
676
+ ...rest,
677
+ ...lang ? { "x-editor-language": lang } : {}
678
+ });
679
+ }
670
680
  var SchemaType = Object.assign({}, import_typebox.Type, {
681
+ String: StringWithLang,
671
682
  NodeRef,
672
683
  TypedInput
673
684
  });
@@ -1,4 +1,4 @@
1
- (function(){var s=document.createElement("style");s.textContent="[data-v-e5c98949] .node-red-vue-input-error-message{color:var(--red-ui-text-color-error)}[data-v-e5c98949] .nrg-label{display:inline-block;width:100%;cursor:default}[data-v-e5c98949] .form-row input[type=text],[data-v-e5c98949] .form-row input[type=number],[data-v-e5c98949] .form-row input[type=password]{height:34px;padding:0 8px;box-sizing:border-box}.container[data-v-508cfdb8]{position:relative}.expand-button[data-v-508cfdb8]{position:absolute;top:-23px;right:0;z-index:10;transition:color .3s ease;cursor:pointer}.nrg-required[data-v-642581e2]{color:var(--red-ui-text-color-error);margin-left:2px}\n";document.head.appendChild(s);})();
1
+ (function(){var s=document.createElement("style");s.textContent="[data-v-e5c98949] .node-red-vue-input-error-message{color:var(--red-ui-text-color-error)}[data-v-e5c98949] .nrg-label{display:inline-block;width:100%;cursor:default}[data-v-e5c98949] .form-row input[type=text],[data-v-e5c98949] .form-row input[type=number],[data-v-e5c98949] .form-row input[type=password]{height:34px;padding:0 8px;box-sizing:border-box}.container[data-v-508cfdb8]{position:relative}.expand-button[data-v-508cfdb8]{position:absolute;top:-23px;right:0;z-index:10;transition:color .3s ease;cursor:pointer}.nrg-required[data-v-2a313890]{color:var(--red-ui-text-color-error);margin-left:2px}\n";document.head.appendChild(s);})();
2
2
  import { defineComponent as je, resolveComponent as qe, openBlock as te, createElementBlock as re, Fragment as Ke, createElementVNode as ue, createCommentVNode as ge, createVNode as Vn, toDisplayString as Pe, withDirectives as ws, vShow as bs, renderList as fr, createTextVNode as pr, createBlock as ze, createApp as Es } from "/nrg/assets/vue.esm-browser.prod.js";
3
3
  function Ss() {
4
4
  }
@@ -6168,7 +6168,13 @@ function Dn(e, t, r) {
6168
6168
  language: t["x-editor-language"]
6169
6169
  } : { key: e, label: s, inputType: "text", required: r, htmlType: "text" };
6170
6170
  default:
6171
- return {
6171
+ return t["x-editor-language"] ? {
6172
+ key: e,
6173
+ label: s,
6174
+ inputType: "editor",
6175
+ required: r,
6176
+ language: t["x-editor-language"]
6177
+ } : {
6172
6178
  key: e,
6173
6179
  label: s,
6174
6180
  inputType: "text",
@@ -6348,7 +6354,7 @@ function Bo(e, t, r, s, m, o) {
6348
6354
  ))
6349
6355
  ]);
6350
6356
  }
6351
- const Mn = /* @__PURE__ */ Ie(zo, [["render", Bo], ["__scopeId", "data-v-642581e2"]]), gs = {}, sr = {};
6357
+ const Mn = /* @__PURE__ */ Ie(zo, [["render", Bo], ["__scopeId", "data-v-2a313890"]]), gs = {}, sr = {};
6352
6358
  function Qo(e) {
6353
6359
  Object.assign(gs, e);
6354
6360
  }
@@ -1183,14 +1183,15 @@ function minifier() {
1183
1183
  return {
1184
1184
  name: "vite-plugin-node-red:client:minifier",
1185
1185
  apply: "build",
1186
- renderChunk: {
1187
- order: "post",
1188
- async handler(code, chunk, outputOptions) {
1189
- if (outputOptions.format === "es" && chunk.fileName.endsWith(".js")) {
1190
- const result = await transform(code, { minify: true });
1191
- return result.code;
1186
+ async generateBundle(_options, bundle) {
1187
+ for (const [fileName, chunk] of Object.entries(bundle)) {
1188
+ if (chunk.type === "chunk" && fileName.endsWith(".js")) {
1189
+ const result = await transform(chunk.code, {
1190
+ minify: true
1191
+ });
1192
+ chunk.code = result.code;
1193
+ chunk.map = null;
1192
1194
  }
1193
- return code;
1194
1195
  }
1195
1196
  }
1196
1197
  };
@@ -1204,8 +1205,6 @@ import fs8 from "fs";
1204
1205
  import mime2 from "mime-types";
1205
1206
  var VIRTUAL_ID = "virtual:nrg/node-definitions";
1206
1207
  var RESOLVED_ID = "\0" + VIRTUAL_ID;
1207
- var VIRTUAL_ENTRY_ID = "virtual:nrg/client-entry";
1208
- var RESOLVED_ENTRY_ID = "\0" + VIRTUAL_ENTRY_ID;
1209
1208
  var SKIP_DEFAULTS = /* @__PURE__ */ new Set(["x", "y", "z", "g", "wires", "type", "id"]);
1210
1209
  function getDefaultsFromSchema(schema) {
1211
1210
  if (!schema?.properties) return void 0;
@@ -1243,6 +1242,7 @@ function resolveIcon(iconsDir, type) {
1243
1242
  function nodeDefinitionsInliner(serverOutDir, entryPath, iconsDir, componentsDir, nodesDir, hasUserEntry = true) {
1244
1243
  let _nodeTypes = [];
1245
1244
  let _definitions = {};
1245
+ const cacheDir = path8.resolve("node_modules", ".nrg", "client");
1246
1246
  return {
1247
1247
  name: "vite-plugin-node-red:client:node-definitions-inliner",
1248
1248
  enforce: "pre",
@@ -1302,64 +1302,93 @@ function nodeDefinitionsInliner(serverOutDir, entryPath, iconsDir, componentsDir
1302
1302
  outputsSchema
1303
1303
  };
1304
1304
  }
1305
+ if (!hasUserEntry) {
1306
+ const nodesCache = path8.resolve(cacheDir, "nodes");
1307
+ if (fs8.existsSync(nodesCache)) {
1308
+ fs8.rmSync(nodesCache, { recursive: true });
1309
+ }
1310
+ fs8.mkdirSync(nodesCache, { recursive: true });
1311
+ for (const type of _nodeTypes) {
1312
+ const userTsPath = nodesDir ? path8.resolve(nodesDir, `${type}.ts`) : null;
1313
+ if (userTsPath && fs8.existsSync(userTsPath)) continue;
1314
+ const content = [
1315
+ `// auto-generated by nrg`,
1316
+ `import { defineNode } from "@bonsae/nrg/client";`,
1317
+ ``,
1318
+ `export default defineNode({`,
1319
+ ` type: ${JSON.stringify(type)},`,
1320
+ `});`,
1321
+ ``
1322
+ ].join("\n");
1323
+ fs8.writeFileSync(path8.resolve(nodesCache, `${type}.ts`), content);
1324
+ }
1325
+ const entryContent = generateEntryCode("");
1326
+ fs8.mkdirSync(path8.dirname(path8.resolve(cacheDir, "index.ts")), {
1327
+ recursive: true
1328
+ });
1329
+ fs8.writeFileSync(path8.resolve(cacheDir, "index.ts"), entryContent);
1330
+ }
1305
1331
  },
1306
1332
  resolveId(id) {
1307
1333
  if (id === VIRTUAL_ID) return RESOLVED_ID;
1308
- if (id === VIRTUAL_ENTRY_ID) return RESOLVED_ENTRY_ID;
1309
1334
  },
1310
1335
  load(id) {
1311
- if (id === RESOLVED_ENTRY_ID) return "";
1312
- if (id !== RESOLVED_ID) return;
1313
- return `export default ${JSON.stringify(_definitions)};`;
1336
+ if (id === RESOLVED_ID)
1337
+ return `export default ${JSON.stringify(_definitions)};`;
1338
+ if (!hasUserEntry && id === entryPath) {
1339
+ return generateEntryCode("");
1340
+ }
1314
1341
  },
1315
1342
  transform(code, id) {
1316
- if (id !== entryPath && id !== RESOLVED_ENTRY_ID) return;
1317
- const nrgImports = /* @__PURE__ */ new Set(["__setSchemas"]);
1318
- const lines = [`import __nrgSchemas from "${VIRTUAL_ID}";`];
1319
- const postLines = [`__setSchemas(__nrgSchemas);`];
1320
- if (componentsDir && fs8.existsSync(componentsDir)) {
1321
- const formImports = [];
1322
- const formEntries = [];
1323
- for (const type of _nodeTypes) {
1324
- const componentPath = path8.resolve(componentsDir, `${type}.vue`);
1325
- if (fs8.existsSync(componentPath)) {
1326
- const varName = `__nrgForm_${type.replace(/-/g, "_")}`;
1327
- formImports.push(
1328
- `import ${varName} from ${JSON.stringify(componentPath)};`
1329
- );
1330
- formEntries.push(`${JSON.stringify(type)}: ${varName}`);
1331
- }
1332
- }
1333
- if (formImports.length > 0) {
1334
- lines.push(...formImports);
1335
- nrgImports.add("__setForms");
1336
- postLines.push(`__setForms({ ${formEntries.join(", ")} });`);
1343
+ if (id !== entryPath) return;
1344
+ if (!hasUserEntry) return;
1345
+ return { code: generateEntryCode(code), map: null };
1346
+ }
1347
+ };
1348
+ function generateEntryCode(userCode) {
1349
+ const nrgImports = /* @__PURE__ */ new Set(["__setSchemas"]);
1350
+ const lines = [`import __nrgSchemas from "${VIRTUAL_ID}";`];
1351
+ const postLines = [`__setSchemas(__nrgSchemas);`];
1352
+ if (componentsDir && fs8.existsSync(componentsDir)) {
1353
+ const formImports = [];
1354
+ const formEntries = [];
1355
+ for (const type of _nodeTypes) {
1356
+ const componentPath = path8.resolve(componentsDir, `${type}.vue`);
1357
+ if (fs8.existsSync(componentPath)) {
1358
+ const varName = `__nrgForm_${type.replace(/-/g, "_")}`;
1359
+ formImports.push(
1360
+ `import ${varName} from ${JSON.stringify(componentPath)};`
1361
+ );
1362
+ formEntries.push(`${JSON.stringify(type)}: ${varName}`);
1337
1363
  }
1338
1364
  }
1339
- if (!hasUserEntry) {
1340
- const defVarNames = [];
1341
- for (const type of _nodeTypes) {
1342
- const varName = `__nrgNodeDef_${type.replace(/-/g, "_")}`;
1343
- const tsPath = nodesDir ? path8.resolve(nodesDir, `${type}.ts`) : null;
1344
- if (tsPath && fs8.existsSync(tsPath)) {
1345
- lines.push(`import ${varName} from ${JSON.stringify(tsPath)};`);
1346
- } else {
1347
- lines.push(`const ${varName} = { type: ${JSON.stringify(type)} };`);
1348
- }
1349
- defVarNames.push(varName);
1350
- }
1351
- if (defVarNames.length > 0) {
1352
- nrgImports.add("registerTypes");
1353
- postLines.push(`registerTypes([${defVarNames.join(", ")}]);`);
1354
- }
1365
+ if (formImports.length > 0) {
1366
+ lines.push(...formImports);
1367
+ nrgImports.add("__setForms");
1368
+ postLines.push(`__setForms({ ${formEntries.join(", ")} });`);
1355
1369
  }
1356
- const importLine = `import { ${[...nrgImports].join(", ")} } from "@bonsae/nrg/client";`;
1357
- lines.splice(1, 0, importLine);
1358
- lines.push(...postLines);
1359
- lines.push("");
1360
- return { code: lines.join("\n") + code, map: null };
1361
1370
  }
1362
- };
1371
+ if (!hasUserEntry) {
1372
+ const nodesCache = path8.resolve(cacheDir, "nodes");
1373
+ const defVarNames = [];
1374
+ for (const type of _nodeTypes) {
1375
+ const varName = `__nrgNodeDef_${type.replace(/-/g, "_")}`;
1376
+ const userTsPath = nodesDir ? path8.resolve(nodesDir, `${type}.ts`) : null;
1377
+ const tsPath = userTsPath && fs8.existsSync(userTsPath) ? userTsPath : path8.resolve(nodesCache, `${type}.ts`);
1378
+ lines.push(`import ${varName} from ${JSON.stringify(tsPath)};`);
1379
+ defVarNames.push(varName);
1380
+ }
1381
+ if (defVarNames.length > 0) {
1382
+ nrgImports.add("registerTypes");
1383
+ postLines.push(`registerTypes([${defVarNames.join(", ")}]);`);
1384
+ }
1385
+ }
1386
+ const importLine = `import { ${[...nrgImports].join(", ")} } from "@bonsae/nrg/client";`;
1387
+ lines.splice(1, 0, importLine);
1388
+ lines.push(...postLines);
1389
+ lines.push("");
1390
+ return lines.join("\n") + userCode;
1391
+ }
1363
1392
  }
1364
1393
 
1365
1394
  // src/vite/client/plugins/static-copy.ts
@@ -1525,7 +1554,7 @@ async function build2(clientBuildOptions, buildContext) {
1525
1554
  build: {
1526
1555
  outDir: buildContext.outDir,
1527
1556
  emptyOutDir: false,
1528
- sourcemap: buildContext.isDev ? "inline" : true,
1557
+ sourcemap: buildContext.isDev ? "inline" : false,
1529
1558
  minify: !buildContext.isDev && format !== "es",
1530
1559
  copyPublicDir: false,
1531
1560
  lib: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bonsae/nrg",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "NRG framework — build Node-RED nodes with Vue 3, TypeScript, and JSON Schema",
5
5
  "author": "Allan Oricil <allanoricil@duck.com>",
6
6
  "license": "MIT",
@@ -23,6 +23,9 @@
23
23
  "lint:fix": "eslint src/ --fix",
24
24
  "format": "prettier --write \"src/**/*.{ts,vue,json}\"",
25
25
  "format:check": "prettier --check \"src/**/*.{ts,vue,json}\"",
26
+ "docs:dev": "pnpm --dir docs dev",
27
+ "docs:build": "pnpm --dir docs build",
28
+ "docs:preview": "pnpm --dir docs preview",
26
29
  "prepare": "husky"
27
30
  },
28
31
  "files": [
@@ -291,6 +291,16 @@ function buildField(
291
291
  return { key, label, inputType: "text", required, htmlType: "text" };
292
292
 
293
293
  default:
294
+ // string with editor language → code editor
295
+ if (schema["x-editor-language"]) {
296
+ return {
297
+ key,
298
+ label,
299
+ inputType: "editor",
300
+ required,
301
+ language: schema["x-editor-language"],
302
+ };
303
+ }
294
304
  // string (or untyped)
295
305
  return {
296
306
  key,
@@ -26,15 +26,29 @@ function NodeRef<T extends new (...args: any[]) => any>(
26
26
  } as unknown as TNodeRef<InstanceType<T>>;
27
27
  }
28
28
 
29
- function TypedInput(options?: SchemaOptions): TTypedInput {
29
+ function TypedInput(
30
+ options?: SchemaOptions & { types?: string[] },
31
+ ): TTypedInput {
32
+ const { types, ...rest } = options ?? {};
30
33
  return {
31
34
  ...TypedInputSchema,
32
- ...options,
35
+ ...rest,
36
+ ...(types ? { "x-typed-types": types } : {}),
33
37
  [Kind]: "TypedInput",
34
38
  } as unknown as TTypedInput;
35
39
  }
36
40
 
41
+ const _OriginalString = BaseType.String.bind(BaseType);
42
+ function StringWithLang(options?: SchemaOptions & { lang?: string }) {
43
+ const { lang, ...rest } = options ?? {};
44
+ return _OriginalString({
45
+ ...rest,
46
+ ...(lang ? { "x-editor-language": lang } : {}),
47
+ });
48
+ }
49
+
37
50
  const SchemaType = Object.assign({}, BaseType, {
51
+ String: StringWithLang,
38
52
  NodeRef,
39
53
  TypedInput,
40
54
  });
@@ -173,7 +173,7 @@ async function build(
173
173
  build: {
174
174
  outDir: buildContext.outDir,
175
175
  emptyOutDir: false,
176
- sourcemap: buildContext.isDev ? "inline" : true,
176
+ sourcemap: buildContext.isDev ? "inline" : false,
177
177
  minify: !buildContext.isDev && format !== "es",
178
178
  copyPublicDir: false,
179
179
  lib: {
@@ -6,15 +6,16 @@ function minifier(): Plugin {
6
6
  name: "vite-plugin-node-red:client:minifier",
7
7
  apply: "build",
8
8
 
9
- renderChunk: {
10
- order: "post",
11
- async handler(code, chunk, outputOptions) {
12
- if (outputOptions.format === "es" && chunk.fileName.endsWith(".js")) {
13
- const result = await transform(code, { minify: true });
14
- return result.code;
9
+ async generateBundle(_options, bundle) {
10
+ for (const [fileName, chunk] of Object.entries(bundle)) {
11
+ if (chunk.type === "chunk" && fileName.endsWith(".js")) {
12
+ const result = await transform(chunk.code, {
13
+ minify: true,
14
+ });
15
+ chunk.code = result.code;
16
+ chunk.map = null as any;
15
17
  }
16
- return code;
17
- },
18
+ }
18
19
  },
19
20
  };
20
21
  }
@@ -8,9 +8,6 @@ import mime from "mime-types";
8
8
  const VIRTUAL_ID = "virtual:nrg/node-definitions";
9
9
  const RESOLVED_ID = "\0" + VIRTUAL_ID;
10
10
 
11
- const VIRTUAL_ENTRY_ID = "virtual:nrg/client-entry";
12
- const RESOLVED_ENTRY_ID = "\0" + VIRTUAL_ENTRY_ID;
13
-
14
11
  const SKIP_DEFAULTS = new Set(["x", "y", "z", "g", "wires", "type", "id"]);
15
12
 
16
13
  function getDefaultsFromSchema(
@@ -77,6 +74,8 @@ function nodeDefinitionsInliner(
77
74
  ): Plugin {
78
75
  let _nodeTypes: string[] = [];
79
76
  let _definitions: Record<string, any> = {};
77
+ // Cache directory for generated files (inside node_modules, gitignored)
78
+ const cacheDir = path.resolve("node_modules", ".nrg", "client");
80
79
 
81
80
  return {
82
81
  name: "vite-plugin-node-red:client:node-definitions-inliner",
@@ -145,80 +144,132 @@ function nodeDefinitionsInliner(
145
144
  outputsSchema,
146
145
  };
147
146
  }
147
+
148
+ // When no user entry, generate node definition files in cache
149
+ // so they appear as separate files in browser devtools.
150
+ if (!hasUserEntry) {
151
+ const nodesCache = path.resolve(cacheDir, "nodes");
152
+ if (fs.existsSync(nodesCache)) {
153
+ fs.rmSync(nodesCache, { recursive: true });
154
+ }
155
+ fs.mkdirSync(nodesCache, { recursive: true });
156
+
157
+ for (const type of _nodeTypes) {
158
+ // Skip if user has a physical node definition file
159
+ const userTsPath = nodesDir
160
+ ? path.resolve(nodesDir, `${type}.ts`)
161
+ : null;
162
+ if (userTsPath && fs.existsSync(userTsPath)) continue;
163
+
164
+ const content = [
165
+ `// auto-generated by nrg`,
166
+ `import { defineNode } from "@bonsae/nrg/client";`,
167
+ ``,
168
+ `export default defineNode({`,
169
+ ` type: ${JSON.stringify(type)},`,
170
+ `});`,
171
+ ``,
172
+ ].join("\n");
173
+ fs.writeFileSync(path.resolve(nodesCache, `${type}.ts`), content);
174
+ }
175
+
176
+ // Also write the entry file to cache
177
+ const entryContent = generateEntryCode("");
178
+ fs.mkdirSync(path.dirname(path.resolve(cacheDir, "index.ts")), {
179
+ recursive: true,
180
+ });
181
+ fs.writeFileSync(path.resolve(cacheDir, "index.ts"), entryContent);
182
+ }
148
183
  },
149
184
 
150
185
  resolveId(id) {
151
186
  if (id === VIRTUAL_ID) return RESOLVED_ID;
152
- if (id === VIRTUAL_ENTRY_ID) return RESOLVED_ENTRY_ID;
153
187
  },
154
188
 
155
189
  load(id) {
156
- if (id === RESOLVED_ENTRY_ID) return "";
157
- if (id !== RESOLVED_ID) return;
158
- return `export default ${JSON.stringify(_definitions)};`;
190
+ if (id === RESOLVED_ID)
191
+ return `export default ${JSON.stringify(_definitions)};`;
192
+
193
+ // For auto-generated entries, return the generated code directly
194
+ // so the browser devtools shows the actual source instead of the
195
+ // placeholder comment on disk.
196
+ if (!hasUserEntry && id === entryPath) {
197
+ return generateEntryCode("");
198
+ }
159
199
  },
160
200
 
161
201
  transform(code, id) {
162
- if (id !== entryPath && id !== RESOLVED_ENTRY_ID) return;
202
+ if (id !== entryPath) return;
203
+ // When the user provides their own entry, prepend the generated
204
+ // code to their source. For auto-generated entries, the load hook
205
+ // already returned the full code.
206
+ if (!hasUserEntry) return;
163
207
 
164
- const nrgImports = new Set<string>(["__setSchemas"]);
165
- const lines = [`import __nrgSchemas from "${VIRTUAL_ID}";`];
166
- const postLines: string[] = [`__setSchemas(__nrgSchemas);`];
208
+ return { code: generateEntryCode(code), map: null };
209
+ },
210
+ };
167
211
 
168
- // Auto-detect form components by convention: {componentsDir}/{type}.vue
169
- if (componentsDir && fs.existsSync(componentsDir)) {
170
- const formImports: string[] = [];
171
- const formEntries: string[] = [];
212
+ function generateEntryCode(userCode: string): string {
213
+ const nrgImports = new Set<string>(["__setSchemas"]);
214
+ const lines = [`import __nrgSchemas from "${VIRTUAL_ID}";`];
215
+ const postLines: string[] = [`__setSchemas(__nrgSchemas);`];
172
216
 
173
- for (const type of _nodeTypes) {
174
- const componentPath = path.resolve(componentsDir, `${type}.vue`);
175
- if (fs.existsSync(componentPath)) {
176
- const varName = `__nrgForm_${type.replace(/-/g, "_")}`;
177
- formImports.push(
178
- `import ${varName} from ${JSON.stringify(componentPath)};`,
179
- );
180
- formEntries.push(`${JSON.stringify(type)}: ${varName}`);
181
- }
182
- }
217
+ // Auto-detect form components by convention: {componentsDir}/{type}.vue
218
+ if (componentsDir && fs.existsSync(componentsDir)) {
219
+ const formImports: string[] = [];
220
+ const formEntries: string[] = [];
183
221
 
184
- if (formImports.length > 0) {
185
- lines.push(...formImports);
186
- nrgImports.add("__setForms");
187
- postLines.push(`__setForms({ ${formEntries.join(", ")} });`);
222
+ for (const type of _nodeTypes) {
223
+ const componentPath = path.resolve(componentsDir, `${type}.vue`);
224
+ if (fs.existsSync(componentPath)) {
225
+ const varName = `__nrgForm_${type.replace(/-/g, "_")}`;
226
+ formImports.push(
227
+ `import ${varName} from ${JSON.stringify(componentPath)};`,
228
+ );
229
+ formEntries.push(`${JSON.stringify(type)}: ${varName}`);
188
230
  }
189
231
  }
190
232
 
191
- // Auto-register only when no user entry was provided.
192
- // When the user provides client/index.ts, they control registration.
193
- if (!hasUserEntry) {
194
- const defVarNames: string[] = [];
195
- for (const type of _nodeTypes) {
196
- const varName = `__nrgNodeDef_${type.replace(/-/g, "_")}`;
197
- const tsPath = nodesDir ? path.resolve(nodesDir, `${type}.ts`) : null;
198
-
199
- if (tsPath && fs.existsSync(tsPath)) {
200
- lines.push(`import ${varName} from ${JSON.stringify(tsPath)};`);
201
- } else {
202
- lines.push(`const ${varName} = { type: ${JSON.stringify(type)} };`);
203
- }
204
- defVarNames.push(varName);
205
- }
233
+ if (formImports.length > 0) {
234
+ lines.push(...formImports);
235
+ nrgImports.add("__setForms");
236
+ postLines.push(`__setForms({ ${formEntries.join(", ")} });`);
237
+ }
238
+ }
206
239
 
207
- if (defVarNames.length > 0) {
208
- nrgImports.add("registerTypes");
209
- postLines.push(`registerTypes([${defVarNames.join(", ")}]);`);
210
- }
240
+ // Auto-register only when no user entry was provided.
241
+ if (!hasUserEntry) {
242
+ const nodesCache = path.resolve(cacheDir, "nodes");
243
+ const defVarNames: string[] = [];
244
+
245
+ for (const type of _nodeTypes) {
246
+ const varName = `__nrgNodeDef_${type.replace(/-/g, "_")}`;
247
+ // Use physical file if user has one, otherwise use cached generated file
248
+ const userTsPath = nodesDir
249
+ ? path.resolve(nodesDir, `${type}.ts`)
250
+ : null;
251
+ const tsPath =
252
+ userTsPath && fs.existsSync(userTsPath)
253
+ ? userTsPath
254
+ : path.resolve(nodesCache, `${type}.ts`);
255
+ lines.push(`import ${varName} from ${JSON.stringify(tsPath)};`);
256
+ defVarNames.push(varName);
211
257
  }
212
258
 
213
- // Build the @bonsae/nrg/client import line
214
- const importLine = `import { ${[...nrgImports].join(", ")} } from "@bonsae/nrg/client";`;
215
- lines.splice(1, 0, importLine);
259
+ if (defVarNames.length > 0) {
260
+ nrgImports.add("registerTypes");
261
+ postLines.push(`registerTypes([${defVarNames.join(", ")}]);`);
262
+ }
263
+ }
216
264
 
217
- lines.push(...postLines);
218
- lines.push("");
219
- return { code: lines.join("\n") + code, map: null };
220
- },
221
- };
265
+ // Build the @bonsae/nrg/client import line
266
+ const importLine = `import { ${[...nrgImports].join(", ")} } from "@bonsae/nrg/client";`;
267
+ lines.splice(1, 0, importLine);
268
+
269
+ lines.push(...postLines);
270
+ lines.push("");
271
+ return lines.join("\n") + userCode;
272
+ }
222
273
  }
223
274
 
224
275
  export { nodeDefinitionsInliner };