nexfpack 0.1.0 → 0.1.2

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/LICENSE CHANGED
@@ -1,21 +1,21 @@
1
- MIT License
2
-
3
- Copyright (c) 2026 Nexfteam
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Nexfteam
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
21
  SOFTWARE.
package/README-CN.md CHANGED
@@ -1,99 +1,99 @@
1
- # Nexfpack
2
-
3
- 语言: [English](https://github.com/nexfteam/Nexfpack/blob/main/README.md) | 简体中文(当前)
4
-
5
- 一个用于将Node.js脚本或模块打包为单个可执行文件的工具。
6
-
7
- ## 安装
8
-
9
- ```bash
10
- npm install -g nexfpack
11
- # or
12
- pnpm add -g nexfpack
13
- # or
14
- yarn global add nexfpack
15
- ```
16
-
17
- ## 快速开始
18
-
19
- ### CLI命令
20
-
21
- 创建 `nexfpack.config.json` 配置文件并运行以下CLI命令:
22
-
23
- ```bash
24
- nexfpack nexfpack.config.json
25
- ```
26
-
27
- ### CommonJS API
28
-
29
- ```javascript
30
- const nexfpack = require('nexfpack');
31
-
32
- const config = {
33
- "name": "my-app",
34
- "entry": "index.js",
35
- "output": "dist",
36
- "ignore": [".gitignore", "README.md"],
37
- "autoRun": true
38
- };
39
-
40
- nexfpack(config);
41
-
42
- // 或者使用配置文件
43
- // nexfpack({configFile: 'nexfpack.config.json'})
44
- ```
45
-
46
- ### ESM API
47
-
48
- ```javascript
49
- import nexfpack from 'nexfpack';
50
-
51
- const config = {
52
- "name": "my-app",
53
- "entry": "index.js",
54
- "output": "dist",
55
- "ignore": [".gitignore", "README.md"],
56
- "autoRun": true
57
- };
58
-
59
- nexfpack(config);
60
-
61
- // 或者使用配置文件
62
- // nexfpack({configFile: 'nexfpack.config.json'})
63
- ```
64
-
65
- ### 配置文件示例
66
-
67
- ```json
68
- {
69
- "name": "my-app",
70
- "entry": "index.js",
71
- "output": "dist",
72
- "ignore": [".gitignore", "README.md"],
73
- "autoRun": true
74
- }
75
- ```
76
-
77
- ## 配置项
78
-
79
- | 名称 | 类型 | 描述 | 默认值 |
80
- | --- | --- | --- | --- |
81
- | name | string | 应用名称 | `nexfpack-app` |
82
- | root | string | 根目录 | 若使用配置文件,则为文件所在目录,否则为 `process.cwd()` |
83
- | relativeRoot | boolean | `root` 是否为相对路径 | `false` |
84
- | entry | string | 入口文件路径 | `index.js` |
85
- | output | string | 输出目录 | `dist` |
86
- | tempdir | string | 打包过程中的临时目录 | `.nexfpack-temp` |
87
- | autoDeleteTempFiles | boolean | 是否自动删除临时文件 | `true` |
88
- | ignorefile | string | 忽略文件路径,优先级高于 `ignore` | `.nexfpackignore` |
89
- | ignore | array | 忽略的文件或目录,支持 glob 语法 | `[]` |
90
- | enabledSign | boolean | 是否启用签名(暂不支持) | `false` |
91
- | autoRun | boolean | 是否自动运行打包后的可执行文件 | `false` |
92
-
93
- ## 环境要求
94
-
95
- - Node.js >= 20.12.0
96
-
97
- ## License
98
-
99
- MIT
1
+ # Nexfpack
2
+
3
+ 语言: [English](https://github.com/nexfteam/Nexfpack/blob/main/README.md) | 简体中文(当前)
4
+
5
+ 一个用于将Node.js脚本或模块打包为单个可执行文件的工具。
6
+
7
+ ## 安装
8
+
9
+ ```bash
10
+ npm install -g nexfpack
11
+ # or
12
+ pnpm add -g nexfpack
13
+ # or
14
+ yarn global add nexfpack
15
+ ```
16
+
17
+ ## 快速开始
18
+
19
+ ### CLI命令
20
+
21
+ 创建 `nexfpack.config.json` 配置文件并运行以下CLI命令:
22
+
23
+ ```bash
24
+ nexfpack nexfpack.config.json
25
+ ```
26
+
27
+ ### CommonJS API
28
+
29
+ ```javascript
30
+ const nexfpack = require('nexfpack');
31
+
32
+ const config = {
33
+ "name": "my-app",
34
+ "entry": "index.js",
35
+ "output": "dist",
36
+ "ignore": [".gitignore", "README.md"],
37
+ "autoRun": true
38
+ };
39
+
40
+ nexfpack(config);
41
+
42
+ // 或者使用配置文件
43
+ // nexfpack({configFile: 'nexfpack.config.json'})
44
+ ```
45
+
46
+ ### ESM API
47
+
48
+ ```javascript
49
+ import nexfpack from 'nexfpack';
50
+
51
+ const config = {
52
+ "name": "my-app",
53
+ "entry": "index.js",
54
+ "output": "dist",
55
+ "ignore": [".gitignore", "README.md"],
56
+ "autoRun": true
57
+ };
58
+
59
+ nexfpack(config);
60
+
61
+ // 或者使用配置文件
62
+ // nexfpack({configFile: 'nexfpack.config.json'})
63
+ ```
64
+
65
+ ### 配置文件示例
66
+
67
+ ```json
68
+ {
69
+ "name": "my-app",
70
+ "entry": "index.js",
71
+ "output": "dist",
72
+ "ignore": [".gitignore", "README.md"],
73
+ "autoRun": true
74
+ }
75
+ ```
76
+
77
+ ## 配置项
78
+
79
+ | 名称 | 类型 | 描述 | 默认值 |
80
+ | --- | --- | --- | --- |
81
+ | name | string | 应用名称 | `nexfpack-app` |
82
+ | root | string | 根目录 | 若使用配置文件,则为文件所在目录,否则为 `process.cwd()` |
83
+ | relativeRoot | boolean | `root` 是否为相对路径 | `false` |
84
+ | entry | string | 入口文件路径 | `index.js` |
85
+ | output | string | 输出目录 | `dist` |
86
+ | tempdir | string | 打包过程中的临时目录 | `.nexfpack-temp` |
87
+ | autoDeleteTempFiles | boolean | 是否自动删除临时文件 | `true` |
88
+ | ignorefile | string | 忽略文件路径,优先级高于 `ignore` | `.nexfpackignore` |
89
+ | ignore | array | 忽略的文件或目录,支持 glob 语法 | `[]` |
90
+ | enabledSign | boolean | 是否启用签名(暂不支持) | `false` |
91
+ | autoRun | boolean | 是否自动运行打包后的可执行文件 | `false` |
92
+
93
+ ## 环境要求
94
+
95
+ - Node.js >= 20.12.0
96
+
97
+ ## License
98
+
99
+ MIT
package/cli.mjs CHANGED
@@ -1,13 +1,13 @@
1
- #!/usr/bin/env node
2
- import nexfpack from "./index.mjs";
3
-
4
- const args = process.argv.slice(2);
5
- if(args.length === 0) {
6
- console.log("Usage: nexfpack <config-file-path>");
7
- process.exit(1);
8
- }
9
- const configFilePath = args[0];
10
-
11
- nexfpack({
12
- configFile: configFilePath,
1
+ #!/usr/bin/env node
2
+ import nexfpack from "./index.mjs";
3
+
4
+ const args = process.argv.slice(2);
5
+ if(args.length === 0) {
6
+ console.log("Usage: nexfpack <config-file-path>");
7
+ process.exit(1);
8
+ }
9
+ const configFilePath = args[0];
10
+
11
+ nexfpack({
12
+ configFile: configFilePath,
13
13
  });
package/index.d.cts ADDED
@@ -0,0 +1 @@
1
+ export {};
package/index.d.mts ADDED
@@ -0,0 +1,17 @@
1
+ interface NexfpackOptions {
2
+ name?: string;
3
+ root?: string;
4
+ relativeRoot?: boolean;
5
+ entry?: string;
6
+ output?: string;
7
+ tempdir?: string;
8
+ autoDeleteTempFiles?: boolean;
9
+ ignorefile?: string;
10
+ ignore?: string[];
11
+ enabledSign?: boolean;
12
+ autoRun?: boolean;
13
+ configFile?: string;
14
+ }
15
+ declare function nexfpack(options: NexfpackOptions): Promise<void>;
16
+ export default nexfpack;
17
+ export { NexfpackOptions, nexfpack };
@@ -1,278 +1,247 @@
1
- import copyfiles from 'copyfiles'
2
- import fs from 'fs'
3
- import path from 'path'
4
- import os from 'os'
5
- import child_process from 'child_process'
6
- import { pipeline } from 'stream/promises'
7
- import { packTar } from 'modern-tar/fs'
8
- // @ts-ignore
9
- import { inject } from 'postject';
10
- interface NexfpackOptions {
11
- name?: string,
12
- root?: string,
13
- relativeRoot?: boolean,
14
- entry?: string,
15
- output?: string,
16
- tempdir?: string,
17
- autoDeleteTempFiles?: boolean,
18
- ignorefile?: string,
19
- ignore?: string[],
20
- enabledSign?: boolean,
21
- autoRun?: boolean,
22
- configFile?: string,
23
- }
24
-
25
- interface NexfpackOptionsFilled {
26
- name: string,
27
- root: string,
28
- entry: string,
29
- output: string,
30
- tempdir: string,
31
- autoDeleteTempFiles: boolean,
32
- ignore: string[],
33
- enabledSign: boolean,
34
- autoRun: boolean,
35
- }
36
-
37
- async function fillOptions(options: NexfpackOptions): Promise<NexfpackOptionsFilled> {
38
- let cwd: string;
39
- if (options.root && !options.relativeRoot) {
40
- cwd = options.root;
41
- } else if (options.configFile) {
42
- cwd = path.dirname(options.configFile);
43
- } else {
44
- cwd = process.cwd();
45
- }
46
- if (options.root && options.relativeRoot) {
47
- cwd = path.resolve(cwd, options.root);
48
- }
49
- function getIgnores() {
50
- if (options.ignorefile) {
51
- return fs.readFileSync(path.resolve(cwd, options.ignorefile), 'utf8').split('\n').map(line => {
52
- return line.split('#')[0].trim();
53
- }).filter(line => line !== '');
54
- } else if (options.ignore) {
55
- return options.ignore;
56
- } else if (fs.existsSync(path.resolve(cwd, '.nexfpackignore'))) {
57
- return fs.readFileSync(path.resolve(cwd, '.nexfpackignore'), 'utf8').split('\n').map(line => {
58
- return line.split('#')[0].trim();
59
- }).filter(line => line !== '');
60
- } else {
61
- return [];
62
- }
63
- }
64
- return {
65
- name: options.name ?? 'nexfpack-app',
66
- root: cwd,
67
- entry: path.resolve(cwd, options.entry ?? 'index.js'),
68
- output: path.resolve(cwd, options.output ?? 'dist'),
69
- tempdir: path.resolve(cwd, options.tempdir ?? '.nexfpack-temp'),
70
- autoDeleteTempFiles: options.autoDeleteTempFiles ?? true,
71
- ignore: getIgnores(),
72
- enabledSign: options.enabledSign ?? false,
73
- autoRun: options.autoRun ?? false,
74
- }
75
- }
76
-
77
- function listAllFiles(dir: string): string[] {
78
- const result: string[] = [];
79
- const entries = fs.readdirSync(dir);
80
-
81
- for (const entry of entries) {
82
- const fullPath = path.join(dir, entry);
83
- const stat = fs.statSync(fullPath);
84
- if (stat.isDirectory()) {
85
- result.push(...listAllFiles(fullPath));
86
- } else {
87
- result.push(fullPath);
88
- }
89
- }
90
- return result;
91
- }
92
-
93
- async function nexfpack(options: NexfpackOptions) {
94
- try {
95
- console.log(
96
- `
97
- __ _ __ _
98
- / \\ / /\\_____\\ \\ / /
99
- / /\\ \\ / / / ____/\\ \\/ /
100
- / / /\\ \\/ / / /____\\/\\ /_____
101
- /_/ / \\__/ / ____/\\ / \\ ___\\
102
- \\_\\/ \\_\\/ /____\\/ / /\\ \\ \\__/
103
- /_____/\\ /_/ \\_\\ __\\
104
- \\_____\\/ \\ \\ \\_/
105
- \\ \\_\\
106
- _______ _____ \\/_/__
107
- | ___ | /\\ | ___| | | / /
108
- | |___| | / \\ | | | |/ /
109
- | _____| / /\\ \\ | | | |
110
- | | / ____ \\ | |___ | |\\ \\
111
- |_| /_/ \\_\\ |_____| |_| \\_\\
112
-
113
- `)
114
- console.log('▶️ Start packing...');
115
- console.log('🧪 Tips: Nexfpack is very experimental. It may have some errors.')
116
- let config: NexfpackOptions;
117
- if (options.configFile) {
118
- config = JSON.parse(fs.readFileSync(options.configFile, 'utf8')) as NexfpackOptions;
119
- } else {
120
- config = options;
121
- }
122
- const filledConfig = await fillOptions(config);
123
-
124
- if (!fs.existsSync(filledConfig.tempdir)) {
125
- fs.mkdirSync(filledConfig.tempdir, { recursive: true });
126
- } else {
127
- fs.rmSync(filledConfig.tempdir, { recursive: true, force: true });
128
- fs.mkdirSync(filledConfig.tempdir, { recursive: true });
129
- }
130
-
131
- console.log('📄 Copying files...');
132
-
133
- if (!fs.existsSync(path.join(filledConfig.tempdir, 'source-copy'))) {
134
- fs.mkdirSync(path.join(filledConfig.tempdir, 'source-copy'), { recursive: true });
135
- }
136
-
137
- await new Promise<void>((resolve, reject) => {
138
- copyfiles([filledConfig.root, path.join(filledConfig.tempdir, 'source-copy')], { up: 1, exclude: filledConfig.ignore }, (err) => {
139
- if (err) {
140
- console.error('❌ Failed to copy files:', err);
141
- reject(err);
142
- } else {
143
- resolve();
144
- }
145
- })
146
- })
147
-
148
- console.log('📜 Packing source...');
149
-
150
- const tarStream = packTar(path.join(filledConfig.tempdir, 'source-copy'));
151
- const writeStream = fs.createWriteStream(path.join(filledConfig.tempdir, 'source.tar'));
152
- await pipeline(tarStream, writeStream);
153
-
154
- console.log('📦 Packing executable...');
155
- fs.writeFileSync(path.join(filledConfig.tempdir, 'package.json'), JSON.stringify({
156
- name: filledConfig.name,
157
- version: '1.0.0',
158
- type: 'commonjs',
159
- main: 'launcher.cjs',
160
- dependencies: {
161
- "modern-tar": "^0.7.6"
162
- }
163
- }, null, 2));
164
- const installSpawnResult = child_process.spawnSync('npm install --omit=dev', { stdio: 'inherit', cwd: filledConfig.tempdir, shell: true });
165
- if (installSpawnResult.status !== 0) {
166
- throw new Error('❌ Failed to install dependencies');
167
- }
168
-
169
- const allNodeModulesFiles = listAllFiles(path.join(filledConfig.tempdir, 'node_modules'));
170
- const seaConfigContent: any =
171
- {
172
- "main": "./launcher.cjs",
173
- "output": "./sea-prep.blob",
174
- "disableExperimentalSEAWarning": true,
175
- "assets": {
176
- "source.tar": "./source.tar",
177
- }
178
- };
179
- for (const file of allNodeModulesFiles) {
180
- const relativePath = path.relative(path.join(filledConfig.tempdir, 'node_modules'), file);
181
- seaConfigContent.assets[`node_modules/${relativePath}`] = file;
182
- }
183
- fs.writeFileSync(path.join(filledConfig.tempdir, 'sea-config.json'), JSON.stringify(seaConfigContent));
184
- const launcherContent = `
185
- (async () => {
186
- const fs = require('fs');
187
- const path = require('path');
188
- const os = require('os');
189
- const { pathToFileURL } = require('url');
190
- let sea;
191
- try {
192
- sea = require('node:sea');
193
- } catch (e1) {
194
- try {
195
- sea = process.getBuiltinModule('sea');
196
- } catch (e2) {
197
- try {
198
- sea = globalThis.process?.getBuiltinModule?.('sea');
199
- } catch (e3) {
200
- sea = null;
201
- }
202
- }
203
- }
204
-
205
- const tempDir = path.join(os.tmpdir(), 'TEMP-NEXFPACK-' + Date.now() + '-' + Math.random().toString(36).substring(2));
206
- fs.mkdirSync(tempDir, { recursive: true });
207
-
208
- try {
209
- const keys = ${JSON.stringify(Object.keys(seaConfigContent.assets).filter(key => key !== 'source.tar'))};
210
- for (const key of keys) {
211
- const data = sea.getRawAsset(key);
212
- const filePath = path.join(tempDir, 'LANCHER', key);
213
- fs.mkdirSync(path.dirname(filePath), { recursive: true });
214
- fs.writeFileSync(filePath, Buffer.from(data));
215
- }
216
-
217
- const tarData = sea.getRawAsset('source.tar');
218
-
219
- const tarFile = path.join(tempDir, 'source.tar');
220
- fs.writeFileSync(tarFile, Buffer.from(tarData));
221
-
222
- const { unpackTar } = await import(pathToFileURL(path.join(tempDir, 'LANCHER', 'node_modules', 'modern-tar', 'dist', 'fs', 'index.js')));
223
- const extractStream = unpackTar(tempDir);
224
- const tarReadStream = fs.createReadStream(tarFile);
225
- const { pipeline } = require('stream/promises');
226
- await pipeline(tarReadStream, extractStream);
227
-
228
- const entry = path.resolve(tempDir, ${JSON.stringify(filledConfig.entry)});
229
- try {
230
- require(entry);
231
- } catch {
232
- await import(pathToFileURL(entry));
233
- }
234
- } catch (err) {
235
- console.error(err);
236
- }
237
- fs.rmSync(tempDir, { recursive: true, force: true });
238
- })();
239
- `;
240
- fs.writeFileSync(path.join(filledConfig.tempdir, 'launcher.cjs'), launcherContent);
241
- const blobSpawnResult = child_process.spawnSync('node --experimental-sea-config sea-config.json', { stdio: 'inherit', cwd: filledConfig.tempdir, shell: true });
242
- if (blobSpawnResult.status !== 0) {
243
- throw new Error('❌ Failed to generate blob');
244
- }
245
- const NODE_SEA_FUSE = 'NODE_SEA_FUSE_fce680ab2cc467b6e072b8b5df1996b2';
246
- const blobData = fs.readFileSync(path.join(filledConfig.tempdir, 'sea-prep.blob'));
247
- const injectOptions: any = {
248
- sentinelFuse: NODE_SEA_FUSE,
249
- };
250
- if (os.platform() === 'darwin') {
251
- injectOptions.machoSegmentName = 'NODE_SEA';
252
- }
253
- const ext = os.platform() === 'win32' ? '.exe' : '';
254
- const exePath = path.join(filledConfig.output, filledConfig.name + ext);
255
- if (!fs.existsSync(filledConfig.output)) {
256
- fs.mkdirSync(filledConfig.output, { recursive: true });
257
- }
258
- fs.copyFileSync(process.execPath, exePath);
259
- await inject(exePath, 'NODE_SEA_BLOB', blobData, injectOptions);
260
- if (filledConfig.enabledSign) {
261
- console.log("⚠️ Sorry, we can't sign your executable file. Please sign it by yourself.");
262
- }
263
- if (filledConfig.autoDeleteTempFiles) {
264
- console.log('♻️ Deleting temp files...');
265
- fs.rmSync(filledConfig.tempdir, { recursive: true, force: true });
266
- }
267
- console.log('✅ Done!');
268
- if (filledConfig.autoRun) {
269
- console.log('🚀 Auto-run executable...');
270
- child_process.spawnSync(exePath, { stdio: 'inherit', cwd: filledConfig.output, shell: true });
271
- }
272
- } catch (err) {
273
- console.error('❌ Nexfpack Failed:', err);
274
- }
275
- }
276
-
277
- export default nexfpack
278
- export { NexfpackOptions, nexfpack }
1
+ import copyfiles from 'copyfiles';
2
+ import fs from 'fs';
3
+ import path from 'path';
4
+ import os from 'os';
5
+ import child_process from 'child_process';
6
+ import { pipeline } from 'stream/promises';
7
+ import { packTar } from 'modern-tar/fs';
8
+ // @ts-ignore
9
+ import { inject } from 'postject';
10
+ async function fillOptions(options) {
11
+ let cwd;
12
+ if (options.root && !options.relativeRoot) {
13
+ cwd = options.root;
14
+ }
15
+ else if (options.configFile) {
16
+ cwd = path.dirname(options.configFile);
17
+ }
18
+ else {
19
+ cwd = process.cwd();
20
+ }
21
+ if (options.root && options.relativeRoot) {
22
+ cwd = path.resolve(cwd, options.root);
23
+ }
24
+ function getIgnores() {
25
+ if (options.ignorefile) {
26
+ return fs.readFileSync(path.resolve(cwd, options.ignorefile), 'utf8').split('\n').map(line => {
27
+ return line.split('#')[0].trim();
28
+ }).filter(line => line !== '');
29
+ }
30
+ else if (options.ignore) {
31
+ return options.ignore;
32
+ }
33
+ else if (fs.existsSync(path.resolve(cwd, '.nexfpackignore'))) {
34
+ return fs.readFileSync(path.resolve(cwd, '.nexfpackignore'), 'utf8').split('\n').map(line => {
35
+ return line.split('#')[0].trim();
36
+ }).filter(line => line !== '');
37
+ }
38
+ else {
39
+ return [];
40
+ }
41
+ }
42
+ return {
43
+ name: options.name ?? 'nexfpack-app',
44
+ root: cwd,
45
+ entry: path.resolve(cwd, options.entry ?? 'index.js'),
46
+ output: path.resolve(cwd, options.output ?? 'dist'),
47
+ tempdir: path.resolve(cwd, options.tempdir ?? '.nexfpack-temp'),
48
+ autoDeleteTempFiles: options.autoDeleteTempFiles ?? true,
49
+ ignore: getIgnores(),
50
+ enabledSign: options.enabledSign ?? false,
51
+ autoRun: options.autoRun ?? false,
52
+ };
53
+ }
54
+ function listAllFiles(dir) {
55
+ const result = [];
56
+ const entries = fs.readdirSync(dir);
57
+ for (const entry of entries) {
58
+ const fullPath = path.join(dir, entry);
59
+ const stat = fs.statSync(fullPath);
60
+ if (stat.isDirectory()) {
61
+ result.push(...listAllFiles(fullPath));
62
+ }
63
+ else {
64
+ result.push(fullPath);
65
+ }
66
+ }
67
+ return result;
68
+ }
69
+ async function nexfpack(options) {
70
+ try {
71
+ console.log(`
72
+ __ _ __ _
73
+ / \\ / /\\_____\\ \\ / /
74
+ / /\\ \\ / / / ____/\\ \\/ /
75
+ / / /\\ \\/ / / /____\\/\\ /_____
76
+ /_/ / \\__/ / ____/\\ / \\ ___\\
77
+ \\_\\/ \\_\\/ /____\\/ / /\\ \\ \\__/
78
+ /_____/\\ /_/ \\_\\ __\\
79
+ \\_____\\/ \\ \\ \\_/
80
+ \\ \\_\\
81
+ _______ _____ \\/_/__
82
+ | ___ | /\\ | ___| | | / /
83
+ | |___| | / \\ | | | |/ /
84
+ | _____| / /\\ \\ | | | |
85
+ | | / ____ \\ | |___ | |\\ \\
86
+ |_| /_/ \\_\\ |_____| |_| \\_\\
87
+
88
+ `);
89
+ console.log('▶️ Start packing...');
90
+ console.log('🧪 Tips: Nexfpack is very experimental. It may have some errors.');
91
+ let config;
92
+ if (options.configFile) {
93
+ config = JSON.parse(fs.readFileSync(options.configFile, 'utf8'));
94
+ }
95
+ else {
96
+ config = options;
97
+ }
98
+ const filledConfig = await fillOptions(config);
99
+ if (!fs.existsSync(filledConfig.tempdir)) {
100
+ fs.mkdirSync(filledConfig.tempdir, { recursive: true });
101
+ }
102
+ else {
103
+ fs.rmSync(filledConfig.tempdir, { recursive: true, force: true });
104
+ fs.mkdirSync(filledConfig.tempdir, { recursive: true });
105
+ }
106
+ console.log('📄 Copying files...');
107
+ if (!fs.existsSync(path.join(filledConfig.tempdir, 'source-copy'))) {
108
+ fs.mkdirSync(path.join(filledConfig.tempdir, 'source-copy'), { recursive: true });
109
+ }
110
+ await new Promise((resolve, reject) => {
111
+ copyfiles([filledConfig.root, path.join(filledConfig.tempdir, 'source-copy')], { up: 1, exclude: filledConfig.ignore }, (err) => {
112
+ if (err) {
113
+ console.error('❌ Failed to copy files:', err);
114
+ reject(err);
115
+ }
116
+ else {
117
+ resolve();
118
+ }
119
+ });
120
+ });
121
+ console.log('📜 Packing source...');
122
+ const tarStream = packTar(path.join(filledConfig.tempdir, 'source-copy'));
123
+ const writeStream = fs.createWriteStream(path.join(filledConfig.tempdir, 'source.tar'));
124
+ await pipeline(tarStream, writeStream);
125
+ console.log('📦 Packing executable...');
126
+ fs.writeFileSync(path.join(filledConfig.tempdir, 'package.json'), JSON.stringify({
127
+ name: filledConfig.name,
128
+ version: '1.0.0',
129
+ type: 'commonjs',
130
+ main: 'launcher.cjs',
131
+ dependencies: {
132
+ "modern-tar": "^0.7.6"
133
+ }
134
+ }, null, 2));
135
+ const installSpawnResult = child_process.spawnSync('npm install --omit=dev', { stdio: 'inherit', cwd: filledConfig.tempdir, shell: true });
136
+ if (installSpawnResult.status !== 0) {
137
+ throw new Error('❌ Failed to install dependencies');
138
+ }
139
+ const allNodeModulesFiles = listAllFiles(path.join(filledConfig.tempdir, 'node_modules'));
140
+ const seaConfigContent = {
141
+ "main": "./launcher.cjs",
142
+ "output": "./sea-prep.blob",
143
+ "disableExperimentalSEAWarning": true,
144
+ "assets": {
145
+ "source.tar": "./source.tar",
146
+ }
147
+ };
148
+ for (const file of allNodeModulesFiles) {
149
+ const relativePath = path.relative(path.join(filledConfig.tempdir, 'node_modules'), file);
150
+ seaConfigContent.assets[`node_modules/${relativePath}`] = file;
151
+ }
152
+ fs.writeFileSync(path.join(filledConfig.tempdir, 'sea-config.json'), JSON.stringify(seaConfigContent));
153
+ const launcherContent = `
154
+ (async () => {
155
+ const fs = require('fs');
156
+ const path = require('path');
157
+ const os = require('os');
158
+ const { pathToFileURL } = require('url');
159
+ let sea;
160
+ try {
161
+ sea = require('node:sea');
162
+ } catch (e1) {
163
+ try {
164
+ sea = process.getBuiltinModule('sea');
165
+ } catch (e2) {
166
+ try {
167
+ sea = globalThis.process?.getBuiltinModule?.('sea');
168
+ } catch (e3) {
169
+ sea = null;
170
+ }
171
+ }
172
+ }
173
+
174
+ const tempDir = path.join(os.tmpdir(), 'TEMP-NEXFPACK-' + Date.now() + '-' + Math.random().toString(36).substring(2));
175
+ fs.mkdirSync(tempDir, { recursive: true });
176
+
177
+ try {
178
+ const keys = ${JSON.stringify(Object.keys(seaConfigContent.assets).filter(key => key !== 'source.tar'))};
179
+ for (const key of keys) {
180
+ const data = sea.getRawAsset(key);
181
+ const filePath = path.join(tempDir, 'LANCHER', key);
182
+ fs.mkdirSync(path.dirname(filePath), { recursive: true });
183
+ fs.writeFileSync(filePath, Buffer.from(data));
184
+ }
185
+
186
+ const tarData = sea.getRawAsset('source.tar');
187
+
188
+ const tarFile = path.join(tempDir, 'source.tar');
189
+ fs.writeFileSync(tarFile, Buffer.from(tarData));
190
+
191
+ const { unpackTar } = await import(pathToFileURL(path.join(tempDir, 'LANCHER', 'node_modules', 'modern-tar', 'dist', 'fs', 'index.js')));
192
+ const extractStream = unpackTar(tempDir);
193
+ const tarReadStream = fs.createReadStream(tarFile);
194
+ const { pipeline } = require('stream/promises');
195
+ await pipeline(tarReadStream, extractStream);
196
+
197
+ const entry = path.resolve(tempDir, ${JSON.stringify(filledConfig.entry)});
198
+ try {
199
+ require(entry);
200
+ } catch {
201
+ await import(pathToFileURL(entry));
202
+ }
203
+ } catch (err) {
204
+ console.error(err);
205
+ }
206
+ fs.rmSync(tempDir, { recursive: true, force: true });
207
+ })();
208
+ `;
209
+ fs.writeFileSync(path.join(filledConfig.tempdir, 'launcher.cjs'), launcherContent);
210
+ const blobSpawnResult = child_process.spawnSync('node --experimental-sea-config sea-config.json', { stdio: 'inherit', cwd: filledConfig.tempdir, shell: true });
211
+ if (blobSpawnResult.status !== 0) {
212
+ throw new Error('❌ Failed to generate blob');
213
+ }
214
+ const NODE_SEA_FUSE = 'NODE_SEA_FUSE_fce680ab2cc467b6e072b8b5df1996b2';
215
+ const blobData = fs.readFileSync(path.join(filledConfig.tempdir, 'sea-prep.blob'));
216
+ const injectOptions = {
217
+ sentinelFuse: NODE_SEA_FUSE,
218
+ };
219
+ if (os.platform() === 'darwin') {
220
+ injectOptions.machoSegmentName = 'NODE_SEA';
221
+ }
222
+ const ext = os.platform() === 'win32' ? '.exe' : '';
223
+ const exePath = path.join(filledConfig.output, filledConfig.name + ext);
224
+ if (!fs.existsSync(filledConfig.output)) {
225
+ fs.mkdirSync(filledConfig.output, { recursive: true });
226
+ }
227
+ fs.copyFileSync(process.execPath, exePath);
228
+ await inject(exePath, 'NODE_SEA_BLOB', blobData, injectOptions);
229
+ if (filledConfig.enabledSign) {
230
+ console.log("⚠️ Sorry, we can't sign your executable file. Please sign it by yourself.");
231
+ }
232
+ if (filledConfig.autoDeleteTempFiles) {
233
+ console.log('♻️ Deleting temp files...');
234
+ fs.rmSync(filledConfig.tempdir, { recursive: true, force: true });
235
+ }
236
+ console.log('✅ Done!');
237
+ if (filledConfig.autoRun) {
238
+ console.log('🚀 Auto-run executable...');
239
+ child_process.spawnSync(exePath, { stdio: 'inherit', cwd: filledConfig.output, shell: true });
240
+ }
241
+ }
242
+ catch (err) {
243
+ console.error('❌ Nexfpack Failed:', err);
244
+ }
245
+ }
246
+ export default nexfpack;
247
+ export { nexfpack };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nexfpack",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "A tool for building single executable files from Node.js scripts or modules.",
5
5
  "license": "MIT",
6
6
  "repository": "https://github.com/nexfteam/Nexfpack",
@@ -10,6 +10,7 @@
10
10
  "homepage": "https://github.com/nexfteam/Nexfpack#readme",
11
11
  "author": "nexfteam",
12
12
  "type": "module",
13
+ "types": "index.d.mts",
13
14
  "main": "index.cjs",
14
15
  "module": "index.mjs",
15
16
  "exports": {
@@ -21,6 +22,16 @@
21
22
  "bin": {
22
23
  "nexfpack": "cli.mjs"
23
24
  },
25
+ "files": [
26
+ "index.cjs",
27
+ "index.mjs",
28
+ "cli.mjs",
29
+ "README.md",
30
+ "README-CN.md",
31
+ "LICENSE",
32
+ "index.d.mts",
33
+ "index.d.cts"
34
+ ],
24
35
  "scripts": {
25
36
  "compile": "tsc"
26
37
  },
package/index.cts DELETED
@@ -1,3 +0,0 @@
1
- (async() => {
2
- module.exports = await import("./index.mjs");
3
- })();
package/tsconfig.json DELETED
@@ -1,19 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- "target": "ES2022",
4
- "module": "NodeNext",
5
- "moduleResolution": "NodeNext",
6
- "types": ["node"],
7
- "lib": ["ES2022", "ESNext"],
8
- "outDir": "./",
9
- "rootDir": "./",
10
- "strict": true,
11
- "esModuleInterop": true,
12
- "skipLibCheck": true,
13
- "declaration": true,
14
- "declarationMap": false,
15
- "sourceMap": false
16
- },
17
- "include": ["**/*.mts", "**/*.cts", "**/*.ts"],
18
- "exclude": ["node_modules", "test", "**/*.d.ts", "**/*.d.mts", "**/*.d.cts", "**/test.*"]
19
- }