seabox 0.1.0-beta.4 ā 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 +60 -233
- package/bin/seabox-rebuild.mjs +11 -18
- package/bin/seabox.mjs +39 -27
- package/lib/blob.mjs +2 -1
- package/lib/build.mjs +8 -6
- package/lib/config.mjs +11 -2
- package/lib/diagnostics.mjs +203 -0
- package/lib/fetch-node.mjs +5 -4
- package/lib/inject.mjs +19 -35
- package/lib/multi-target-builder.mjs +188 -103
- package/lib/native-scanner.mjs +7 -14
- package/lib/require-shim.mjs +7 -5
- package/lib/rolldown-bundler.mjs +13 -17
- package/lib/unsign.cjs +26 -16
- package/package.json +2 -1
|
@@ -11,7 +11,6 @@ import { fileURLToPath } from 'url';
|
|
|
11
11
|
import Module from 'module';
|
|
12
12
|
|
|
13
13
|
import { bundleWithRollup } from './rolldown-bundler.mjs';
|
|
14
|
-
import { scanDependenciesForNativeModules } from './native-scanner.mjs';
|
|
15
14
|
import { BuildCache } from './build-cache.mjs';
|
|
16
15
|
import { parseTarget } from './config.mjs';
|
|
17
16
|
import { generateManifest, serializeManifest } from './manifest.mjs';
|
|
@@ -21,6 +20,7 @@ import { injectBlob } from './inject.mjs';
|
|
|
21
20
|
import { generateEncryptionKey, encryptAssets, keyToObfuscatedCode } from './crypto-assets.mjs';
|
|
22
21
|
import { obfuscateBootstrap } from './obfuscate.mjs';
|
|
23
22
|
import { bundleEntry } from './entry-bundler.mjs';
|
|
23
|
+
import * as diag from './diagnostics.mjs';
|
|
24
24
|
|
|
25
25
|
const __filename = fileURLToPath(import.meta.url);
|
|
26
26
|
const __dirname = path.dirname(__filename);
|
|
@@ -37,8 +37,11 @@ export class MultiTargetBuilder {
|
|
|
37
37
|
constructor(config, projectRoot = process.cwd()) {
|
|
38
38
|
this.config = config;
|
|
39
39
|
this.projectRoot = projectRoot;
|
|
40
|
-
this.cache = new BuildCache(path.join(projectRoot, '.seabox-cache'));
|
|
40
|
+
this.cache = new BuildCache(path.join(projectRoot, 'node_modules', '.cache', '.seabox-cache'));
|
|
41
41
|
this.verbose = config.verbose || false;
|
|
42
|
+
|
|
43
|
+
// Set verbose mode for diagnostics
|
|
44
|
+
diag.setVerbose(this.verbose);
|
|
42
45
|
}
|
|
43
46
|
|
|
44
47
|
/**
|
|
@@ -46,43 +49,39 @@ export class MultiTargetBuilder {
|
|
|
46
49
|
* @returns {Promise<Array<{target: string, path: string}>>}
|
|
47
50
|
*/
|
|
48
51
|
async buildAll() {
|
|
49
|
-
|
|
50
|
-
console.log('āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā\n');
|
|
52
|
+
diag.header('Seabox Multi-Target Build');
|
|
51
53
|
|
|
52
54
|
// Step 1: Bundle entry once (platform-agnostic JavaScript)
|
|
53
55
|
const { bundledPath, nativeModules, detectedAssets } = await this.bundleEntry();
|
|
54
56
|
|
|
55
57
|
if (this.verbose && detectedAssets.size > 0) {
|
|
56
|
-
|
|
58
|
+
diag.separator();
|
|
59
|
+
diag.info('Auto-detected assets:');
|
|
57
60
|
for (const assetPath of detectedAssets) {
|
|
58
|
-
|
|
61
|
+
diag.listItem(assetPath, 1);
|
|
59
62
|
}
|
|
60
63
|
}
|
|
61
64
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
if (this.verbose && allNativeModules.size > 0) {
|
|
69
|
-
console.log('\nš¦ Native modules detected:');
|
|
70
|
-
for (const [name, info] of allNativeModules) {
|
|
71
|
-
console.log(` - ${name}: ${info.packageRoot}`);
|
|
65
|
+
if (this.verbose && nativeModules.size > 0) {
|
|
66
|
+
diag.separator();
|
|
67
|
+
diag.info('Native modules detected:');
|
|
68
|
+
for (const [name, info] of nativeModules) {
|
|
69
|
+
diag.listItem(`${name}: ${info.packageRoot}`, 1);
|
|
72
70
|
}
|
|
73
71
|
}
|
|
74
72
|
|
|
75
|
-
// Step
|
|
76
|
-
|
|
73
|
+
// Step 2: Build all targets (can be parallelized)
|
|
74
|
+
diag.separator();
|
|
75
|
+
diag.info(`Building ${this.config.outputs.length} target(s)...`);
|
|
76
|
+
diag.separator();
|
|
77
77
|
|
|
78
78
|
const buildPromises = this.config.outputs.map((output, index) =>
|
|
79
|
-
this.buildTarget(output, bundledPath,
|
|
79
|
+
this.buildTarget(output, bundledPath, nativeModules, detectedAssets, index + 1)
|
|
80
80
|
);
|
|
81
81
|
|
|
82
82
|
const results = await Promise.all(buildPromises);
|
|
83
83
|
|
|
84
|
-
|
|
85
|
-
console.log('āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā\n');
|
|
84
|
+
diag.summary('All builds completed successfully!');
|
|
86
85
|
|
|
87
86
|
return results;
|
|
88
87
|
}
|
|
@@ -92,7 +91,7 @@ export class MultiTargetBuilder {
|
|
|
92
91
|
* @returns {Promise<{bundledPath: string, nativeModules: Map}>}
|
|
93
92
|
*/
|
|
94
93
|
async bundleEntry() {
|
|
95
|
-
|
|
94
|
+
diag.step(1, 6, 'Bundling application with Rollup...');
|
|
96
95
|
|
|
97
96
|
const entryPath = path.resolve(this.projectRoot, this.config.entry);
|
|
98
97
|
|
|
@@ -114,45 +113,12 @@ export class MultiTargetBuilder {
|
|
|
114
113
|
this.verbose
|
|
115
114
|
);
|
|
116
115
|
|
|
117
|
-
|
|
116
|
+
diag.success(`Bundle created: ${bundledPath}`);
|
|
118
117
|
|
|
119
118
|
return result;
|
|
120
119
|
}
|
|
121
120
|
|
|
122
|
-
/**
|
|
123
|
-
* Scan node_modules for native modules
|
|
124
|
-
* @returns {Promise<Array>}
|
|
125
|
-
*/
|
|
126
|
-
async scanNativeModules() {
|
|
127
|
-
if (this.verbose) {
|
|
128
|
-
console.log('\n[2/6] š Scanning node_modules for native modules...');
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
const nativeModules = await scanDependenciesForNativeModules(this.projectRoot, this.verbose);
|
|
132
|
-
|
|
133
|
-
return nativeModules;
|
|
134
|
-
}
|
|
135
121
|
|
|
136
|
-
/**
|
|
137
|
-
* Merge detected native modules from bundler and scanner
|
|
138
|
-
*/
|
|
139
|
-
mergeNativeModules(bundlerModules, scannedModules) {
|
|
140
|
-
const merged = new Map(bundlerModules);
|
|
141
|
-
|
|
142
|
-
// Add scanned modules that weren't detected during bundling
|
|
143
|
-
for (const scanned of scannedModules) {
|
|
144
|
-
if (!merged.has(scanned.name)) {
|
|
145
|
-
merged.set(scanned.name, {
|
|
146
|
-
packageRoot: scanned.path,
|
|
147
|
-
moduleName: scanned.name,
|
|
148
|
-
buildPath: path.join(scanned.path, 'build/Release'),
|
|
149
|
-
binaryFiles: scanned.binaryFiles
|
|
150
|
-
});
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
return merged;
|
|
155
|
-
}
|
|
156
122
|
|
|
157
123
|
/**
|
|
158
124
|
* Build a single target
|
|
@@ -167,8 +133,7 @@ export class MultiTargetBuilder {
|
|
|
167
133
|
const { target, path: outputPath, output: executableName } = outputConfig;
|
|
168
134
|
const { nodeVersion, platform, arch } = parseTarget(target);
|
|
169
135
|
|
|
170
|
-
|
|
171
|
-
console.log('āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā');
|
|
136
|
+
diag.subheader(`[Build ${buildNumber}] Target: ${target}`);
|
|
172
137
|
|
|
173
138
|
// Step 1: Rebuild native modules for this target
|
|
174
139
|
const rebuiltModules = await this.rebuildNativeModulesForTarget(
|
|
@@ -213,6 +178,41 @@ export class MultiTargetBuilder {
|
|
|
213
178
|
|
|
214
179
|
const allAssets = Array.from(assetMap.values());
|
|
215
180
|
|
|
181
|
+
// Verbose logging: List all embedded assets
|
|
182
|
+
if (this.verbose) {
|
|
183
|
+
diag.separator();
|
|
184
|
+
diag.verbose(`Embedded Assets (${allAssets.length} total):`, 1);
|
|
185
|
+
|
|
186
|
+
const nativeAssets = allAssets.filter(a => a.assetKey.includes('.node'));
|
|
187
|
+
const libraryAssets = allAssets.filter(a => a.isBinary && !a.assetKey.includes('.node'));
|
|
188
|
+
const regularAssets = allAssets.filter(a => !a.isBinary);
|
|
189
|
+
|
|
190
|
+
if (nativeAssets.length > 0) {
|
|
191
|
+
diag.verbose(`Native Modules (${nativeAssets.length}):`, 1);
|
|
192
|
+
for (const asset of nativeAssets) {
|
|
193
|
+
const size = diag.formatSize(fs.statSync(asset.sourcePath).size);
|
|
194
|
+
const displayName = path.basename(asset.assetKey);
|
|
195
|
+
diag.verbose(`- ${displayName} (${size})`, 2);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
if (libraryAssets.length > 0) {
|
|
200
|
+
diag.verbose(`Platform Libraries (${libraryAssets.length}):`, 1);
|
|
201
|
+
for (const asset of libraryAssets) {
|
|
202
|
+
const size = diag.formatSize(fs.statSync(asset.sourcePath).size);
|
|
203
|
+
diag.verbose(`- ${asset.assetKey} (${size})`, 2);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
if (regularAssets.length > 0) {
|
|
208
|
+
diag.verbose(`Regular Assets (${regularAssets.length}):`, 1);
|
|
209
|
+
for (const asset of regularAssets) {
|
|
210
|
+
const size = diag.formatSize(fs.statSync(asset.sourcePath).size);
|
|
211
|
+
diag.verbose(`- ${asset.assetKey} (${size})`, 2);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
216
|
// Step 7: Generate SEA
|
|
217
217
|
await this.generateSEAForTarget({
|
|
218
218
|
assets: allAssets,
|
|
@@ -228,7 +228,12 @@ export class MultiTargetBuilder {
|
|
|
228
228
|
});
|
|
229
229
|
|
|
230
230
|
const finalPath = path.join(outputPath, executableName);
|
|
231
|
-
|
|
231
|
+
diag.success(`Build complete: ${finalPath}`);
|
|
232
|
+
|
|
233
|
+
// Apply custom signing if configured
|
|
234
|
+
if (this.config.sign) {
|
|
235
|
+
await this.applyCustomSigning(finalPath, target, platform, arch, nodeVersion, buildNumber);
|
|
236
|
+
}
|
|
232
237
|
|
|
233
238
|
return {
|
|
234
239
|
target,
|
|
@@ -244,24 +249,42 @@ export class MultiTargetBuilder {
|
|
|
244
249
|
return [];
|
|
245
250
|
}
|
|
246
251
|
|
|
247
|
-
|
|
252
|
+
diag.buildStep(buildNumber, 1, `Rebuilding ${nativeModules.size} native module(s)...`);
|
|
248
253
|
|
|
249
254
|
const rebuiltAssets = [];
|
|
250
255
|
const { platform, arch } = parseTarget(target);
|
|
251
256
|
|
|
252
|
-
for (const [
|
|
257
|
+
for (const [moduleKey, moduleInfo] of nativeModules) {
|
|
258
|
+
const moduleName = moduleInfo.moduleName || path.basename(moduleKey, '.node');
|
|
259
|
+
|
|
253
260
|
try {
|
|
261
|
+
// Skip rebuilding if this is already a prebuild for the target platform
|
|
262
|
+
if (moduleInfo.isPrebuild) {
|
|
263
|
+
diag.verbose(`Using prebuild: ${moduleName}`, 2);
|
|
264
|
+
|
|
265
|
+
const relativeFromProject = path.relative(this.projectRoot, moduleInfo.binaryPath).replace(/\\/g, '/');
|
|
266
|
+
|
|
267
|
+
rebuiltAssets.push({
|
|
268
|
+
sourcePath: moduleInfo.binaryPath,
|
|
269
|
+
assetKey: relativeFromProject,
|
|
270
|
+
isBinary: true,
|
|
271
|
+
hash: await this.computeHash(moduleInfo.binaryPath)
|
|
272
|
+
});
|
|
273
|
+
continue;
|
|
274
|
+
}
|
|
275
|
+
|
|
254
276
|
// Check cache first
|
|
255
277
|
const cachedBuild = this.cache.getCachedNativeBuild(moduleInfo.packageRoot, target);
|
|
256
278
|
|
|
257
279
|
if (cachedBuild) {
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
280
|
+
diag.verbose(`Using cached build: ${moduleName}`, 2);
|
|
281
|
+
|
|
282
|
+
// Use relative path from project root
|
|
283
|
+
const relativeFromProject = path.relative(this.projectRoot, cachedBuild).replace(/\\/g, '/');
|
|
261
284
|
|
|
262
285
|
rebuiltAssets.push({
|
|
263
286
|
sourcePath: cachedBuild,
|
|
264
|
-
assetKey:
|
|
287
|
+
assetKey: relativeFromProject,
|
|
265
288
|
isBinary: true,
|
|
266
289
|
hash: await this.computeHash(cachedBuild)
|
|
267
290
|
});
|
|
@@ -269,9 +292,7 @@ export class MultiTargetBuilder {
|
|
|
269
292
|
}
|
|
270
293
|
|
|
271
294
|
// Rebuild the module
|
|
272
|
-
|
|
273
|
-
console.log(` š§ Rebuilding: ${moduleName}`);
|
|
274
|
-
}
|
|
295
|
+
diag.verbose(`Rebuilding: ${moduleName}`, 2);
|
|
275
296
|
|
|
276
297
|
await this.rebuildNativeModule(moduleInfo.packageRoot, target);
|
|
277
298
|
|
|
@@ -282,23 +303,24 @@ export class MultiTargetBuilder {
|
|
|
282
303
|
// Cache the build
|
|
283
304
|
this.cache.cacheNativeBuild(moduleInfo.packageRoot, target, builtPath);
|
|
284
305
|
|
|
306
|
+
// Use relative path from project root
|
|
307
|
+
const relativeFromProject = path.relative(this.projectRoot, builtPath).replace(/\\/g, '/');
|
|
308
|
+
|
|
285
309
|
rebuiltAssets.push({
|
|
286
310
|
sourcePath: builtPath,
|
|
287
|
-
assetKey:
|
|
311
|
+
assetKey: relativeFromProject,
|
|
288
312
|
isBinary: true,
|
|
289
313
|
hash: await this.computeHash(builtPath)
|
|
290
314
|
});
|
|
291
315
|
|
|
292
|
-
|
|
293
|
-
console.log(` ā Built: ${moduleName} -> ${builtPath}`);
|
|
294
|
-
}
|
|
316
|
+
diag.verbose(`Built: ${moduleName} -> ${builtPath}`, 2);
|
|
295
317
|
}
|
|
296
318
|
} catch (err) {
|
|
297
|
-
|
|
319
|
+
diag.warn(`Failed to rebuild ${moduleName}: ${err.message}`, 2);
|
|
298
320
|
}
|
|
299
321
|
}
|
|
300
322
|
|
|
301
|
-
|
|
323
|
+
diag.success('Native modules processed');
|
|
302
324
|
return rebuiltAssets;
|
|
303
325
|
}
|
|
304
326
|
|
|
@@ -349,33 +371,43 @@ export class MultiTargetBuilder {
|
|
|
349
371
|
|
|
350
372
|
/**
|
|
351
373
|
* Collect platform-specific libraries (DLLs, SOs, DYLIBs) that need filesystem extraction
|
|
352
|
-
*
|
|
374
|
+
* Only includes libraries explicitly listed in config patterns - no automatic discovery.
|
|
375
|
+
* Libraries referenced in code (e.g., path.join(__dirname, './lib/foo.dll')) are
|
|
376
|
+
* already captured by the bundler's asset detection.
|
|
377
|
+
*
|
|
378
|
+
* @param {string[]} libraryPatterns - Explicit glob patterns for libraries from config
|
|
353
379
|
* @param {string} platform - Target platform
|
|
354
380
|
* @param {string} arch - Target architecture
|
|
355
381
|
* @param {number} buildNumber - Build number for display
|
|
356
382
|
*/
|
|
357
383
|
async collectPlatformLibraries(libraryPatterns, platform, arch, buildNumber) {
|
|
358
|
-
//
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
? libraryPatterns
|
|
362
|
-
: getDefaultLibraryPatterns(platform);
|
|
363
|
-
|
|
364
|
-
if (!patterns || patterns.length === 0) {
|
|
384
|
+
// Only process explicitly configured library patterns
|
|
385
|
+
// Do NOT use default patterns - this prevents automatic inclusion of unrelated DLLs
|
|
386
|
+
if (!libraryPatterns || libraryPatterns.length === 0) {
|
|
365
387
|
return [];
|
|
366
388
|
}
|
|
367
389
|
|
|
368
|
-
|
|
390
|
+
diag.buildStep(buildNumber, 4, `Collecting platform libraries (${platform})...`);
|
|
369
391
|
|
|
370
392
|
const { glob } = await import('glob');
|
|
371
393
|
const libraries = [];
|
|
372
394
|
|
|
373
|
-
for (const pattern of
|
|
395
|
+
for (const pattern of libraryPatterns) {
|
|
374
396
|
const matches = await glob(pattern, {
|
|
375
397
|
cwd: this.projectRoot,
|
|
376
398
|
nodir: true,
|
|
377
399
|
absolute: false,
|
|
378
|
-
ignore: [
|
|
400
|
+
ignore: [
|
|
401
|
+
'**/node_modules/**',
|
|
402
|
+
'**/.git/**',
|
|
403
|
+
'**/dist/**',
|
|
404
|
+
'**/build/**',
|
|
405
|
+
'**/out/**',
|
|
406
|
+
'**/bin/**',
|
|
407
|
+
'**/obj/**',
|
|
408
|
+
'**/tools/**',
|
|
409
|
+
'**/.seabox-cache/**'
|
|
410
|
+
]
|
|
379
411
|
});
|
|
380
412
|
|
|
381
413
|
for (const match of matches) {
|
|
@@ -391,7 +423,7 @@ export class MultiTargetBuilder {
|
|
|
391
423
|
}
|
|
392
424
|
|
|
393
425
|
if (libraries.length > 0) {
|
|
394
|
-
|
|
426
|
+
diag.success(`Collected ${libraries.length} library file(s)`);
|
|
395
427
|
}
|
|
396
428
|
|
|
397
429
|
return libraries;
|
|
@@ -407,7 +439,7 @@ export class MultiTargetBuilder {
|
|
|
407
439
|
return [];
|
|
408
440
|
}
|
|
409
441
|
|
|
410
|
-
|
|
442
|
+
diag.buildStep(buildNumber, 2, 'Collecting config assets...');
|
|
411
443
|
|
|
412
444
|
const { glob } = await import('glob');
|
|
413
445
|
const assets = [];
|
|
@@ -417,7 +449,17 @@ export class MultiTargetBuilder {
|
|
|
417
449
|
cwd: this.projectRoot,
|
|
418
450
|
nodir: true,
|
|
419
451
|
absolute: false,
|
|
420
|
-
ignore: [
|
|
452
|
+
ignore: [
|
|
453
|
+
'**/node_modules/**',
|
|
454
|
+
'**/.git/**',
|
|
455
|
+
'**/dist/**',
|
|
456
|
+
'**/build/**',
|
|
457
|
+
'**/out/**',
|
|
458
|
+
'**/bin/**',
|
|
459
|
+
'**/obj/**',
|
|
460
|
+
'**/tools/**',
|
|
461
|
+
'**/.seabox-cache/**'
|
|
462
|
+
]
|
|
421
463
|
});
|
|
422
464
|
|
|
423
465
|
for (const match of matches) {
|
|
@@ -433,7 +475,7 @@ export class MultiTargetBuilder {
|
|
|
433
475
|
}
|
|
434
476
|
|
|
435
477
|
if (assets.length > 0) {
|
|
436
|
-
|
|
478
|
+
diag.success(`Collected ${assets.length} config asset(s)`);
|
|
437
479
|
}
|
|
438
480
|
|
|
439
481
|
return assets;
|
|
@@ -449,7 +491,7 @@ export class MultiTargetBuilder {
|
|
|
449
491
|
return [];
|
|
450
492
|
}
|
|
451
493
|
|
|
452
|
-
|
|
494
|
+
diag.buildStep(buildNumber, 3, 'Processing auto-detected assets...');
|
|
453
495
|
|
|
454
496
|
const assets = [];
|
|
455
497
|
|
|
@@ -468,7 +510,7 @@ export class MultiTargetBuilder {
|
|
|
468
510
|
}
|
|
469
511
|
|
|
470
512
|
if (assets.length > 0) {
|
|
471
|
-
|
|
513
|
+
diag.success(`Collected ${assets.length} auto-detected asset(s)`);
|
|
472
514
|
}
|
|
473
515
|
|
|
474
516
|
return assets;
|
|
@@ -487,7 +529,7 @@ export class MultiTargetBuilder {
|
|
|
487
529
|
* Prepare final entry with bootstrap
|
|
488
530
|
*/
|
|
489
531
|
async prepareFinalEntry(bundledEntryPath, buildNumber) {
|
|
490
|
-
|
|
532
|
+
diag.buildStep(buildNumber, 3, 'Preparing bootstrap...');
|
|
491
533
|
|
|
492
534
|
const bootstrapPath = path.join(__dirname, 'bootstrap.cjs');
|
|
493
535
|
const bootstrapContent = fs.readFileSync(bootstrapPath, 'utf8');
|
|
@@ -509,9 +551,7 @@ const SEA_ENCRYPTED_ASSETS = new Set([]);
|
|
|
509
551
|
" 'use strict';\n" + encryptionSetup
|
|
510
552
|
);
|
|
511
553
|
|
|
512
|
-
|
|
513
|
-
console.log(' ā Encryption enabled and bootstrap obfuscated');
|
|
514
|
-
}
|
|
554
|
+
diag.verbose('Encryption enabled and bootstrap obfuscated', 2);
|
|
515
555
|
|
|
516
556
|
augmentedBootstrap = obfuscateBootstrap(augmentedBootstrap);
|
|
517
557
|
}
|
|
@@ -526,7 +566,7 @@ const SEA_ENCRYPTED_ASSETS = new Set([]);
|
|
|
526
566
|
const finalPath = bundledEntryPath.replace('.js', '-final.js');
|
|
527
567
|
fs.writeFileSync(finalPath, finalEntry, 'utf8');
|
|
528
568
|
|
|
529
|
-
|
|
569
|
+
diag.success('Bootstrap prepared');
|
|
530
570
|
return finalPath;
|
|
531
571
|
}
|
|
532
572
|
|
|
@@ -547,7 +587,7 @@ const SEA_ENCRYPTED_ASSETS = new Set([]);
|
|
|
547
587
|
buildNumber
|
|
548
588
|
} = options;
|
|
549
589
|
|
|
550
|
-
|
|
590
|
+
diag.buildStep(buildNumber, 4, 'Generating SEA blob...');
|
|
551
591
|
|
|
552
592
|
// Generate manifest
|
|
553
593
|
const manifest = generateManifest(
|
|
@@ -583,16 +623,16 @@ const SEA_ENCRYPTED_ASSETS = new Set([]);
|
|
|
583
623
|
|
|
584
624
|
// Generate blob
|
|
585
625
|
await generateBlob(seaConfigPath, process.execPath);
|
|
586
|
-
|
|
626
|
+
diag.success('SEA blob generated');
|
|
587
627
|
|
|
588
628
|
// Fetch Node binary
|
|
589
|
-
|
|
629
|
+
diag.buildStep(buildNumber, 5, 'Fetching Node.js binary...');
|
|
590
630
|
const cacheDir = path.join(this.projectRoot, 'node_modules', '.cache', 'sea-node-binaries');
|
|
591
631
|
const nodeBinary = await fetchNodeBinary(nodeVersion, platform, arch, cacheDir);
|
|
592
|
-
|
|
632
|
+
diag.success('Node binary ready');
|
|
593
633
|
|
|
594
634
|
// Inject blob
|
|
595
|
-
|
|
635
|
+
diag.buildStep(buildNumber, 6, 'Injecting blob into executable...');
|
|
596
636
|
const outputExe = path.join(outputDir, executableName);
|
|
597
637
|
await injectBlob(nodeBinary, blobOutputPath, outputExe, platform, this.verbose, rcedit);
|
|
598
638
|
|
|
@@ -601,8 +641,8 @@ const SEA_ENCRYPTED_ASSETS = new Set([]);
|
|
|
601
641
|
fs.rmSync(tempDir, { recursive: true, force: true });
|
|
602
642
|
}
|
|
603
643
|
|
|
604
|
-
const
|
|
605
|
-
|
|
644
|
+
const size = diag.formatSize(fs.statSync(outputExe).size);
|
|
645
|
+
diag.success(`Injected (${size})`);
|
|
606
646
|
}
|
|
607
647
|
|
|
608
648
|
/**
|
|
@@ -617,4 +657,49 @@ const SEA_ENCRYPTED_ASSETS = new Set([]);
|
|
|
617
657
|
stream.on('error', reject);
|
|
618
658
|
});
|
|
619
659
|
}
|
|
660
|
+
|
|
661
|
+
/**
|
|
662
|
+
* Apply custom signing script
|
|
663
|
+
* @param {string} exePath - Path to executable
|
|
664
|
+
* @param {string} target - Build target
|
|
665
|
+
* @param {string} platform - Platform (win32, linux, darwin)
|
|
666
|
+
* @param {string} arch - Architecture (x64, arm64)
|
|
667
|
+
* @param {string} nodeVersion - Node version
|
|
668
|
+
* @param {number} buildNumber - Build number
|
|
669
|
+
*/
|
|
670
|
+
async applyCustomSigning(exePath, target, platform, arch, nodeVersion, buildNumber) {
|
|
671
|
+
diag.buildStep(buildNumber, 7, 'Applying custom signing...');
|
|
672
|
+
|
|
673
|
+
try {
|
|
674
|
+
const signScriptPath = path.resolve(this.projectRoot, this.config.sign);
|
|
675
|
+
|
|
676
|
+
if (!fs.existsSync(signScriptPath)) {
|
|
677
|
+
throw new Error(`Signing script not found: ${signScriptPath}`);
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
// Dynamic import of the signing script - convert to file:// URL for Windows
|
|
681
|
+
const { pathToFileURL } = await import('url');
|
|
682
|
+
const signModuleURL = pathToFileURL(signScriptPath).href;
|
|
683
|
+
const signModule = await import(signModuleURL);
|
|
684
|
+
const signFunction = signModule.default;
|
|
685
|
+
|
|
686
|
+
if (typeof signFunction !== 'function') {
|
|
687
|
+
throw new Error('Signing script must export a default function');
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
// Call the signing function with config
|
|
691
|
+
await signFunction({
|
|
692
|
+
exePath: path.resolve(exePath),
|
|
693
|
+
target,
|
|
694
|
+
platform,
|
|
695
|
+
arch,
|
|
696
|
+
nodeVersion,
|
|
697
|
+
projectRoot: this.projectRoot
|
|
698
|
+
});
|
|
699
|
+
|
|
700
|
+
diag.success('Custom signing applied');
|
|
701
|
+
} catch (error) {
|
|
702
|
+
throw new Error(`Signing failed: ${error.message}`);
|
|
703
|
+
}
|
|
704
|
+
}
|
|
620
705
|
}
|
package/lib/native-scanner.mjs
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
import fs from 'fs/promises';
|
|
7
7
|
import fsSync from 'fs';
|
|
8
8
|
import path from 'path';
|
|
9
|
+
import * as diag from './diagnostics.mjs';
|
|
9
10
|
|
|
10
11
|
/**
|
|
11
12
|
* @typedef {Object} NativeModuleMetadata
|
|
@@ -27,15 +28,11 @@ export async function scanDependenciesForNativeModules(projectRoot, verbose = fa
|
|
|
27
28
|
const nodeModulesPath = path.join(projectRoot, 'node_modules');
|
|
28
29
|
|
|
29
30
|
if (!fsSync.existsSync(nodeModulesPath)) {
|
|
30
|
-
|
|
31
|
-
console.log('[NativeScanner] No node_modules directory found');
|
|
32
|
-
}
|
|
31
|
+
diag.verbose('No node_modules directory found');
|
|
33
32
|
return [];
|
|
34
33
|
}
|
|
35
34
|
|
|
36
|
-
|
|
37
|
-
console.log('[NativeScanner] Scanning node_modules for native modules');
|
|
38
|
-
}
|
|
35
|
+
diag.verbose('Scanning node_modules for native modules');
|
|
39
36
|
|
|
40
37
|
/**
|
|
41
38
|
* Recursively scan a directory for packages
|
|
@@ -71,9 +68,7 @@ export async function scanDependenciesForNativeModules(projectRoot, verbose = fa
|
|
|
71
68
|
}
|
|
72
69
|
}
|
|
73
70
|
} catch (err) {
|
|
74
|
-
|
|
75
|
-
console.warn('[NativeScanner] Error scanning directory:', dir, err.message);
|
|
76
|
-
}
|
|
71
|
+
diag.verbose(`Error scanning directory: ${dir} - ${err.message}`);
|
|
77
72
|
}
|
|
78
73
|
}
|
|
79
74
|
|
|
@@ -152,11 +147,9 @@ export async function scanDependenciesForNativeModules(projectRoot, verbose = fa
|
|
|
152
147
|
|
|
153
148
|
await scanDir(nodeModulesPath);
|
|
154
149
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
console.log(` - ${mod.name}@${mod.version} (${mod.binaryFiles.length} binaries)`);
|
|
159
|
-
}
|
|
150
|
+
diag.verbose(`Found ${nativeModules.length} native modules`);
|
|
151
|
+
for (const mod of nativeModules) {
|
|
152
|
+
diag.verbose(`- ${mod.name}@${mod.version} (${mod.binaryFiles.length} binaries)`, 1);
|
|
160
153
|
}
|
|
161
154
|
|
|
162
155
|
return nativeModules;
|
package/lib/require-shim.mjs
CHANGED
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
* SEA-aware require replacement that intercepts .node module loads
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
+
import * as diag from './diagnostics.mjs';
|
|
7
|
+
|
|
6
8
|
/**
|
|
7
9
|
* Generate the __requireSeabox shim code
|
|
8
10
|
* @returns {string} - The shim code to inject
|
|
@@ -58,9 +60,9 @@ function __requireSeabox(id) {
|
|
|
58
60
|
process.dlopen(module, resolvedPath);
|
|
59
61
|
return module.exports;
|
|
60
62
|
} else {
|
|
61
|
-
console.error('[
|
|
62
|
-
console.error('
|
|
63
|
-
console.error('
|
|
63
|
+
console.error('[X] Native module not found in map');
|
|
64
|
+
console.error(' Requested:', id);
|
|
65
|
+
console.error(' Available:', Object.keys(global.__seaNativeModuleMap));
|
|
64
66
|
}
|
|
65
67
|
}
|
|
66
68
|
}
|
|
@@ -88,7 +90,7 @@ function __requireSeabox(id) {
|
|
|
88
90
|
* @returns {Object} - { code: string, count: number }
|
|
89
91
|
*/
|
|
90
92
|
export function replaceRequireCalls(sourceCode, verbose = false) {
|
|
91
|
-
|
|
93
|
+
diag.verbose('Replacing require() calls with __requireSeabox()');
|
|
92
94
|
|
|
93
95
|
const requirePattern = /\brequire\s*\(/g;
|
|
94
96
|
let replacementCount = 0;
|
|
@@ -98,7 +100,7 @@ export function replaceRequireCalls(sourceCode, verbose = false) {
|
|
|
98
100
|
return '__requireSeabox(';
|
|
99
101
|
});
|
|
100
102
|
|
|
101
|
-
|
|
103
|
+
diag.verbose(`Replaced ${replacementCount} require() calls`);
|
|
102
104
|
|
|
103
105
|
return {
|
|
104
106
|
code: transformedCode,
|