@bonsae/nrg 0.6.1 → 0.6.3

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.
Files changed (84) hide show
  1. package/README.md +5 -5
  2. package/package.json +13 -77
  3. package/{src/core/client → shims}/components.d.ts +2 -0
  4. package/{src/tsconfig → tsconfig}/client.json +3 -3
  5. package/types/client.d.ts +37 -0
  6. package/types/index.d.ts +211 -0
  7. package/types/server.d.ts +2307 -0
  8. package/types/vite.d.ts +12 -0
  9. package/build/vite/utils.js +0 -56
  10. package/src/core/client/app.vue +0 -185
  11. package/src/core/client/components/node-red-config-input.vue +0 -79
  12. package/src/core/client/components/node-red-editor-input.vue +0 -307
  13. package/src/core/client/components/node-red-input-label.vue +0 -53
  14. package/src/core/client/components/node-red-input.vue +0 -93
  15. package/src/core/client/components/node-red-json-schema-form.vue +0 -444
  16. package/src/core/client/components/node-red-select-input.vue +0 -108
  17. package/src/core/client/components/node-red-toggle.vue +0 -115
  18. package/src/core/client/components/node-red-typed-input.vue +0 -158
  19. package/src/core/client/index.ts +0 -500
  20. package/src/core/client/tsconfig.json +0 -18
  21. package/src/core/constants.ts +0 -18
  22. package/src/core/errors.ts +0 -9
  23. package/src/core/server/api/index.ts +0 -1
  24. package/src/core/server/api/serve-nrg-resources.ts +0 -54
  25. package/src/core/server/index.ts +0 -191
  26. package/src/core/server/nodes/config-node.ts +0 -67
  27. package/src/core/server/nodes/factories.ts +0 -136
  28. package/src/core/server/nodes/index.ts +0 -5
  29. package/src/core/server/nodes/io-node.ts +0 -179
  30. package/src/core/server/nodes/node.ts +0 -259
  31. package/src/core/server/nodes/types/config-node.ts +0 -28
  32. package/src/core/server/nodes/types/factories.ts +0 -130
  33. package/src/core/server/nodes/types/index.ts +0 -4
  34. package/src/core/server/nodes/types/io-node.ts +0 -40
  35. package/src/core/server/nodes/types/node.ts +0 -41
  36. package/src/core/server/nodes/utils.ts +0 -106
  37. package/src/core/server/schemas/base.ts +0 -66
  38. package/src/core/server/schemas/index.ts +0 -3
  39. package/src/core/server/schemas/type.ts +0 -95
  40. package/src/core/server/schemas/types/index.ts +0 -82
  41. package/src/core/server/tsconfig.json +0 -17
  42. package/src/core/server/types/index.ts +0 -220
  43. package/src/core/server/utils.ts +0 -56
  44. package/src/core/server/validator.ts +0 -36
  45. package/src/core/validator.ts +0 -222
  46. package/src/index.ts +0 -2
  47. package/src/types.ts +0 -189
  48. package/src/utils.ts +0 -20
  49. package/src/vite/async-utils.ts +0 -61
  50. package/src/vite/client/build.ts +0 -227
  51. package/src/vite/client/index.ts +0 -1
  52. package/src/vite/client/plugins/html-generator.ts +0 -75
  53. package/src/vite/client/plugins/index.ts +0 -5
  54. package/src/vite/client/plugins/locales-generator.ts +0 -126
  55. package/src/vite/client/plugins/minifier.ts +0 -23
  56. package/src/vite/client/plugins/node-definitions-inliner.ts +0 -275
  57. package/src/vite/client/plugins/static-copy.ts +0 -43
  58. package/src/vite/defaults.ts +0 -77
  59. package/src/vite/errors.ts +0 -37
  60. package/src/vite/index.ts +0 -2
  61. package/src/vite/logger.ts +0 -94
  62. package/src/vite/node-red-launcher.ts +0 -344
  63. package/src/vite/plugin.ts +0 -61
  64. package/src/vite/plugins/build.ts +0 -85
  65. package/src/vite/plugins/index.ts +0 -2
  66. package/src/vite/plugins/server.ts +0 -267
  67. package/src/vite/server/build.ts +0 -124
  68. package/src/vite/server/index.ts +0 -1
  69. package/src/vite/server/plugins/index.ts +0 -3
  70. package/src/vite/server/plugins/output-wrapper.ts +0 -109
  71. package/src/vite/server/plugins/package-json-generator.ts +0 -203
  72. package/src/vite/server/plugins/type-generator.ts +0 -442
  73. package/src/vite/types.ts +0 -174
  74. package/src/vite/utils.ts +0 -72
  75. /package/{build/index.js → index.js} +0 -0
  76. /package/{build/server → server}/index.cjs +0 -0
  77. /package/{build/server → server}/resources/nrg-client.js +0 -0
  78. /package/{build/server → server}/resources/vue.esm-browser.js +0 -0
  79. /package/{build/server → server}/resources/vue.esm-browser.prod.js +0 -0
  80. /package/{src/core/client → shims}/globals.d.ts +0 -0
  81. /package/{src/core/client → shims}/shims-vue.d.ts +0 -0
  82. /package/{src/tsconfig → tsconfig}/base.json +0 -0
  83. /package/{src/tsconfig → tsconfig}/server.json +0 -0
  84. /package/{build/vite → vite}/index.js +0 -0
