Last active
October 23, 2024 11:33
-
-
Save marcus-j-davies/a7a9d7193939cc481460aad3e7f8bd03 to your computer and use it in GitHub Desktop.
Node Red V4 - SFE (Single File Executable)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const esbuild = require('esbuild'); | |
const { cp, readFile, writeFile } = require('fs/promises'); | |
const { exists } = require('fs-extra'); | |
const OutputDIR = './build'; | |
const InputFile = `./main-source.js`; | |
const OutputFile = `${OutputDIR}/node-red.js`; | |
const FinalPKG = `${OutputDIR}/package.json`; | |
const GotSourceFile = './node_modules/got/dist/source/index.js'; | |
const GotPackageFile = './node_modules/got/package.json'; | |
const NRPackageFile = './node_modules/node-red/package.json'; | |
const RequestSourceFile = | |
'./node_modules/@node-red/nodes/core/network/21-httprequest.js'; | |
const EmbeddedHomePath = 'Home'; | |
/* Updated Later */ | |
let IsFlowBeingEmbedded = false; | |
/* Things to not include during bundling */ | |
const Externals = [ | |
'@node-red/nodes', | |
'@node-red/editor-client', | |
'@node-rs', | |
'oauth2orize', | |
'got' | |
]; | |
/* Native Binding Handling */ | |
const NativeNodeModulesPlugin = { | |
name: 'native-node-modules', | |
setup(build) { | |
build.onResolve({ filter: /\.node$/, namespace: 'file' }, (args) => ({ | |
path: require.resolve(args.path, { paths: [args.resolveDir] }), | |
namespace: 'node-file' | |
})); | |
build.onLoad({ filter: /.*/, namespace: 'node-file' }, (args) => ({ | |
contents: ` | |
import path from ${JSON.stringify(args.path)} | |
try { module.exports = require(path) } | |
catch {} | |
` | |
})); | |
build.onResolve({ filter: /\.node$/, namespace: 'node-file' }, (args) => ({ | |
path: args.path, | |
namespace: 'file' | |
})); | |
const opts = build.initialOptions; | |
opts.loader = opts.loader || {}; | |
opts.loader['.node'] = 'file'; | |
opts.loader['.sh'] = 'binary'; | |
} | |
}; | |
/* ES Build Got */ | |
const ESBGot = async () => { | |
const config = { | |
entryPoints: [GotSourceFile], | |
bundle: true, | |
platform: 'node', | |
target: 'node20', | |
allowOverwrite: true, | |
outfile: GotSourceFile | |
}; | |
await esbuild.build(config); | |
const Package = require(GotPackageFile); | |
Package.type = 'commonjs'; | |
await writeFile(GotPackageFile, JSON.stringify(Package, null, 2)); | |
}; | |
/* Main */ | |
const Run = async () => { | |
// is Flow being embdded | |
IsFlowBeingEmbedded = await exists(EmbeddedHomePath); | |
await ESBGot(); | |
const config = { | |
entryPoints: [InputFile], | |
plugins: [NativeNodeModulesPlugin], | |
bundle: true, | |
platform: 'node', | |
target: 'node20', | |
outfile: OutputFile, | |
external: Externals | |
}; | |
await esbuild.build(config); | |
const NRV = require(NRPackageFile).version; | |
let patchedNR = (await readFile(OutputFile, 'utf-8')).replace( | |
'= getVersion()', | |
`= "${NRV}"` | |
); | |
if (IsFlowBeingEmbedded) { | |
patchedNR = patchedNR | |
.replace('return fs.ensureDir(libFlowsDir)', '') | |
.replace( | |
'writePromises.push(util.writeFile(sectionFilename2, sectionContent2, sectionFilename2 + ".backup"));', | |
'' | |
) | |
.replace( | |
'writePromises.push(util.writeFile(sectionFilename, sectionContent, sectionFilename + ".backup"));', | |
'' | |
) | |
.replace( | |
'await fs.ensureDir(fspath.join(settings.userDir, "node_modules"));', | |
'' | |
); | |
} | |
await writeFile(OutputFile, patchedNR); | |
const PatchedRequest = (await readFile(RequestSourceFile, 'utf-8')).replace( | |
"const { got } = await import('got')", | |
"const { got } = require('got')" | |
); | |
await writeFile(RequestSourceFile, PatchedRequest); | |
for (const ext of Externals) { | |
const path = ext.startsWith('./') ? ext : `node_modules/${ext}`; | |
if (await exists(path)) { | |
await cp(path, `${OutputDIR}/${path}`, { recursive: true }); | |
} | |
} | |
const PKG = { | |
name: 'node-red', | |
bin: 'node-red.js', | |
pkg: { | |
assets: ['node_modules/**'] | |
} | |
}; | |
if (IsFlowBeingEmbedded) { | |
PKG.pkg.assets.push(`${EmbeddedHomePath}/**`); | |
await cp(EmbeddedHomePath, `${OutputDIR}/${EmbeddedHomePath}`, { | |
recursive: true | |
}); | |
} | |
await writeFile(FinalPKG, JSON.stringify(PKG, null, 2)); | |
}; | |
Run().catch((err) => { | |
console.error(err); | |
process.exit(1); | |
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const http = require('http'); | |
const express = require('express'); | |
const RED = require('node-red'); | |
const { exists } = require('fs-extra'); | |
const { join } = require('path'); | |
const Run = async () => { | |
const app = express(); | |
const server = http.createServer(app); | |
/* Port */ | |
const port = 1880; | |
/* Creds Key */ | |
const secret = '#########################'; /* Should be changed to your own */ | |
/* Lock UI */ | |
const disableUI = false; /* Should be set to true, before compiling to an SFE */ | |
/* Think setting.js */ | |
const settings = { | |
flowFile: 'flows.json', | |
flowFilePretty: true, | |
httpAdminRoot: '/', | |
httpNodeRoot: '/', | |
userDir: join(process.cwd(), 'Home'), | |
credentialSecret: secret, | |
disableEditor: disableUI, | |
editorTheme: { | |
projects: { | |
enabled: false | |
}, | |
tours: false | |
} | |
}; | |
/* Redirect to embedded Home Dir - if we embedded the Home folder in the output */ | |
/* Dont mess with this */ | |
if (await exists('/snapshot/NR/build/Home')) { | |
settings.userDir = '/snapshot/NR/build/Home'; | |
} | |
RED.init(server, settings); | |
app.use(settings.httpAdminRoot, RED.httpAdmin); | |
app.use(settings.httpNodeRoot, RED.httpNode); | |
server.listen(port); | |
RED.start(); | |
}; | |
Run(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"name": "node-redex", | |
"dependencies": { | |
"@yao-pkg/pkg": "5.12.0", | |
"esbuild": "0.23.0", | |
"node-red": "4.0.2" | |
}, | |
"scripts": { | |
"design": "node main-source.js", | |
"build": "node build.js", | |
"package": "cd ./build && pkg . --compress gzip -t host --targets node20 --output ./dist/node-red" | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment