seabox 0.1.0-beta.3 → 0.1.0-beta.4

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.
@@ -0,0 +1,199 @@
1
+ /**
2
+ * build-cache.mjs
3
+ * Build caching system to speed up incremental builds.
4
+ */
5
+
6
+ import fs from 'fs';
7
+ import path from 'path';
8
+ import crypto from 'crypto';
9
+
10
+ /**
11
+ * @typedef {Object} CacheEntry
12
+ * @property {string} hash - Content hash of input
13
+ * @property {number} timestamp - When cached
14
+ * @property {any} data - Cached data
15
+ */
16
+
17
+ export class BuildCache {
18
+ /**
19
+ * @param {string} cacheDir - Cache directory path
20
+ */
21
+ constructor(cacheDir = '.seabox-cache') {
22
+ this.cacheDir = cacheDir;
23
+ this.ensureCacheDir();
24
+ }
25
+
26
+ /**
27
+ * Ensure cache directory exists
28
+ */
29
+ ensureCacheDir() {
30
+ if (!fs.existsSync(this.cacheDir)) {
31
+ fs.mkdirSync(this.cacheDir, { recursive: true });
32
+ }
33
+
34
+ // Create subdirectories
35
+ const subdirs = ['bundles', 'natives', 'blobs'];
36
+ for (const subdir of subdirs) {
37
+ const dirPath = path.join(this.cacheDir, subdir);
38
+ if (!fs.existsSync(dirPath)) {
39
+ fs.mkdirSync(dirPath, { recursive: true });
40
+ }
41
+ }
42
+ }
43
+
44
+ /**
45
+ * Compute cache key from entry path and config
46
+ * @param {string} entryPath - Entry file path
47
+ * @param {Object} config - Build config
48
+ * @returns {string}
49
+ */
50
+ computeCacheKey(entryPath, config) {
51
+ const configStr = JSON.stringify({
52
+ bundler: config.bundler,
53
+ entry: entryPath
54
+ });
55
+
56
+ return crypto
57
+ .createHash('sha256')
58
+ .update(configStr)
59
+ .digest('hex')
60
+ .substring(0, 16);
61
+ }
62
+
63
+ /**
64
+ * Get cached bundle if valid
65
+ * @param {string} entryPath - Entry file path
66
+ * @param {Object} config - Build config
67
+ * @returns {Object|null}
68
+ */
69
+ async getCachedBundle(entryPath, config) {
70
+ const cacheKey = this.computeCacheKey(entryPath, config);
71
+ const cachePath = path.join(this.cacheDir, 'bundles', `${cacheKey}.json`);
72
+
73
+ if (!fs.existsSync(cachePath)) {
74
+ return null;
75
+ }
76
+
77
+ try {
78
+ const cached = JSON.parse(fs.readFileSync(cachePath, 'utf8'));
79
+
80
+ // Validate that source files haven't changed
81
+ if (await this.isValid(cached, entryPath)) {
82
+ return cached;
83
+ }
84
+ } catch (err) {
85
+ // Invalid cache entry
86
+ }
87
+
88
+ return null;
89
+ }
90
+
91
+ /**
92
+ * Cache a bundle result
93
+ * @param {string} entryPath - Entry file path
94
+ * @param {Object} config - Build config
95
+ * @param {Object} bundleResult - Bundle result to cache
96
+ */
97
+ async cacheBundle(entryPath, config, bundleResult) {
98
+ const cacheKey = this.computeCacheKey(entryPath, config);
99
+ const cachePath = path.join(this.cacheDir, 'bundles', `${cacheKey}.json`);
100
+
101
+ const cacheEntry = {
102
+ hash: await this.hashFile(entryPath),
103
+ timestamp: Date.now(),
104
+ data: bundleResult
105
+ };
106
+
107
+ fs.writeFileSync(cachePath, JSON.stringify(cacheEntry, null, 2));
108
+ }
109
+
110
+ /**
111
+ * Get cached native module build
112
+ * @param {string} moduleRoot - Module root path
113
+ * @param {string} target - Build target
114
+ * @returns {string|null} - Path to cached .node file
115
+ */
116
+ getCachedNativeBuild(moduleRoot, target) {
117
+ const cacheKey = crypto
118
+ .createHash('sha256')
119
+ .update(moduleRoot + target)
120
+ .digest('hex')
121
+ .substring(0, 16);
122
+
123
+ const cachePath = path.join(this.cacheDir, 'natives', `${cacheKey}.node`);
124
+
125
+ if (fs.existsSync(cachePath)) {
126
+ // Check if source has changed
127
+ const bindingGypPath = path.join(moduleRoot, 'binding.gyp');
128
+ if (fs.existsSync(bindingGypPath)) {
129
+ const cacheStats = fs.statSync(cachePath);
130
+ const sourceStats = fs.statSync(bindingGypPath);
131
+
132
+ if (cacheStats.mtime > sourceStats.mtime) {
133
+ return cachePath;
134
+ }
135
+ }
136
+ }
137
+
138
+ return null;
139
+ }
140
+
141
+ /**
142
+ * Cache a native module build
143
+ * @param {string} moduleRoot - Module root path
144
+ * @param {string} target - Build target
145
+ * @param {string} builtBinaryPath - Path to built .node file
146
+ */
147
+ cacheNativeBuild(moduleRoot, target, builtBinaryPath) {
148
+ const cacheKey = crypto
149
+ .createHash('sha256')
150
+ .update(moduleRoot + target)
151
+ .digest('hex')
152
+ .substring(0, 16);
153
+
154
+ const cachePath = path.join(this.cacheDir, 'natives', `${cacheKey}.node`);
155
+
156
+ fs.copyFileSync(builtBinaryPath, cachePath);
157
+ }
158
+
159
+ /**
160
+ * Check if cache entry is valid
161
+ * @param {CacheEntry} cached - Cached entry
162
+ * @param {string} filePath - Source file path
163
+ * @returns {Promise<boolean>}
164
+ */
165
+ async isValid(cached, filePath) {
166
+ if (!fs.existsSync(filePath)) {
167
+ return false;
168
+ }
169
+
170
+ const currentHash = await this.hashFile(filePath);
171
+ return currentHash === cached.hash;
172
+ }
173
+
174
+ /**
175
+ * Compute hash of a file
176
+ * @param {string} filePath - File path
177
+ * @returns {Promise<string>}
178
+ */
179
+ hashFile(filePath) {
180
+ return new Promise((resolve, reject) => {
181
+ const hash = crypto.createHash('sha256');
182
+ const stream = fs.createReadStream(filePath);
183
+
184
+ stream.on('data', chunk => hash.update(chunk));
185
+ stream.on('end', () => resolve(hash.digest('hex')));
186
+ stream.on('error', reject);
187
+ });
188
+ }
189
+
190
+ /**
191
+ * Clear cache
192
+ */
193
+ clear() {
194
+ if (fs.existsSync(this.cacheDir)) {
195
+ fs.rmSync(this.cacheDir, { recursive: true, force: true });
196
+ }
197
+ this.ensureCacheDir();
198
+ }
199
+ }
package/lib/build.mjs ADDED
@@ -0,0 +1,75 @@
1
+ /**
2
+ * build.mjs (v2)
3
+ * Main build orchestrator using v2 architecture with multi-target support.
4
+ */
5
+
6
+ import { loadConfig } from './config.mjs';
7
+ import { MultiTargetBuilder } from './multi-target-builder.mjs';
8
+ import fs from 'fs';
9
+
10
+ /**
11
+ * Main build function for v2
12
+ * @param {Object} options - Build options
13
+ * @param {string} [options.configPath] - Path to config file
14
+ * @param {boolean} [options.verbose] - Enable verbose logging
15
+ * @param {boolean} [options.debug] - Keep temporary build files
16
+ * @param {string} [options.projectRoot] - Project root directory
17
+ */
18
+ export async function build(options = {}) {
19
+ // Support both options object and legacy process.argv parsing
20
+ let configPath = options.configPath;
21
+ let verbose = options.verbose;
22
+ let debug = options.debug;
23
+ let projectRoot = options.projectRoot || process.cwd();
24
+
25
+ // Fallback to process.argv if called without options
26
+ if (!options.configPath && !options.verbose && !options.debug) {
27
+ const args = process.argv.slice(2);
28
+ configPath = args.includes('--config') ? args[args.indexOf('--config') + 1] : null;
29
+ verbose = args.includes('--verbose');
30
+ debug = args.includes('--debug');
31
+ }
32
+
33
+ try {
34
+ // Load configuration
35
+ const config = loadConfig(configPath, projectRoot);
36
+
37
+ // Config should exist if we got here (CLI checks first)
38
+ if (!config) {
39
+ throw new Error('No configuration found. Run: npx seabox init');
40
+ }
41
+
42
+ // Override verbose from CLI if specified
43
+ if (verbose) {
44
+ config.verbose = true;
45
+ }
46
+
47
+ // Create and run multi-target builder
48
+ const builder = new MultiTargetBuilder(config, projectRoot);
49
+ const results = await builder.buildAll();
50
+
51
+ // Display results
52
+ console.log('📦 Output files:');
53
+ for (const result of results) {
54
+ const stats = fs.statSync(result.path);
55
+ const sizeMB = (stats.size / 1024 / 1024).toFixed(2);
56
+ console.log(` ${result.target}: ${result.path} (${sizeMB} MB)`);
57
+ }
58
+ console.log('');
59
+
60
+ return results;
61
+ } catch (error) {
62
+ console.error('\n❌ Build failed:', error.message);
63
+ if (verbose || process.argv.includes('--verbose')) {
64
+ console.error(error.stack);
65
+ }
66
+ throw error;
67
+ }
68
+ }
69
+
70
+ // Run if called directly
71
+ if (import.meta.url === `file://${process.argv[1]}`) {
72
+ build().catch(error => {
73
+ process.exit(1);
74
+ });
75
+ }
package/lib/config.mjs ADDED
@@ -0,0 +1,234 @@
1
+ /**
2
+ * config.mjs
3
+ * Load and validate SEA configuration from seabox.config.json or package.json.
4
+ */
5
+
6
+ import fs from 'fs';
7
+ import path from 'path';
8
+
9
+ /**
10
+ * @typedef {Object} OutputTarget
11
+ * @property {string} path - Output directory
12
+ * @property {string} target - Node version and platform (e.g., node24.11.0-win32-x64)
13
+ * @property {string} output - Executable filename
14
+ * @property {string[]} [libraries] - Glob patterns for shared libraries (DLLs/SOs) requiring filesystem extraction
15
+ * @property {Object} [rcedit] - Windows executable customization options
16
+ */
17
+
18
+ /**
19
+ * @typedef {Object} BundlerConfig
20
+ * @property {string[]} [external] - Modules to externalize
21
+ * @property {Array} [plugins] - Rollup plugins
22
+ * @property {boolean} [minify] - Minify output
23
+ * @property {boolean} [sourcemap] - Generate sourcemaps
24
+ */
25
+
26
+ /**
27
+ * @typedef {Object} SeaboxConfig
28
+ * @property {string} entry - Entry point source file
29
+ * @property {OutputTarget[]} outputs - Multi-target output configurations
30
+ * @property {string[]} [assets] - Glob patterns for assets to embed (auto-detected assets are merged)
31
+ * @property {BundlerConfig} [bundler] - Bundler configuration
32
+ * @property {boolean} [encryptAssets] - Enable asset encryption
33
+ * @property {string[]} [encryptExclude] - Assets to exclude from encryption
34
+ * @property {boolean} [useSnapshot] - Enable V8 snapshot
35
+ * @property {boolean} [useCodeCache] - Enable V8 code cache
36
+ * @property {string} [cacheLocation] - Cache directory for extracted binaries
37
+ * @property {boolean} [verbose] - Enable verbose logging
38
+ */
39
+
40
+ /**
41
+ * Load SEA configuration from seabox.config.json or package.json
42
+ * @param {string} [configPath] - Optional path to config file
43
+ * @param {string} [projectRoot] - Project root directory
44
+ * @returns {SeaboxConfig|null} Config object or null if not found
45
+ */
46
+ export function loadConfig(configPath, projectRoot = process.cwd()) {
47
+ let config;
48
+
49
+ // Priority: CLI arg > seabox.config.json > package.json "seabox" field
50
+ if (configPath) {
51
+ // If explicit config path provided, it must exist
52
+ const fullPath = path.resolve(projectRoot, configPath);
53
+ if (!fs.existsSync(fullPath)) {
54
+ throw new Error(`Config file not found: ${fullPath}`);
55
+ }
56
+ config = JSON.parse(fs.readFileSync(fullPath, 'utf8'));
57
+ } else if (fs.existsSync(path.join(projectRoot, 'seabox.config.json'))) {
58
+ // Check for seabox.config.json
59
+ const configFile = path.join(projectRoot, 'seabox.config.json');
60
+ config = JSON.parse(fs.readFileSync(configFile, 'utf8'));
61
+ } else {
62
+ // Check for "seabox" field in package.json
63
+ const pkgPath = path.join(projectRoot, 'package.json');
64
+
65
+ if (fs.existsSync(pkgPath)) {
66
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
67
+
68
+ if (pkg.seabox) {
69
+ config = normalizeConfig(pkg.seabox, pkg);
70
+ } else {
71
+ // No config found anywhere - return null
72
+ return null;
73
+ }
74
+ } else {
75
+ // No package.json - return null
76
+ return null;
77
+ }
78
+ }
79
+
80
+ validateConfig(config);
81
+
82
+ return config;
83
+ }
84
+
85
+ /**
86
+ * Normalize config from package.json to standard format
87
+ * @param {Object} pkgConfig - Config from package.json "seabox" field
88
+ * @param {Object} pkg - package.json contents
89
+ * @returns {SeaboxConfig}
90
+ */
91
+ export function normalizeConfig(pkgConfig, pkg) {
92
+ // If already in outputs format, return as-is
93
+ if (pkgConfig.outputs) {
94
+ return {
95
+ ...pkgConfig,
96
+ assets: pkgConfig.assets || [],
97
+ bundler: pkgConfig.bundler || { external: [] },
98
+ _packageName: pkg.name,
99
+ _packageVersion: pkg.version
100
+ };
101
+ }
102
+
103
+ // Convert old targets format to outputs format
104
+ const outputs = (pkgConfig.targets || []).map(target => ({
105
+ path: pkgConfig.outputPath || 'dist',
106
+ target: target,
107
+ output: pkgConfig.output || 'app.exe',
108
+ libraries: pkgConfig.binaries || pkgConfig.libraries,
109
+ rcedit: pkgConfig.rcedit
110
+ }));
111
+
112
+ return {
113
+ entry: pkgConfig.entry,
114
+ outputs: outputs,
115
+ assets: pkgConfig.assets || [],
116
+ bundler: {
117
+ external: pkgConfig.external || []
118
+ },
119
+ encryptAssets: pkgConfig.encryptAssets || false,
120
+ encryptExclude: pkgConfig.encryptExclude || [],
121
+ useSnapshot: pkgConfig.useSnapshot || false,
122
+ useCodeCache: pkgConfig.useCodeCache || false,
123
+ cacheLocation: pkgConfig.cacheLocation,
124
+ verbose: pkgConfig.verbose || false,
125
+ _packageName: pkg.name,
126
+ _packageVersion: pkg.version
127
+ };
128
+ }
129
+
130
+ /**
131
+ * Validate configuration
132
+ * @param {SeaboxConfig} config
133
+ */
134
+ export function validateConfig(config) {
135
+ // Required fields
136
+ if (!config.entry) {
137
+ throw new Error('Missing required field: entry');
138
+ }
139
+
140
+ if (!config.outputs || !Array.isArray(config.outputs) || config.outputs.length === 0) {
141
+ throw new Error('Missing required field: outputs (must be non-empty array)');
142
+ }
143
+
144
+ // Validate each output target
145
+ for (const output of config.outputs) {
146
+ if (!output.path) {
147
+ throw new Error('Output target missing required field: path');
148
+ }
149
+ if (!output.target) {
150
+ throw new Error('Output target missing required field: target');
151
+ }
152
+ if (!output.output) {
153
+ throw new Error('Output target missing required field: output');
154
+ }
155
+
156
+ // Validate target format
157
+ const targetPattern = /^node\d+\.\d+\.\d+-\w+-\w+$/;
158
+ if (!targetPattern.test(output.target)) {
159
+ throw new Error(`Invalid target format: "${output.target}". Expected: nodeX.Y.Z-platform-arch`);
160
+ }
161
+ }
162
+ }
163
+
164
+ /**
165
+ * Parse a target string into components
166
+ * @param {string} target - e.g., "node24.11.0-win32-x64"
167
+ * @returns {{nodeVersion: string, platform: string, arch: string}}
168
+ */
169
+ export function parseTarget(target) {
170
+ const match = target.match(/^node(\d+\.\d+\.\d+)-(\w+)-(\w+)$/);
171
+ if (!match) {
172
+ throw new Error(`Cannot parse target: ${target}`);
173
+ }
174
+ return {
175
+ nodeVersion: match[1],
176
+ platform: match[2],
177
+ arch: match[3]
178
+ };
179
+ }
180
+
181
+ /**
182
+ * Get default library patterns for a platform
183
+ * @param {string} platform - Platform name (win32, linux, darwin)
184
+ * @returns {string[]}
185
+ */
186
+ export function getDefaultLibraryPatterns(platform) {
187
+ switch (platform) {
188
+ case 'win32':
189
+ return ['**/*.dll'];
190
+ case 'linux':
191
+ return ['**/*.so', '**/*.so.*'];
192
+ case 'darwin':
193
+ return ['**/*.dylib'];
194
+ default:
195
+ return [];
196
+ }
197
+ }
198
+
199
+ /**
200
+ * Generate default configuration
201
+ * @param {Object} options - Options for config generation
202
+ * @returns {SeaboxConfig}
203
+ */
204
+ export function generateDefaultConfig(options = {}) {
205
+ return {
206
+ entry: options.entry || './src/index.js',
207
+ outputs: [
208
+ {
209
+ path: './dist/win',
210
+ target: 'node24.11.0-win32-x64',
211
+ output: 'app.exe',
212
+ libraries: ['**/*.dll']
213
+ },
214
+ {
215
+ path: './dist/linux',
216
+ target: 'node24.11.0-linux-x64',
217
+ output: 'app',
218
+ libraries: ['**/*.so', '**/*.so.*']
219
+ },
220
+ {
221
+ path: './dist/macos',
222
+ target: 'node24.11.0-darwin-arm64',
223
+ output: 'app',
224
+ libraries: ['**/*.dylib']
225
+ }
226
+ ],
227
+ assets: [],
228
+ bundler: {
229
+ external: []
230
+ },
231
+ encryptAssets: false,
232
+ useSnapshot: true
233
+ };
234
+ }
@@ -1,17 +1,16 @@
1
1
  /**
2
- * crypto-assets.js
2
+ * crypto-assets.mjs
3
3
  * Asset encryption/decryption utilities for SEA applications.
4
- * The encryption key is embedded in the bootstrap code and, when using snapshots,
5
- * becomes part of the V8 bytecode, providing obfuscation.
6
4
  */