@@ -1,344 +0,0 @@
1
- import type { ChildProcess } from "child_process";
2
- import { spawn } from "child_process";
3
- import getPort from "get-port";
4
- import detect from "detect-port";
5
- import { builtinModules } from "module";
6
- import treeKill from "tree-kill";
7
- import fs from "fs";
8
- import os from "os";
9
- import path from "path";
10
- import { build as esbuild } from "esbuild";
11
- import { withTimeout, retry } from "./async-utils";
12
- import { NodeRedStartError } from "./errors";
13
- import { Logger } from "./logger";
14
- import type { Logger, NodeRedLauncherOptions } from "./types";
15
-
16
- class NodeRedLauncher {
17
- private compiledRuntimeSettingsFilepath: string | null = null;
18
- private process: ChildProcess | null = null;
19
- private bufferedLogs: string[] = [];
20
- private isReady: boolean = false;
21
- private port: number | null = null;
22
-
23
- private readonly outDir: string;
24
- private readonly options: NodeRedLauncherOptions;
25
- private readonly logger: Logger;
26
-
27
- constructor(outDir: string, options: NodeRedLauncherOptions) {
28
- this.outDir = outDir;
29
- this.options = options;
30
- this.logger = new Logger({
31
- name: "vite-plugin-node-red",
32
- prefix: "node-red",
33
- });
34
- }
35
-
36
- get preferredPort(): number {
37
- return this.options.runtime?.port ?? 1880;
38
- }
39
-
40
- get restartDelay(): number {
41
- return this.options.restartDelay ?? 1000;
42
- }
43
-
44
- get pid(): number | null {
45
- return this.process?.pid ?? null;
46
- }
47
-
48
- get nodeRedCommand(): string {
49
- const version = this.options.runtime?.version;
50
- if (version === "latest") {
51
- return "node-red@latest";
52
- }
53
- if (version) {
54
- return `node-red@${version}`;
55
- }
56
- return "node-red";
57
- }
58
-
59
- private findRuntimeSettingsFilepath(): string | null {
60
- const runtimeSettingsFilepath = this.options.runtime.settingsFilepath;
61
- if (runtimeSettingsFilepath) {
62
- const resolved = path.resolve(runtimeSettingsFilepath);
63
- if (fs.existsSync(resolved)) {
64
- return resolved;
65
- }
66
- this.logger.warn(`Settings file not found: ${runtimeSettingsFilepath}`);
67
- return null;
68
- }
69
-
70
- const resolved = path.resolve("node-red.settings.ts");
71
- if (fs.existsSync(resolved)) {
72
- return resolved;
73
- }
74
-
75
- return null;
76
- }
77
-
78
- private async compileRuntimeSettingsFile(
79
- runtimeSettingsFilepath: string,
80
- ): Promise<string> {
81
- const compiledRuntimeSettingsFilepath = path.join(
82
- os.tmpdir(),
83
- `node-red.settings.${process.pid}.cjs`,
84
- );
85
-
86
- // NOTE: I need to include "node:" modules which are a new common standard
87
- const nodeBuiltins = [
88
- ...builtinModules,
89
- ...builtinModules.map((m) => `node:${m}`),
90
- ];
91
-
92
- const settingsDir = path
93
- .dirname(runtimeSettingsFilepath)
94
- .split(path.sep)
95
- .join("/");
96
- const settingsFile = runtimeSettingsFilepath.split(path.sep).join("/");
97
-
98
- // NOTE: im hardcoding node18 because it doesn't really matter
99
- await esbuild({
100
- entryPoints: [runtimeSettingsFilepath],
101
- outfile: compiledRuntimeSettingsFilepath,
102
- format: "cjs",
103
- platform: "node",
104
- target: "node18",
105
- bundle: true,
106
- define: {
107
- "import.meta.dirname": JSON.stringify(settingsDir),
108
- "import.meta.filename": JSON.stringify(settingsFile),
109
- "import.meta.url": JSON.stringify(`file://${settingsFile}`),
110
- },
111
- external: [...nodeBuiltins, "node-red", "@node-red/*"],
112
- });
113
-
114
- this.compiledRuntimeSettingsFilepath = compiledRuntimeSettingsFilepath;
115
- return compiledRuntimeSettingsFilepath;
116
- }
117
-
118
- private async generateRuntimeSettingsFile(): Promise<string> {
119
- const userRuntimeSettingsFilepath = this.findRuntimeSettingsFilepath();
120
- let compiledRuntimeSettingsFilepath: string | null = null;
121
- if (userRuntimeSettingsFilepath) {
122
- compiledRuntimeSettingsFilepath = await this.compileRuntimeSettingsFile(
123
- userRuntimeSettingsFilepath,
124
- );
125
- }
126
-
127
- const outDir = path.resolve(this.outDir).split(path.sep).join("/");
128
- const cwd = process.cwd().split(path.sep).join("/");
129
- const userDir = path.resolve(cwd, ".node-red");
130
- const finalRuntimeSettingsFile = compiledRuntimeSettingsFilepath
131
- ? `
132
- const compiledRuntimeSettings = require("${compiledRuntimeSettingsFilepath
133
- .split(path.sep)
134
- .join("/")}");
135
- const settings = compiledRuntimeSettings.default || compiledRuntimeSettings;
136
- settings.uiPort = ${this.port};
137
- if(!settings.userDir){
138
- settings.userDir = "${userDir}";
139
- }
140
- settings.nodesDir = settings.nodesDir || [];
141
- if (!settings.nodesDir.includes("${outDir}")) {
142
- settings.nodesDir.push("${outDir}");
143
- }
144
- if(!settings.flowFile){
145
- settings.flowFile = "flows.json";
146
- }
147
- module.exports = settings;
148
- `
149
- : `
150
- const settings = {
151
- uiPort: ${this.port},
152
- userDir: "${userDir}",
153
- flowFile: "flows.json",
154
- nodesDir: ["${outDir}"],
155
- };
156
- module.exports = settings;
157
- `;
158
-
159
- const finalRuntimeSettingsFilepath = path.join(
160
- os.tmpdir(),
161
- `node-red-settings-final-${process.pid}.cjs`,
162
- );
163
-
164
- fs.writeFileSync(finalRuntimeSettingsFilepath, finalRuntimeSettingsFile);
165
- this.compiledRuntimeSettingsFilepath = finalRuntimeSettingsFilepath;
166
- return finalRuntimeSettingsFilepath;
167
- }
168
-
169
- private log(line: string): void {
170
- if (line.includes("Server now running at")) {
171
- return;
172
- }
173
- this.logger.raw(line);
174
- }
175
-
176
- async start(): Promise<number> {
177
- this.port = await getPort({ port: this.preferredPort });
178
-
179
- const startProcess = (): Promise => {
180
- // eslint-disable-next-line no-async-promise-executor
181
- return new Promise(async (resolve, reject) => {
182
- try {
183
- const settingsPath = await this.generateRuntimeSettingsFile();
184
- const args = this.options.args ?? [];
185
-
186
- this.bufferedLogs = [];
187
- this.isReady = false;
188
-
189
- this.process = spawn(
190
- "npx",
191
- [this.nodeRedCommand, "-s", settingsPath, ...args],
192
- {
193
- stdio: ["ignore", "pipe", "pipe"],
194
- shell: true,
195
- },
196
- );
197
-
198
- this.process.stdout?.on("data", (data) => {
199
- const lines = data.toString().split("\n").filter(Boolean);
200
-
201
- for (const line of lines) {
202
- if (this.isReady) {
203
- this.log(line);
204
- } else {
205
- this.bufferedLogs.push(line);
206
- }
207
-
208
- if (
209
- line.includes("Started flows") ||
210
- line.includes("Server now running")
211
- ) {
212
- this.isReady = true;
213
- resolve();
214
- }
215
- }
216
- });
217
-
218
- this.process.stderr?.on("data", (data) => {
219
- const lines = data.toString().split("\n").filter(Boolean);
220
-
221
- for (const line of lines) {
222
- if (this.isReady) {
223
- this.logger.error(`${line}`);
224
- } else {
225
- this.bufferedLogs.push(line);
226
- }
227
- }
228
- });
229
-
230
- this.process.on("error", (error) => {
231
- reject(new NodeRedStartError(error));
232
- });
233
-
234
- this.process.on("exit", (code) => {
235
- if (!this.isReady && code !== 0 && code !== null) {
236
- reject(
237
- new NodeRedStartError(
238
- new Error(`Process exited with code ${code}`),
239
- ),
240
- );
241
- }
242
- resolve();
243
- });
244
- } catch (error) {
245
- reject(new NodeRedStartError(error as Error));
246
- }
247
- });
248
- };
249
-
250
- try {
251
- await retry(startProcess, { attempts: 3, delay: 100 });
252
- return this.port;
253
- } catch (error) {
254
- if (this.process) {
255
- const pid = this.process.pid;
256
- if (pid) {
257
- treeKill(pid, "SIGKILL");
258
- }
259
- this.process = null;
260
- }
261
- throw new NodeRedStartError(error as Error);
262
- }
263
- }
264
-
265
- async stop(skipPortUsageCheck: boolean = false): Promise<void> {
266
- if (!this.process) return;
267
-
268
- const pid = this.process.pid;
269
- const currentPort = this.port;
270
-
271
- if (!pid) {
272
- this.process = null;
273
- return;
274
- }
275
-
276
- const stopProcess = new Promise<void>((resolve) => {
277
- this.process!.once("exit", () => {
278
- this.process = null;
279
- resolve();
280
- });
281
-
282
- treeKill(pid, "SIGTERM", (error) => {
283
- if (error) {
284
- try {
285
- process.kill(pid, "SIGTERM");
286
- } catch {
287
- this.process = null;
288
- resolve();
289
- }
290
- }
291
- });
292
- });
293
-
294
- try {
295
- await withTimeout(stopProcess, 10000);
296
- } catch {
297
- this.logger.warn("Graceful shutdown timed out, force killing...");
298
- await new Promise<void>((resolve) => {
299
- treeKill(pid, "SIGKILL", () => {
300
- this.process = null;
301
- resolve();
302
- });
303
- });
304
- }
305
-
306
- if (!skipPortUsageCheck && currentPort) {
307
- const checkPortUsage = async (): Promise<void> => {
308
- const availablePort = await detect(currentPort);
309
- if (availablePort !== currentPort) {
310
- throw new Error("Port still in use");
311
- }
312
- };
313
-
314
- try {
315
- await retry(checkPortUsage, { attempts: 5, delay: 100 });
316
- } catch {
317
- this.logger.warn(
318
- `Port ${currentPort} may still be in use. If restart fails, try again in a few seconds.`,
319
- );
320
- }
321
- }
322
-
323
- this.port = null;
324
- }
325
-
326
- flushLogs(): void {
327
- for (const line of this.bufferedLogs) {
328
- this.log(line);
329
- }
330
- this.bufferedLogs = [];
331
- }
332
-
333
- cleanup(): void {
334
- if (
335
- this.compiledRuntimeSettingsFilepath &&
336
- fs.existsSync(this.compiledRuntimeSettingsFilepath)
337
- ) {
338
- fs.unlinkSync(this.compiledRuntimeSettingsFilepath);
339
- this.compiledRuntimeSettingsFilepath = null;
340
- }
341
- }
342
- }
343
-
344
- export { NodeRedLauncher };
@@ -1,61 +0,0 @@
1
- import type { Plugin } from "vite";
2
- import path from "path";
3
- import type { NodeRedPluginOptions } from "./types";
4
- import {
5
- DEFAULT_CLIENT_BUILD_OPTIONS,
6
- DEFAULT_SERVER_BUILD_OPTIONS,
7
- DEFAULT_NODE_RED_LAUNCHER_OPTIONS,
8
- DEFAULT_EXTRA_FILES_COPY_TARGETS,
9
- DEFAULT_OUTPUT_DIR,
10
- } from "./defaults";
11
- import { getPackageName, mergeOptions } from "./utils";
12
- import { NodeRedLauncher } from "./node-red-launcher";
13
- import { serverPlugin, buildPlugin } from "./plugins";
14
-
15
- function nodeRed(options: NodeRedPluginOptions = {}): Plugin[] {
16
- const { outDir = DEFAULT_OUTPUT_DIR } = options;
17
-
18
- const clientBuildOptions = mergeOptions(
19
- DEFAULT_CLIENT_BUILD_OPTIONS,
20
- options.clientBuildOptions,
21
- );
22
- const serverBuildOptions = mergeOptions(
23
- DEFAULT_SERVER_BUILD_OPTIONS,
24
- options.serverBuildOptions,
25
- );
26
- const nodeRedLauncherOptions = mergeOptions(
27
- DEFAULT_NODE_RED_LAUNCHER_OPTIONS,
28
- options.nodeRedLauncherOptions,
29
- );
30
- const extraFilesCopyTargets =
31
- options.extraFilesCopyTargets ?? DEFAULT_EXTRA_FILES_COPY_TARGETS;
32
-
33
- const resolvedOutDir = path.resolve(outDir);
34
- const buildContext = {
35
- outDir: resolvedOutDir,
36
- packageName: getPackageName(),
37
- isDev: process.env.NODE_ENV === "development",
38
- };
39
- const nodeRedLauncher = new NodeRedLauncher(
40
- resolvedOutDir,
41
- nodeRedLauncherOptions,
42
- );
43
-
44
- return [
45
- serverPlugin({
46
- nodeRedLauncher,
47
- serverBuildOptions,
48
- clientBuildOptions,
49
- extraFilesCopyTargets,
50
- buildContext,
51
- }),
52
- buildPlugin({
53
- serverBuildOptions,
54
- clientBuildOptions,
55
- extraFilesCopyTargets,
56
- buildContext,
57
- }),
58
- ];
59
- }
60
-
61
- export { nodeRed };
@@ -1,85 +0,0 @@
1
- import type { Plugin } from "vite";
2
- import { execSync } from "child_process";
3
- import fs from "fs";
4
- import path from "path";
5
- import { BuildError } from "../errors";
6
- import { logger } from "../logger";
7
- import type { BuildPluginOptions } from "../types";
8
- import { build as buildServer } from "../server";
9
- import { build as buildClient } from "../client";
10
- import { cleanDir, copyFiles } from "../utils";
11
-
12
- function buildPlugin(options: BuildPluginOptions): Plugin {
13
- const {
14
- serverBuildOptions,
15
- clientBuildOptions,
16
- extraFilesCopyTargets,
17
- buildContext,
18
- } = options;
19
-
20
- return {
21
- name: "vite-plugin-node-red:build",
22
- apply: "build",
23
-
24
- async buildStart() {
25
- try {
26
- logger.intro();
27
-
28
- logger.startSpinner("Type checking");
29
- const serverTsconfig = path.resolve(
30
- serverBuildOptions.srcDir ?? "./src/server",
31
- "tsconfig.json",
32
- );
33
- const clientTsconfig = path.resolve(
34
- clientBuildOptions.srcDir ?? "./src/client",
35
- "tsconfig.json",
36
- );
37
- const tsconfigsToCheck = [serverTsconfig, clientTsconfig].filter((p) =>
38
- fs.existsSync(p),
39
- );
40
- try {
41
- for (const tsconfig of tsconfigsToCheck) {
42
- execSync(`npx tsc -p ${tsconfig} --noEmit`, {
43
- stdio: ["inherit", "pipe", "pipe"],
44
- encoding: "utf-8",
45
- });
46
- }
47
- logger.stopSpinner("Type checked");
48
- } catch (e: any) {
49
- logger.stopSpinner("Type check failed");
50
- const output = (e.stdout || "") + (e.stderr || "");
51
- if (output) {
52
- console.error(output);
53
- }
54
- throw new BuildError("type-check", e);
55
- }
56
-
57
- logger.startSpinner("Cleaning");
58
- cleanDir(buildContext.outDir);
59
- logger.stopSpinner("Cleaned");
60
-
61
- logger.startSpinner("Building");
62
- await buildServer(serverBuildOptions, buildContext);
63
- await buildClient(clientBuildOptions, buildContext);
64
- logger.stopSpinner("Built");
65
-
66
- if (extraFilesCopyTargets.length) {
67
- logger.startSpinner("Copying extra files");
68
- copyFiles(extraFilesCopyTargets, buildContext.outDir);
69
- logger.stopSpinner("Copied extra files");
70
- }
71
- logger.success("Success");
72
- process.exit(0);
73
- } catch (error) {
74
- if (error instanceof BuildError) {
75
- logger.error(`Build failed: ${error.message}`, error.cause);
76
- } else {
77
- logger.error("Unexpected error during build", error as Error);
78
- }
79
- process.exit(1);
80
- }
81
- },
82
- };
83
- }
84
-
85
- export { buildPlugin };
@@ -1,2 +0,0 @@
1
- export { serverPlugin } from "./server";
2
- export { buildPlugin } from "./build";