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.
@@ -1,395 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- /**
4
- * Native Module Rebuilder for SEA Builder
5
- *
6
- * Scans package dependencies to identify native modules (those with binding.gyp
7
- * or gypfile: true in package.json) and rebuilds them using node-gyp for a
8
- * target Node.js version and platform.
9
- *
10
- * Supports cross-compilation for different platforms and architectures.
11
- * Inspired by @electron/rebuild's module identification strategy.
12
- */
13
-
14
- const fs = require('fs');
15
- const path = require('path');
16
- const { execSync } = require('child_process');
17
-
18
- class NativeModuleRebuilder {
19
- constructor(buildPath, options = {}) {
20
- this.buildPath = buildPath || process.cwd();
21
- this.nativeModules = new Map();
22
- this.visitedPaths = new Set();
23
-
24
- // Target configuration
25
- this.targetNodeVersion = options.nodeVersion || process.version.replace('v', '');
26
- this.targetPlatform = options.platform || process.platform;
27
- this.targetArch = options.arch || process.arch;
28
-
29
- // Parse from target string if provided (e.g., "node24.11.0-win32-x64")
30
- if (options.target) {
31
- const match = options.target.match(/^node(\d+\.\d+\.\d+)-(\w+)-(\w+)$/);
32
- if (match) {
33
- this.targetNodeVersion = match[1];
34
- this.targetPlatform = match[2];
35
- this.targetArch = match[3];
36
- }
37
- }
38
- }
39
-
40
- /**
41
- * Read and parse package.json from a directory
42
- */
43
- readPackageJson(modulePath) {
44
- const packageJsonPath = path.join(modulePath, 'package.json');
45
- try {
46
- if (fs.existsSync(packageJsonPath)) {
47
- const content = fs.readFileSync(packageJsonPath, 'utf8');
48
- return JSON.parse(content);
49
- }
50
- } catch (error) {
51
- console.warn(`Warning: Could not read package.json at ${packageJsonPath}:`, error.message);
52
- }
53
- return null;
54
- }
55
-
56
- /**
57
- * Check if a module is a native module
58
- * A module is considered native if it has:
59
- * - A binding.gyp file, OR
60
- * - gypfile: true in package.json
61
- */
62
- isNativeModule(modulePath) {
63
- // Check for binding.gyp
64
- const bindingGypPath = path.join(modulePath, 'binding.gyp');
65
- if (fs.existsSync(bindingGypPath)) {
66
- return true;
67
- }
68
-
69
- // Check for gypfile: true in package.json
70
- const packageJson = this.readPackageJson(modulePath);
71
- if (packageJson && packageJson.gypfile === true) {
72
- return true;
73
- }
74
-
75
- return false;
76
- }
77
-
78
- /**
79
- * Resolve the actual path of a dependency
80
- */
81
- resolveDependencyPath(fromPath, depName) {
82
- const possiblePaths = [];
83
-
84
- // Check in local node_modules
85
- let currentPath = fromPath;
86
- while (currentPath !== path.parse(currentPath).root) {
87
- const nodeModulesPath = path.join(currentPath, 'node_modules', depName);
88
- if (fs.existsSync(nodeModulesPath)) {
89
- possiblePaths.push(nodeModulesPath);
90
- }
91
- currentPath = path.dirname(currentPath);
92
- }
93
-
94
- // Return the first valid path, resolving symlinks
95
- for (const testPath of possiblePaths) {
96
- try {
97
- return fs.realpathSync(testPath);
98
- } catch (error) {
99
- // Skip if symlink is broken
100
- continue;
101
- }
102
- }
103
-
104
- return null;
105
- }
106
-
107
- /**
108
- * Scan a directory's dependencies recursively
109
- */
110
- scanDependencies(modulePath, depth = 0) {
111
- // Resolve symlinks to avoid scanning the same module multiple times
112
- let realPath;
113
- try {
114
- realPath = fs.realpathSync(modulePath);
115
- } catch (error) {
116
- console.warn(`Warning: Could not resolve path ${modulePath}:`, error.message);
117
- return;
118
- }
119
-
120
- // Skip if already visited
121
- if (this.visitedPaths.has(realPath)) {
122
- return;
123
- }
124
- this.visitedPaths.add(realPath);
125
-
126
- // Check if this module is a native module
127
- if (this.isNativeModule(realPath)) {
128
- const packageJson = this.readPackageJson(realPath);
129
- const moduleName = packageJson ? packageJson.name : path.basename(realPath);
130
-
131
- if (!this.nativeModules.has(realPath)) {
132
- this.nativeModules.set(realPath, moduleName);
133
- console.log(`Found native module: ${moduleName} at ${realPath}`);
134
- }
135
- }
136
-
137
- // Read package.json to find dependencies
138
- const packageJson = this.readPackageJson(realPath);
139
- if (!packageJson) {
140
- return;
141
- }
142
-
143
- // Collect all types of dependencies
144
- const allDeps = {
145
- ...packageJson.dependencies,
146
- ...packageJson.optionalDependencies,
147
- ...packageJson.devDependencies
148
- };
149
-
150
- // Scan each dependency
151
- for (const depName of Object.keys(allDeps)) {
152
- const depPath = this.resolveDependencyPath(realPath, depName);
153
- if (depPath) {
154
- this.scanDependencies(depPath, depth + 1);
155
- }
156
- }
157
-
158
- // Also check nested node_modules
159
- const nodeModulesPath = path.join(realPath, 'node_modules');
160
- if (fs.existsSync(nodeModulesPath)) {
161
- try {
162
- const modules = fs.readdirSync(nodeModulesPath);
163
- for (const moduleName of modules) {
164
- if (moduleName === '.bin') continue;
165
-
166
- const modulePath = path.join(nodeModulesPath, moduleName);
167
- const stat = fs.lstatSync(modulePath);
168
-
169
- if (stat.isDirectory()) {
170
- // Handle scoped packages (e.g., @robgeoltd/lib-m2)
171
- if (moduleName.startsWith('@')) {
172
- const scopedModules = fs.readdirSync(modulePath);
173
- for (const scopedModule of scopedModules) {
174
- const scopedPath = path.join(modulePath, scopedModule);
175
- this.scanDependencies(scopedPath, depth + 1);
176
- }
177
- } else {
178
- this.scanDependencies(modulePath, depth + 1);
179
- }
180
- }
181
- }
182
- } catch (error) {
183
- console.warn(`Warning: Could not read node_modules at ${nodeModulesPath}:`, error.message);
184
- }
185
- }
186
- }
187
-
188
- /**
189
- * Get platform-specific configuration for cross-compilation
190
- */
191
- getCrossCompileEnv() {
192
- const env = { ...process.env };
193
-
194
- // Set target platform and architecture
195
- if (this.targetPlatform !== process.platform) {
196
- console.log(` Cross-compiling for platform: ${this.targetPlatform}`);
197
- }
198
- if (this.targetArch !== process.arch) {
199
- console.log(` Cross-compiling for architecture: ${this.targetArch}`);
200
- }
201
-
202
- // Map common arch names to node-gyp format
203
- const archMap = {
204
- 'x64': 'x64',
205
- 'ia32': 'ia32',
206
- 'arm': 'arm',
207
- 'arm64': 'arm64'
208
- };
209
-
210
- env.npm_config_target = this.targetNodeVersion;
211
- env.npm_config_arch = archMap[this.targetArch] || this.targetArch;
212
- env.npm_config_target_arch = archMap[this.targetArch] || this.targetArch;
213
- env.npm_config_disturl = 'https://nodejs.org/dist';
214
- env.npm_config_runtime = 'node';
215
- env.npm_config_build_from_source = 'true';
216
-
217
- return env;
218
- }
219
-
220
- /**
221
- * Rebuild a native module using node-gyp for the target platform
222
- */
223
- rebuildModule(modulePath, moduleName) {
224
- console.log(`\nRebuilding ${moduleName}...`);
225
- console.log(` Path: ${modulePath}`);
226
- console.log(` Target: Node.js ${this.targetNodeVersion} (${this.targetPlatform}-${this.targetArch})`);
227
-
228
- // Determine if we should use npx node-gyp or just node-gyp
229
- let useNpx = false;
230
- try {
231
- execSync('node-gyp --version', { stdio: 'pipe' });
232
- } catch (error) {
233
- useNpx = true;
234
- }
235
-
236
- const nodeGypCmd = useNpx ? 'npx node-gyp' : 'node-gyp';
237
-
238
- try {
239
- const env = this.getCrossCompileEnv();
240
-
241
- // Clean previous builds
242
- try {
243
- execSync(`${nodeGypCmd} clean`, {
244
- cwd: modulePath,
245
- stdio: 'pipe',
246
- env
247
- });
248
- } catch (cleanError) {
249
- // Ignore clean errors
250
- }
251
-
252
- // Configure for target
253
- const configureCmd = `${nodeGypCmd} configure --target=${this.targetNodeVersion} --arch=${this.targetArch} --dist-url=https://nodejs.org/dist`;
254
- execSync(configureCmd, {
255
- cwd: modulePath,
256
- stdio: 'inherit',
257
- env
258
- });
259
-
260
- // Build
261
- execSync(`${nodeGypCmd} build`, {
262
- cwd: modulePath,
263
- stdio: 'inherit',
264
- env
265
- });
266
-
267
- console.log(`✓ Successfully rebuilt ${moduleName}`);
268
- return true;
269
- } catch (error) {
270
- console.error(`✗ Failed to rebuild ${moduleName}:`, error.message);
271
- return false;
272
- }
273
- }
274
-
275
- /**
276
- * Main rebuild process
277
- */
278
- async rebuild() {
279
- console.log('Starting native module rebuild process...');
280
- console.log(`Build path: ${this.buildPath}`);
281
- console.log(`Target: Node.js ${this.targetNodeVersion} (${this.targetPlatform}-${this.targetArch})\n`);
282
-
283
- // Verify node-gyp is available
284
- try {
285
- execSync('node-gyp --version', { stdio: 'pipe' });
286
- } catch (error) {
287
- // Try with npx
288
- try {
289
- execSync('npx node-gyp --version', { stdio: 'pipe' });
290
- } catch (npxError) {
291
- console.error('Error: node-gyp is not installed or not in PATH');
292
- console.error('Install it with: npm install -g node-gyp');
293
- console.error('Or ensure it is in your project dependencies');
294
- process.exit(1);
295
- }
296
- }
297
-
298
- // Scan for native modules
299
- console.log('Scanning dependencies for native modules...\n');
300
- this.scanDependencies(this.buildPath);
301
-
302
- if (this.nativeModules.size === 0) {
303
- console.log('\nNo native modules found.');
304
- return;
305
- }
306
-
307
- console.log(`\nFound ${this.nativeModules.size} native module(s) to rebuild.\n`);
308
- console.log('='.repeat(60));
309
-
310
- // Rebuild each native module
311
- let successCount = 0;
312
- let failCount = 0;
313
-
314
- for (const [modulePath, moduleName] of this.nativeModules) {
315
- const success = this.rebuildModule(modulePath, moduleName);
316
- if (success) {
317
- successCount++;
318
- } else {
319
- failCount++;
320
- }
321
- }
322
-
323
- // Summary
324
- console.log('\n' + '='.repeat(60));
325
- console.log('\nRebuild Summary:');
326
- console.log(` Total modules: ${this.nativeModules.size}`);
327
- console.log(` Successful: ${successCount}`);
328
- console.log(` Failed: ${failCount}`);
329
-
330
- if (failCount > 0) {
331
- console.log('\nSome modules failed to rebuild. Check the errors above.');
332
- process.exit(1);
333
- } else {
334
- console.log('\n✓ All native modules rebuilt successfully!');
335
- }
336
- }
337
- }
338
-
339
- // Main execution
340
- if (require.main === module) {
341
- const args = process.argv.slice(2);
342
-
343
- // Parse command line arguments
344
- const options = {};
345
- let buildPath = process.cwd();
346
-
347
- for (let i = 0; i < args.length; i++) {
348
- const arg = args[i];
349
-
350
- if (arg === '--target' && args[i + 1]) {
351
- options.target = args[i + 1];
352
- i++;
353
- } else if (arg === '--node-version' && args[i + 1]) {
354
- options.nodeVersion = args[i + 1];
355
- i++;
356
- } else if (arg === '--platform' && args[i + 1]) {
357
- options.platform = args[i + 1];
358
- i++;
359
- } else if (arg === '--arch' && args[i + 1]) {
360
- options.arch = args[i + 1];
361
- i++;
362
- } else if (arg === '--help' || arg === '-h') {
363
- console.log(`
364
- seabox-rebuild - Rebuild native modules for target Node.js version
365
-
366
- Usage:
367
- seabox-rebuild [options] [build-path]
368
-
369
- Options:
370
- --target <target> Target in format: nodeX.Y.Z-platform-arch
371
- Example: node24.11.0-win32-x64
372
- --node-version <version> Target Node.js version (e.g., 24.11.0)
373
- --platform <platform> Target platform (win32, linux, darwin)
374
- --arch <arch> Target architecture (x64, arm64, ia32)
375
- --help, -h Show this help message
376
-
377
- Examples:
378
- seabox-rebuild --target node24.11.0-win32-x64
379
- seabox-rebuild --node-version 24.11.0 --platform linux --arch x64
380
- seabox-rebuild /path/to/project
381
- `);
382
- process.exit(0);
383
- } else if (!arg.startsWith('--')) {
384
- buildPath = arg;
385
- }
386
- }
387
-
388
- const rebuilder = new NativeModuleRebuilder(buildPath, options);
389
- rebuilder.rebuild().catch(error => {
390
- console.error('Fatal error:', error);
391
- process.exit(1);
392
- });
393
- }
394
-
395
- module.exports = { NativeModuleRebuilder };
package/bin/seabox.js DELETED
@@ -1,81 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- /**
4
- * CLI entry point for SEA builder
5
- */
6
-
7
- const { build } = require('../lib');
8
- const fs = require('fs');
9
- const path = require('path');
10
-
11
- async function main() {
12
- const args = process.argv.slice(2);
13
-
14
- const configPath = args.includes('--config') ? args[args.indexOf('--config') + 1] : undefined;
15
- const verbose = args.includes('--verbose');
16
- const debug = args.includes('--debug');
17
-
18
- let showHelp = args.includes('--help') || args.includes('-h');
19
-
20
- // Try to load config to check if it exists
21
- if (!configPath) {
22
- const pkgPath = path.join(process.cwd(), 'package.json');
23
- if (!fs.existsSync(pkgPath)) {
24
- showHelp = true;
25
- }
26
- const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
27
- if (!pkg.seabox) {
28
- showHelp = true;
29
- }
30
- }
31
-
32
- // Show help
33
- if (showHelp) {
34
- console.log(`
35
- SeaBox - Node.js Single Executable Application Builder
36
-
37
- Usage:
38
- seabox [options]
39
-
40
- Options:
41
- --config <path> Path to config file (default: reads from package.json)
42
- --verbose Enable verbose logging
43
- --debug Keep temporary build files for debugging
44
- --help, -h Show this help message
45
-
46
- Configuration:
47
- Add a "seabox" field to your package.json:
48
- {
49
- "seabox": {
50
- "entry": "./out/app.js",
51
- "assets": ["./out/**/*"],
52
- "binaries": ["*.node", "*.dll"],
53
- "targets": ["node24.11.0-win32-x64"],
54
- "output": "myapp.exe",
55
- "outputPath": "dist",
56
- "useSnapshot": false
57
- }
58
- }
59
-
60
- For more information, see the documentation.
61
- `);
62
-
63
- }
64
-
65
- try {
66
- await build({
67
- configPath,
68
- verbose,
69
- debug,
70
- projectRoot: process.cwd()
71
- });
72
- } catch (error) {
73
- console.error('Build failed:', error.message);
74
- if (verbose) {
75
- console.error(error.stack);
76
- }
77
- process.exit(1);
78
- }
79
- }
80
-
81
- main();
package/lib/bindings.js DELETED
@@ -1,31 +0,0 @@
1
- /**
2
- * bindings.js - SEA-compatible replacement for the 'bindings' npm package
3
- * This version uses process.dlopen to load native modules from the SEA cache
4
- */
5
-
6
- const path = require('path');
7
-
8
- module.exports = function bindings(name) {
9
- // Ensure .node extension
10
- if (!name.endsWith('.node')) {
11
- name += '.node';
12
- }
13
-
14
- // Get cache directory from environment
15
- const cacheDir = process.env.SEA_CACHE_DIR;
16
- if (!cacheDir) {
17
- throw new Error('SEA_CACHE_DIR not set - bindings shim requires SEA context');
18
- }
19
-
20
- // Construct path to native module in cache
21
- const binaryPath = path.join(cacheDir, name);
22
-
23
- // Load using process.dlopen
24
- const exports = {};
25
- try {
26
- process.dlopen({ exports }, binaryPath);
27
- return exports;
28
- } catch (err) {
29
- throw new Error(`Could not load native module "${name}" from SEA cache: ${err.message}`);
30
- }
31
- };