seabox 0.1.0-beta.3 → 0.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 +177 -154
- package/bin/seabox-rebuild.mjs +88 -0
- package/bin/seabox.mjs +147 -0
- package/lib/{blob.js → blob.mjs} +14 -19
- package/lib/bootstrap.cjs +753 -0
- package/lib/build-cache.mjs +199 -0
- package/lib/build.mjs +77 -0
- package/lib/config.mjs +243 -0
- package/lib/{crypto-assets.js → crypto-assets.mjs} +12 -47
- package/lib/diagnostics.mjs +203 -0
- package/lib/entry-bundler.mjs +64 -0
- package/lib/{fetch-node.js → fetch-node.mjs} +15 -20
- package/lib/index.mjs +26 -0
- package/lib/inject.mjs +95 -0
- package/lib/{manifest.js → manifest.mjs} +5 -11
- package/lib/multi-target-builder.mjs +705 -0
- package/lib/native-scanner.mjs +203 -0
- package/lib/{obfuscate.js → obfuscate.mjs} +9 -31
- package/lib/require-shim.mjs +113 -0
- package/lib/rolldown-bundler.mjs +411 -0
- package/lib/{unsign.js → unsign.cjs} +31 -46
- package/package.json +10 -5
- package/bin/seabox-rebuild.js +0 -395
- package/bin/seabox.js +0 -81
- package/lib/bindings.js +0 -31
- package/lib/bootstrap.js +0 -697
- package/lib/build.js +0 -283
- package/lib/bytenode-hack.js +0 -56
- package/lib/config.js +0 -119
- package/lib/index.js +0 -27
- package/lib/inject.js +0 -114
- package/lib/scanner.js +0 -153
package/README.md
CHANGED
|
@@ -1,22 +1,23 @@
|
|
|
1
1
|
# seabox
|
|
2
2
|
|
|
3
|
-
A reusable tool for building Node.js Single Executable Applications (SEA) with native-module support.
|
|
3
|
+
A reusable tool for building Node.js Single Executable Applications (SEA) with native-module support and binary extraction.
|
|
4
4
|
|
|
5
5
|
## Features
|
|
6
6
|
|
|
7
7
|
- Bundle Node.js applications into standalone executables
|
|
8
|
-
- Automatic
|
|
9
|
-
-
|
|
8
|
+
- **Automatic asset detection** from `path.join(__dirname, ...)` patterns
|
|
9
|
+
- **Automatic native module detection** (.node files) with pattern transforms
|
|
10
|
+
- Platform-specific library extraction (DLLs, shared libraries)
|
|
11
|
+
- Asset encryption with obfuscated keys
|
|
10
12
|
- Multi-platform targeting (Windows, Linux, macOS)
|
|
11
13
|
- V8 snapshot support for faster startup
|
|
12
14
|
- Integrity checking for extracted binaries
|
|
13
15
|
- Automatic code signature removal before injection
|
|
14
|
-
- Simple configuration via package.json
|
|
15
16
|
|
|
16
17
|
## Use case
|
|
17
|
-
This tooling was created as an alternative to pkg, which is unfortunatly deprecated, and where forks were running foul of virus checkers. By using node's SEA, the executables are directly from nodejs's distribution source, and built using node's native Single Executable Application solution. Unfortunatly this does mean native modules embedded within the exe cannot run directly and must be extracted to a location on the disk on first run - This tooling automates that process for you, while providing arbitrary asset embedding. Embedded assets are _not_ extracted and access to them is handled by intercepting require and fs.
|
|
18
|
+
This tooling was created as an alternative to pkg, which is unfortunatly deprecated, and where forks were running foul of virus checkers. By using node's SEA, the executables are directly downloaded from nodejs's distribution source, and built using node's native Single Executable Application solution. Unfortunatly this does mean native modules embedded within the exe cannot run directly and must be extracted to a location on the disk on first run - This tooling automates that process for you, while providing arbitrary asset embedding. Embedded assets are _not_ extracted and access to them is handled by intercepting require and fs.
|
|
18
19
|
|
|
19
|
-
Note: **V8 snapshot includes and embedds the original source**, this is currently a limitation of Node's SEA tooling as far as I can tell; thus the snapshot is only useful for faster startup.
|
|
20
|
+
Note: **V8 snapshot includes and embedds the original source**, this is currently a limitation of Node's SEA tooling as far as I can tell; thus the snapshot is only useful for faster startup. Its possible to get around this by using bytenode's vm.script hack (embed the bytenode code as an asset and run another vm snapshot with the faux script input) and I'll look into supporting it in the future.
|
|
20
21
|
|
|
21
22
|
## Installation
|
|
22
23
|
|
|
@@ -26,65 +27,82 @@ npm install --save-dev seabox
|
|
|
26
27
|
|
|
27
28
|
## Configuration
|
|
28
29
|
|
|
29
|
-
|
|
30
|
+
Create a `seabox.config.json` file in your project root:
|
|
30
31
|
|
|
31
32
|
```json
|
|
32
33
|
{
|
|
33
|
-
"
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
"./
|
|
37
|
-
"
|
|
38
|
-
"
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
"
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
"node24.11.0-win32-x64"
|
|
48
|
-
],
|
|
49
|
-
"output": "myapp.exe",
|
|
50
|
-
"outputPath": "dist",
|
|
51
|
-
"disableExperimentalSEAWarning": true,
|
|
52
|
-
"useSnapshot": true,
|
|
53
|
-
"useCodeCache": false
|
|
54
|
-
}
|
|
34
|
+
"entry": "./src/index.js",
|
|
35
|
+
"outputs": [
|
|
36
|
+
{
|
|
37
|
+
"path": "./dist/win",
|
|
38
|
+
"target": "node24.11.0-win32-x64",
|
|
39
|
+
"output": "myapp.exe"
|
|
40
|
+
}
|
|
41
|
+
],
|
|
42
|
+
"bundler": {
|
|
43
|
+
"external": []
|
|
44
|
+
},
|
|
45
|
+
"encryptAssets": false,
|
|
46
|
+
"useSnapshot": true,
|
|
47
|
+
"verbose": false
|
|
55
48
|
}
|
|
56
49
|
```
|
|
57
50
|
|
|
58
51
|
## Configuration Options
|
|
59
52
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
53
|
+
| Option | Type | Required | Description |
|
|
54
|
+
|--------|------|----------|-------------|
|
|
55
|
+
| `entry` | `string` | Yes | Path to your application's entry point |
|
|
56
|
+
| `outputs` | `array` | Yes | Array of build targets |
|
|
57
|
+
| `outputs[].path` | `string` | Yes | Output directory for this target |
|
|
58
|
+
| `outputs[].target` | `string` | Yes | Build target (format: `nodeX.Y.Z-platform-arch`) |
|
|
59
|
+
| `outputs[].output` | `string` | Yes | Output filename |
|
|
60
|
+
| `outputs[].libraries` | `array` | No | Explicit glob patterns for shared libraries (DLLs/SOs) requiring filesystem extraction. Libraries referenced in code via `, ...)` are automatically detected. |
|
|
61
|
+
| `outputs[].rcedit` | `object` | No | Windows executable metadata (icon, version info) |
|
|
62
|
+
| `assets` | `array` | No | Glob patterns for assets to embed (merged with auto-detected assets) |
|
|
63
|
+
| `bundler` | `object` | No | Rolldown Bundler options |
|
|
64
|
+
| `bundler.external` | `array` | No | Modules to exclude from bundling |
|
|
65
|
+
| `bundler.plugins` | `array` | No | Additional Rolldown plugins |
|
|
66
|
+
| `bundler.minify` | `boolean` | No | Minify bundled code |
|
|
67
|
+
| `bundler.sourcemap` | `boolean` | No | Generate source maps |
|
|
68
|
+
| `encryptAssets` | `boolean` | No | Enable asset encryption (default: false) |
|
|
69
|
+
| `encryptExclude` | `array` | No | Glob patterns to exclude from encryption |
|
|
70
|
+
| `useSnapshot` | `boolean` | No | Enable V8 startup snapshots (default: true) |
|
|
71
|
+
| `useCodeCache` | `boolean` | No | Enable V8 code cache (default: false) |
|
|
72
|
+
| `cacheLocation` | `string` | No | Path for code cache storage |
|
|
73
|
+
| `sign` | `string` | No | Path to custom signing script (.mjs/.cjs) |
|
|
74
|
+
| `verbose` | `boolean` | No | Enable verbose logging (default: false) |
|
|
74
75
|
|
|
75
76
|
## Usage
|
|
76
77
|
|
|
77
|
-
|
|
78
|
+
### CLI Commands
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
# Build executable(s)
|
|
82
|
+
npx seabox build
|
|
83
|
+
|
|
84
|
+
# Build with verbose output
|
|
85
|
+
npx seabox build --verbose
|
|
86
|
+
|
|
87
|
+
# Specify custom config file
|
|
88
|
+
npx seabox build --config custom-config.json
|
|
78
89
|
|
|
79
|
-
|
|
90
|
+
# Initialize a new config file
|
|
91
|
+
npx seabox init
|
|
80
92
|
|
|
81
|
-
|
|
93
|
+
# Show help
|
|
94
|
+
npx seabox help
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### npm Scripts (Recommended)
|
|
98
|
+
|
|
99
|
+
Add to your `package.json`:
|
|
82
100
|
|
|
83
101
|
```json
|
|
84
102
|
{
|
|
85
103
|
"scripts": {
|
|
86
|
-
"build
|
|
87
|
-
"build:
|
|
104
|
+
"build": "seabox build",
|
|
105
|
+
"build:verbose": "seabox build --verbose"
|
|
88
106
|
}
|
|
89
107
|
}
|
|
90
108
|
```
|
|
@@ -92,27 +110,13 @@ Add a build script to your `package.json`:
|
|
|
92
110
|
Then run:
|
|
93
111
|
|
|
94
112
|
```bash
|
|
95
|
-
npm run build
|
|
96
|
-
```
|
|
97
|
-
|
|
98
|
-
### CLI
|
|
99
|
-
|
|
100
|
-
```bash
|
|
101
|
-
# Build using package.json configuration
|
|
102
|
-
npx seabox
|
|
103
|
-
|
|
104
|
-
# Build using a standalone config file (alternative to package.json)
|
|
105
|
-
npx seabox --config sea-config.json
|
|
106
|
-
|
|
107
|
-
# Verbose output
|
|
108
|
-
npx seabox --verbose
|
|
109
|
-
|
|
113
|
+
npm run build
|
|
110
114
|
```
|
|
111
115
|
|
|
112
116
|
### Programmatic API
|
|
113
117
|
|
|
114
118
|
```javascript
|
|
115
|
-
|
|
119
|
+
import { build } from 'seabox';
|
|
116
120
|
|
|
117
121
|
await build({
|
|
118
122
|
projectRoot: process.cwd(),
|
|
@@ -122,118 +126,139 @@ await build({
|
|
|
122
126
|
|
|
123
127
|
## How It Works
|
|
124
128
|
|
|
125
|
-
|
|
126
|
-
2. **Manifest Generation**: Creates a runtime manifest with metadata for binary extraction
|
|
127
|
-
3. **Bootstrap Injection**: Prepends bootstrap code to handle native module extraction
|
|
128
|
-
4. **Blob Creation**: Uses Node.js SEA tooling to create the application blob
|
|
129
|
-
5. **Binary Fetching**: Downloads the target Node.js binary and removes its signature
|
|
130
|
-
6. **Injection**: Uses postject to inject the blob into the Node binary
|
|
131
|
-
7. **Output**: Produces a standalone executable ready for signing and distribution
|
|
129
|
+
seabox automates the entire SEA build process:
|
|
132
130
|
|
|
133
|
-
|
|
131
|
+
1. **Bundling** - Automatically bundles your app with Rolldown, detecting:
|
|
132
|
+
- Native module patterns (`bindings`, `node-gyp-build`, direct `.node` requires)
|
|
133
|
+
- Asset references via `path.join(__dirname, 'relative/path')`
|
|
134
|
+
- Additional
|
|
134
135
|
|
|
135
|
-
|
|
136
|
-
-
|
|
137
|
-
-
|
|
138
|
-
-
|
|
136
|
+
2. **Asset Collection** - Gathers assets from three sources:
|
|
137
|
+
- **Auto-detected**: Files referenced via `path.join(__dirname, ...)` patterns
|
|
138
|
+
- **Config globs**: Patterns specified in `assets` array
|
|
139
|
+
- **Libraries**: Platform-specific shared libraries (DLLs/SOs)
|
|
139
140
|
|
|
140
|
-
|
|
141
|
+
3. **Native Module Rebuilding** - Rebuilds native modules for target platform
|
|
141
142
|
|
|
142
|
-
|
|
143
|
+
4. **Bootstrap Injection** - Adds runtime code for asset loading and native module extraction
|
|
143
144
|
|
|
144
|
-
|
|
145
|
+
5. **SEA Blob Creation** - Packages everything using Node.js SEA tooling
|
|
145
146
|
|
|
146
|
-
|
|
147
|
-
{
|
|
148
|
-
"sea": {
|
|
149
|
-
"cacheLocation": "%LOCALAPPDATA%\\myapp-cache"
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
```
|
|
147
|
+
6. **Binary Preparation** - Downloads target Node.js binary and removes code signature
|
|
153
148
|
|
|
154
|
-
|
|
155
|
-
- **Windows**: `%LOCALAPPDATA%`, `%APPDATA%`, `%TEMP%`, etc.
|
|
156
|
-
- **Unix/Linux/macOS**: `$HOME`, `$TMPDIR`, `${XDG_CACHE_HOME}`, etc.
|
|
149
|
+
7. **Injection** - Uses `postject` to inject the blob into the Node.js binary
|
|
157
150
|
|
|
158
|
-
**
|
|
151
|
+
8. **Output** - Produces standalone executable(s) ready for distribution
|
|
159
152
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
153
|
+
### Automatic Asset Detection
|
|
154
|
+
|
|
155
|
+
**Like pkg**, seabox automatically detects and embeds assets referenced in your code:
|
|
156
|
+
|
|
157
|
+
```javascript
|
|
158
|
+
import path from 'path';
|
|
159
|
+
import { fileURLToPath } from 'url';
|
|
160
|
+
|
|
161
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
164
162
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
163
|
+
// This asset will be automatically detected and embedded
|
|
164
|
+
const configPath = path.join(__dirname, '../config/app.json');
|
|
165
|
+
const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
|
|
168
166
|
```
|
|
169
167
|
|
|
170
|
-
|
|
168
|
+
**Asset sources (merged and deduplicated):**
|
|
169
|
+
1. **Auto-detected** from code analysis during bundling
|
|
170
|
+
2. **Config globs** from `assets: ["./data/**/*", "./public/**/*"]`
|
|
171
|
+
3. **Platform libraries** from `outputs[].libraries` (e.g., DLLs for Windows)
|
|
171
172
|
|
|
172
|
-
|
|
173
|
+
### Native Module Support
|
|
173
174
|
|
|
174
|
-
|
|
175
|
-
# Rebuild for a specific target
|
|
176
|
-
npx seabox-rebuild --target node24.11.0-win32-x64
|
|
175
|
+
seabox automatically handles native modules without any configuration:
|
|
177
176
|
|
|
178
|
-
|
|
179
|
-
|
|
177
|
+
**Supported patterns:**
|
|
178
|
+
- `require('bindings')('module')` - Standard bindings package
|
|
179
|
+
- `require('./build/Release/addon.node')` - Direct requires
|
|
180
|
+
- `require('node-gyp-build')(__dirname)` - Prebuild binaries
|
|
181
|
+
- `require('node-pre-gyp')` patterns - Pre-compiled binaries
|
|
180
182
|
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
183
|
+
**At runtime:**
|
|
184
|
+
- Native modules are extracted to a cache directory on first run
|
|
185
|
+
- Modules are integrity-checked with SHA-256 hashes
|
|
186
|
+
- Custom `require()` shim loads modules from cache
|
|
187
|
+
|
|
188
|
+
### Platform-Specific Libraries
|
|
189
|
+
|
|
190
|
+
Libraries that require filesystem access (like DLLs loaded via `dlopen`) can be included in two ways:
|
|
184
191
|
|
|
185
|
-
|
|
186
|
-
- Scan all dependencies for native modules (those with `binding.gyp` or `gypfile: true`)
|
|
187
|
-
- Rebuild each one using `node-gyp` for the target platform and Node.js version
|
|
188
|
-
- Download necessary headers for cross-compilation
|
|
192
|
+
**1. Automatic Detection (Recommended)**
|
|
189
193
|
|
|
190
|
-
|
|
194
|
+
If your code references a DLL using `path.join(__dirname, ...)`, it will be automatically detected and included:
|
|
195
|
+
|
|
196
|
+
```javascript
|
|
197
|
+
// This will be automatically detected during bundling
|
|
198
|
+
const dllPath = path.join(__dirname, './lib/RGDevice.dll');
|
|
199
|
+
```
|
|
191
200
|
|
|
192
|
-
|
|
201
|
+
**2. Explicit Glob Patterns**
|
|
193
202
|
|
|
194
|
-
|
|
203
|
+
You can also explicitly specify library patterns in your config:
|
|
195
204
|
|
|
196
205
|
```json
|
|
197
206
|
{
|
|
198
|
-
"
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
"icon": ".\\assets\\myapp.ico",
|
|
203
|
-
"file-version": "1.2.3.4",
|
|
204
|
-
"product-version": "1.2.3.4",
|
|
205
|
-
"version-string": {
|
|
206
|
-
"CompanyName": "My Company",
|
|
207
|
-
"FileDescription": "My Application",
|
|
208
|
-
"ProductName": "MyApp",
|
|
209
|
-
"InternalName": "myapp.exe",
|
|
210
|
-
"OriginalFilename": "myapp.exe",
|
|
211
|
-
"LegalCopyright": "Copyright (C) 2025 My Company"
|
|
212
|
-
}
|
|
207
|
+
"outputs": [
|
|
208
|
+
{
|
|
209
|
+
"target": "node24.11.0-win32-x64",
|
|
210
|
+
"libraries": ["lib/*.dll"] // Manually specify DLLs to include
|
|
213
211
|
}
|
|
214
|
-
|
|
212
|
+
]
|
|
215
213
|
}
|
|
216
214
|
```
|
|
217
215
|
|
|
218
|
-
### rcedit Options
|
|
219
216
|
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
217
|
+
These files are extracted on first run (like `.node` files) since they need to be loaded from the filesystem.
|
|
218
|
+
|
|
219
|
+
### Code Signature Removal
|
|
220
|
+
|
|
221
|
+
Required before SEA injection. Platform-specific tools needed:
|
|
222
|
+
- **Windows**: `signtool.exe` (from Windows SDK)
|
|
223
|
+
- **macOS**: `codesign` (included with Xcode)
|
|
224
|
+
- **Linux**: Not required
|
|
225
|
+
|
|
226
|
+
### Custom Signing
|
|
227
|
+
|
|
228
|
+
You can apply code signing after the build completes by specifying a custom signing script:
|
|
229
|
+
|
|
230
|
+
```json
|
|
231
|
+
{
|
|
232
|
+
"sign": "./scripts/sign.mjs"
|
|
233
|
+
}
|
|
234
|
+
```
|
|
233
235
|
|
|
234
|
-
The
|
|
236
|
+
The signing script must export a default function that receives a config object:
|
|
237
|
+
|
|
238
|
+
```javascript
|
|
239
|
+
// scripts/sign.mjs
|
|
240
|
+
export default async function sign(config) {
|
|
241
|
+
const { exePath, target, platform, arch, nodeVersion, projectRoot } = config;
|
|
242
|
+
|
|
243
|
+
// Example: Windows code signing with signtool
|
|
244
|
+
if (platform === 'win32') {
|
|
245
|
+
execSync(`signtool sign /fd SHA256 /a "${exePath}"`);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
// Example: macOS code signing
|
|
249
|
+
if (platform === 'darwin') {
|
|
250
|
+
execSync(`codesign --force --sign "Developer ID" "${exePath}"`);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
```
|
|
235
254
|
|
|
236
|
-
|
|
255
|
+
**Config parameters:**
|
|
256
|
+
- `exePath` - Absolute path to the built executable
|
|
257
|
+
- `target` - Full target string (e.g., "node24.11.0-win32-x64")
|
|
258
|
+
- `platform` - Platform name (win32, linux, darwin)
|
|
259
|
+
- `arch` - Architecture (x64, arm64)
|
|
260
|
+
- `nodeVersion` - Node.js version
|
|
261
|
+
- `projectRoot` - Absolute path to project root
|
|
237
262
|
|
|
238
263
|
## Asset Encryption
|
|
239
264
|
|
|
@@ -254,25 +279,23 @@ seabox supports optional AES-256-GCM encryption of embedded assets to protect yo
|
|
|
254
279
|
1. **Build Time**: A random 256-bit encryption key is generated
|
|
255
280
|
2. **Asset Encryption**: Non-binary assets are encrypted using AES-256-GCM
|
|
256
281
|
3. **Key Embedding**: The encryption key is obfuscated and embedded in the bootstrap code
|
|
257
|
-
4. **Key Obfuscation**: the bootstrap and key code are obfuscated
|
|
282
|
+
4. **Key Obfuscation**: the bootstrap and key code are obfuscated
|
|
258
283
|
5. **Runtime Decryption**: Assets are transparently decrypted when accessed
|
|
259
284
|
|
|
260
285
|
### Considerations
|
|
261
286
|
|
|
262
287
|
- **Binary files** (`.node`, `.dll`, `.so`, `.dylib`) are **never encrypted** as they must be extracted as-is
|
|
263
288
|
- The manifest (`sea-manifest.json`) is **not encrypted** to allow bootstrap initialization
|
|
264
|
-
- **V8 snapshot includes the original source**, this is currently a limitation of Node's SEA.
|
|
289
|
+
- **V8 snapshot includes the original source**, this is currently a limitation of Node's SEA tooling.
|
|
265
290
|
- Encryption provides **obfuscation**, not cryptographic security against determined attackers
|
|
266
291
|
- The bootloader code, that includes the encryption key, is obfuscated in the source embedded by Node's SEA
|
|
267
292
|
|
|
293
|
+
## Contributing
|
|
268
294
|
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
- **Windows**: `win32-x64`, `win32-arm64`
|
|
272
|
-
- **Linux**: `linux-x64`, `linux-arm64`
|
|
273
|
-
- **macOS**: `darwin-x64`, `darwin-arm64`
|
|
295
|
+
Contributions welcome! Please open an issue or PR on [GitHub](https://github.com/MeirionHughes/seabox).
|
|
274
296
|
|
|
275
297
|
## License
|
|
276
298
|
|
|
277
299
|
MIT
|
|
278
|
-
|
|
300
|
+
|
|
301
|
+
Copyright © 2025 Meirion Hughes
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* seabox-rebuild.mjs
|
|
4
|
+
* Rebuild native modules for target platform/architecture
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { execSync } from 'child_process';
|
|
8
|
+
import fs from 'fs';
|
|
9
|
+
import path from 'path';
|
|
10
|
+
import { fileURLToPath } from 'url';
|
|
11
|
+
import * as diag from '../lib/diagnostics.mjs';
|
|
12
|
+
|
|
13
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
14
|
+
const __dirname = path.dirname(__filename);
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Rebuild a native module for a specific target
|
|
18
|
+
* @param {string} modulePath - Path to the native module
|
|
19
|
+
* @param {string} platform - Target platform (win32, linux, darwin)
|
|
20
|
+
* @param {string} arch - Target architecture (x64, arm64)
|
|
21
|
+
* @param {boolean} verbose - Enable verbose logging
|
|
22
|
+
*/
|
|
23
|
+
function rebuildNativeModule(modulePath, platform, arch, verbose = false) {
|
|
24
|
+
diag.setVerbose(verbose);
|
|
25
|
+
|
|
26
|
+
diag.verbose(`Rebuilding native module: ${modulePath}`);
|
|
27
|
+
diag.verbose(`Target: ${platform}-${arch}`);
|
|
28
|
+
|
|
29
|
+
const packageJsonPath = path.join(modulePath, 'package.json');
|
|
30
|
+
if (!fs.existsSync(packageJsonPath)) {
|
|
31
|
+
throw new Error(`No package.json found in ${modulePath}`);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const pkg = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
|
|
35
|
+
const moduleName = pkg.name;
|
|
36
|
+
|
|
37
|
+
// Check if module has native bindings
|
|
38
|
+
const hasBindingGyp = fs.existsSync(path.join(modulePath, 'binding.gyp'));
|
|
39
|
+
if (!hasBindingGyp && !pkg.gypfile) {
|
|
40
|
+
diag.verbose(`Module ${moduleName} does not appear to have native bindings, skipping`);
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
try {
|
|
45
|
+
// Use node-gyp to rebuild for the target platform
|
|
46
|
+
const cmd = `npx node-gyp rebuild --target_platform=${platform} --target_arch=${arch}`;
|
|
47
|
+
|
|
48
|
+
diag.verbose(`Running: ${cmd}`);
|
|
49
|
+
|
|
50
|
+
execSync(cmd, {
|
|
51
|
+
cwd: modulePath,
|
|
52
|
+
stdio: verbose ? 'inherit' : 'pipe',
|
|
53
|
+
env: {
|
|
54
|
+
...process.env,
|
|
55
|
+
npm_config_target_platform: platform,
|
|
56
|
+
npm_config_target_arch: arch
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
diag.verbose(`Successfully rebuilt ${moduleName}`);
|
|
61
|
+
} catch (error) {
|
|
62
|
+
diag.verbose(`Failed to rebuild ${moduleName}: ${error.message}`);
|
|
63
|
+
throw error;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// CLI entry point
|
|
68
|
+
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
69
|
+
const args = process.argv.slice(2);
|
|
70
|
+
|
|
71
|
+
if (args.length < 3) {
|
|
72
|
+
diag.error('Usage: seabox-rebuild <module-path> <platform> <arch> [--verbose]');
|
|
73
|
+
process.exit(1);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const [modulePath, platform, arch] = args;
|
|
77
|
+
const verbose = args.includes('--verbose') || args.includes('-v');
|
|
78
|
+
|
|
79
|
+
try {
|
|
80
|
+
rebuildNativeModule(modulePath, platform, arch, verbose);
|
|
81
|
+
process.exit(0);
|
|
82
|
+
} catch (error) {
|
|
83
|
+
diag.error(`Rebuild failed: ${error.message}`);
|
|
84
|
+
process.exit(1);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export { rebuildNativeModule };
|