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 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 cachedName = `node-${version}-linux-x64`;
110
- const tarballName = `${cachedName}.tar.gz`;
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. This will create the directory `${dir}/node-${version}`
185
- // with the Node.js source tarball contents in it.
189
+ // Streaming unpack or unzip.
186
190
  try {
187
- await Promise.all([
188
- promises.pipeline(tarballStream, zlib__default["default"].createGunzip(), tar__default["default"].x({
189
- cwd: dir,
190
- })),
191
- tarballWritePromise,
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 nodeSourcePath = await nodeUtils.getNodeSourceForVersion(nodeVersion, tmpdir, logger);
260
- const appPath = path__default["default"].join(outputPath, "app");
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
- logger.stepStarting("Run esbuild");
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
- await this.execCommand("npx", [
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 = (_d = this.clean) !== null && _d !== void 0 ? _d : true;
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 cachedName = `node-${version}-linux-x64`;
97
- const tarballName = `${cachedName}.tar.gz`;
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. This will create the directory `${dir}/node-${version}`
172
- // with the Node.js source tarball contents in it.
175
+ // Streaming unpack or unzip.
173
176
  try {
174
- await Promise.all([
175
- pipeline(tarballStream, zlib.createGunzip(), tar.x({
176
- cwd: dir,
177
- })),
178
- tarballWritePromise,
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 nodeSourcePath = await nodeUtils.getNodeSourceForVersion(nodeVersion, tmpdir, logger);
247
- const appPath = path.join(outputPath, "app");
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
- logger.stepStarting("Run esbuild");
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
- await this.execCommand("npx", [
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 = (_d = this.clean) !== null && _d !== void 0 ? _d : true;
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];
@@ -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.1",
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": "sea -s test/sea/config.json",
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",