nodejs-sea 1.0.1 → 1.1.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 +2 -0
- package/lib/cli.js +57 -21
- package/lib/cli.mjs +57 -22
- package/lib/node-utils.d.ts +1 -1
- package/package.json +6 -2
package/README.md
CHANGED
|
@@ -67,7 +67,9 @@ Options:
|
|
|
67
67
|
-s, --sea-config Path of the sea config file [string] [default: "sea/config.json"]
|
|
68
68
|
-n, --node-version Node.js version [string] [default: "*"]
|
|
69
69
|
-c, --clean Remove generated files [boolean] [default: true]
|
|
70
|
+
-p, --platform Target platform (linux, darwin, win32) [string] [default: current platform]
|
|
70
71
|
--help Show help [boolean]
|
|
72
|
+
-e, --env-file Path to .env file [string] [default: .env]
|
|
71
73
|
```
|
|
72
74
|
|
|
73
75
|
## Example
|
package/lib/cli.js
CHANGED
|
@@ -5,6 +5,7 @@ var child_process = require('child_process');
|
|
|
5
5
|
var fs = require('fs');
|
|
6
6
|
var path = require('path');
|
|
7
7
|
var clipanion = require('clipanion');
|
|
8
|
+
var dotenv = require('dotenv');
|
|
8
9
|
var rimraf = require('rimraf');
|
|
9
10
|
var chalk = require('chalk');
|
|
10
11
|
var cliProgress = require('cli-progress');
|
|
@@ -20,6 +21,7 @@ function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'defau
|
|
|
20
21
|
|
|
21
22
|
var fs__default = /*#__PURE__*/_interopDefaultLegacy(fs);
|
|
22
23
|
var path__default = /*#__PURE__*/_interopDefaultLegacy(path);
|
|
24
|
+
var dotenv__default = /*#__PURE__*/_interopDefaultLegacy(dotenv);
|
|
23
25
|
var chalk__default = /*#__PURE__*/_interopDefaultLegacy(chalk);
|
|
24
26
|
var cliProgress__default = /*#__PURE__*/_interopDefaultLegacy(cliProgress);
|
|
25
27
|
var crypto__default = /*#__PURE__*/_interopDefaultLegacy(crypto);
|
|
@@ -68,7 +70,7 @@ class LoggerImpl {
|
|
|
68
70
|
|
|
69
71
|
class NodeUtils {
|
|
70
72
|
// Download and unpack a tarball containing the code for a specific Node.js version.
|
|
71
|
-
async getNodeSourceForVersion(range, dir, logger, retries = 2) {
|
|
73
|
+
async getNodeSourceForVersion(range, dir, logger, platform = "linux", retries = 2) {
|
|
72
74
|
var _a, _b;
|
|
73
75
|
logger.stepStarting(`Looking for Node.js version matching ${JSON.stringify(range)}`);
|
|
74
76
|
let inputIsFileUrl = false;
|
|
@@ -106,10 +108,13 @@ class NodeUtils {
|
|
|
106
108
|
version = `v${ver.version}`;
|
|
107
109
|
releaseBaseUrl = `https://nodejs.org/download/release/${version}`;
|
|
108
110
|
}
|
|
109
|
-
const
|
|
110
|
-
const
|
|
111
|
+
const nodePlatform = platform === "win32" ? "win" : platform;
|
|
112
|
+
const arch = "x64";
|
|
113
|
+
const ext = platform === "win32" ? "zip" : "tar.gz";
|
|
114
|
+
const cachedName = `node-${version}-${nodePlatform}-${arch}`;
|
|
115
|
+
const tarballName = `${cachedName}.${ext}`;
|
|
111
116
|
const cachedTarballPath = path__default["default"].join(dir, tarballName);
|
|
112
|
-
const cachedNodePath = path__default["default"].join(dir, cachedName, "bin", "node");
|
|
117
|
+
const cachedNodePath = path__default["default"].join(dir, cachedName, platform === "win32" ? "node.exe" : path__default["default"].join("bin", "node"));
|
|
113
118
|
let hasCachedTarball = false;
|
|
114
119
|
try {
|
|
115
120
|
hasCachedTarball = fs__default["default"].statSync(cachedTarballPath).size > 0;
|
|
@@ -181,21 +186,28 @@ class NodeUtils {
|
|
|
181
186
|
// unpack below in order not to lose any data.
|
|
182
187
|
tarballWritePromise = promises.pipeline(tarballStream, fs.createWriteStream(cachedTarballPath));
|
|
183
188
|
}
|
|
184
|
-
// Streaming unpack
|
|
185
|
-
// with the Node.js source tarball contents in it.
|
|
189
|
+
// Streaming unpack or unzip.
|
|
186
190
|
try {
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
})
|
|
191
|
-
|
|
192
|
-
|
|
191
|
+
if (ext === "zip") {
|
|
192
|
+
if (tarballWritePromise)
|
|
193
|
+
await tarballWritePromise;
|
|
194
|
+
logger.stepStarting(`Unzipping to ${dir}`);
|
|
195
|
+
child_process.execSync(`unzip -q -o "${cachedTarballPath}" -d "${dir}"`);
|
|
196
|
+
}
|
|
197
|
+
else {
|
|
198
|
+
await Promise.all([
|
|
199
|
+
promises.pipeline(tarballStream, zlib__default["default"].createGunzip(), tar__default["default"].x({
|
|
200
|
+
cwd: dir,
|
|
201
|
+
})),
|
|
202
|
+
tarballWritePromise,
|
|
203
|
+
]);
|
|
204
|
+
}
|
|
193
205
|
}
|
|
194
206
|
catch (err) {
|
|
195
207
|
if (retries > 0) {
|
|
196
208
|
logger.stepFailed(err);
|
|
197
209
|
logger.stepStarting("Re-trying");
|
|
198
|
-
return await this.getNodeSourceForVersion(range, dir, logger, retries - 1);
|
|
210
|
+
return await this.getNodeSourceForVersion(range, dir, logger, platform, retries - 1);
|
|
199
211
|
}
|
|
200
212
|
throw err;
|
|
201
213
|
}
|
|
@@ -212,12 +224,18 @@ class PackCommand extends clipanion.Command {
|
|
|
212
224
|
this.input = clipanion.Option.String(`-s,--sea-config`, {
|
|
213
225
|
description: `Path of the sea config file. Default is sea/config.json`,
|
|
214
226
|
});
|
|
227
|
+
this.envFile = clipanion.Option.String(`-e,--env-file`, {
|
|
228
|
+
description: `Path to .env file. Default is .env`,
|
|
229
|
+
});
|
|
215
230
|
this.nodeVersion = clipanion.Option.String(`-n,--node-version`, {
|
|
216
231
|
description: `Node.js version. Default is 22.11.0`,
|
|
217
232
|
});
|
|
218
233
|
this.clean = clipanion.Option.String(`-c,--clean`, {
|
|
219
234
|
description: `Remove generated files. Default is true`,
|
|
220
235
|
});
|
|
236
|
+
this.platform = clipanion.Option.String(`-p,--platform`, {
|
|
237
|
+
description: `Target platform (linux, darwin, win32). Default is current platform`,
|
|
238
|
+
});
|
|
221
239
|
}
|
|
222
240
|
/**
|
|
223
241
|
* run command
|
|
@@ -238,7 +256,7 @@ class PackCommand extends clipanion.Command {
|
|
|
238
256
|
throw new clipanion.UsageError(`Command failed`);
|
|
239
257
|
}
|
|
240
258
|
async execute() {
|
|
241
|
-
var _a, _b, _c, _d;
|
|
259
|
+
var _a, _b, _c, _d, _e, _f;
|
|
242
260
|
const logger = new LoggerImpl();
|
|
243
261
|
const nodeUtils = new NodeUtils();
|
|
244
262
|
const tmpdir = path__default["default"].join(currentWorkingDirectory, "node_modules/.cache/nodejs-sea");
|
|
@@ -256,8 +274,10 @@ class PackCommand extends clipanion.Command {
|
|
|
256
274
|
const outputPath = config.output.split("/").slice(0, -1).join("/");
|
|
257
275
|
const copyFiles = (_c = config.copyFiles) !== null && _c !== void 0 ? _c : [];
|
|
258
276
|
const esbuildConfig = config.esbuild;
|
|
259
|
-
const
|
|
260
|
-
const
|
|
277
|
+
const platform = (_d = this.platform) !== null && _d !== void 0 ? _d : process.platform;
|
|
278
|
+
const nodeSourcePath = await nodeUtils.getNodeSourceForVersion(nodeVersion, tmpdir, logger, platform);
|
|
279
|
+
const executableName = platform === "win32" ? "app.exe" : "app";
|
|
280
|
+
const appPath = path__default["default"].join(outputPath, executableName);
|
|
261
281
|
logger.stepStarting("Cleaning dist directory");
|
|
262
282
|
await rimraf.rimraf(outputPath, { glob: false });
|
|
263
283
|
fs__default["default"].mkdirSync(outputPath, { recursive: true });
|
|
@@ -270,24 +290,40 @@ class PackCommand extends clipanion.Command {
|
|
|
270
290
|
logger.stepCompleted();
|
|
271
291
|
}
|
|
272
292
|
if (esbuildConfig) {
|
|
273
|
-
|
|
293
|
+
// Load env file
|
|
294
|
+
const envFile = (_e = this.envFile) !== null && _e !== void 0 ? _e : ".env";
|
|
295
|
+
const envPath = path__default["default"].resolve(currentWorkingDirectory, envFile);
|
|
296
|
+
if (fs__default["default"].existsSync(envPath)) {
|
|
297
|
+
logger.stepStarting("Run esbuild - with env");
|
|
298
|
+
dotenv__default["default"].config({ path: envPath });
|
|
299
|
+
esbuildConfig.define = {
|
|
300
|
+
...esbuildConfig.define,
|
|
301
|
+
...Object.fromEntries(Object.entries(process.env).map(([key, value]) => [`process.env.${key}`, JSON.stringify(value)])),
|
|
302
|
+
};
|
|
303
|
+
}
|
|
304
|
+
else {
|
|
305
|
+
logger.stepStarting("Run esbuild - without env");
|
|
306
|
+
}
|
|
274
307
|
await esbuild.build(esbuildConfig);
|
|
275
308
|
logger.stepCompleted();
|
|
276
309
|
}
|
|
277
310
|
logger.stepStarting("Inject to NodeJS Single Execute Application");
|
|
278
311
|
await this.execCommand("node", ["--experimental-sea-config", configFilePath]);
|
|
279
312
|
await this.execCommand("cp", [nodeSourcePath, appPath]);
|
|
280
|
-
|
|
281
|
-
"postject",
|
|
313
|
+
const postjectCommand = [
|
|
282
314
|
appPath,
|
|
283
315
|
"NODE_SEA_BLOB",
|
|
284
316
|
config.output,
|
|
285
317
|
"--sentinel-fuse",
|
|
286
318
|
"NODE_SEA_FUSE_fce680ab2cc467b6e072b8b5df1996b2",
|
|
287
|
-
]
|
|
319
|
+
];
|
|
320
|
+
if (platform === "darwin") {
|
|
321
|
+
postjectCommand.push("--macho-segment-name", "NODE_SEA");
|
|
322
|
+
}
|
|
323
|
+
await this.execCommand("postject", postjectCommand);
|
|
288
324
|
logger.stepCompleted();
|
|
289
325
|
// clear temp file
|
|
290
|
-
const clean = (
|
|
326
|
+
const clean = (_f = this.clean) !== null && _f !== void 0 ? _f : true;
|
|
291
327
|
if (clean) {
|
|
292
328
|
logger.stepStarting("Remove generated files");
|
|
293
329
|
const cleanFiles = [config.output];
|
package/lib/cli.mjs
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { spawn } from 'child_process';
|
|
2
|
+
import { execSync, spawn } from 'child_process';
|
|
3
3
|
import fs, { createReadStream, createWriteStream } from 'fs';
|
|
4
4
|
import path from 'path';
|
|
5
5
|
import { runExit, Command, Option, UsageError } from 'clipanion';
|
|
6
|
+
import dotenv from 'dotenv';
|
|
6
7
|
import { rimraf } from 'rimraf';
|
|
7
8
|
import chalk from 'chalk';
|
|
8
9
|
import cliProgress from 'cli-progress';
|
|
@@ -55,7 +56,7 @@ class LoggerImpl {
|
|
|
55
56
|
|
|
56
57
|
class NodeUtils {
|
|
57
58
|
// Download and unpack a tarball containing the code for a specific Node.js version.
|
|
58
|
-
async getNodeSourceForVersion(range, dir, logger, retries = 2) {
|
|
59
|
+
async getNodeSourceForVersion(range, dir, logger, platform = "linux", retries = 2) {
|
|
59
60
|
var _a, _b;
|
|
60
61
|
logger.stepStarting(`Looking for Node.js version matching ${JSON.stringify(range)}`);
|
|
61
62
|
let inputIsFileUrl = false;
|
|
@@ -93,10 +94,13 @@ class NodeUtils {
|
|
|
93
94
|
version = `v${ver.version}`;
|
|
94
95
|
releaseBaseUrl = `https://nodejs.org/download/release/${version}`;
|
|
95
96
|
}
|
|
96
|
-
const
|
|
97
|
-
const
|
|
97
|
+
const nodePlatform = platform === "win32" ? "win" : platform;
|
|
98
|
+
const arch = "x64";
|
|
99
|
+
const ext = platform === "win32" ? "zip" : "tar.gz";
|
|
100
|
+
const cachedName = `node-${version}-${nodePlatform}-${arch}`;
|
|
101
|
+
const tarballName = `${cachedName}.${ext}`;
|
|
98
102
|
const cachedTarballPath = path.join(dir, tarballName);
|
|
99
|
-
const cachedNodePath = path.join(dir, cachedName, "bin", "node");
|
|
103
|
+
const cachedNodePath = path.join(dir, cachedName, platform === "win32" ? "node.exe" : path.join("bin", "node"));
|
|
100
104
|
let hasCachedTarball = false;
|
|
101
105
|
try {
|
|
102
106
|
hasCachedTarball = fs.statSync(cachedTarballPath).size > 0;
|
|
@@ -168,21 +172,28 @@ class NodeUtils {
|
|
|
168
172
|
// unpack below in order not to lose any data.
|
|
169
173
|
tarballWritePromise = pipeline(tarballStream, createWriteStream(cachedTarballPath));
|
|
170
174
|
}
|
|
171
|
-
// Streaming unpack
|
|
172
|
-
// with the Node.js source tarball contents in it.
|
|
175
|
+
// Streaming unpack or unzip.
|
|
173
176
|
try {
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
})
|
|
178
|
-
|
|
179
|
-
|
|
177
|
+
if (ext === "zip") {
|
|
178
|
+
if (tarballWritePromise)
|
|
179
|
+
await tarballWritePromise;
|
|
180
|
+
logger.stepStarting(`Unzipping to ${dir}`);
|
|
181
|
+
execSync(`unzip -q -o "${cachedTarballPath}" -d "${dir}"`);
|
|
182
|
+
}
|
|
183
|
+
else {
|
|
184
|
+
await Promise.all([
|
|
185
|
+
pipeline(tarballStream, zlib.createGunzip(), tar.x({
|
|
186
|
+
cwd: dir,
|
|
187
|
+
})),
|
|
188
|
+
tarballWritePromise,
|
|
189
|
+
]);
|
|
190
|
+
}
|
|
180
191
|
}
|
|
181
192
|
catch (err) {
|
|
182
193
|
if (retries > 0) {
|
|
183
194
|
logger.stepFailed(err);
|
|
184
195
|
logger.stepStarting("Re-trying");
|
|
185
|
-
return await this.getNodeSourceForVersion(range, dir, logger, retries - 1);
|
|
196
|
+
return await this.getNodeSourceForVersion(range, dir, logger, platform, retries - 1);
|
|
186
197
|
}
|
|
187
198
|
throw err;
|
|
188
199
|
}
|
|
@@ -199,12 +210,18 @@ class PackCommand extends Command {
|
|
|
199
210
|
this.input = Option.String(`-s,--sea-config`, {
|
|
200
211
|
description: `Path of the sea config file. Default is sea/config.json`,
|
|
201
212
|
});
|
|
213
|
+
this.envFile = Option.String(`-e,--env-file`, {
|
|
214
|
+
description: `Path to .env file. Default is .env`,
|
|
215
|
+
});
|
|
202
216
|
this.nodeVersion = Option.String(`-n,--node-version`, {
|
|
203
217
|
description: `Node.js version. Default is 22.11.0`,
|
|
204
218
|
});
|
|
205
219
|
this.clean = Option.String(`-c,--clean`, {
|
|
206
220
|
description: `Remove generated files. Default is true`,
|
|
207
221
|
});
|
|
222
|
+
this.platform = Option.String(`-p,--platform`, {
|
|
223
|
+
description: `Target platform (linux, darwin, win32). Default is current platform`,
|
|
224
|
+
});
|
|
208
225
|
}
|
|
209
226
|
/**
|
|
210
227
|
* run command
|
|
@@ -225,7 +242,7 @@ class PackCommand extends Command {
|
|
|
225
242
|
throw new UsageError(`Command failed`);
|
|
226
243
|
}
|
|
227
244
|
async execute() {
|
|
228
|
-
var _a, _b, _c, _d;
|
|
245
|
+
var _a, _b, _c, _d, _e, _f;
|
|
229
246
|
const logger = new LoggerImpl();
|
|
230
247
|
const nodeUtils = new NodeUtils();
|
|
231
248
|
const tmpdir = path.join(currentWorkingDirectory, "node_modules/.cache/nodejs-sea");
|
|
@@ -243,8 +260,10 @@ class PackCommand extends Command {
|
|
|
243
260
|
const outputPath = config.output.split("/").slice(0, -1).join("/");
|
|
244
261
|
const copyFiles = (_c = config.copyFiles) !== null && _c !== void 0 ? _c : [];
|
|
245
262
|
const esbuildConfig = config.esbuild;
|
|
246
|
-
const
|
|
247
|
-
const
|
|
263
|
+
const platform = (_d = this.platform) !== null && _d !== void 0 ? _d : process.platform;
|
|
264
|
+
const nodeSourcePath = await nodeUtils.getNodeSourceForVersion(nodeVersion, tmpdir, logger, platform);
|
|
265
|
+
const executableName = platform === "win32" ? "app.exe" : "app";
|
|
266
|
+
const appPath = path.join(outputPath, executableName);
|
|
248
267
|
logger.stepStarting("Cleaning dist directory");
|
|
249
268
|
await rimraf(outputPath, { glob: false });
|
|
250
269
|
fs.mkdirSync(outputPath, { recursive: true });
|
|
@@ -257,24 +276,40 @@ class PackCommand extends Command {
|
|
|
257
276
|
logger.stepCompleted();
|
|
258
277
|
}
|
|
259
278
|
if (esbuildConfig) {
|
|
260
|
-
|
|
279
|
+
// Load env file
|
|
280
|
+
const envFile = (_e = this.envFile) !== null && _e !== void 0 ? _e : ".env";
|
|
281
|
+
const envPath = path.resolve(currentWorkingDirectory, envFile);
|
|
282
|
+
if (fs.existsSync(envPath)) {
|
|
283
|
+
logger.stepStarting("Run esbuild - with env");
|
|
284
|
+
dotenv.config({ path: envPath });
|
|
285
|
+
esbuildConfig.define = {
|
|
286
|
+
...esbuildConfig.define,
|
|
287
|
+
...Object.fromEntries(Object.entries(process.env).map(([key, value]) => [`process.env.${key}`, JSON.stringify(value)])),
|
|
288
|
+
};
|
|
289
|
+
}
|
|
290
|
+
else {
|
|
291
|
+
logger.stepStarting("Run esbuild - without env");
|
|
292
|
+
}
|
|
261
293
|
await esbuild.build(esbuildConfig);
|
|
262
294
|
logger.stepCompleted();
|
|
263
295
|
}
|
|
264
296
|
logger.stepStarting("Inject to NodeJS Single Execute Application");
|
|
265
297
|
await this.execCommand("node", ["--experimental-sea-config", configFilePath]);
|
|
266
298
|
await this.execCommand("cp", [nodeSourcePath, appPath]);
|
|
267
|
-
|
|
268
|
-
"postject",
|
|
299
|
+
const postjectCommand = [
|
|
269
300
|
appPath,
|
|
270
301
|
"NODE_SEA_BLOB",
|
|
271
302
|
config.output,
|
|
272
303
|
"--sentinel-fuse",
|
|
273
304
|
"NODE_SEA_FUSE_fce680ab2cc467b6e072b8b5df1996b2",
|
|
274
|
-
]
|
|
305
|
+
];
|
|
306
|
+
if (platform === "darwin") {
|
|
307
|
+
postjectCommand.push("--macho-segment-name", "NODE_SEA");
|
|
308
|
+
}
|
|
309
|
+
await this.execCommand("postject", postjectCommand);
|
|
275
310
|
logger.stepCompleted();
|
|
276
311
|
// clear temp file
|
|
277
|
-
const clean = (
|
|
312
|
+
const clean = (_f = this.clean) !== null && _f !== void 0 ? _f : true;
|
|
278
313
|
if (clean) {
|
|
279
314
|
logger.stepStarting("Remove generated files");
|
|
280
315
|
const cleanFiles = [config.output];
|
package/lib/node-utils.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import { Logger } from "./logger";
|
|
2
2
|
export declare class NodeUtils {
|
|
3
|
-
getNodeSourceForVersion(range: string, dir: string, logger: Logger, retries?: number): Promise<string>;
|
|
3
|
+
getNodeSourceForVersion(range: string, dir: string, logger: Logger, platform?: string, retries?: number): Promise<string>;
|
|
4
4
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nodejs-sea",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "A powerful package for NodeJS single executable applications (SEA), support good for NestJS framework",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"license": "MIT",
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
"precommit": "lint-staged",
|
|
19
19
|
"prepare": "husky",
|
|
20
20
|
"prepack": "rm -rf lib && rollup -c",
|
|
21
|
-
"test": "
|
|
21
|
+
"build:test": "node lib/cli.js -s test/sea/config.json -e test/.env",
|
|
22
22
|
"standard-version": "standard-version",
|
|
23
23
|
"build": "rollup -c"
|
|
24
24
|
},
|
|
@@ -29,6 +29,7 @@
|
|
|
29
29
|
"@rollup/plugin-commonjs": "^22.0.2",
|
|
30
30
|
"@rollup/plugin-node-resolve": "^14.1.0",
|
|
31
31
|
"@rollup/plugin-typescript": "^8.5.0",
|
|
32
|
+
"@types/adm-zip": "^0.5.7",
|
|
32
33
|
"@types/cli-progress": "^3.11.6",
|
|
33
34
|
"@types/node": "^18.7.15",
|
|
34
35
|
"@types/tar": "^6.1.2",
|
|
@@ -55,8 +56,11 @@
|
|
|
55
56
|
},
|
|
56
57
|
"dependencies": {
|
|
57
58
|
"@pkgjs/nv": "^0.2.2",
|
|
59
|
+
"adm-zip": "^0.5.16",
|
|
60
|
+
"chalk": "^5.6.2",
|
|
58
61
|
"cli-progress": "^3.12.0",
|
|
59
62
|
"clipanion": "^3.2.0-rc.12",
|
|
63
|
+
"dotenv": "^17.2.3",
|
|
60
64
|
"esbuild": "^0.24.0",
|
|
61
65
|
"postject": "^1.0.0-alpha.6",
|
|
62
66
|
"rimraf": "^6.0.1",
|