seabox 0.1.2 → 0.3.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/.mocharc.json +6 -6
- package/LICENSE.MD +21 -21
- package/README.md +310 -310
- package/bin/seabox-rebuild.mjs +107 -88
- package/bin/seabox.mjs +150 -147
- package/lib/blob.mjs +104 -104
- package/lib/bootstrap.cjs +756 -756
- package/lib/build-cache.mjs +206 -199
- package/lib/build.mjs +77 -77
- package/lib/config.mjs +243 -243
- package/lib/crypto-assets.mjs +125 -125
- package/lib/diagnostics.mjs +203 -203
- package/lib/entry-bundler.mjs +64 -64
- package/lib/fetch-node.mjs +172 -172
- package/lib/index.mjs +26 -26
- package/lib/inject.mjs +106 -106
- package/lib/manifest.mjs +100 -100
- package/lib/multi-target-builder.mjs +701 -697
- package/lib/native-scanner.mjs +203 -203
- package/lib/obfuscate.mjs +51 -51
- package/lib/require-shim.mjs +113 -113
- package/lib/rolldown-bundler.mjs +411 -411
- package/lib/unsign.cjs +197 -169
- package/package.json +61 -61
package/lib/build-cache.mjs
CHANGED
|
@@ -1,199 +1,206 @@
|
|
|
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
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
.
|
|
121
|
-
.
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
if
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
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 (e.g., node24.11.0-win32-x64)
|
|
114
|
+
* @returns {string|null} - Path to cached .node file
|
|
115
|
+
*/
|
|
116
|
+
getCachedNativeBuild(moduleRoot, target) {
|
|
117
|
+
// Include the FULL target string (with Node.js version) in cache key
|
|
118
|
+
// This ensures different Node.js versions have separate cache entries
|
|
119
|
+
const cacheKey = crypto
|
|
120
|
+
.createHash('sha256')
|
|
121
|
+
.update(`${moduleRoot}||${target}`)
|
|
122
|
+
.digest('hex')
|
|
123
|
+
.substring(0, 16);
|
|
124
|
+
|
|
125
|
+
const cachePath = path.join(this.cacheDir, 'natives', `${cacheKey}.node`);
|
|
126
|
+
|
|
127
|
+
if (fs.existsSync(cachePath)) {
|
|
128
|
+
// Check if source has changed
|
|
129
|
+
const bindingGypPath = path.join(moduleRoot, 'binding.gyp');
|
|
130
|
+
if (fs.existsSync(bindingGypPath)) {
|
|
131
|
+
const cacheStats = fs.statSync(cachePath);
|
|
132
|
+
const sourceStats = fs.statSync(bindingGypPath);
|
|
133
|
+
|
|
134
|
+
if (cacheStats.mtime > sourceStats.mtime) {
|
|
135
|
+
return cachePath;
|
|
136
|
+
}
|
|
137
|
+
} else {
|
|
138
|
+
// If no binding.gyp, still return cached binary (e.g., prebuild scenario)
|
|
139
|
+
return cachePath;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
return null;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Cache a native module build
|
|
148
|
+
* @param {string} moduleRoot - Module root path
|
|
149
|
+
* @param {string} target - Build target (e.g., node24.11.0-win32-x64)
|
|
150
|
+
* @param {string} builtBinaryPath - Path to built .node file
|
|
151
|
+
*/
|
|
152
|
+
cacheNativeBuild(moduleRoot, target, builtBinaryPath) {
|
|
153
|
+
// Use the FULL target string (with Node.js version) in cache key
|
|
154
|
+
// This ensures different Node.js versions have separate cache entries
|
|
155
|
+
const cacheKey = crypto
|
|
156
|
+
.createHash('sha256')
|
|
157
|
+
.update(`${moduleRoot}||${target}`)
|
|
158
|
+
.digest('hex')
|
|
159
|
+
.substring(0, 16);
|
|
160
|
+
|
|
161
|
+
const cachePath = path.join(this.cacheDir, 'natives', `${cacheKey}.node`);
|
|
162
|
+
|
|
163
|
+
fs.copyFileSync(builtBinaryPath, cachePath);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Check if cache entry is valid
|
|
168
|
+
* @param {CacheEntry} cached - Cached entry
|
|
169
|
+
* @param {string} filePath - Source file path
|
|
170
|
+
* @returns {Promise<boolean>}
|
|
171
|
+
*/
|
|
172
|
+
async isValid(cached, filePath) {
|
|
173
|
+
if (!fs.existsSync(filePath)) {
|
|
174
|
+
return false;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
const currentHash = await this.hashFile(filePath);
|
|
178
|
+
return currentHash === cached.hash;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Compute hash of a file
|
|
183
|
+
* @param {string} filePath - File path
|
|
184
|
+
* @returns {Promise<string>}
|
|
185
|
+
*/
|
|
186
|
+
hashFile(filePath) {
|
|
187
|
+
return new Promise((resolve, reject) => {
|
|
188
|
+
const hash = crypto.createHash('sha256');
|
|
189
|
+
const stream = fs.createReadStream(filePath);
|
|
190
|
+
|
|
191
|
+
stream.on('data', chunk => hash.update(chunk));
|
|
192
|
+
stream.on('end', () => resolve(hash.digest('hex')));
|
|
193
|
+
stream.on('error', reject);
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Clear cache
|
|
199
|
+
*/
|
|
200
|
+
clear() {
|
|
201
|
+
if (fs.existsSync(this.cacheDir)) {
|
|
202
|
+
fs.rmSync(this.cacheDir, { recursive: true, force: true });
|
|
203
|
+
}
|
|
204
|
+
this.ensureCacheDir();
|
|
205
|
+
}
|
|
206
|
+
}
|
package/lib/build.mjs
CHANGED
|
@@ -1,77 +1,77 @@
|
|
|
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 * as diag from './diagnostics.mjs';
|
|
9
|
-
import fs from 'fs';
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* Main build function for v2
|
|
13
|
-
* @param {Object} options - Build options
|
|
14
|
-
* @param {string} [options.configPath] - Path to config file
|
|
15
|
-
* @param {boolean} [options.verbose] - Enable verbose logging
|
|
16
|
-
* @param {boolean} [options.debug] - Keep temporary build files
|
|
17
|
-
* @param {string} [options.projectRoot] - Project root directory
|
|
18
|
-
*/
|
|
19
|
-
export async function build(options = {}) {
|
|
20
|
-
// Support both options object and legacy process.argv parsing
|
|
21
|
-
let configPath = options.configPath;
|
|
22
|
-
let verbose = options.verbose;
|
|
23
|
-
let debug = options.debug;
|
|
24
|
-
let projectRoot = options.projectRoot || process.cwd();
|
|
25
|
-
|
|
26
|
-
// Fallback to process.argv if called without options
|
|
27
|
-
if (!options.configPath && !options.verbose && !options.debug) {
|
|
28
|
-
const args = process.argv.slice(2);
|
|
29
|
-
configPath = args.includes('--config') ? args[args.indexOf('--config') + 1] : null;
|
|
30
|
-
verbose = args.includes('--verbose');
|
|
31
|
-
debug = args.includes('--debug');
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
try {
|
|
35
|
-
// Load configuration
|
|
36
|
-
const config = loadConfig(configPath, projectRoot);
|
|
37
|
-
|
|
38
|
-
// Config should exist if we got here (CLI checks first)
|
|
39
|
-
if (!config) {
|
|
40
|
-
throw new Error('No configuration found. Run: npx seabox init');
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
// Override verbose from CLI if specified
|
|
44
|
-
if (verbose) {
|
|
45
|
-
config.verbose = true;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
// Create and run multi-target builder
|
|
49
|
-
const builder = new MultiTargetBuilder(config, projectRoot);
|
|
50
|
-
const results = await builder.buildAll();
|
|
51
|
-
|
|
52
|
-
// Display results
|
|
53
|
-
diag.separator();
|
|
54
|
-
diag.info('Output files:');
|
|
55
|
-
for (const result of results) {
|
|
56
|
-
const size = diag.formatSize(fs.statSync(result.path).size);
|
|
57
|
-
diag.info(` ${result.target}: ${result.path} (${size})`);
|
|
58
|
-
}
|
|
59
|
-
diag.separator();
|
|
60
|
-
|
|
61
|
-
return results;
|
|
62
|
-
} catch (error) {
|
|
63
|
-
diag.separator();
|
|
64
|
-
diag.error(`Build failed: ${error.message}`);
|
|
65
|
-
if (verbose || process.argv.includes('--verbose')) {
|
|
66
|
-
console.error(error.stack);
|
|
67
|
-
}
|
|
68
|
-
throw error;
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
// Run if called directly
|
|
73
|
-
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
74
|
-
build().catch(error => {
|
|
75
|
-
process.exit(1);
|
|
76
|
-
});
|
|
77
|
-
}
|
|
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 * as diag from './diagnostics.mjs';
|
|
9
|
+
import fs from 'fs';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Main build function for v2
|
|
13
|
+
* @param {Object} options - Build options
|
|
14
|
+
* @param {string} [options.configPath] - Path to config file
|
|
15
|
+
* @param {boolean} [options.verbose] - Enable verbose logging
|
|
16
|
+
* @param {boolean} [options.debug] - Keep temporary build files
|
|
17
|
+
* @param {string} [options.projectRoot] - Project root directory
|
|
18
|
+
*/
|
|
19
|
+
export async function build(options = {}) {
|
|
20
|
+
// Support both options object and legacy process.argv parsing
|
|
21
|
+
let configPath = options.configPath;
|
|
22
|
+
let verbose = options.verbose;
|
|
23
|
+
let debug = options.debug;
|
|
24
|
+
let projectRoot = options.projectRoot || process.cwd();
|
|
25
|
+
|
|
26
|
+
// Fallback to process.argv if called without options
|
|
27
|
+
if (!options.configPath && !options.verbose && !options.debug) {
|
|
28
|
+
const args = process.argv.slice(2);
|
|
29
|
+
configPath = args.includes('--config') ? args[args.indexOf('--config') + 1] : null;
|
|
30
|
+
verbose = args.includes('--verbose');
|
|
31
|
+
debug = args.includes('--debug');
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
try {
|
|
35
|
+
// Load configuration
|
|
36
|
+
const config = loadConfig(configPath, projectRoot);
|
|
37
|
+
|
|
38
|
+
// Config should exist if we got here (CLI checks first)
|
|
39
|
+
if (!config) {
|
|
40
|
+
throw new Error('No configuration found. Run: npx seabox init');
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Override verbose from CLI if specified
|
|
44
|
+
if (verbose) {
|
|
45
|
+
config.verbose = true;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Create and run multi-target builder
|
|
49
|
+
const builder = new MultiTargetBuilder(config, projectRoot);
|
|
50
|
+
const results = await builder.buildAll();
|
|
51
|
+
|
|
52
|
+
// Display results
|
|
53
|
+
diag.separator();
|
|
54
|
+
diag.info('Output files:');
|
|
55
|
+
for (const result of results) {
|
|
56
|
+
const size = diag.formatSize(fs.statSync(result.path).size);
|
|
57
|
+
diag.info(` ${result.target}: ${result.path} (${size})`);
|
|
58
|
+
}
|
|
59
|
+
diag.separator();
|
|
60
|
+
|
|
61
|
+
return results;
|
|
62
|
+
} catch (error) {
|
|
63
|
+
diag.separator();
|
|
64
|
+
diag.error(`Build failed: ${error.message}`);
|
|
65
|
+
if (verbose || process.argv.includes('--verbose')) {
|
|
66
|
+
console.error(error.stack);
|
|
67
|
+
}
|
|
68
|
+
throw error;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Run if called directly
|
|
73
|
+
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
74
|
+
build().catch(error => {
|
|
75
|
+
process.exit(1);
|
|
76
|
+
});
|
|
77
|
+
}
|