7
5
 
8
- const crypto = require('crypto');
6
+ import crypto from 'crypto';
7
+ import fs from 'fs';
9
8
 
10
9
  /**
11
10
  * Generate a random encryption key.
12
11
  * @returns {Buffer} - 32-byte key for AES-256
13
12
  */
14
- function generateEncryptionKey() {
13
+ export function generateEncryptionKey() {
15
14
  return crypto.randomBytes(32);
16
15
  }
17
16
 
@@ -21,7 +20,7 @@ function generateEncryptionKey() {
21
20
  * @param {Buffer} key - 32-byte encryption key
22
21
  * @returns {Buffer} - Encrypted data with IV and auth tag prepended
23
22
  */
24
- function encryptAsset(data, key) {
23
+ export function encryptAsset(data, key) {
25
24
  // Generate a random IV for this encryption
26
25
  const iv = crypto.randomBytes(16);
27
26
 
@@ -47,7 +46,7 @@ function encryptAsset(data, key) {
47
46
  * @param {Buffer} key - 32-byte encryption key
48
47
  * @returns {Buffer} - Decrypted data
49
48
  */
50
- function decryptAsset(encryptedData, key) {
49
+ export function decryptAsset(encryptedData, key) {
51
50
  // Extract IV, auth tag, and encrypted content
52
51
  const iv = encryptedData.slice(0, 16);
53
52
  const authTag = encryptedData.slice(16, 32);
@@ -68,16 +67,16 @@ function decryptAsset(encryptedData, key) {
68
67
 
69
68
  /**
70
69
  * Encrypt multiple assets.
71
- * @param {import('./scanner').AssetEntry[]} assets - Assets to encrypt
70
+ * @param {Array} assets - Assets to encrypt
72
71
  * @param {Buffer} key - Encryption key
73
- * @param {string[]} [excludePatterns] - Patterns to exclude from encryption (e.g., ['*.txt'])
72
+ * @param {string[]} [excludePatterns] - Patterns to exclude from encryption
74
73
  * @returns {Map<string, Buffer>} - Map of asset key to encrypted content
75
74
  */
76
- function encryptAssets(assets, key, excludePatterns = []) {
75
+ export function encryptAssets(assets, key, excludePatterns = []) {
77
76
  const encrypted = new Map();
78
77
 
79
78
  for (const asset of assets) {
80
- // Skip binaries - they need to be extracted as-is
79
+ // Skip binaries
81
80
  if (asset.isBinary) {
82
81
  continue;
83
82
  }
@@ -96,7 +95,7 @@ function encryptAssets(assets, key, excludePatterns = []) {
96
95
  }
97
96
 
98
97
  // Get asset content
99
- const content = asset.content || require('fs').readFileSync(asset.sourcePath);
98
+ const content = asset.content || fs.readFileSync(asset.sourcePath);
100
99
 
101
100
  // Encrypt it
102
101
  const encryptedContent = encryptAsset(content, key);
@@ -106,37 +105,12 @@ function encryptAssets(assets, key, excludePatterns = []) {
106
105
  return encrypted;
107
106
  }
108
107
 
109
- /**
110
- * Generate the encryption key as a code string for embedding in bootstrap.
111
- * Returns a string that evaluates to a Buffer containing the key.
112
- * @param {Buffer} key - Encryption key
113
- * @returns {string} - JavaScript code that creates the key Buffer
114
- */
115
- function keyToCode(key) {
116
- // Convert key to hex string for embedding
117
- const hexKey = key.toString('hex');
118
-
119
- // Return code that reconstructs the Buffer
120
- // Using multiple obfuscation techniques:
121
- // 1. Split the hex string
122
- // 2. Use Array.from with character codes
123
- // 3. XOR with a simple constant (adds one more layer)
124
-
125
- const parts = [];
126
- for (let i = 0; i < hexKey.length; i += 8) {
127
- parts.push(hexKey.slice(i, i + 8));
128
- }
129
-
130
- return `Buffer.from('${hexKey}', 'hex')`;
131
- }
132
-
133
108
  /**
134
109
  * Generate a more obfuscated version of the key embedding code.
135
- * This version splits the key and uses character code manipulation.
136
110
  * @param {Buffer} key - Encryption key
137
111
  * @returns {string} - Obfuscated JavaScript code
138
112
  */
139
- function keyToObfuscatedCode(key) {
113
+ export function keyToObfuscatedCode(key) {
140
114
  // Convert to array of byte values
141
115
  const bytes = Array.from(key);
142
116
 
@@ -149,12 +123,3 @@ function keyToObfuscatedCode(key) {
149
123
  // Return code that reconstructs the key
150
124
  return `Buffer.from([${masked.join(',')}].map(b => b ^ 0x${xorMask.toString(16)}))`;
151
125
  }
152
-
153
- module.exports = {
154
- generateEncryptionKey,
155
- encryptAsset,
156
- decryptAsset,
157
- encryptAssets,
158
- keyToCode,
159
- keyToObfuscatedCode
160
- };
@@ -0,0 +1,64 @@
1
+ /**
2
+ * entry-bundler.mjs
3
+ * Bundles the entry file with bootstrap and require shim
4
+ */
5
+
6
+ import Module from 'module';
7
+ import { fileURLToPath } from 'url';
8
+
9
+ const __filename = fileURLToPath(import.meta.url);
10
+ const require = Module.createRequire(import.meta.url);
11
+
12
+ const { generateRequireShim, replaceRequireCalls } = require('./require-shim.mjs');
13
+
14
+ /**
15
+ * Bundle the entry file for SEA
16
+ * @param {string} entryContent - The entry file content
17
+ * @param {string} augmentedBootstrap - The bootstrap code
18
+ * @param {boolean} useSnapshot - Whether to use snapshot mode
19
+ * @param {boolean} verbose - Enable verbose logging
20
+ * @returns {string} - The bundled entry content
21
+ */
22
+ export function bundleEntry(entryContent, augmentedBootstrap, useSnapshot, verbose) {
23
+ // Generate the require shim and replace require() calls
24
+ const requireSeaboxShim = generateRequireShim();
25
+ const transformed = replaceRequireCalls(entryContent, verbose);
26
+ const transformedContent = transformed.code;
27
+
28
+ let bundledEntry;
29
+
30
+ if (useSnapshot) {
31
+ // Snapshot mode
32
+ const parts = [
33
+ augmentedBootstrap,
34
+ '\n\n',
35
+ requireSeaboxShim,
36
+ '\n\n',
37
+ '// Application entry - will be wrapped by bootstrap\'s setDeserializeMainFunction interceptor\n',
38
+ '(function() {\n',
39
+ ' const v8 = __originalRequire(\'v8\');\n',
40
+ ' if (v8.startupSnapshot && v8.startupSnapshot.isBuildingSnapshot()) {\n',
41
+ ' v8.startupSnapshot.setDeserializeMainFunction(() => {\n',
42
+ ' if (typeof exports === \'undefined\') {\n',
43
+ ' var exports = {};\n',
44
+ ' var module = { exports: exports };\n',
45
+ ' }\n',
46
+ ' // Run the application code\n',
47
+ transformedContent,
48
+ '\n',
49
+ ' });\n',
50
+ ' } else {\n',
51
+ ' // Not building snapshot, run normally\n',
52
+ transformedContent,
53
+ '\n',
54
+ ' }\n',
55
+ '})();\n'
56
+ ];
57
+ bundledEntry = parts.join('');
58
+ } else {
59
+ // Non-snapshot mode
60
+ bundledEntry = augmentedBootstrap + '\n\n' + requireSeaboxShim + '\n' + transformedContent;
61
+ }
62
+
63
+ return bundledEntry;
64
+ }