@bonsae/nrg 0.18.4 → 0.19.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/README.md +38 -45
- package/package.json +1 -1
- package/server/index.cjs +96 -10
- package/server/resources/nrg-client.js +2269 -2233
- package/test/client/component/config.js +11 -0
- package/test/client/component/index.js +218 -235
- package/test/client/component/nrg.css +1 -0
- package/test/client/component/setup.js +1549 -140
- package/test/client/e2e/index.js +721 -367
- package/test/client/unit/index.js +204 -16
- package/test/client/unit/setup.js +209 -19
- package/test/server/unit/index.js +25 -4
- package/tsconfig/core/client.json +1 -1
- package/tsconfig/test/client/component.json +1 -1
- package/types/client.d.ts +98 -18
- package/types/server.d.ts +50 -12
- package/types/shims/brands.d.ts +32 -0
- package/types/shims/{form → client/form}/components/node-red-editor-input.vue.d.ts +1 -1
- package/types/shims/{form → client/form}/components/node-red-json-schema-form.vue.d.ts +21 -2
- package/types/shims/{form → client/form}/components/node-red-select-input.vue.d.ts +1 -0
- package/types/shims/{form → client/form}/components/node-red-typed-input.vue.d.ts +1 -0
- package/types/shims/client/types.d.ts +206 -0
- package/types/shims/components.d.ts +8 -8
- package/types/shims/constants.d.ts +4 -0
- package/types/shims/schema-options.d.ts +23 -10
- package/types/shims/typebox.d.ts +2 -2
- package/types/test-client-component.d.ts +170 -55
- package/types/test-client-e2e.d.ts +50 -0
- package/types/test-client-unit.d.ts +86 -22
- package/types/test-server-unit.d.ts +3 -1
- package/types/vite.d.ts +38 -9
- package/vite/index.js +733 -528
- /package/types/shims/{form → client/form}/components/node-red-config-input.vue.d.ts +0 -0
- /package/types/shims/{form → client/form}/components/node-red-input-label.vue.d.ts +0 -0
- /package/types/shims/{form → client/form}/components/node-red-input.vue.d.ts +0 -0
- /package/types/shims/{form → client/form}/components/node-red-toggle.vue.d.ts +0 -0
- /package/types/shims/{globals.d.ts → client/globals.d.ts} +0 -0
package/vite/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// src/vite/plugin.ts
|
|
2
|
-
import
|
|
2
|
+
import path14 from "path";
|
|
3
3
|
|
|
4
4
|
// src/vite/defaults.ts
|
|
5
5
|
var DEFAULT_OUTPUT_DIR = "./dist";
|
|
@@ -52,6 +52,40 @@ var DEFAULT_EXTRA_FILES_COPY_TARGETS = [
|
|
|
52
52
|
// src/vite/utils.ts
|
|
53
53
|
import fs from "fs";
|
|
54
54
|
import path from "path";
|
|
55
|
+
|
|
56
|
+
// src/vite/errors.ts
|
|
57
|
+
var PluginError = class extends Error {
|
|
58
|
+
constructor(message, code, cause) {
|
|
59
|
+
super(message);
|
|
60
|
+
this.code = code;
|
|
61
|
+
this.cause = cause;
|
|
62
|
+
this.name = "PluginError";
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
var NodeRedStartError = class extends PluginError {
|
|
66
|
+
constructor(cause) {
|
|
67
|
+
super("Failed to start Node-RED", "NODE_RED_START_FAILED", cause);
|
|
68
|
+
this.name = "NodeRedStartError";
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
var BuildError = class extends PluginError {
|
|
72
|
+
constructor(phase, cause) {
|
|
73
|
+
super(
|
|
74
|
+
`Failed to build ${phase}`,
|
|
75
|
+
`BUILD_${phase.toUpperCase()}_FAILED`,
|
|
76
|
+
cause
|
|
77
|
+
);
|
|
78
|
+
this.name = "BuildError";
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
var ConfigError = class extends PluginError {
|
|
82
|
+
constructor(message) {
|
|
83
|
+
super(message, "CONFIG_INVALID");
|
|
84
|
+
this.name = "ConfigError";
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
// src/vite/utils.ts
|
|
55
89
|
function cleanDir(dir) {
|
|
56
90
|
if (fs.existsSync(dir)) {
|
|
57
91
|
fs.rmSync(dir, { recursive: true });
|
|
@@ -86,6 +120,23 @@ function getPackageName() {
|
|
|
86
120
|
}
|
|
87
121
|
return "node-red-nodes";
|
|
88
122
|
}
|
|
123
|
+
var SLUG_PATTERN = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
124
|
+
function slugify(input) {
|
|
125
|
+
return input.normalize("NFKD").replace(/[\u0300-\u036f]/g, "").toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
126
|
+
}
|
|
127
|
+
function resolveSlug(userSlug) {
|
|
128
|
+
if (userSlug !== void 0) {
|
|
129
|
+
const trimmed = userSlug.trim();
|
|
130
|
+
if (!SLUG_PATTERN.test(trimmed)) {
|
|
131
|
+
const suggestion = slugify(trimmed);
|
|
132
|
+
throw new ConfigError(
|
|
133
|
+
`Invalid dev server slug ${JSON.stringify(userSlug)}. A slug must be URL-safe: lowercase letters, digits and single hyphens (matching ${String(SLUG_PATTERN)}).` + (suggestion ? ` Did you mean ${JSON.stringify(suggestion)}?` : "")
|
|
134
|
+
);
|
|
135
|
+
}
|
|
136
|
+
return trimmed;
|
|
137
|
+
}
|
|
138
|
+
return slugify(path.basename(process.cwd())) || "app";
|
|
139
|
+
}
|
|
89
140
|
function mergeOptions(defaults, overrides) {
|
|
90
141
|
if (!overrides) return { ...defaults };
|
|
91
142
|
const result = { ...defaults };
|
|
@@ -106,17 +157,8 @@ function mergeOptions(defaults, overrides) {
|
|
|
106
157
|
return result;
|
|
107
158
|
}
|
|
108
159
|
|
|
109
|
-
// src/vite/node-red-launcher.ts
|
|
110
|
-
import
|
|
111
|
-
import getPort from "get-port";
|
|
112
|
-
import detect from "detect-port";
|
|
113
|
-
import { builtinModules, createRequire } from "module";
|
|
114
|
-
import treeKill from "tree-kill";
|
|
115
|
-
import fs2 from "fs";
|
|
116
|
-
import os from "os";
|
|
117
|
-
import path2 from "path";
|
|
118
|
-
import { pathToFileURL } from "url";
|
|
119
|
-
import { build as esbuild } from "esbuild";
|
|
160
|
+
// src/vite/node-red-launcher/index.ts
|
|
161
|
+
import fs4 from "fs";
|
|
120
162
|
|
|
121
163
|
// src/vite/async-utils.ts
|
|
122
164
|
function debounce(fn, delay) {
|
|
@@ -160,32 +202,6 @@ async function retry(fn, options = {}) {
|
|
|
160
202
|
throw lastError;
|
|
161
203
|
}
|
|
162
204
|
|
|
163
|
-
// src/vite/errors.ts
|
|
164
|
-
var PluginError = class extends Error {
|
|
165
|
-
constructor(message, code, cause) {
|
|
166
|
-
super(message);
|
|
167
|
-
this.code = code;
|
|
168
|
-
this.cause = cause;
|
|
169
|
-
this.name = "PluginError";
|
|
170
|
-
}
|
|
171
|
-
};
|
|
172
|
-
var NodeRedStartError = class extends PluginError {
|
|
173
|
-
constructor(cause) {
|
|
174
|
-
super("Failed to start Node-RED", "NODE_RED_START_FAILED", cause);
|
|
175
|
-
this.name = "NodeRedStartError";
|
|
176
|
-
}
|
|
177
|
-
};
|
|
178
|
-
var BuildError = class extends PluginError {
|
|
179
|
-
constructor(phase, cause) {
|
|
180
|
-
super(
|
|
181
|
-
`Failed to build ${phase}`,
|
|
182
|
-
`BUILD_${phase.toUpperCase()}_FAILED`,
|
|
183
|
-
cause
|
|
184
|
-
);
|
|
185
|
-
this.name = "BuildError";
|
|
186
|
-
}
|
|
187
|
-
};
|
|
188
|
-
|
|
189
205
|
// src/vite/logger.ts
|
|
190
206
|
import * as clackLogger from "@clack/prompts";
|
|
191
207
|
var color = {
|
|
@@ -259,200 +275,388 @@ var Logger = class _Logger {
|
|
|
259
275
|
};
|
|
260
276
|
var logger = new Logger({ name: "vite-plugin-node-red" });
|
|
261
277
|
|
|
262
|
-
// src/vite/node-red-launcher.ts
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
278
|
+
// src/vite/node-red-launcher/entry-point.ts
|
|
279
|
+
import { exec } from "child_process";
|
|
280
|
+
import { randomUUID } from "crypto";
|
|
281
|
+
import { createRequire } from "module";
|
|
282
|
+
import fs2 from "fs";
|
|
283
|
+
import os from "os";
|
|
284
|
+
import path2 from "path";
|
|
285
|
+
function getNodeRedCommand(version) {
|
|
286
|
+
return version ? `node-red@${version}` : "node-red";
|
|
287
|
+
}
|
|
288
|
+
function resolveNodeRedFromLocalNodeModules() {
|
|
289
|
+
try {
|
|
290
|
+
const require_ = createRequire(path2.join(process.cwd(), "package.json"));
|
|
291
|
+
const pkgJsonPath = require_.resolve("node-red/package.json");
|
|
292
|
+
const pkgDir = path2.dirname(pkgJsonPath);
|
|
293
|
+
const pkg = JSON.parse(fs2.readFileSync(pkgJsonPath, "utf-8"));
|
|
294
|
+
const bin = typeof pkg.bin === "string" ? pkg.bin : pkg.bin?.["node-red"];
|
|
295
|
+
if (!bin) return null;
|
|
296
|
+
const entry = path2.resolve(pkgDir, bin);
|
|
297
|
+
return fs2.existsSync(entry) ? entry : null;
|
|
298
|
+
} catch {
|
|
299
|
+
return null;
|
|
279
300
|
}
|
|
280
|
-
|
|
281
|
-
|
|
301
|
+
}
|
|
302
|
+
async function resolveNodeRed(options) {
|
|
303
|
+
const { version, npxTimeoutMs = 3e5, logger: logger2 } = options;
|
|
304
|
+
if (version && !/^[\w.^~<>=*-]+$/.test(version)) {
|
|
305
|
+
throw new NodeRedStartError(
|
|
306
|
+
new Error(`Invalid node-red version "${version}"`)
|
|
307
|
+
);
|
|
282
308
|
}
|
|
283
|
-
|
|
284
|
-
|
|
309
|
+
const nodeRedCommand = getNodeRedCommand(version);
|
|
310
|
+
logger2.info(`Resolving ${nodeRedCommand} entry point...`);
|
|
311
|
+
const hasExplicitVersion = version !== void 0 && version !== "latest";
|
|
312
|
+
if (!hasExplicitVersion) {
|
|
313
|
+
const localEntry = resolveNodeRedFromLocalNodeModules();
|
|
314
|
+
if (localEntry) {
|
|
315
|
+
logger2.info(`Resolved from local node_modules: ${localEntry}`);
|
|
316
|
+
return localEntry;
|
|
317
|
+
}
|
|
285
318
|
}
|
|
286
|
-
|
|
287
|
-
|
|
319
|
+
logger2.info(
|
|
320
|
+
hasExplicitVersion ? `Using configured version (${version}), downloading via npx...` : `Not found locally, downloading via npx (this may take a while)...`
|
|
321
|
+
);
|
|
322
|
+
const resolverScript = path2.join(
|
|
323
|
+
os.tmpdir(),
|
|
324
|
+
`nrg-resolve-node-red-${process.pid}-${randomUUID()}.cjs`
|
|
325
|
+
);
|
|
326
|
+
fs2.writeFileSync(
|
|
327
|
+
resolverScript,
|
|
328
|
+
`const fs = require("fs");
|
|
329
|
+
const path = require("path");
|
|
330
|
+
const isWin = process.platform === "win32";
|
|
331
|
+
const binName = isWin ? "node-red.cmd" : "node-red";
|
|
332
|
+
const dirs = process.env.PATH.split(path.delimiter);
|
|
333
|
+
for (const d of dirs) {
|
|
334
|
+
const f = path.join(d, binName);
|
|
335
|
+
if (fs.existsSync(f)) {
|
|
336
|
+
if (isWin) {
|
|
337
|
+
const nodeRedDir = path.resolve(d, "..", "node-red");
|
|
338
|
+
const pkg = JSON.parse(fs.readFileSync(path.join(nodeRedDir, "package.json"), "utf-8"));
|
|
339
|
+
const bin = typeof pkg.bin === "string" ? pkg.bin : pkg.bin["node-red"];
|
|
340
|
+
process.stdout.write(path.resolve(nodeRedDir, bin));
|
|
341
|
+
} else {
|
|
342
|
+
process.stdout.write(fs.realpathSync(f));
|
|
343
|
+
}
|
|
344
|
+
break;
|
|
288
345
|
}
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
346
|
+
}`
|
|
347
|
+
);
|
|
348
|
+
try {
|
|
349
|
+
const stdout = await new Promise((resolve, reject) => {
|
|
350
|
+
exec(
|
|
351
|
+
`npx --yes -p ${nodeRedCommand} node "${resolverScript}"`,
|
|
352
|
+
{ timeout: npxTimeoutMs },
|
|
353
|
+
(error, stdout2) => {
|
|
354
|
+
if (error) reject(error);
|
|
355
|
+
else resolve(stdout2);
|
|
356
|
+
}
|
|
357
|
+
);
|
|
358
|
+
});
|
|
359
|
+
const entryPoint = stdout.trim();
|
|
360
|
+
if (!entryPoint || !fs2.existsSync(entryPoint)) {
|
|
361
|
+
throw new NodeRedStartError(
|
|
362
|
+
new Error(
|
|
363
|
+
`Could not resolve node-red entry point: ${entryPoint || "(empty)"}`
|
|
364
|
+
)
|
|
365
|
+
);
|
|
293
366
|
}
|
|
294
|
-
|
|
295
|
-
|
|
367
|
+
logger2.info(`Resolved via npx: ${entryPoint}`);
|
|
368
|
+
return entryPoint;
|
|
369
|
+
} finally {
|
|
370
|
+
try {
|
|
371
|
+
fs2.unlinkSync(resolverScript);
|
|
372
|
+
} catch {
|
|
296
373
|
}
|
|
297
|
-
return "node-red";
|
|
298
374
|
}
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
// src/vite/node-red-launcher/settings.ts
|
|
378
|
+
import { builtinModules } from "module";
|
|
379
|
+
import fs3 from "fs";
|
|
380
|
+
import os2 from "os";
|
|
381
|
+
import path3 from "path";
|
|
382
|
+
import { pathToFileURL } from "url";
|
|
383
|
+
import { build as esbuild } from "esbuild";
|
|
384
|
+
function findUserRuntimeSettingsFilepath(settingsFilepath, logger2) {
|
|
385
|
+
if (settingsFilepath) {
|
|
386
|
+
const resolved2 = path3.resolve(settingsFilepath);
|
|
387
|
+
if (fs3.existsSync(resolved2)) {
|
|
388
|
+
return resolved2;
|
|
312
389
|
}
|
|
390
|
+
logger2.warn(`Settings file not found: ${settingsFilepath}`);
|
|
313
391
|
return null;
|
|
314
392
|
}
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
393
|
+
const resolved = path3.resolve("node-red.settings.ts");
|
|
394
|
+
if (fs3.existsSync(resolved)) {
|
|
395
|
+
return resolved;
|
|
396
|
+
}
|
|
397
|
+
return null;
|
|
398
|
+
}
|
|
399
|
+
async function compileRuntimeSettingsFile(runtimeSettingsFilepath, port) {
|
|
400
|
+
const compiledRuntimeSettingsFilepath = path3.join(
|
|
401
|
+
os2.tmpdir(),
|
|
402
|
+
`node-red.settings.${process.pid}-${port}.cjs`
|
|
403
|
+
);
|
|
404
|
+
const nodeBuiltins2 = [
|
|
405
|
+
...builtinModules,
|
|
406
|
+
...builtinModules.map((m) => `node:${m}`)
|
|
407
|
+
];
|
|
408
|
+
const settingsDir = path3.dirname(runtimeSettingsFilepath).split(path3.sep).join("/");
|
|
409
|
+
const settingsFile = runtimeSettingsFilepath.split(path3.sep).join("/");
|
|
410
|
+
await esbuild({
|
|
411
|
+
entryPoints: [runtimeSettingsFilepath],
|
|
412
|
+
outfile: compiledRuntimeSettingsFilepath,
|
|
413
|
+
format: "cjs",
|
|
414
|
+
platform: "node",
|
|
415
|
+
target: "node18",
|
|
416
|
+
bundle: true,
|
|
417
|
+
define: {
|
|
418
|
+
"import.meta.dirname": JSON.stringify(settingsDir),
|
|
419
|
+
"import.meta.filename": JSON.stringify(settingsFile),
|
|
420
|
+
"import.meta.url": JSON.stringify(pathToFileURL(settingsFile).href)
|
|
421
|
+
},
|
|
422
|
+
external: [...nodeBuiltins2, "node-red", "@node-red/*"]
|
|
423
|
+
});
|
|
424
|
+
return compiledRuntimeSettingsFilepath;
|
|
425
|
+
}
|
|
426
|
+
async function generateRuntimeSettings(options) {
|
|
427
|
+
const { outDir, port, settingsFilepath, httpAdminRoot, logger: logger2 } = options;
|
|
428
|
+
const tempFiles = [];
|
|
429
|
+
const userRuntimeSettingsFilepath = findUserRuntimeSettingsFilepath(
|
|
430
|
+
settingsFilepath,
|
|
431
|
+
logger2
|
|
432
|
+
);
|
|
433
|
+
let compiledRuntimeSettingsFilepath = null;
|
|
434
|
+
if (userRuntimeSettingsFilepath) {
|
|
435
|
+
compiledRuntimeSettingsFilepath = await compileRuntimeSettingsFile(
|
|
436
|
+
userRuntimeSettingsFilepath,
|
|
437
|
+
port
|
|
319
438
|
);
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
"import.meta.filename": JSON.stringify(settingsFile),
|
|
336
|
-
"import.meta.url": JSON.stringify(pathToFileURL(settingsFile).href)
|
|
337
|
-
},
|
|
338
|
-
external: [...nodeBuiltins2, "node-red", "@node-red/*"]
|
|
339
|
-
});
|
|
340
|
-
this.compiledRuntimeSettingsFilepath = compiledRuntimeSettingsFilepath;
|
|
341
|
-
return compiledRuntimeSettingsFilepath;
|
|
342
|
-
}
|
|
343
|
-
async generateRuntimeSettingsFile() {
|
|
344
|
-
const userRuntimeSettingsFilepath = this.findRuntimeSettingsFilepath();
|
|
345
|
-
let compiledRuntimeSettingsFilepath = null;
|
|
346
|
-
if (userRuntimeSettingsFilepath) {
|
|
347
|
-
compiledRuntimeSettingsFilepath = await this.compileRuntimeSettingsFile(
|
|
348
|
-
userRuntimeSettingsFilepath
|
|
349
|
-
);
|
|
350
|
-
}
|
|
351
|
-
const outDir = path2.resolve(this.outDir).split(path2.sep).join("/");
|
|
352
|
-
const cwd = process.cwd().split(path2.sep).join("/");
|
|
353
|
-
const userDir = path2.resolve(cwd, ".node-red").split(path2.sep).join("/");
|
|
354
|
-
const finalRuntimeSettingsFile = compiledRuntimeSettingsFilepath ? `
|
|
355
|
-
const compiledRuntimeSettings = require("${compiledRuntimeSettingsFilepath.split(path2.sep).join("/")}");
|
|
439
|
+
tempFiles.push(compiledRuntimeSettingsFilepath);
|
|
440
|
+
}
|
|
441
|
+
const normalizedOutDir = path3.resolve(outDir).split(path3.sep).join("/");
|
|
442
|
+
const cwd = process.cwd().split(path3.sep).join("/");
|
|
443
|
+
const userDir = path3.resolve(cwd, ".node-red").split(path3.sep).join("/");
|
|
444
|
+
const userDirLiteral = JSON.stringify(userDir);
|
|
445
|
+
const outDirLiteral = JSON.stringify(normalizedOutDir);
|
|
446
|
+
const httpAdminRootAssignment = httpAdminRoot ? `settings.httpAdminRoot = ${JSON.stringify(httpAdminRoot)};
|
|
447
|
+
` : "";
|
|
448
|
+
const httpAdminRootEntry = httpAdminRoot ? `
|
|
449
|
+
httpAdminRoot: ${JSON.stringify(httpAdminRoot)},` : "";
|
|
450
|
+
const finalRuntimeSettingsFile = compiledRuntimeSettingsFilepath ? `
|
|
451
|
+
const compiledRuntimeSettings = require(${JSON.stringify(
|
|
452
|
+
compiledRuntimeSettingsFilepath.split(path3.sep).join("/")
|
|
453
|
+
)});
|
|
356
454
|
const settings = compiledRuntimeSettings.default || compiledRuntimeSettings;
|
|
357
|
-
settings.uiPort = ${
|
|
358
|
-
if(!settings.userDir){
|
|
359
|
-
settings.userDir =
|
|
455
|
+
settings.uiPort = ${port};
|
|
456
|
+
${httpAdminRootAssignment}if(!settings.userDir){
|
|
457
|
+
settings.userDir = ${userDirLiteral};
|
|
360
458
|
}
|
|
361
459
|
settings.nodesDir = settings.nodesDir || [];
|
|
362
|
-
if (!settings.nodesDir.includes(
|
|
363
|
-
settings.nodesDir.push(
|
|
460
|
+
if (!settings.nodesDir.includes(${outDirLiteral})) {
|
|
461
|
+
settings.nodesDir.push(${outDirLiteral});
|
|
364
462
|
}
|
|
365
463
|
if(!settings.flowFile){
|
|
366
464
|
settings.flowFile = "flows.json";
|
|
367
465
|
}
|
|
466
|
+
// the welcome tour overlay intercepts pointer events \u2014 fatal for e2e and
|
|
467
|
+
// noise for dev; explicit user settings still win
|
|
468
|
+
settings.editorTheme = settings.editorTheme || {};
|
|
469
|
+
if (settings.editorTheme.tours === undefined) {
|
|
470
|
+
settings.editorTheme.tours = false;
|
|
471
|
+
}
|
|
368
472
|
module.exports = settings;
|
|
369
473
|
` : `
|
|
370
474
|
const settings = {
|
|
371
|
-
uiPort: ${
|
|
372
|
-
userDir:
|
|
475
|
+
uiPort: ${port},
|
|
476
|
+
userDir: ${userDirLiteral},
|
|
373
477
|
flowFile: "flows.json",
|
|
374
|
-
nodesDir: [
|
|
478
|
+
nodesDir: [${outDirLiteral}],${httpAdminRootEntry}
|
|
479
|
+
// the welcome tour overlay intercepts pointer events \u2014 fatal for e2e
|
|
480
|
+
editorTheme: { tours: false },
|
|
375
481
|
};
|
|
376
482
|
module.exports = settings;
|
|
377
483
|
`;
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
484
|
+
const finalRuntimeSettingsFilepath = path3.join(
|
|
485
|
+
os2.tmpdir(),
|
|
486
|
+
`node-red-settings-final-${process.pid}-${port}.cjs`
|
|
487
|
+
);
|
|
488
|
+
fs3.writeFileSync(finalRuntimeSettingsFilepath, finalRuntimeSettingsFile);
|
|
489
|
+
tempFiles.push(finalRuntimeSettingsFilepath);
|
|
490
|
+
return { filepath: finalRuntimeSettingsFilepath, tempFiles };
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
// src/vite/node-red-launcher/process.ts
|
|
494
|
+
import { spawn } from "child_process";
|
|
495
|
+
import detect from "detect-port";
|
|
496
|
+
import getPort from "get-port";
|
|
497
|
+
import treeKill from "tree-kill";
|
|
498
|
+
var READY_MARKERS = ["Started flows", "Server now running"];
|
|
499
|
+
function start(options) {
|
|
500
|
+
const { entryPoint, settingsPath, args, onLine } = options;
|
|
501
|
+
const child = spawn(
|
|
502
|
+
process.execPath,
|
|
503
|
+
[entryPoint, "-s", settingsPath, ...args],
|
|
504
|
+
{
|
|
505
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
398
506
|
}
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
507
|
+
);
|
|
508
|
+
let isReady = false;
|
|
509
|
+
let resolveReady;
|
|
510
|
+
let rejectReady;
|
|
511
|
+
const ready = new Promise((resolve, reject) => {
|
|
512
|
+
resolveReady = resolve;
|
|
513
|
+
rejectReady = reject;
|
|
514
|
+
});
|
|
515
|
+
const emitLine = (rawLine, source) => {
|
|
516
|
+
const line = rawLine.endsWith("\r") ? rawLine.slice(0, -1) : rawLine;
|
|
517
|
+
if (!line) return;
|
|
518
|
+
onLine(line, source, isReady);
|
|
519
|
+
if (source === "stdout" && READY_MARKERS.some((marker) => line.includes(marker))) {
|
|
520
|
+
isReady = true;
|
|
521
|
+
resolveReady();
|
|
406
522
|
}
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
const
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
fs2.writeFileSync(
|
|
415
|
-
resolverScript,
|
|
416
|
-
`const fs = require("fs");
|
|
417
|
-
const path = require("path");
|
|
418
|
-
const isWin = process.platform === "win32";
|
|
419
|
-
const binName = isWin ? "node-red.cmd" : "node-red";
|
|
420
|
-
const dirs = process.env.PATH.split(path.delimiter);
|
|
421
|
-
for (const d of dirs) {
|
|
422
|
-
const f = path.join(d, binName);
|
|
423
|
-
if (fs.existsSync(f)) {
|
|
424
|
-
if (isWin) {
|
|
425
|
-
const nodeRedDir = path.resolve(d, "..", "node-red");
|
|
426
|
-
const pkg = JSON.parse(fs.readFileSync(path.join(nodeRedDir, "package.json"), "utf-8"));
|
|
427
|
-
const bin = typeof pkg.bin === "string" ? pkg.bin : pkg.bin["node-red"];
|
|
428
|
-
process.stdout.write(path.resolve(nodeRedDir, bin));
|
|
429
|
-
} else {
|
|
430
|
-
process.stdout.write(fs.realpathSync(f));
|
|
523
|
+
};
|
|
524
|
+
const remainders = { stdout: "", stderr: "" };
|
|
525
|
+
const handleData = (data, source) => {
|
|
526
|
+
const lines = (remainders[source] + data.toString()).split("\n");
|
|
527
|
+
remainders[source] = lines.pop() ?? "";
|
|
528
|
+
for (const line of lines) {
|
|
529
|
+
emitLine(line, source);
|
|
431
530
|
}
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
{ encoding: "utf-8", timeout: 3e5 }
|
|
440
|
-
).trim();
|
|
441
|
-
if (!entryPoint || !fs2.existsSync(entryPoint)) {
|
|
442
|
-
throw new NodeRedStartError(
|
|
443
|
-
new Error(
|
|
444
|
-
`Could not resolve node-red entry point: ${entryPoint || "(empty)"}`
|
|
445
|
-
)
|
|
446
|
-
);
|
|
531
|
+
};
|
|
532
|
+
const flushRemainders = () => {
|
|
533
|
+
for (const source of ["stdout", "stderr"]) {
|
|
534
|
+
const rest = remainders[source];
|
|
535
|
+
remainders[source] = "";
|
|
536
|
+
if (rest) {
|
|
537
|
+
emitLine(rest, source);
|
|
447
538
|
}
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
539
|
+
}
|
|
540
|
+
};
|
|
541
|
+
child.stdout?.on("data", (data) => handleData(data, "stdout"));
|
|
542
|
+
child.stderr?.on("data", (data) => handleData(data, "stderr"));
|
|
543
|
+
child.on("error", (error) => {
|
|
544
|
+
rejectReady(new NodeRedStartError(error));
|
|
545
|
+
});
|
|
546
|
+
child.on("exit", (code) => {
|
|
547
|
+
flushRemainders();
|
|
548
|
+
if (!isReady && code !== 0 && code !== null) {
|
|
549
|
+
rejectReady(
|
|
550
|
+
new NodeRedStartError(new Error(`Process exited with code ${code}`))
|
|
551
|
+
);
|
|
552
|
+
return;
|
|
553
|
+
}
|
|
554
|
+
resolveReady();
|
|
555
|
+
});
|
|
556
|
+
return { child, ready };
|
|
557
|
+
}
|
|
558
|
+
function kill(pid) {
|
|
559
|
+
return new Promise((resolve) => {
|
|
560
|
+
treeKill(pid, "SIGKILL", () => resolve());
|
|
561
|
+
});
|
|
562
|
+
}
|
|
563
|
+
async function stop(options) {
|
|
564
|
+
const { child, pid, gracefulTimeoutMs = 1e4, logger: logger2 } = options;
|
|
565
|
+
if (child.exitCode !== null || child.signalCode !== null) {
|
|
566
|
+
return;
|
|
567
|
+
}
|
|
568
|
+
const exited = new Promise((resolve) => {
|
|
569
|
+
child.once("exit", () => resolve());
|
|
570
|
+
treeKill(pid, "SIGTERM", (error) => {
|
|
571
|
+
if (error) {
|
|
572
|
+
try {
|
|
573
|
+
process.kill(pid, "SIGTERM");
|
|
574
|
+
} catch {
|
|
575
|
+
resolve();
|
|
576
|
+
}
|
|
454
577
|
}
|
|
578
|
+
});
|
|
579
|
+
});
|
|
580
|
+
try {
|
|
581
|
+
await withTimeout(exited, gracefulTimeoutMs);
|
|
582
|
+
} catch {
|
|
583
|
+
logger2.warn("Graceful shutdown timed out, force killing...");
|
|
584
|
+
await kill(pid);
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
async function acquirePort(options) {
|
|
588
|
+
const { preferredPort, retryDelay = 2e3, logger: logger2 } = options;
|
|
589
|
+
const available = await detect(preferredPort);
|
|
590
|
+
if (available === preferredPort) {
|
|
591
|
+
return preferredPort;
|
|
592
|
+
}
|
|
593
|
+
logger2.warn(`Port ${preferredPort} is still in use, waiting...`);
|
|
594
|
+
await new Promise((resolve) => setTimeout(resolve, retryDelay));
|
|
595
|
+
const retryAvailable = await detect(preferredPort);
|
|
596
|
+
if (retryAvailable === preferredPort) {
|
|
597
|
+
return preferredPort;
|
|
598
|
+
}
|
|
599
|
+
const fallbackPort = await getPort({ port: preferredPort });
|
|
600
|
+
logger2.warn(
|
|
601
|
+
`Port ${preferredPort} still occupied, using port ${fallbackPort}`
|
|
602
|
+
);
|
|
603
|
+
return fallbackPort;
|
|
604
|
+
}
|
|
605
|
+
async function waitForPortRelease(port, options = {}) {
|
|
606
|
+
const { attempts = 10, delay = 300 } = options;
|
|
607
|
+
const checkPortUsage = async () => {
|
|
608
|
+
const availablePort = await detect(port);
|
|
609
|
+
if (availablePort !== port) {
|
|
610
|
+
throw new Error("Port still in use");
|
|
455
611
|
}
|
|
612
|
+
};
|
|
613
|
+
try {
|
|
614
|
+
await retry(checkPortUsage, { attempts, delay });
|
|
615
|
+
return true;
|
|
616
|
+
} catch {
|
|
617
|
+
return false;
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
// src/vite/node-red-launcher/index.ts
|
|
622
|
+
var NodeRedLauncher = class {
|
|
623
|
+
operationQueue = Promise.resolve();
|
|
624
|
+
process = null;
|
|
625
|
+
unwatchExit = null;
|
|
626
|
+
nodeRedEntryPoint = null;
|
|
627
|
+
tempFiles = [];
|
|
628
|
+
bufferedLogs = [];
|
|
629
|
+
port = null;
|
|
630
|
+
outDir;
|
|
631
|
+
options;
|
|
632
|
+
_slug;
|
|
633
|
+
logger;
|
|
634
|
+
constructor(outDir, options, slug = "") {
|
|
635
|
+
this.outDir = outDir;
|
|
636
|
+
this.options = options;
|
|
637
|
+
this._slug = slug;
|
|
638
|
+
this.logger = new Logger({
|
|
639
|
+
name: "vite-plugin-node-red",
|
|
640
|
+
prefix: "node-red"
|
|
641
|
+
});
|
|
642
|
+
}
|
|
643
|
+
get preferredPort() {
|
|
644
|
+
return this.options.runtime?.port ?? 1880;
|
|
645
|
+
}
|
|
646
|
+
get slug() {
|
|
647
|
+
return this._slug;
|
|
648
|
+
}
|
|
649
|
+
get basePath() {
|
|
650
|
+
return this._slug ? `/${this._slug}/` : "/";
|
|
651
|
+
}
|
|
652
|
+
get restartDelay() {
|
|
653
|
+
return this.options.restartDelay ?? 1e3;
|
|
654
|
+
}
|
|
655
|
+
get pid() {
|
|
656
|
+
return this.process?.child.pid ?? null;
|
|
657
|
+
}
|
|
658
|
+
get nodeRedCommand() {
|
|
659
|
+
return getNodeRedCommand(this.options.runtime?.version);
|
|
456
660
|
}
|
|
457
661
|
log(line) {
|
|
458
662
|
if (line.includes("Server now running at")) {
|
|
@@ -460,150 +664,122 @@ for (const d of dirs) {
|
|
|
460
664
|
}
|
|
461
665
|
this.logger.raw(line);
|
|
462
666
|
}
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
667
|
+
handleProcessLine(line, source, ready) {
|
|
668
|
+
if (!ready) {
|
|
669
|
+
this.bufferedLogs.push(line);
|
|
670
|
+
} else if (source === "stderr") {
|
|
671
|
+
this.logger.error(line);
|
|
467
672
|
} else {
|
|
673
|
+
this.log(line);
|
|
674
|
+
}
|
|
675
|
+
}
|
|
676
|
+
async killProcess() {
|
|
677
|
+
if (!this.process) return;
|
|
678
|
+
this.stopWatchingExit();
|
|
679
|
+
const pid = this.process.child.pid;
|
|
680
|
+
if (pid) {
|
|
681
|
+
await kill(pid);
|
|
682
|
+
}
|
|
683
|
+
this.process = null;
|
|
684
|
+
}
|
|
685
|
+
watchForUnexpectedExit(managed) {
|
|
686
|
+
const onExit = (code, signal) => {
|
|
687
|
+
this.unwatchExit = null;
|
|
688
|
+
this.process = null;
|
|
468
689
|
this.logger.warn(
|
|
469
|
-
`
|
|
690
|
+
`Node-RED exited unexpectedly (${signal ?? `code ${code}`})`
|
|
470
691
|
);
|
|
471
|
-
await new Promise((resolve) => setTimeout(resolve, 2e3));
|
|
472
|
-
const retryAvailable = await detect(this.preferredPort);
|
|
473
|
-
if (retryAvailable === this.preferredPort) {
|
|
474
|
-
this.port = this.preferredPort;
|
|
475
|
-
} else {
|
|
476
|
-
this.logger.warn(
|
|
477
|
-
`Port ${this.preferredPort} still occupied, using port ${retryAvailable}`
|
|
478
|
-
);
|
|
479
|
-
this.port = await getPort({ port: this.preferredPort });
|
|
480
|
-
}
|
|
481
|
-
}
|
|
482
|
-
const nodeRedEntryPoint = this.resolveNodeRedEntryPoint();
|
|
483
|
-
const startProcess = () => {
|
|
484
|
-
return new Promise(async (resolve, reject) => {
|
|
485
|
-
try {
|
|
486
|
-
const settingsPath = await this.generateRuntimeSettingsFile();
|
|
487
|
-
const args = this.options.args ?? [];
|
|
488
|
-
this.bufferedLogs = [];
|
|
489
|
-
this.isReady = false;
|
|
490
|
-
this.process = spawn(
|
|
491
|
-
process.execPath,
|
|
492
|
-
[nodeRedEntryPoint, "-s", settingsPath, ...args],
|
|
493
|
-
{
|
|
494
|
-
stdio: ["ignore", "pipe", "pipe"]
|
|
495
|
-
}
|
|
496
|
-
);
|
|
497
|
-
this.process.stdout?.on("data", (data) => {
|
|
498
|
-
const lines = data.toString().split("\n").filter(Boolean);
|
|
499
|
-
for (const line of lines) {
|
|
500
|
-
if (this.isReady) {
|
|
501
|
-
this.log(line);
|
|
502
|
-
} else {
|
|
503
|
-
this.bufferedLogs.push(line);
|
|
504
|
-
}
|
|
505
|
-
if (line.includes("Started flows") || line.includes("Server now running")) {
|
|
506
|
-
this.isReady = true;
|
|
507
|
-
resolve(void 0);
|
|
508
|
-
}
|
|
509
|
-
}
|
|
510
|
-
});
|
|
511
|
-
this.process.stderr?.on("data", (data) => {
|
|
512
|
-
const lines = data.toString().split("\n").filter(Boolean);
|
|
513
|
-
for (const line of lines) {
|
|
514
|
-
if (this.isReady) {
|
|
515
|
-
this.logger.error(`${line}`);
|
|
516
|
-
} else {
|
|
517
|
-
this.bufferedLogs.push(line);
|
|
518
|
-
}
|
|
519
|
-
}
|
|
520
|
-
});
|
|
521
|
-
this.process.on("error", (error) => {
|
|
522
|
-
reject(new NodeRedStartError(error));
|
|
523
|
-
});
|
|
524
|
-
this.process.on("exit", (code) => {
|
|
525
|
-
if (!this.isReady && code !== 0 && code !== null) {
|
|
526
|
-
reject(
|
|
527
|
-
new NodeRedStartError(
|
|
528
|
-
new Error(`Process exited with code ${code}`)
|
|
529
|
-
)
|
|
530
|
-
);
|
|
531
|
-
}
|
|
532
|
-
resolve(void 0);
|
|
533
|
-
});
|
|
534
|
-
} catch (error) {
|
|
535
|
-
reject(new NodeRedStartError(error));
|
|
536
|
-
}
|
|
537
|
-
});
|
|
538
692
|
};
|
|
693
|
+
managed.child.once("exit", onExit);
|
|
694
|
+
this.unwatchExit = () => managed.child.off("exit", onExit);
|
|
695
|
+
}
|
|
696
|
+
stopWatchingExit() {
|
|
697
|
+
this.unwatchExit?.();
|
|
698
|
+
this.unwatchExit = null;
|
|
699
|
+
}
|
|
700
|
+
// start/stop interleaving (e.g. SIGINT while Node-RED is booting) would
|
|
701
|
+
// race on process/port state and could leak a spawned process, so all
|
|
702
|
+
// lifecycle operations run strictly one at a time
|
|
703
|
+
enqueue(operation) {
|
|
704
|
+
const result = this.operationQueue.then(operation, operation);
|
|
705
|
+
this.operationQueue = result.catch(() => {
|
|
706
|
+
});
|
|
707
|
+
return result;
|
|
708
|
+
}
|
|
709
|
+
async start() {
|
|
710
|
+
return this.enqueue(() => this.doStart());
|
|
711
|
+
}
|
|
712
|
+
async stop(skipPortUsageCheck = false) {
|
|
713
|
+
return this.enqueue(() => this.doStop(skipPortUsageCheck));
|
|
714
|
+
}
|
|
715
|
+
async doStart() {
|
|
539
716
|
try {
|
|
717
|
+
this.port = await acquirePort({
|
|
718
|
+
preferredPort: this.preferredPort,
|
|
719
|
+
logger: this.logger
|
|
720
|
+
});
|
|
721
|
+
if (!this.nodeRedEntryPoint || !fs4.existsSync(this.nodeRedEntryPoint)) {
|
|
722
|
+
this.nodeRedEntryPoint = await resolveNodeRed({
|
|
723
|
+
version: this.options.runtime?.version,
|
|
724
|
+
logger: this.logger
|
|
725
|
+
});
|
|
726
|
+
}
|
|
727
|
+
const nodeRedEntryPoint = this.nodeRedEntryPoint;
|
|
728
|
+
const settings = await generateRuntimeSettings({
|
|
729
|
+
outDir: this.outDir,
|
|
730
|
+
port: this.port,
|
|
731
|
+
settingsFilepath: this.options.runtime?.settingsFilepath,
|
|
732
|
+
httpAdminRoot: this._slug ? this.basePath : void 0,
|
|
733
|
+
logger: this.logger
|
|
734
|
+
});
|
|
735
|
+
for (const file of settings.tempFiles) {
|
|
736
|
+
if (!this.tempFiles.includes(file)) {
|
|
737
|
+
this.tempFiles.push(file);
|
|
738
|
+
}
|
|
739
|
+
}
|
|
740
|
+
const startProcess = async () => {
|
|
741
|
+
await this.killProcess();
|
|
742
|
+
this.bufferedLogs = [];
|
|
743
|
+
this.process = start({
|
|
744
|
+
entryPoint: nodeRedEntryPoint,
|
|
745
|
+
settingsPath: settings.filepath,
|
|
746
|
+
args: this.options.args ?? [],
|
|
747
|
+
onLine: (line, source, ready) => this.handleProcessLine(line, source, ready)
|
|
748
|
+
});
|
|
749
|
+
await this.process.ready;
|
|
750
|
+
};
|
|
540
751
|
await retry(startProcess, { attempts: 3, delay: 100 });
|
|
752
|
+
this.watchForUnexpectedExit(this.process);
|
|
541
753
|
return this.port;
|
|
542
754
|
} catch (error) {
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
if (pid) {
|
|
546
|
-
treeKill(pid, "SIGKILL");
|
|
547
|
-
}
|
|
548
|
-
this.process = null;
|
|
549
|
-
}
|
|
550
|
-
throw new NodeRedStartError(error);
|
|
755
|
+
await this.killProcess();
|
|
756
|
+
throw error instanceof NodeRedStartError ? error : new NodeRedStartError(error);
|
|
551
757
|
}
|
|
552
758
|
}
|
|
553
|
-
async
|
|
759
|
+
async doStop(skipPortUsageCheck) {
|
|
554
760
|
if (!this.process) return;
|
|
555
|
-
|
|
761
|
+
this.stopWatchingExit();
|
|
762
|
+
const pid = this.process.child.pid;
|
|
556
763
|
const currentPort = this.port;
|
|
557
764
|
if (!pid) {
|
|
558
765
|
this.process = null;
|
|
766
|
+
this.port = null;
|
|
559
767
|
return;
|
|
560
768
|
}
|
|
561
|
-
|
|
562
|
-
this.process.
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
});
|
|
566
|
-
treeKill(pid, "SIGTERM", (error) => {
|
|
567
|
-
if (error) {
|
|
568
|
-
try {
|
|
569
|
-
process.kill(pid, "SIGTERM");
|
|
570
|
-
} catch {
|
|
571
|
-
this.process = null;
|
|
572
|
-
resolve(void 0);
|
|
573
|
-
}
|
|
574
|
-
}
|
|
575
|
-
});
|
|
769
|
+
await stop({
|
|
770
|
+
child: this.process.child,
|
|
771
|
+
pid,
|
|
772
|
+
logger: this.logger
|
|
576
773
|
});
|
|
577
|
-
|
|
578
|
-
await withTimeout(stopProcess, 1e4);
|
|
579
|
-
} catch {
|
|
580
|
-
this.logger.warn("Graceful shutdown timed out, force killing...");
|
|
581
|
-
await new Promise((resolve) => {
|
|
582
|
-
treeKill(pid, "SIGKILL", () => {
|
|
583
|
-
this.process = null;
|
|
584
|
-
resolve(void 0);
|
|
585
|
-
});
|
|
586
|
-
});
|
|
587
|
-
}
|
|
774
|
+
this.process = null;
|
|
588
775
|
if (!skipPortUsageCheck && currentPort) {
|
|
589
|
-
const
|
|
590
|
-
|
|
591
|
-
if (availablePort !== currentPort) {
|
|
592
|
-
throw new Error("Port still in use");
|
|
593
|
-
}
|
|
594
|
-
};
|
|
595
|
-
try {
|
|
596
|
-
await retry(checkPortUsage, { attempts: 10, delay: 300 });
|
|
597
|
-
} catch {
|
|
776
|
+
const released = await waitForPortRelease(currentPort);
|
|
777
|
+
if (!released) {
|
|
598
778
|
this.logger.warn(
|
|
599
779
|
`Port ${currentPort} still in use after stop. Force killing...`
|
|
600
780
|
);
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
treeKill(pid, "SIGKILL", () => resolve());
|
|
604
|
-
});
|
|
605
|
-
await new Promise((resolve) => setTimeout(resolve, 1e3));
|
|
606
|
-
}
|
|
781
|
+
await kill(pid);
|
|
782
|
+
await waitForPortRelease(currentPort);
|
|
607
783
|
}
|
|
608
784
|
}
|
|
609
785
|
this.port = null;
|
|
@@ -615,32 +791,35 @@ for (const d of dirs) {
|
|
|
615
791
|
this.bufferedLogs = [];
|
|
616
792
|
}
|
|
617
793
|
cleanup() {
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
794
|
+
for (const file of this.tempFiles) {
|
|
795
|
+
try {
|
|
796
|
+
fs4.unlinkSync(file);
|
|
797
|
+
} catch {
|
|
798
|
+
}
|
|
621
799
|
}
|
|
800
|
+
this.tempFiles = [];
|
|
622
801
|
}
|
|
623
802
|
};
|
|
624
803
|
|
|
625
804
|
// src/vite/plugins/server.ts
|
|
626
805
|
import chokidar from "chokidar";
|
|
627
806
|
import treeKill2 from "tree-kill";
|
|
628
|
-
import
|
|
807
|
+
import path13 from "path";
|
|
629
808
|
|
|
630
809
|
// src/vite/server/build.ts
|
|
631
810
|
import { build as viteBuild } from "vite";
|
|
632
|
-
import
|
|
633
|
-
import
|
|
811
|
+
import fs7 from "fs";
|
|
812
|
+
import path6 from "path";
|
|
634
813
|
|
|
635
814
|
// src/vite/server/plugins/type-generator.ts
|
|
636
815
|
import dts from "vite-plugin-dts";
|
|
637
|
-
import
|
|
638
|
-
import
|
|
816
|
+
import fs5 from "fs";
|
|
817
|
+
import path4 from "path";
|
|
639
818
|
import ts from "typescript";
|
|
640
819
|
function collectTsFiles(dir) {
|
|
641
|
-
if (!
|
|
642
|
-
return
|
|
643
|
-
const full =
|
|
820
|
+
if (!fs5.existsSync(dir)) return [];
|
|
821
|
+
return fs5.readdirSync(dir, { withFileTypes: true }).flatMap((dirent) => {
|
|
822
|
+
const full = path4.join(dir, dirent.name);
|
|
644
823
|
if (dirent.isDirectory()) return collectTsFiles(full);
|
|
645
824
|
if (dirent.isFile() && dirent.name.endsWith(".ts") && !dirent.name.endsWith(".d.ts"))
|
|
646
825
|
return [full];
|
|
@@ -655,7 +834,7 @@ var BASE_CLASS_SLOTS = {
|
|
|
655
834
|
ConfigNode: ["Config", "Credentials", "Settings"]
|
|
656
835
|
};
|
|
657
836
|
function getNodeTypeExports(filePath) {
|
|
658
|
-
const content =
|
|
837
|
+
const content = fs5.readFileSync(filePath, "utf-8");
|
|
659
838
|
const source = ts.createSourceFile(
|
|
660
839
|
filePath,
|
|
661
840
|
content,
|
|
@@ -717,7 +896,7 @@ var SCHEMA_PROP_SEMANTICS = {
|
|
|
717
896
|
settingsSchema: "SettingsSchema"
|
|
718
897
|
};
|
|
719
898
|
function getSchemaReferences(filePath) {
|
|
720
|
-
const content =
|
|
899
|
+
const content = fs5.readFileSync(filePath, "utf-8");
|
|
721
900
|
const source = ts.createSourceFile(
|
|
722
901
|
filePath,
|
|
723
902
|
content,
|
|
@@ -809,7 +988,7 @@ function getSchemaReferences(filePath) {
|
|
|
809
988
|
return result;
|
|
810
989
|
}
|
|
811
990
|
function getFactoryInfo(filePath) {
|
|
812
|
-
const content =
|
|
991
|
+
const content = fs5.readFileSync(filePath, "utf-8");
|
|
813
992
|
const source = ts.createSourceFile(
|
|
814
993
|
filePath,
|
|
815
994
|
content,
|
|
@@ -843,13 +1022,13 @@ function buildTypeArg(schemaMap, semanticName, portNameMap) {
|
|
|
843
1022
|
return `[${names.map((n) => `Infer<typeof ${n}>`).join(", ")}]`;
|
|
844
1023
|
}
|
|
845
1024
|
function buildNodeReexports(srcDir, entryFile) {
|
|
846
|
-
const nodesDir =
|
|
1025
|
+
const nodesDir = path4.join(srcDir, "nodes");
|
|
847
1026
|
const nodeFiles = collectTsFiles(nodesDir);
|
|
848
1027
|
return nodeFiles.map((file) => {
|
|
849
|
-
const rel =
|
|
1028
|
+
const rel = path4.relative(path4.dirname(entryFile), file).replace(/\\/g, "/");
|
|
850
1029
|
const relPath = rel.startsWith(".") ? rel : `./${rel}`;
|
|
851
1030
|
const specifier = relPath.replace(/\.ts$/, "");
|
|
852
|
-
const ns = toPascalCase(
|
|
1031
|
+
const ns = toPascalCase(path4.basename(file, ".ts"));
|
|
853
1032
|
const schemaRefs = getSchemaReferences(file);
|
|
854
1033
|
const factoryInfo = getFactoryInfo(file);
|
|
855
1034
|
const lines = [];
|
|
@@ -876,9 +1055,9 @@ function buildNodeReexports(srcDir, entryFile) {
|
|
|
876
1055
|
if (hasSchemas) {
|
|
877
1056
|
const bySource2 = /* @__PURE__ */ new Map();
|
|
878
1057
|
for (const ref of schemaRefs) {
|
|
879
|
-
const resolvedSource =
|
|
880
|
-
|
|
881
|
-
|
|
1058
|
+
const resolvedSource = path4.relative(
|
|
1059
|
+
path4.dirname(entryFile),
|
|
1060
|
+
path4.resolve(path4.dirname(file), ref.importSource)
|
|
882
1061
|
).replace(/\\/g, "/");
|
|
883
1062
|
const sourceSpecifier = resolvedSource.startsWith(".") ? resolvedSource : `./${resolvedSource}`;
|
|
884
1063
|
if (!bySource2.has(sourceSpecifier))
|
|
@@ -924,9 +1103,9 @@ function buildNodeReexports(srcDir, entryFile) {
|
|
|
924
1103
|
}
|
|
925
1104
|
const bySource = /* @__PURE__ */ new Map();
|
|
926
1105
|
for (const ref of schemaRefs) {
|
|
927
|
-
const resolvedSource =
|
|
928
|
-
|
|
929
|
-
|
|
1106
|
+
const resolvedSource = path4.relative(
|
|
1107
|
+
path4.dirname(entryFile),
|
|
1108
|
+
path4.resolve(path4.dirname(file), ref.importSource)
|
|
930
1109
|
).replace(/\\/g, "/");
|
|
931
1110
|
const sourceSpecifier = resolvedSource.startsWith(".") ? resolvedSource : `./${resolvedSource}`;
|
|
932
1111
|
if (!bySource.has(sourceSpecifier)) bySource.set(sourceSpecifier, []);
|
|
@@ -940,12 +1119,12 @@ function buildNodeReexports(srcDir, entryFile) {
|
|
|
940
1119
|
}
|
|
941
1120
|
function typeGenerator(options) {
|
|
942
1121
|
const { srcDir, outDir, entryFiles } = options;
|
|
943
|
-
const serverTsconfig =
|
|
944
|
-
const rootTsconfig =
|
|
945
|
-
const userTsconfig =
|
|
946
|
-
const clientDir =
|
|
1122
|
+
const serverTsconfig = path4.resolve(srcDir, "tsconfig.json");
|
|
1123
|
+
const rootTsconfig = path4.resolve("tsconfig.json");
|
|
1124
|
+
const userTsconfig = fs5.existsSync(serverTsconfig) ? serverTsconfig : rootTsconfig;
|
|
1125
|
+
const clientDir = path4.relative(
|
|
947
1126
|
process.cwd(),
|
|
948
|
-
|
|
1127
|
+
path4.join(path4.dirname(srcDir), "client")
|
|
949
1128
|
);
|
|
950
1129
|
const augmentedContents = /* @__PURE__ */ new Map();
|
|
951
1130
|
let origReadFile = null;
|
|
@@ -957,9 +1136,9 @@ function typeGenerator(options) {
|
|
|
957
1136
|
for (const entryFile of entryFiles) {
|
|
958
1137
|
const reexports = buildNodeReexports(srcDir, entryFile);
|
|
959
1138
|
if (!reexports) continue;
|
|
960
|
-
const original =
|
|
1139
|
+
const original = fs5.readFileSync(entryFile, "utf-8");
|
|
961
1140
|
augmentedContents.set(
|
|
962
|
-
|
|
1141
|
+
path4.resolve(entryFile),
|
|
963
1142
|
`${original}
|
|
964
1143
|
${reexports}
|
|
965
1144
|
`
|
|
@@ -968,7 +1147,7 @@ ${reexports}
|
|
|
968
1147
|
if (augmentedContents.size === 0) return;
|
|
969
1148
|
origReadFile = ts.sys.readFile.bind(ts.sys);
|
|
970
1149
|
ts.sys.readFile = (fileName, encoding) => {
|
|
971
|
-
const resolved =
|
|
1150
|
+
const resolved = path4.resolve(fileName);
|
|
972
1151
|
return augmentedContents.get(resolved) ?? origReadFile(fileName, encoding);
|
|
973
1152
|
};
|
|
974
1153
|
},
|
|
@@ -992,7 +1171,7 @@ ${reexports}
|
|
|
992
1171
|
}
|
|
993
1172
|
},
|
|
994
1173
|
outDir,
|
|
995
|
-
...
|
|
1174
|
+
...fs5.existsSync(userTsconfig) && { tsconfigPath: userTsconfig },
|
|
996
1175
|
compilerOptions: {
|
|
997
1176
|
noEmit: false,
|
|
998
1177
|
declaration: true,
|
|
@@ -1065,8 +1244,8 @@ function esmWrapper() {
|
|
|
1065
1244
|
}
|
|
1066
1245
|
|
|
1067
1246
|
// src/vite/server/plugins/package-json-generator.ts
|
|
1068
|
-
import
|
|
1069
|
-
import
|
|
1247
|
+
import fs6 from "fs";
|
|
1248
|
+
import path5 from "path";
|
|
1070
1249
|
import { builtinModules as builtinModules2 } from "node:module";
|
|
1071
1250
|
var nodeBuiltins = /* @__PURE__ */ new Set([
|
|
1072
1251
|
...builtinModules2,
|
|
@@ -1155,13 +1334,13 @@ function packageJsonGenerator(options) {
|
|
|
1155
1334
|
}
|
|
1156
1335
|
},
|
|
1157
1336
|
closeBundle() {
|
|
1158
|
-
const rootPackageJsonPath =
|
|
1159
|
-
if (!
|
|
1337
|
+
const rootPackageJsonPath = path5.resolve("./package.json");
|
|
1338
|
+
if (!fs6.existsSync(rootPackageJsonPath)) {
|
|
1160
1339
|
logger.warn(`package.json not found: ${rootPackageJsonPath}`);
|
|
1161
1340
|
return;
|
|
1162
1341
|
}
|
|
1163
1342
|
const rootPackageJson = JSON.parse(
|
|
1164
|
-
|
|
1343
|
+
fs6.readFileSync(rootPackageJsonPath, "utf-8")
|
|
1165
1344
|
);
|
|
1166
1345
|
const sourceDeps = rootPackageJson.dependencies ?? {};
|
|
1167
1346
|
const peerDeps = rootPackageJson.peerDependencies ?? {};
|
|
@@ -1173,12 +1352,12 @@ function packageJsonGenerator(options) {
|
|
|
1173
1352
|
if (sourceDeps[dep]) {
|
|
1174
1353
|
distDependencies[dep] = sourceDeps[dep];
|
|
1175
1354
|
} else {
|
|
1176
|
-
const dependencyPackageJsonPath =
|
|
1355
|
+
const dependencyPackageJsonPath = path5.resolve(
|
|
1177
1356
|
`./node_modules/${dep}/package.json`
|
|
1178
1357
|
);
|
|
1179
|
-
if (
|
|
1358
|
+
if (fs6.existsSync(dependencyPackageJsonPath)) {
|
|
1180
1359
|
const dependencyPackageJson = JSON.parse(
|
|
1181
|
-
|
|
1360
|
+
fs6.readFileSync(dependencyPackageJsonPath, "utf-8")
|
|
1182
1361
|
);
|
|
1183
1362
|
distDependencies[dep] = `^${dependencyPackageJson.version}`;
|
|
1184
1363
|
}
|
|
@@ -1215,11 +1394,11 @@ function packageJsonGenerator(options) {
|
|
|
1215
1394
|
}
|
|
1216
1395
|
}
|
|
1217
1396
|
}
|
|
1218
|
-
if (!
|
|
1219
|
-
|
|
1397
|
+
if (!fs6.existsSync(outDir)) {
|
|
1398
|
+
fs6.mkdirSync(outDir, { recursive: true });
|
|
1220
1399
|
}
|
|
1221
|
-
|
|
1222
|
-
|
|
1400
|
+
fs6.writeFileSync(
|
|
1401
|
+
path5.join(outDir, "package.json"),
|
|
1223
1402
|
JSON.stringify(distPackageJson, null, 2)
|
|
1224
1403
|
);
|
|
1225
1404
|
}
|
|
@@ -1238,11 +1417,11 @@ async function build(serverOpts, buildContext) {
|
|
|
1238
1417
|
nodeTarget = "node22"
|
|
1239
1418
|
} = serverOpts;
|
|
1240
1419
|
const entries = Array.isArray(entry) ? entry : [entry];
|
|
1241
|
-
const resolvedSrcDir =
|
|
1420
|
+
const resolvedSrcDir = path6.resolve(srcDir);
|
|
1242
1421
|
const entryPoints = {};
|
|
1243
1422
|
for (const entry2 of entries) {
|
|
1244
|
-
const entryFilePath =
|
|
1245
|
-
if (
|
|
1423
|
+
const entryFilePath = path6.join(resolvedSrcDir, entry2);
|
|
1424
|
+
if (fs7.existsSync(entryFilePath)) {
|
|
1246
1425
|
const fileName = entry2.replace(/\.ts$/, "");
|
|
1247
1426
|
entryPoints[fileName] = entryFilePath;
|
|
1248
1427
|
}
|
|
@@ -1318,7 +1497,7 @@ module.exports = function (RED) {
|
|
|
1318
1497
|
});
|
|
1319
1498
|
};
|
|
1320
1499
|
`;
|
|
1321
|
-
|
|
1500
|
+
fs7.writeFileSync(path6.join(buildContext.outDir, "index.js"), bridgeCode);
|
|
1322
1501
|
}
|
|
1323
1502
|
} catch (error) {
|
|
1324
1503
|
throw new BuildError("server", error);
|
|
@@ -1328,12 +1507,12 @@ module.exports = function (RED) {
|
|
|
1328
1507
|
// src/vite/client/build.ts
|
|
1329
1508
|
import { build as viteBuild2 } from "vite";
|
|
1330
1509
|
import vue from "@vitejs/plugin-vue";
|
|
1331
|
-
import
|
|
1332
|
-
import
|
|
1510
|
+
import fs13 from "fs";
|
|
1511
|
+
import path12 from "path";
|
|
1333
1512
|
|
|
1334
1513
|
// src/vite/client/plugins/help-generator.ts
|
|
1335
|
-
import
|
|
1336
|
-
import
|
|
1514
|
+
import fs8 from "fs";
|
|
1515
|
+
import path7 from "path";
|
|
1337
1516
|
import { pathToFileURL as pathToFileURL2 } from "url";
|
|
1338
1517
|
import { createRequire as createRequire2 } from "module";
|
|
1339
1518
|
|
|
@@ -1655,9 +1834,9 @@ ${table}
|
|
|
1655
1834
|
`;
|
|
1656
1835
|
}
|
|
1657
1836
|
function loadNodeLabels(labelPath) {
|
|
1658
|
-
if (!
|
|
1837
|
+
if (!fs8.existsSync(labelPath)) return {};
|
|
1659
1838
|
try {
|
|
1660
|
-
const raw = JSON.parse(
|
|
1839
|
+
const raw = JSON.parse(fs8.readFileSync(labelPath, "utf-8"));
|
|
1661
1840
|
return {
|
|
1662
1841
|
description: raw.description,
|
|
1663
1842
|
configs: raw.configs,
|
|
@@ -1699,10 +1878,10 @@ function generateHelpDoc(nodeClass, labels, t) {
|
|
|
1699
1878
|
if (inputSection) lines.push(inputSection);
|
|
1700
1879
|
}
|
|
1701
1880
|
if (nodeClass.outputsSchema) {
|
|
1702
|
-
const
|
|
1703
|
-
if (Array.isArray(
|
|
1881
|
+
const os3 = nodeClass.outputsSchema;
|
|
1882
|
+
if (Array.isArray(os3)) {
|
|
1704
1883
|
const portSections = [];
|
|
1705
|
-
|
|
1884
|
+
os3.forEach((schema, i) => {
|
|
1706
1885
|
const title = `${t.sections.port} ${i + 1}`;
|
|
1707
1886
|
const portPropLabels = labels.outputs?.[i];
|
|
1708
1887
|
const section = generateSchemaSection({
|
|
@@ -1721,9 +1900,9 @@ function generateHelpDoc(nodeClass, labels, t) {
|
|
|
1721
1900
|
${portSections.join("\n")}`
|
|
1722
1901
|
);
|
|
1723
1902
|
}
|
|
1724
|
-
} else if (!("type" in
|
|
1903
|
+
} else if (!("type" in os3 || "properties" in os3)) {
|
|
1725
1904
|
const portSections = [];
|
|
1726
|
-
for (const [portName, schema] of Object.entries(
|
|
1905
|
+
for (const [portName, schema] of Object.entries(os3)) {
|
|
1727
1906
|
const portPropLabels = labels.outputs?.[portName];
|
|
1728
1907
|
const section = generateSchemaSection({
|
|
1729
1908
|
title: portName,
|
|
@@ -1745,7 +1924,7 @@ ${portSections.join("\n")}`
|
|
|
1745
1924
|
const outputPropLabels = labels.outputs?.[0];
|
|
1746
1925
|
const section = generateSchemaSection({
|
|
1747
1926
|
title: t.sections.output,
|
|
1748
|
-
schema:
|
|
1927
|
+
schema: os3,
|
|
1749
1928
|
t,
|
|
1750
1929
|
labels: outputPropLabels,
|
|
1751
1930
|
includeDefault: false
|
|
@@ -1756,9 +1935,9 @@ ${portSections.join("\n")}`
|
|
|
1756
1935
|
return lines.join("\n").trim();
|
|
1757
1936
|
}
|
|
1758
1937
|
function discoverLanguages(labelsDir, nodeType) {
|
|
1759
|
-
const nodeLabelsDir =
|
|
1760
|
-
if (!
|
|
1761
|
-
return
|
|
1938
|
+
const nodeLabelsDir = path7.join(labelsDir, nodeType);
|
|
1939
|
+
if (!fs8.existsSync(nodeLabelsDir)) return [];
|
|
1940
|
+
return fs8.readdirSync(nodeLabelsDir).filter((f) => f.endsWith(".json")).map((f) => path7.basename(f, ".json"));
|
|
1762
1941
|
}
|
|
1763
1942
|
function helpGenerator(options) {
|
|
1764
1943
|
const { outDir, localesOutDir, docsDir, labelsDir } = options;
|
|
@@ -1767,15 +1946,15 @@ function helpGenerator(options) {
|
|
|
1767
1946
|
apply: "build",
|
|
1768
1947
|
enforce: "post",
|
|
1769
1948
|
async closeBundle() {
|
|
1770
|
-
const esmPath =
|
|
1771
|
-
const cjsPath =
|
|
1949
|
+
const esmPath = path7.resolve(outDir, "index.mjs");
|
|
1950
|
+
const cjsPath = path7.resolve(outDir, "index.js");
|
|
1772
1951
|
let packageFn;
|
|
1773
1952
|
try {
|
|
1774
|
-
if (
|
|
1953
|
+
if (fs8.existsSync(esmPath)) {
|
|
1775
1954
|
const fileUrl = pathToFileURL2(esmPath).href + `?t=${Date.now()}`;
|
|
1776
1955
|
const mod = await import(fileUrl);
|
|
1777
1956
|
packageFn = mod?.default ?? mod;
|
|
1778
|
-
} else if (
|
|
1957
|
+
} else if (fs8.existsSync(cjsPath)) {
|
|
1779
1958
|
const require2 = createRequire2(import.meta.url);
|
|
1780
1959
|
delete require2.cache[cjsPath];
|
|
1781
1960
|
const rawMod = require2(cjsPath);
|
|
@@ -1792,10 +1971,10 @@ function helpGenerator(options) {
|
|
|
1792
1971
|
const languages = discoverLanguages(labelsDir, type);
|
|
1793
1972
|
if (!languages.includes("en-US")) languages.push("en-US");
|
|
1794
1973
|
for (const lang of languages) {
|
|
1795
|
-
const manualMd =
|
|
1796
|
-
const manualHtml =
|
|
1797
|
-
if (
|
|
1798
|
-
const labelPath =
|
|
1974
|
+
const manualMd = path7.join(docsDir, type, `${lang}.md`);
|
|
1975
|
+
const manualHtml = path7.join(docsDir, type, `${lang}.html`);
|
|
1976
|
+
if (fs8.existsSync(manualMd) || fs8.existsSync(manualHtml)) continue;
|
|
1977
|
+
const labelPath = path7.join(labelsDir, type, `${lang}.json`);
|
|
1799
1978
|
const labels = loadNodeLabels(labelPath);
|
|
1800
1979
|
const t = getHelpTranslations(lang);
|
|
1801
1980
|
const content = generateHelpDoc(NodeClass, labels, t);
|
|
@@ -1809,11 +1988,11 @@ ${content}
|
|
|
1809
1988
|
}
|
|
1810
1989
|
}
|
|
1811
1990
|
for (const [lang, scripts] of helpByLang) {
|
|
1812
|
-
const langDir =
|
|
1813
|
-
|
|
1814
|
-
const indexPath =
|
|
1815
|
-
const existing =
|
|
1816
|
-
|
|
1991
|
+
const langDir = path7.join(localesOutDir, lang);
|
|
1992
|
+
fs8.mkdirSync(langDir, { recursive: true });
|
|
1993
|
+
const indexPath = path7.join(langDir, "index.html");
|
|
1994
|
+
const existing = fs8.existsSync(indexPath) ? fs8.readFileSync(indexPath, "utf-8") : "";
|
|
1995
|
+
fs8.writeFileSync(
|
|
1817
1996
|
indexPath,
|
|
1818
1997
|
existing + (existing ? "\n" : "") + scripts.join("\n"),
|
|
1819
1998
|
"utf-8"
|
|
@@ -1825,8 +2004,8 @@ ${content}
|
|
|
1825
2004
|
|
|
1826
2005
|
// src/vite/client/plugins/html-generator.ts
|
|
1827
2006
|
import mime from "mime-types";
|
|
1828
|
-
import
|
|
1829
|
-
import
|
|
2007
|
+
import fs9 from "fs";
|
|
2008
|
+
import path8 from "path";
|
|
1830
2009
|
function htmlGenerator(options) {
|
|
1831
2010
|
const { packageName, licensePath } = options;
|
|
1832
2011
|
return {
|
|
@@ -1836,7 +2015,7 @@ function htmlGenerator(options) {
|
|
|
1836
2015
|
generateBundle(_, bundle) {
|
|
1837
2016
|
const resourcesTags = Object.keys(bundle).map((fileName) => {
|
|
1838
2017
|
const asset = bundle[fileName];
|
|
1839
|
-
const srcPath =
|
|
2018
|
+
const srcPath = path8.join(
|
|
1840
2019
|
"resources",
|
|
1841
2020
|
packageName,
|
|
1842
2021
|
fileName.replace(/^resources\/?/, "")
|
|
@@ -1864,8 +2043,8 @@ function htmlGenerator(options) {
|
|
|
1864
2043
|
return null;
|
|
1865
2044
|
}
|
|
1866
2045
|
}).filter(Boolean).join("\n");
|
|
1867
|
-
const licenseBanner = licensePath &&
|
|
1868
|
-
${
|
|
2046
|
+
const licenseBanner = licensePath && fs9.existsSync(licensePath) ? `<!--
|
|
2047
|
+
${fs9.readFileSync(licensePath, "utf-8")}
|
|
1869
2048
|
-->` : "";
|
|
1870
2049
|
this.emitFile({
|
|
1871
2050
|
type: "asset",
|
|
@@ -1878,8 +2057,8 @@ ${resourcesTags}`
|
|
|
1878
2057
|
}
|
|
1879
2058
|
|
|
1880
2059
|
// src/vite/client/plugins/locales-generator.ts
|
|
1881
|
-
import
|
|
1882
|
-
import
|
|
2060
|
+
import fs10 from "fs";
|
|
2061
|
+
import path9 from "path";
|
|
1883
2062
|
import { merge } from "es-toolkit";
|
|
1884
2063
|
function localesGenerator(options) {
|
|
1885
2064
|
const { outDir, docsDir, labelsDir } = options;
|
|
@@ -1897,103 +2076,113 @@ function localesGenerator(options) {
|
|
|
1897
2076
|
];
|
|
1898
2077
|
const frameworkLabels = {
|
|
1899
2078
|
"en-US": {
|
|
1900
|
-
configs: { name: "Name" },
|
|
2079
|
+
configs: { name: "Name", returnProperty: "Return key" },
|
|
1901
2080
|
toggles: {
|
|
1902
2081
|
validateInput: "Validate Input",
|
|
1903
2082
|
validateOutput: "Validate Output",
|
|
1904
2083
|
errorPort: "Error Port",
|
|
1905
2084
|
completePort: "Complete Port",
|
|
1906
|
-
statusPort: "Status Port"
|
|
2085
|
+
statusPort: "Status Port",
|
|
2086
|
+
returnPropertyOverride: "Override return prop key"
|
|
1907
2087
|
}
|
|
1908
2088
|
},
|
|
1909
2089
|
de: {
|
|
1910
|
-
configs: { name: "Name" },
|
|
2090
|
+
configs: { name: "Name", returnProperty: "R\xFCckgabe-Schl\xFCssel" },
|
|
1911
2091
|
toggles: {
|
|
1912
2092
|
validateInput: "Eingabe validieren",
|
|
1913
2093
|
validateOutput: "Ausgabe validieren",
|
|
1914
2094
|
errorPort: "Fehler-Port",
|
|
1915
2095
|
completePort: "Abschluss-Port",
|
|
1916
|
-
statusPort: "Status-Port"
|
|
2096
|
+
statusPort: "Status-Port",
|
|
2097
|
+
returnPropertyOverride: "R\xFCckgabe-Schl\xFCssel \xFCberschreiben"
|
|
1917
2098
|
}
|
|
1918
2099
|
},
|
|
1919
2100
|
"es-ES": {
|
|
1920
|
-
configs: { name: "Nombre" },
|
|
2101
|
+
configs: { name: "Nombre", returnProperty: "Clave de retorno" },
|
|
1921
2102
|
toggles: {
|
|
1922
2103
|
validateInput: "Validar entrada",
|
|
1923
2104
|
validateOutput: "Validar salida",
|
|
1924
2105
|
errorPort: "Puerto de error",
|
|
1925
2106
|
completePort: "Puerto de completado",
|
|
1926
|
-
statusPort: "Puerto de estado"
|
|
2107
|
+
statusPort: "Puerto de estado",
|
|
2108
|
+
returnPropertyOverride: "Sobrescribir clave de retorno"
|
|
1927
2109
|
}
|
|
1928
2110
|
},
|
|
1929
2111
|
fr: {
|
|
1930
|
-
configs: { name: "Nom" },
|
|
2112
|
+
configs: { name: "Nom", returnProperty: "Cl\xE9 de retour" },
|
|
1931
2113
|
toggles: {
|
|
1932
2114
|
validateInput: "Valider l'entr\xE9e",
|
|
1933
2115
|
validateOutput: "Valider la sortie",
|
|
1934
2116
|
errorPort: "Port d'erreur",
|
|
1935
2117
|
completePort: "Port de compl\xE9tion",
|
|
1936
|
-
statusPort: "Port de statut"
|
|
2118
|
+
statusPort: "Port de statut",
|
|
2119
|
+
returnPropertyOverride: "Remplacer la cl\xE9 de retour"
|
|
1937
2120
|
}
|
|
1938
2121
|
},
|
|
1939
2122
|
ko: {
|
|
1940
|
-
configs: { name: "\uC774\uB984" },
|
|
2123
|
+
configs: { name: "\uC774\uB984", returnProperty: "\uBC18\uD658 \uD0A4" },
|
|
1941
2124
|
toggles: {
|
|
1942
2125
|
validateInput: "\uC785\uB825 \uAC80\uC99D",
|
|
1943
2126
|
validateOutput: "\uCD9C\uB825 \uAC80\uC99D",
|
|
1944
2127
|
errorPort: "\uC624\uB958 \uD3EC\uD2B8",
|
|
1945
2128
|
completePort: "\uC644\uB8CC \uD3EC\uD2B8",
|
|
1946
|
-
statusPort: "\uC0C1\uD0DC \uD3EC\uD2B8"
|
|
2129
|
+
statusPort: "\uC0C1\uD0DC \uD3EC\uD2B8",
|
|
2130
|
+
returnPropertyOverride: "\uBC18\uD658 \uD0A4 \uC7AC\uC815\uC758"
|
|
1947
2131
|
}
|
|
1948
2132
|
},
|
|
1949
2133
|
"pt-BR": {
|
|
1950
|
-
configs: { name: "Nome" },
|
|
2134
|
+
configs: { name: "Nome", returnProperty: "Chave de retorno" },
|
|
1951
2135
|
toggles: {
|
|
1952
2136
|
validateInput: "Validar Entrada",
|
|
1953
2137
|
validateOutput: "Validar Sa\xEDda",
|
|
1954
2138
|
errorPort: "Porta de Erro",
|
|
1955
2139
|
completePort: "Porta de Conclus\xE3o",
|
|
1956
|
-
statusPort: "Porta de Status"
|
|
2140
|
+
statusPort: "Porta de Status",
|
|
2141
|
+
returnPropertyOverride: "Sobrescrever chave de retorno"
|
|
1957
2142
|
}
|
|
1958
2143
|
},
|
|
1959
2144
|
ru: {
|
|
1960
|
-
configs: { name: "\u0418\u043C\u044F" },
|
|
2145
|
+
configs: { name: "\u0418\u043C\u044F", returnProperty: "\u041A\u043B\u044E\u0447 \u0432\u043E\u0437\u0432\u0440\u0430\u0442\u0430" },
|
|
1961
2146
|
toggles: {
|
|
1962
2147
|
validateInput: "\u041F\u0440\u043E\u0432\u0435\u0440\u0438\u0442\u044C \u0432\u0445\u043E\u0434",
|
|
1963
2148
|
validateOutput: "\u041F\u0440\u043E\u0432\u0435\u0440\u0438\u0442\u044C \u0432\u044B\u0445\u043E\u0434",
|
|
1964
2149
|
errorPort: "\u041F\u043E\u0440\u0442 \u043E\u0448\u0438\u0431\u043A\u0438",
|
|
1965
2150
|
completePort: "\u041F\u043E\u0440\u0442 \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043D\u0438\u044F",
|
|
1966
|
-
statusPort: "\u041F\u043E\u0440\u0442 \u0441\u0442\u0430\u0442\u0443\u0441\u0430"
|
|
2151
|
+
statusPort: "\u041F\u043E\u0440\u0442 \u0441\u0442\u0430\u0442\u0443\u0441\u0430",
|
|
2152
|
+
returnPropertyOverride: "\u041F\u0435\u0440\u0435\u043E\u043F\u0440\u0435\u0434\u0435\u043B\u0438\u0442\u044C \u043A\u043B\u044E\u0447 \u0432\u043E\u0437\u0432\u0440\u0430\u0442\u0430"
|
|
1967
2153
|
}
|
|
1968
2154
|
},
|
|
1969
2155
|
ja: {
|
|
1970
|
-
configs: { name: "\u540D\u524D" },
|
|
2156
|
+
configs: { name: "\u540D\u524D", returnProperty: "\u623B\u308A\u30AD\u30FC" },
|
|
1971
2157
|
toggles: {
|
|
1972
2158
|
validateInput: "\u5165\u529B\u691C\u8A3C",
|
|
1973
2159
|
validateOutput: "\u51FA\u529B\u691C\u8A3C",
|
|
1974
2160
|
errorPort: "\u30A8\u30E9\u30FC\u30DD\u30FC\u30C8",
|
|
1975
2161
|
completePort: "\u5B8C\u4E86\u30DD\u30FC\u30C8",
|
|
1976
|
-
statusPort: "\u30B9\u30C6\u30FC\u30BF\u30B9\u30DD\u30FC\u30C8"
|
|
2162
|
+
statusPort: "\u30B9\u30C6\u30FC\u30BF\u30B9\u30DD\u30FC\u30C8",
|
|
2163
|
+
returnPropertyOverride: "\u623B\u308A\u30AD\u30FC\u3092\u4E0A\u66F8\u304D"
|
|
1977
2164
|
}
|
|
1978
2165
|
},
|
|
1979
2166
|
"zh-CN": {
|
|
1980
|
-
configs: { name: "\u540D\u79F0" },
|
|
2167
|
+
configs: { name: "\u540D\u79F0", returnProperty: "\u8FD4\u56DE\u952E" },
|
|
1981
2168
|
toggles: {
|
|
1982
2169
|
validateInput: "\u9A8C\u8BC1\u8F93\u5165",
|
|
1983
2170
|
validateOutput: "\u9A8C\u8BC1\u8F93\u51FA",
|
|
1984
2171
|
errorPort: "\u9519\u8BEF\u7AEF\u53E3",
|
|
1985
2172
|
completePort: "\u5B8C\u6210\u7AEF\u53E3",
|
|
1986
|
-
statusPort: "\u72B6\u6001\u7AEF\u53E3"
|
|
2173
|
+
statusPort: "\u72B6\u6001\u7AEF\u53E3",
|
|
2174
|
+
returnPropertyOverride: "\u8986\u76D6\u8FD4\u56DE\u952E"
|
|
1987
2175
|
}
|
|
1988
2176
|
},
|
|
1989
2177
|
"zh-TW": {
|
|
1990
|
-
configs: { name: "\u540D\u7A31" },
|
|
2178
|
+
configs: { name: "\u540D\u7A31", returnProperty: "\u8FD4\u56DE\u9375" },
|
|
1991
2179
|
toggles: {
|
|
1992
2180
|
validateInput: "\u9A57\u8B49\u8F38\u5165",
|
|
1993
2181
|
validateOutput: "\u9A57\u8B49\u8F38\u51FA",
|
|
1994
2182
|
errorPort: "\u932F\u8AA4\u7AEF\u53E3",
|
|
1995
2183
|
completePort: "\u5B8C\u6210\u7AEF\u53E3",
|
|
1996
|
-
statusPort: "\u72C0\u614B\u7AEF\u53E3"
|
|
2184
|
+
statusPort: "\u72C0\u614B\u7AEF\u53E3",
|
|
2185
|
+
returnPropertyOverride: "\u8986\u84CB\u8FD4\u56DE\u9375"
|
|
1997
2186
|
}
|
|
1998
2187
|
}
|
|
1999
2188
|
};
|
|
@@ -2012,17 +2201,17 @@ Supported: ${languages.join(", ")}`
|
|
|
2012
2201
|
}
|
|
2013
2202
|
function forEachFile(baseDir, fileExtensions, processFile) {
|
|
2014
2203
|
const langMap = /* @__PURE__ */ new Map();
|
|
2015
|
-
if (!
|
|
2016
|
-
const nodeDirs =
|
|
2204
|
+
if (!fs10.existsSync(baseDir)) return langMap;
|
|
2205
|
+
const nodeDirs = fs10.readdirSync(baseDir, { withFileTypes: true }).filter((d) => d.isDirectory());
|
|
2017
2206
|
for (const nodeDir of nodeDirs) {
|
|
2018
2207
|
const nodeType = nodeDir.name;
|
|
2019
|
-
const nodePath =
|
|
2020
|
-
const files =
|
|
2208
|
+
const nodePath = path9.join(baseDir, nodeType);
|
|
2209
|
+
const files = fs10.readdirSync(nodePath);
|
|
2021
2210
|
for (const file of files) {
|
|
2022
|
-
const ext =
|
|
2211
|
+
const ext = path9.extname(file);
|
|
2023
2212
|
if (!fileExtensions.includes(ext)) continue;
|
|
2024
|
-
const lang =
|
|
2025
|
-
const filePath =
|
|
2213
|
+
const lang = path9.basename(file, ext);
|
|
2214
|
+
const filePath = path9.join(nodePath, file);
|
|
2026
2215
|
validateLanguage(lang, filePath);
|
|
2027
2216
|
const value = processFile({ ext, filePath, nodeType });
|
|
2028
2217
|
if (value == null) continue;
|
|
@@ -2040,10 +2229,10 @@ Supported: ${languages.join(", ")}`
|
|
|
2040
2229
|
}
|
|
2041
2230
|
function writeOutput(langMap, fileName, serialize) {
|
|
2042
2231
|
for (const [lang, data] of langMap.entries()) {
|
|
2043
|
-
const langOutDir =
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
2232
|
+
const langOutDir = path9.join(outDir, lang);
|
|
2233
|
+
fs10.mkdirSync(langOutDir, { recursive: true });
|
|
2234
|
+
fs10.writeFileSync(
|
|
2235
|
+
path9.join(langOutDir, fileName),
|
|
2047
2236
|
serialize(data),
|
|
2048
2237
|
"utf-8"
|
|
2049
2238
|
);
|
|
@@ -2055,7 +2244,7 @@ Supported: ${languages.join(", ")}`
|
|
|
2055
2244
|
({ ext, filePath, nodeType }) => {
|
|
2056
2245
|
const type = ext === ".html" ? "text/html" : ext === ".md" ? "text/markdown" : null;
|
|
2057
2246
|
if (!type) return null;
|
|
2058
|
-
const content =
|
|
2247
|
+
const content = fs10.readFileSync(filePath, "utf-8");
|
|
2059
2248
|
return [
|
|
2060
2249
|
`<script type="${type}" data-help-name="${nodeType}">
|
|
2061
2250
|
${content}
|
|
@@ -2072,7 +2261,7 @@ ${content}
|
|
|
2072
2261
|
labelsDir,
|
|
2073
2262
|
[".json"],
|
|
2074
2263
|
({ filePath, nodeType }) => {
|
|
2075
|
-
const parsed = JSON.parse(
|
|
2264
|
+
const parsed = JSON.parse(fs10.readFileSync(filePath, "utf-8"));
|
|
2076
2265
|
if (parsed[nodeType] && typeof parsed[nodeType] === "object") {
|
|
2077
2266
|
console.warn(
|
|
2078
2267
|
`[locales] Warning: "${filePath}" uses nested format (root key "${nodeType}"). Label files should be flat \u2014 the node type is added automatically. See https://bonsaedev.github.io/nrg/guide/building-and-running`
|
|
@@ -2122,8 +2311,8 @@ function minifier() {
|
|
|
2122
2311
|
// src/vite/client/plugins/node-definitions-inliner.ts
|
|
2123
2312
|
import { createRequire as createRequire3 } from "module";
|
|
2124
2313
|
import { pathToFileURL as pathToFileURL3 } from "url";
|
|
2125
|
-
import
|
|
2126
|
-
import
|
|
2314
|
+
import path10 from "path";
|
|
2315
|
+
import fs11 from "fs";
|
|
2127
2316
|
import mime2 from "mime-types";
|
|
2128
2317
|
var VIRTUAL_ID = "virtual:nrg/node-definitions";
|
|
2129
2318
|
var RESOLVED_ID = "\0" + VIRTUAL_ID;
|
|
@@ -2154,9 +2343,9 @@ function getCredentialsFromSchema(schema) {
|
|
|
2154
2343
|
return result;
|
|
2155
2344
|
}
|
|
2156
2345
|
function resolveIcon(iconsDir, type) {
|
|
2157
|
-
if (!
|
|
2158
|
-
return
|
|
2159
|
-
if (
|
|
2346
|
+
if (!fs11.existsSync(iconsDir)) return void 0;
|
|
2347
|
+
return fs11.readdirSync(iconsDir).find((f) => {
|
|
2348
|
+
if (path10.basename(f, path10.extname(f)) !== type) return false;
|
|
2160
2349
|
const mimeType = mime2.lookup(f);
|
|
2161
2350
|
return mimeType !== false && mimeType.startsWith("image/");
|
|
2162
2351
|
});
|
|
@@ -2164,7 +2353,7 @@ function resolveIcon(iconsDir, type) {
|
|
|
2164
2353
|
function nodeDefinitionsInliner(serverOutDir, entryPath, iconsDir, componentsDir, nodesDir, hasUserEntry = true) {
|
|
2165
2354
|
let _nodeTypes = [];
|
|
2166
2355
|
let _definitions = {};
|
|
2167
|
-
const cacheDir =
|
|
2356
|
+
const cacheDir = path10.resolve("node_modules", ".nrg", "client");
|
|
2168
2357
|
return {
|
|
2169
2358
|
name: "vite-plugin-node-red:client:node-definitions-inliner",
|
|
2170
2359
|
enforce: "pre",
|
|
@@ -2173,14 +2362,14 @@ function nodeDefinitionsInliner(serverOutDir, entryPath, iconsDir, componentsDir
|
|
|
2173
2362
|
async buildStart() {
|
|
2174
2363
|
_nodeTypes = [];
|
|
2175
2364
|
_definitions = {};
|
|
2176
|
-
const esmEntryPath =
|
|
2177
|
-
const cjsEntryPath =
|
|
2365
|
+
const esmEntryPath = path10.resolve(serverOutDir, "index.mjs");
|
|
2366
|
+
const cjsEntryPath = path10.resolve(serverOutDir, "index.js");
|
|
2178
2367
|
let packageFn;
|
|
2179
|
-
if (
|
|
2368
|
+
if (fs11.existsSync(esmEntryPath)) {
|
|
2180
2369
|
const fileUrl = pathToFileURL3(esmEntryPath).href + `?t=${Date.now()}`;
|
|
2181
2370
|
const mod = await import(fileUrl);
|
|
2182
2371
|
packageFn = mod?.default ?? mod;
|
|
2183
|
-
} else if (
|
|
2372
|
+
} else if (fs11.existsSync(cjsEntryPath)) {
|
|
2184
2373
|
const require2 = createRequire3(import.meta.url);
|
|
2185
2374
|
delete require2.cache[cjsEntryPath];
|
|
2186
2375
|
const rawMod = require2(cjsEntryPath);
|
|
@@ -2221,14 +2410,14 @@ function nodeDefinitionsInliner(serverOutDir, entryPath, iconsDir, componentsDir
|
|
|
2221
2410
|
};
|
|
2222
2411
|
}
|
|
2223
2412
|
if (!hasUserEntry) {
|
|
2224
|
-
const nodesCache =
|
|
2225
|
-
if (
|
|
2226
|
-
|
|
2413
|
+
const nodesCache = path10.resolve(cacheDir, "nodes");
|
|
2414
|
+
if (fs11.existsSync(nodesCache)) {
|
|
2415
|
+
fs11.rmSync(nodesCache, { recursive: true });
|
|
2227
2416
|
}
|
|
2228
|
-
|
|
2417
|
+
fs11.mkdirSync(nodesCache, { recursive: true });
|
|
2229
2418
|
for (const type of _nodeTypes) {
|
|
2230
|
-
const userTsPath = nodesDir ?
|
|
2231
|
-
if (userTsPath &&
|
|
2419
|
+
const userTsPath = nodesDir ? path10.resolve(nodesDir, `${type}.ts`) : null;
|
|
2420
|
+
if (userTsPath && fs11.existsSync(userTsPath)) continue;
|
|
2232
2421
|
const content = [
|
|
2233
2422
|
`// auto-generated by nrg`,
|
|
2234
2423
|
`import { defineNode } from "@bonsae/nrg/client";`,
|
|
@@ -2238,13 +2427,13 @@ function nodeDefinitionsInliner(serverOutDir, entryPath, iconsDir, componentsDir
|
|
|
2238
2427
|
`});`,
|
|
2239
2428
|
``
|
|
2240
2429
|
].join("\n");
|
|
2241
|
-
|
|
2430
|
+
fs11.writeFileSync(path10.resolve(nodesCache, `${type}.ts`), content);
|
|
2242
2431
|
}
|
|
2243
2432
|
const entryContent = generateEntryCode("");
|
|
2244
|
-
|
|
2433
|
+
fs11.mkdirSync(path10.dirname(path10.resolve(cacheDir, "index.ts")), {
|
|
2245
2434
|
recursive: true
|
|
2246
2435
|
});
|
|
2247
|
-
|
|
2436
|
+
fs11.writeFileSync(path10.resolve(cacheDir, "index.ts"), entryContent);
|
|
2248
2437
|
}
|
|
2249
2438
|
},
|
|
2250
2439
|
resolveId(id) {
|
|
@@ -2267,12 +2456,12 @@ function nodeDefinitionsInliner(serverOutDir, entryPath, iconsDir, componentsDir
|
|
|
2267
2456
|
const nrgImports = /* @__PURE__ */ new Set(["__setSchemas"]);
|
|
2268
2457
|
const lines = [`import __nrgSchemas from "${VIRTUAL_ID}";`];
|
|
2269
2458
|
const postLines = [`__setSchemas(__nrgSchemas);`];
|
|
2270
|
-
if (componentsDir &&
|
|
2459
|
+
if (componentsDir && fs11.existsSync(componentsDir)) {
|
|
2271
2460
|
const formImports = [];
|
|
2272
2461
|
const formEntries = [];
|
|
2273
2462
|
for (const type of _nodeTypes) {
|
|
2274
|
-
const componentPath =
|
|
2275
|
-
if (
|
|
2463
|
+
const componentPath = path10.resolve(componentsDir, `${type}.vue`);
|
|
2464
|
+
if (fs11.existsSync(componentPath)) {
|
|
2276
2465
|
const varName = `__nrgForm_${type.replace(/-/g, "_")}`;
|
|
2277
2466
|
formImports.push(
|
|
2278
2467
|
`import ${varName} from ${JSON.stringify(componentPath)};`
|
|
@@ -2287,12 +2476,12 @@ function nodeDefinitionsInliner(serverOutDir, entryPath, iconsDir, componentsDir
|
|
|
2287
2476
|
}
|
|
2288
2477
|
}
|
|
2289
2478
|
if (!hasUserEntry) {
|
|
2290
|
-
const nodesCache =
|
|
2479
|
+
const nodesCache = path10.resolve(cacheDir, "nodes");
|
|
2291
2480
|
const defVarNames = [];
|
|
2292
2481
|
for (const type of _nodeTypes) {
|
|
2293
2482
|
const varName = `__nrgNodeDef_${type.replace(/-/g, "_")}`;
|
|
2294
|
-
const userTsPath = nodesDir ?
|
|
2295
|
-
const tsPath = userTsPath &&
|
|
2483
|
+
const userTsPath = nodesDir ? path10.resolve(nodesDir, `${type}.ts`) : null;
|
|
2484
|
+
const tsPath = userTsPath && fs11.existsSync(userTsPath) ? userTsPath : path10.resolve(nodesCache, `${type}.ts`);
|
|
2296
2485
|
lines.push(`import ${varName} from ${JSON.stringify(tsPath)};`);
|
|
2297
2486
|
defVarNames.push(varName);
|
|
2298
2487
|
}
|
|
@@ -2310,8 +2499,8 @@ function nodeDefinitionsInliner(serverOutDir, entryPath, iconsDir, componentsDir
|
|
|
2310
2499
|
}
|
|
2311
2500
|
|
|
2312
2501
|
// src/vite/client/plugins/static-copy.ts
|
|
2313
|
-
import
|
|
2314
|
-
import
|
|
2502
|
+
import fs12 from "fs";
|
|
2503
|
+
import path11 from "path";
|
|
2315
2504
|
function staticCopy(options) {
|
|
2316
2505
|
const { targets } = options;
|
|
2317
2506
|
return {
|
|
@@ -2320,23 +2509,23 @@ function staticCopy(options) {
|
|
|
2320
2509
|
enforce: "post",
|
|
2321
2510
|
closeBundle() {
|
|
2322
2511
|
for (const { src, dest } of targets) {
|
|
2323
|
-
if (!
|
|
2324
|
-
|
|
2325
|
-
const stat =
|
|
2512
|
+
if (!fs12.existsSync(src)) continue;
|
|
2513
|
+
fs12.mkdirSync(dest, { recursive: true });
|
|
2514
|
+
const stat = fs12.statSync(src);
|
|
2326
2515
|
if (stat.isDirectory()) {
|
|
2327
|
-
const files =
|
|
2516
|
+
const files = fs12.readdirSync(src);
|
|
2328
2517
|
for (const file of files) {
|
|
2329
|
-
const srcFile =
|
|
2330
|
-
const destFile =
|
|
2331
|
-
const fileStat =
|
|
2518
|
+
const srcFile = path11.join(src, file);
|
|
2519
|
+
const destFile = path11.join(dest, file);
|
|
2520
|
+
const fileStat = fs12.statSync(srcFile);
|
|
2332
2521
|
if (fileStat.isDirectory()) {
|
|
2333
|
-
|
|
2522
|
+
fs12.cpSync(srcFile, destFile, { recursive: true });
|
|
2334
2523
|
} else {
|
|
2335
|
-
|
|
2524
|
+
fs12.copyFileSync(srcFile, destFile);
|
|
2336
2525
|
}
|
|
2337
2526
|
}
|
|
2338
2527
|
} else {
|
|
2339
|
-
|
|
2528
|
+
fs12.copyFileSync(src, dest);
|
|
2340
2529
|
}
|
|
2341
2530
|
}
|
|
2342
2531
|
}
|
|
@@ -2357,74 +2546,74 @@ async function build2(clientBuildOptions, buildContext) {
|
|
|
2357
2546
|
globals = {},
|
|
2358
2547
|
manualChunks
|
|
2359
2548
|
} = clientBuildOptions;
|
|
2360
|
-
const physicalEntryPath =
|
|
2549
|
+
const physicalEntryPath = path12.resolve(srcDir, entry);
|
|
2361
2550
|
let entryPath;
|
|
2362
2551
|
let generatedEntry = false;
|
|
2363
|
-
if (
|
|
2552
|
+
if (fs13.existsSync(physicalEntryPath)) {
|
|
2364
2553
|
entryPath = physicalEntryPath;
|
|
2365
2554
|
} else {
|
|
2366
|
-
const cacheDir =
|
|
2367
|
-
const cachedEntryPath =
|
|
2368
|
-
if (!
|
|
2369
|
-
|
|
2555
|
+
const cacheDir = path12.resolve("node_modules", ".nrg", "client");
|
|
2556
|
+
const cachedEntryPath = path12.resolve(cacheDir, entry);
|
|
2557
|
+
if (!fs13.existsSync(cacheDir)) {
|
|
2558
|
+
fs13.mkdirSync(cacheDir, { recursive: true });
|
|
2370
2559
|
}
|
|
2371
|
-
|
|
2560
|
+
fs13.writeFileSync(cachedEntryPath, "// auto-generated entry\n");
|
|
2372
2561
|
entryPath = cachedEntryPath;
|
|
2373
2562
|
generatedEntry = true;
|
|
2374
2563
|
}
|
|
2375
|
-
const iconsDir =
|
|
2376
|
-
staticDirs.icons ??
|
|
2564
|
+
const iconsDir = path12.resolve(
|
|
2565
|
+
staticDirs.icons ?? path12.join(path12.dirname(path12.resolve(srcDir)), "icons")
|
|
2377
2566
|
);
|
|
2378
2567
|
const plugins = [
|
|
2379
2568
|
vue(),
|
|
2380
2569
|
nodeDefinitionsInliner(
|
|
2381
2570
|
buildContext.outDir,
|
|
2382
2571
|
entryPath,
|
|
2383
|
-
|
|
2384
|
-
|
|
2385
|
-
|
|
2572
|
+
fs13.existsSync(iconsDir) ? iconsDir : void 0,
|
|
2573
|
+
path12.resolve(srcDir, "components"),
|
|
2574
|
+
path12.resolve(srcDir, "nodes"),
|
|
2386
2575
|
!generatedEntry
|
|
2387
2576
|
)
|
|
2388
2577
|
];
|
|
2389
2578
|
plugins.push(
|
|
2390
2579
|
htmlGenerator({
|
|
2391
2580
|
packageName: buildContext.packageName,
|
|
2392
|
-
licensePath: licensePath ?
|
|
2581
|
+
licensePath: licensePath ? path12.resolve(licensePath) : void 0
|
|
2393
2582
|
})
|
|
2394
2583
|
);
|
|
2395
2584
|
if (locales) {
|
|
2396
2585
|
const { docsDir = "./locales/docs", labelsDir = "./locales/labels" } = locales;
|
|
2397
|
-
const localesOutDir =
|
|
2586
|
+
const localesOutDir = path12.join(buildContext.outDir, "locales");
|
|
2398
2587
|
plugins.push(
|
|
2399
2588
|
localesGenerator({
|
|
2400
2589
|
outDir: localesOutDir,
|
|
2401
|
-
docsDir:
|
|
2402
|
-
labelsDir:
|
|
2590
|
+
docsDir: path12.resolve(docsDir),
|
|
2591
|
+
labelsDir: path12.resolve(labelsDir)
|
|
2403
2592
|
})
|
|
2404
2593
|
);
|
|
2405
2594
|
plugins.push(
|
|
2406
2595
|
helpGenerator({
|
|
2407
2596
|
outDir: buildContext.outDir,
|
|
2408
2597
|
localesOutDir,
|
|
2409
|
-
docsDir:
|
|
2410
|
-
labelsDir:
|
|
2598
|
+
docsDir: path12.resolve(docsDir),
|
|
2599
|
+
labelsDir: path12.resolve(labelsDir)
|
|
2411
2600
|
})
|
|
2412
2601
|
);
|
|
2413
2602
|
}
|
|
2414
2603
|
const copyTargets = [];
|
|
2415
|
-
const publicDir =
|
|
2416
|
-
staticDirs.public ??
|
|
2604
|
+
const publicDir = path12.resolve(
|
|
2605
|
+
staticDirs.public ?? path12.join(srcDir, "public")
|
|
2417
2606
|
);
|
|
2418
|
-
if (
|
|
2607
|
+
if (fs13.existsSync(publicDir)) {
|
|
2419
2608
|
copyTargets.push({
|
|
2420
2609
|
src: publicDir,
|
|
2421
|
-
dest:
|
|
2610
|
+
dest: path12.join(buildContext.outDir, "resources")
|
|
2422
2611
|
});
|
|
2423
2612
|
}
|
|
2424
|
-
if (
|
|
2613
|
+
if (fs13.existsSync(iconsDir)) {
|
|
2425
2614
|
copyTargets.push({
|
|
2426
2615
|
src: iconsDir,
|
|
2427
|
-
dest:
|
|
2616
|
+
dest: path12.join(buildContext.outDir, "icons")
|
|
2428
2617
|
});
|
|
2429
2618
|
}
|
|
2430
2619
|
if (copyTargets.length > 0) {
|
|
@@ -2452,10 +2641,10 @@ async function build2(clientBuildOptions, buildContext) {
|
|
|
2452
2641
|
configFile: false,
|
|
2453
2642
|
logLevel: "warn",
|
|
2454
2643
|
base: `/resources/${buildContext.packageName}`,
|
|
2455
|
-
publicDir:
|
|
2644
|
+
publicDir: path12.resolve(srcDir, "public"),
|
|
2456
2645
|
resolve: {
|
|
2457
2646
|
alias: {
|
|
2458
|
-
"@":
|
|
2647
|
+
"@": path12.resolve(srcDir)
|
|
2459
2648
|
}
|
|
2460
2649
|
},
|
|
2461
2650
|
plugins,
|
|
@@ -2509,8 +2698,8 @@ async function build2(clientBuildOptions, buildContext) {
|
|
|
2509
2698
|
throw new BuildError("client", error);
|
|
2510
2699
|
} finally {
|
|
2511
2700
|
if (generatedEntry) {
|
|
2512
|
-
if (
|
|
2513
|
-
|
|
2701
|
+
if (fs13.existsSync(entryPath)) {
|
|
2702
|
+
fs13.unlinkSync(entryPath);
|
|
2514
2703
|
}
|
|
2515
2704
|
}
|
|
2516
2705
|
}
|
|
@@ -2531,6 +2720,8 @@ function serverPlugin(options) {
|
|
|
2531
2720
|
extraFilesCopyTargets,
|
|
2532
2721
|
buildContext
|
|
2533
2722
|
} = options;
|
|
2723
|
+
const { slug, basePath } = nodeRedLauncher;
|
|
2724
|
+
const proxyKey = slug ? `^/${slug}(?:/|\\?|$)` : "^/.*";
|
|
2534
2725
|
let nodeRedPort;
|
|
2535
2726
|
let initialStartDone = false;
|
|
2536
2727
|
let isStarting = false;
|
|
@@ -2555,7 +2746,7 @@ function serverPlugin(options) {
|
|
|
2555
2746
|
logger.stopSpinner("Copied extra files");
|
|
2556
2747
|
}
|
|
2557
2748
|
};
|
|
2558
|
-
const
|
|
2749
|
+
const start2 = async (clean = false) => {
|
|
2559
2750
|
if (isStarting) {
|
|
2560
2751
|
pendingStart = true;
|
|
2561
2752
|
return;
|
|
@@ -2570,7 +2761,7 @@ function serverPlugin(options) {
|
|
|
2570
2761
|
logger.stopSpinner("Node-RED started");
|
|
2571
2762
|
const proxyConfig = server.config.server.proxy;
|
|
2572
2763
|
if (proxyConfig && typeof proxyConfig === "object") {
|
|
2573
|
-
const rule = proxyConfig[
|
|
2764
|
+
const rule = proxyConfig[proxyKey];
|
|
2574
2765
|
if (rule && typeof rule === "object") {
|
|
2575
2766
|
rule.target = `http://127.0.0.1:${nodeRedPort}`;
|
|
2576
2767
|
}
|
|
@@ -2588,14 +2779,14 @@ function serverPlugin(options) {
|
|
|
2588
2779
|
} finally {
|
|
2589
2780
|
isStarting = false;
|
|
2590
2781
|
if (pendingStart) {
|
|
2591
|
-
|
|
2782
|
+
start2(false);
|
|
2592
2783
|
}
|
|
2593
2784
|
}
|
|
2594
2785
|
};
|
|
2595
2786
|
const printServerUrls = (vitePort, nodeRedPorts) => {
|
|
2596
2787
|
console.log();
|
|
2597
2788
|
console.log(
|
|
2598
|
-
` ${color2.cyan("Vite")} ${color2.dim("\u279C")} ${color2.cyan(`http://127.0.0.1:${vitePort}
|
|
2789
|
+
` ${color2.cyan("Vite")} ${color2.dim("\u279C")} ${color2.cyan(`http://127.0.0.1:${vitePort}${basePath}`)}`
|
|
2599
2790
|
);
|
|
2600
2791
|
if (nodeRedPorts.actual != nodeRedPorts.preferred) {
|
|
2601
2792
|
console.log(
|
|
@@ -2617,7 +2808,7 @@ function serverPlugin(options) {
|
|
|
2617
2808
|
server: {
|
|
2618
2809
|
host: "127.0.0.1",
|
|
2619
2810
|
proxy: {
|
|
2620
|
-
|
|
2811
|
+
[proxyKey]: {
|
|
2621
2812
|
target: `http://127.0.0.1:${nodeRedLauncher.preferredPort}`,
|
|
2622
2813
|
changeOrigin: true,
|
|
2623
2814
|
ws: true,
|
|
@@ -2660,8 +2851,19 @@ function serverPlugin(options) {
|
|
|
2660
2851
|
},
|
|
2661
2852
|
async configureServer(viteServer) {
|
|
2662
2853
|
server = viteServer;
|
|
2854
|
+
if (slug) {
|
|
2855
|
+
server.middlewares.use((req, res, next) => {
|
|
2856
|
+
if (req.url === "/" || req.url === "") {
|
|
2857
|
+
res.statusCode = 302;
|
|
2858
|
+
res.setHeader("Location", basePath);
|
|
2859
|
+
res.end();
|
|
2860
|
+
return;
|
|
2861
|
+
}
|
|
2862
|
+
next();
|
|
2863
|
+
});
|
|
2864
|
+
}
|
|
2663
2865
|
logger.intro();
|
|
2664
|
-
await
|
|
2866
|
+
await start2(true);
|
|
2665
2867
|
initialStartDone = true;
|
|
2666
2868
|
printServerUrls(server.config.server.port ?? 5173, {
|
|
2667
2869
|
actual: nodeRedPort,
|
|
@@ -2669,20 +2871,20 @@ function serverPlugin(options) {
|
|
|
2669
2871
|
});
|
|
2670
2872
|
logger.startGroup("Node-RED");
|
|
2671
2873
|
nodeRedLauncher.flushLogs();
|
|
2672
|
-
const serverSrcDir =
|
|
2874
|
+
const serverSrcDir = path13.resolve(
|
|
2673
2875
|
serverBuildOptions.srcDir ?? "./server"
|
|
2674
2876
|
);
|
|
2675
|
-
const clientSrcDir =
|
|
2877
|
+
const clientSrcDir = path13.resolve(
|
|
2676
2878
|
clientBuildOptions.srcDir ?? "./client"
|
|
2677
2879
|
);
|
|
2678
|
-
const localesDocsDir =
|
|
2880
|
+
const localesDocsDir = path13.resolve(
|
|
2679
2881
|
clientBuildOptions.locales?.docsDir ?? "./locales/docs"
|
|
2680
2882
|
);
|
|
2681
|
-
const localesLabelsDir =
|
|
2883
|
+
const localesLabelsDir = path13.resolve(
|
|
2682
2884
|
clientBuildOptions.locales?.labelsDir ?? "./locales/labels"
|
|
2683
2885
|
);
|
|
2684
|
-
const iconsDir =
|
|
2685
|
-
clientBuildOptions.staticDirs?.icons ??
|
|
2886
|
+
const iconsDir = path13.resolve(
|
|
2887
|
+
clientBuildOptions.staticDirs?.icons ?? path13.join(path13.dirname(clientSrcDir), "icons")
|
|
2686
2888
|
);
|
|
2687
2889
|
const watchPaths = [
|
|
2688
2890
|
serverSrcDir,
|
|
@@ -2697,12 +2899,12 @@ function serverPlugin(options) {
|
|
|
2697
2899
|
ignored: ["**/node_modules/**", "**/dist/**", "**/.node-red/**"]
|
|
2698
2900
|
});
|
|
2699
2901
|
const debounceBeforeStart = debounce(
|
|
2700
|
-
() =>
|
|
2902
|
+
() => start2(false),
|
|
2701
2903
|
nodeRedLauncher.restartDelay ?? 1e3
|
|
2702
2904
|
);
|
|
2703
2905
|
const handleFileChange = (file, event) => {
|
|
2704
2906
|
if (!initialStartDone) return;
|
|
2705
|
-
logger.info(`${event}: ${
|
|
2907
|
+
logger.info(`${event}: ${path13.relative(process.cwd(), file)}`);
|
|
2706
2908
|
debounceBeforeStart();
|
|
2707
2909
|
};
|
|
2708
2910
|
watcher.on("change", (file) => handleFileChange(file, "Changed"));
|
|
@@ -2780,30 +2982,33 @@ function buildPlugin(options) {
|
|
|
2780
2982
|
}
|
|
2781
2983
|
|
|
2782
2984
|
// src/vite/plugin.ts
|
|
2783
|
-
function
|
|
2784
|
-
const {
|
|
2985
|
+
function nrg(options = {}) {
|
|
2986
|
+
const { build: build3 = {}, server = {} } = options;
|
|
2987
|
+
const { outDir = DEFAULT_OUTPUT_DIR } = build3;
|
|
2785
2988
|
const clientBuildOptions = mergeOptions(
|
|
2786
2989
|
DEFAULT_CLIENT_BUILD_OPTIONS,
|
|
2787
|
-
|
|
2990
|
+
build3.client
|
|
2788
2991
|
);
|
|
2789
2992
|
const serverBuildOptions = mergeOptions(
|
|
2790
2993
|
DEFAULT_SERVER_BUILD_OPTIONS,
|
|
2791
|
-
|
|
2994
|
+
build3.server
|
|
2792
2995
|
);
|
|
2793
2996
|
const nodeRedLauncherOptions = mergeOptions(
|
|
2794
2997
|
DEFAULT_NODE_RED_LAUNCHER_OPTIONS,
|
|
2795
|
-
|
|
2998
|
+
server.nodeRed
|
|
2796
2999
|
);
|
|
2797
|
-
const extraFilesCopyTargets =
|
|
2798
|
-
const resolvedOutDir =
|
|
3000
|
+
const extraFilesCopyTargets = build3.extraFilesCopyTargets ?? DEFAULT_EXTRA_FILES_COPY_TARGETS;
|
|
3001
|
+
const resolvedOutDir = path14.resolve(outDir);
|
|
2799
3002
|
const buildContext = {
|
|
2800
3003
|
outDir: resolvedOutDir,
|
|
2801
3004
|
packageName: getPackageName(),
|
|
2802
3005
|
isDev: process.env.NODE_ENV === "development"
|
|
2803
3006
|
};
|
|
3007
|
+
const slug = resolveSlug(server.slug);
|
|
2804
3008
|
const nodeRedLauncher = new NodeRedLauncher(
|
|
2805
3009
|
resolvedOutDir,
|
|
2806
|
-
nodeRedLauncherOptions
|
|
3010
|
+
nodeRedLauncherOptions,
|
|
3011
|
+
slug
|
|
2807
3012
|
);
|
|
2808
3013
|
return [
|
|
2809
3014
|
serverPlugin({
|
|
@@ -2822,5 +3027,5 @@ function nodeRed(options = {}) {
|
|
|
2822
3027
|
];
|
|
2823
3028
|
}
|
|
2824
3029
|
export {
|
|
2825
|
-
|
|
3030
|
+
nrg
|
|
2826
3031
|
};
|