bunup 0.1.0 → 0.1.3
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 +6 -2
- package/build/cli.js +826 -4
- package/build/cli.mjs +816 -2
- package/build/dtsWorker.js +1 -0
- package/build/index.d.mts +89 -4
- package/build/index.d.ts +89 -4
- package/build/index.js +8 -1
- package/build/index.mjs +6 -1
- package/package.json +22 -7
- package/build/chunk-4DGR6V7O.mjs +0 -3
package/build/cli.js
CHANGED
|
@@ -1,5 +1,827 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
|
-
'use strict';
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
var fs = require('fs');
|
|
5
|
+
var path2 = require('path');
|
|
6
|
+
var rollup = require('rollup');
|
|
7
|
+
var dtsPlugin = require('rollup-plugin-dts');
|
|
8
|
+
var ts = require('typescript');
|
|
9
|
+
var chokidar = require('chokidar');
|
|
10
|
+
|
|
11
|
+
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
12
|
+
|
|
13
|
+
var fs__default = /*#__PURE__*/_interopDefault(fs);
|
|
14
|
+
var path2__default = /*#__PURE__*/_interopDefault(path2);
|
|
15
|
+
var dtsPlugin__default = /*#__PURE__*/_interopDefault(dtsPlugin);
|
|
16
|
+
var ts__default = /*#__PURE__*/_interopDefault(ts);
|
|
17
|
+
var chokidar__default = /*#__PURE__*/_interopDefault(chokidar);
|
|
18
|
+
|
|
19
|
+
// src/errors.ts
|
|
20
|
+
var parseErrorMessage = (error) => {
|
|
21
|
+
if (error instanceof Error) {
|
|
22
|
+
return error.message;
|
|
23
|
+
}
|
|
24
|
+
return String(error);
|
|
25
|
+
};
|
|
26
|
+
var handleError = (error, context) => {
|
|
27
|
+
const errorMessage = parseErrorMessage(error);
|
|
28
|
+
const contextPrefix = "";
|
|
29
|
+
console.error(`\x1B[31m[ERROR]\x1B[0m ${contextPrefix}${errorMessage}`);
|
|
30
|
+
if (process.env.NODE_ENV !== "production" && error instanceof Error && error.stack) {
|
|
31
|
+
console.error(
|
|
32
|
+
"\x1B[2m" + error.stack.split("\n").slice(1).join("\n") + "\x1B[0m"
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
// src/utils.ts
|
|
38
|
+
function escapeRegExp(string) {
|
|
39
|
+
return string.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
40
|
+
}
|
|
41
|
+
function getDefaultOutputExtension(format, packageType) {
|
|
42
|
+
switch (format) {
|
|
43
|
+
case "esm":
|
|
44
|
+
return ".mjs";
|
|
45
|
+
case "cjs":
|
|
46
|
+
return isModulePackage(packageType) ? ".cjs" : ".js";
|
|
47
|
+
case "iife":
|
|
48
|
+
return ".global.js";
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
function getDefaultDtsExtention(format, packageType) {
|
|
52
|
+
switch (format) {
|
|
53
|
+
case "esm":
|
|
54
|
+
return ".d.mts";
|
|
55
|
+
case "cjs":
|
|
56
|
+
return isModulePackage(packageType) ? ".d.cts" : ".d.ts";
|
|
57
|
+
case "iife":
|
|
58
|
+
return ".d.ts";
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
function getEntryNamingFormat(extension) {
|
|
62
|
+
return `[dir]/[name]${extension}`;
|
|
63
|
+
}
|
|
64
|
+
function isModulePackage(packageType) {
|
|
65
|
+
return packageType === "module";
|
|
66
|
+
}
|
|
67
|
+
function getEntryNameOnly(entry) {
|
|
68
|
+
return entry.split("/").pop()?.split(".").slice(0, -1).join(".") || "";
|
|
69
|
+
}
|
|
70
|
+
function formatTime(ms) {
|
|
71
|
+
return ms >= 1e3 ? `${(ms / 1e3).toFixed(2)}s` : `${Math.round(ms)}ms`;
|
|
72
|
+
}
|
|
73
|
+
function getPackageDeps(packageJson) {
|
|
74
|
+
if (!packageJson) return [];
|
|
75
|
+
return Array.from(
|
|
76
|
+
/* @__PURE__ */ new Set([
|
|
77
|
+
...Object.keys(packageJson.dependencies || {}),
|
|
78
|
+
...Object.keys(packageJson.peerDependencies || {})
|
|
79
|
+
])
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
function getResolvedSplitting(splitting, format) {
|
|
83
|
+
return splitting === void 0 ? format === "esm" : splitting;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// src/helpers/external.ts
|
|
87
|
+
function processPatterns(patterns) {
|
|
88
|
+
return patterns.map(
|
|
89
|
+
(pattern) => typeof pattern === "string" ? new RegExp(`^${escapeRegExp(pattern)}($|\\/|\\\\)`) : pattern
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
function getExternalPatterns(options, packageJson) {
|
|
93
|
+
return processPatterns(options.external || []).concat(
|
|
94
|
+
getPackageDeps(packageJson).map(
|
|
95
|
+
(dep) => new RegExp(`^${escapeRegExp(dep)}($|\\/|\\\\)`)
|
|
96
|
+
)
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
function getNoExternalPatterns(options) {
|
|
100
|
+
return processPatterns(options.noExternal || []);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// src/logger.ts
|
|
104
|
+
var logger = {
|
|
105
|
+
MAX_LABEL_LENGTH: 5,
|
|
106
|
+
colors: {
|
|
107
|
+
cli: "183",
|
|
108
|
+
info: "240",
|
|
109
|
+
warn: "221",
|
|
110
|
+
error: "203",
|
|
111
|
+
progress: {
|
|
112
|
+
ESM: "214",
|
|
113
|
+
CJS: "114",
|
|
114
|
+
IIFE: "105",
|
|
115
|
+
DTS: "75"
|
|
116
|
+
},
|
|
117
|
+
default: "255"
|
|
118
|
+
},
|
|
119
|
+
labels: {
|
|
120
|
+
cli: "BUNUP",
|
|
121
|
+
info: "INFO",
|
|
122
|
+
warn: "WARN",
|
|
123
|
+
error: "ERROR"
|
|
124
|
+
},
|
|
125
|
+
formatMessage(colorCode, label, message) {
|
|
126
|
+
const padding = " ".repeat(
|
|
127
|
+
Math.max(0, this.MAX_LABEL_LENGTH - label.length)
|
|
128
|
+
);
|
|
129
|
+
return `\x1B[38;5;${colorCode}m[${label}]\x1B[0m ${padding}${message}`;
|
|
130
|
+
},
|
|
131
|
+
cli(message) {
|
|
132
|
+
const label = this.labels.cli;
|
|
133
|
+
console.log(this.formatMessage(this.colors.cli, label, message));
|
|
134
|
+
},
|
|
135
|
+
info(message) {
|
|
136
|
+
const label = this.labels.info;
|
|
137
|
+
console.log(this.formatMessage(this.colors.info, label, message));
|
|
138
|
+
},
|
|
139
|
+
warn(message) {
|
|
140
|
+
const label = this.labels.warn;
|
|
141
|
+
console.warn(this.formatMessage(this.colors.warn, label, message));
|
|
142
|
+
},
|
|
143
|
+
error(message) {
|
|
144
|
+
const label = this.labels.error;
|
|
145
|
+
console.error(this.formatMessage(this.colors.error, label, message));
|
|
146
|
+
},
|
|
147
|
+
progress(label, message) {
|
|
148
|
+
const labelStr = String(label);
|
|
149
|
+
let colorCode = this.colors.default;
|
|
150
|
+
for (const [key, value] of Object.entries(this.colors.progress)) {
|
|
151
|
+
if (labelStr.includes(key)) {
|
|
152
|
+
colorCode = value;
|
|
153
|
+
break;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
console.log(this.formatMessage(colorCode, labelStr, message));
|
|
157
|
+
}
|
|
158
|
+
};
|
|
159
|
+
function getLoggerProgressLabel(label, name) {
|
|
160
|
+
return `${name ? `${name.replace(/-/g, "_")}_` : ""}${label}`.toUpperCase();
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// src/options.ts
|
|
164
|
+
var DEFAULT_OPTIONS = {
|
|
165
|
+
entry: [],
|
|
166
|
+
format: ["esm"],
|
|
167
|
+
outDir: "dist",
|
|
168
|
+
minify: false,
|
|
169
|
+
watch: false,
|
|
170
|
+
dts: false,
|
|
171
|
+
target: "node",
|
|
172
|
+
external: [],
|
|
173
|
+
clean: true
|
|
174
|
+
};
|
|
175
|
+
function createDefaultBunBuildOptions(options, rootDir) {
|
|
176
|
+
return {
|
|
177
|
+
entrypoints: options.entry.map((e) => `${rootDir}/${e}`),
|
|
178
|
+
outdir: `${rootDir}/${options.outDir}`,
|
|
179
|
+
minify: createMinifyOptions(options),
|
|
180
|
+
target: options.target,
|
|
181
|
+
splitting: options.splitting
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
function createMinifyOptions(options) {
|
|
185
|
+
const { minify, minifyWhitespace, minifyIdentifiers, minifySyntax } = options;
|
|
186
|
+
const defaultValue = minify === true;
|
|
187
|
+
return {
|
|
188
|
+
whitespace: minifyWhitespace ?? defaultValue,
|
|
189
|
+
identifiers: minifyIdentifiers ?? defaultValue,
|
|
190
|
+
syntax: minifySyntax ?? defaultValue
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// src/loaders.ts
|
|
195
|
+
async function loadConfigs(cwd) {
|
|
196
|
+
const configs = [];
|
|
197
|
+
for (const ext of [
|
|
198
|
+
".ts",
|
|
199
|
+
".js",
|
|
200
|
+
".mjs",
|
|
201
|
+
".cjs",
|
|
202
|
+
".mts",
|
|
203
|
+
".cts",
|
|
204
|
+
".json",
|
|
205
|
+
".jsonc"
|
|
206
|
+
]) {
|
|
207
|
+
const filePath = path2__default.default.join(cwd, `bunup.config${ext}`);
|
|
208
|
+
try {
|
|
209
|
+
if (!fs__default.default.existsSync(filePath)) continue;
|
|
210
|
+
let content;
|
|
211
|
+
if (ext === ".json" || ext === ".jsonc") {
|
|
212
|
+
const text = fs__default.default.readFileSync(filePath, "utf8");
|
|
213
|
+
content = JSON.parse(text);
|
|
214
|
+
} else {
|
|
215
|
+
const imported = await import(`file://${filePath}`);
|
|
216
|
+
content = imported.default || imported;
|
|
217
|
+
if (!content) {
|
|
218
|
+
logger.warn(`No default export found in ${filePath}`);
|
|
219
|
+
content = {};
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
if (Array.isArray(content)) {
|
|
223
|
+
for (const item of content) {
|
|
224
|
+
configs.push({
|
|
225
|
+
options: { ...DEFAULT_OPTIONS, ...item },
|
|
226
|
+
rootDir: cwd
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
} else {
|
|
230
|
+
configs.push({
|
|
231
|
+
options: { ...DEFAULT_OPTIONS, ...content },
|
|
232
|
+
rootDir: cwd
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
break;
|
|
236
|
+
} catch (error) {
|
|
237
|
+
logger.error(
|
|
238
|
+
`Failed to load config from ${filePath}: ${parseErrorMessage(error)}`
|
|
239
|
+
);
|
|
240
|
+
}
|
|
241
|
+
if (configs.length > 0) break;
|
|
242
|
+
}
|
|
243
|
+
return configs;
|
|
244
|
+
}
|
|
245
|
+
function loadPackageJson(cwd) {
|
|
246
|
+
const packageJsonPath = path2__default.default.join(cwd, "package.json");
|
|
247
|
+
try {
|
|
248
|
+
if (!fs__default.default.existsSync(packageJsonPath)) {
|
|
249
|
+
return null;
|
|
250
|
+
}
|
|
251
|
+
const text = fs__default.default.readFileSync(packageJsonPath, "utf8");
|
|
252
|
+
const content = JSON.parse(text);
|
|
253
|
+
return content;
|
|
254
|
+
} catch (error) {
|
|
255
|
+
logger.error(
|
|
256
|
+
`Failed to load package.json at ${packageJsonPath}: ${parseErrorMessage(error)}`
|
|
257
|
+
);
|
|
258
|
+
return null;
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
function loadTsconfig(tsconfigPath) {
|
|
262
|
+
if (!fs__default.default.existsSync(tsconfigPath)) {
|
|
263
|
+
return {};
|
|
264
|
+
}
|
|
265
|
+
try {
|
|
266
|
+
const content = fs__default.default.readFileSync(tsconfigPath, "utf8");
|
|
267
|
+
const json = JSON.parse(content);
|
|
268
|
+
return json || {};
|
|
269
|
+
} catch (error) {
|
|
270
|
+
logger.warn(
|
|
271
|
+
`Failed to parse tsconfig at ${tsconfigPath}: ${parseErrorMessage(error)}`
|
|
272
|
+
);
|
|
273
|
+
return {};
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
// src/dts/index.ts
|
|
278
|
+
async function generateDts(rootDir, entry, format, options, dtsOptions = {}) {
|
|
279
|
+
const { absoluteRootDir, absoluteEntry } = validateInputs(rootDir, entry);
|
|
280
|
+
const tsconfigPath = dtsOptions.preferredTsconfigPath ? path2__default.default.resolve(dtsOptions.preferredTsconfigPath) : path2__default.default.join(absoluteRootDir, "tsconfig.json");
|
|
281
|
+
const tsconfig = await loadTsconfig(tsconfigPath);
|
|
282
|
+
const compilerOptions = tsconfig.compilerOptions;
|
|
283
|
+
const packageJson = loadPackageJson(absoluteRootDir);
|
|
284
|
+
const externalPatterns = getExternalPatterns(options, packageJson);
|
|
285
|
+
const noExternalPatterns = getNoExternalPatterns(options);
|
|
286
|
+
let bundle;
|
|
287
|
+
let result;
|
|
288
|
+
try {
|
|
289
|
+
bundle = await rollup.rollup({
|
|
290
|
+
input: absoluteEntry,
|
|
291
|
+
onwarn(warning, handler) {
|
|
292
|
+
if (warning.code === "UNRESOLVED_IMPORT" || warning.code === "CIRCULAR_DEPENDENCY" || warning.code === "EMPTY_BUNDLE") {
|
|
293
|
+
return;
|
|
294
|
+
}
|
|
295
|
+
return handler(warning);
|
|
296
|
+
},
|
|
297
|
+
plugins: [
|
|
298
|
+
dtsPlugin__default.default({
|
|
299
|
+
tsconfig: tsconfigPath,
|
|
300
|
+
compilerOptions: {
|
|
301
|
+
...compilerOptions ? ts__default.default.parseJsonConfigFileContent(
|
|
302
|
+
{ compilerOptions },
|
|
303
|
+
ts__default.default.sys,
|
|
304
|
+
"./"
|
|
305
|
+
).options : {},
|
|
306
|
+
declaration: true,
|
|
307
|
+
noEmit: false,
|
|
308
|
+
emitDeclarationOnly: true,
|
|
309
|
+
noEmitOnError: true,
|
|
310
|
+
checkJs: false,
|
|
311
|
+
declarationMap: false,
|
|
312
|
+
skipLibCheck: true,
|
|
313
|
+
preserveSymlinks: false,
|
|
314
|
+
target: ts.ScriptTarget.ESNext
|
|
315
|
+
}
|
|
316
|
+
})
|
|
317
|
+
],
|
|
318
|
+
external: (source) => externalPatterns.some((re) => re.test(source)) && !noExternalPatterns.some((re) => re.test(source))
|
|
319
|
+
});
|
|
320
|
+
const { output } = await bundle.generate({ format });
|
|
321
|
+
result = output[0].code;
|
|
322
|
+
} catch (rollupError) {
|
|
323
|
+
throw new Error(
|
|
324
|
+
`Rollup bundling failed: ${parseErrorMessage(rollupError)}`
|
|
325
|
+
);
|
|
326
|
+
} finally {
|
|
327
|
+
if (bundle) await bundle.close();
|
|
328
|
+
}
|
|
329
|
+
if (!result) {
|
|
330
|
+
throw new Error("Failed to generate bundled DTS content");
|
|
331
|
+
}
|
|
332
|
+
return result;
|
|
333
|
+
}
|
|
334
|
+
function validateInputs(rootDir, entry) {
|
|
335
|
+
const absoluteRootDir = path2__default.default.resolve(rootDir);
|
|
336
|
+
const absoluteEntry = path2__default.default.resolve(absoluteRootDir, entry);
|
|
337
|
+
if (!fs__default.default.existsSync(absoluteRootDir)) {
|
|
338
|
+
throw new Error(`Root directory does not exist: ${absoluteRootDir}`);
|
|
339
|
+
}
|
|
340
|
+
if (!fs__default.default.existsSync(absoluteEntry)) {
|
|
341
|
+
throw new Error(`Entry file does not exist: ${absoluteEntry}`);
|
|
342
|
+
}
|
|
343
|
+
if (!absoluteEntry.endsWith(".ts")) {
|
|
344
|
+
throw new Error(
|
|
345
|
+
`Entry file must be a TypeScript file (.ts): ${absoluteEntry}`
|
|
346
|
+
);
|
|
347
|
+
}
|
|
348
|
+
if (path2__default.default.relative(absoluteRootDir, absoluteEntry).startsWith("..")) {
|
|
349
|
+
throw new Error(`Entry file must be within rootDir: ${absoluteEntry}`);
|
|
350
|
+
}
|
|
351
|
+
return { absoluteRootDir, absoluteEntry };
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
// src/dts/worker.ts
|
|
355
|
+
self.onmessage = async (event) => {
|
|
356
|
+
const {
|
|
357
|
+
name,
|
|
358
|
+
rootDir,
|
|
359
|
+
outDir,
|
|
360
|
+
entry,
|
|
361
|
+
format,
|
|
362
|
+
packageType,
|
|
363
|
+
dtsOptions,
|
|
364
|
+
options
|
|
365
|
+
} = event.data;
|
|
366
|
+
try {
|
|
367
|
+
const content = await generateDts(
|
|
368
|
+
rootDir,
|
|
369
|
+
entry,
|
|
370
|
+
format,
|
|
371
|
+
options,
|
|
372
|
+
dtsOptions
|
|
373
|
+
);
|
|
374
|
+
const entryName = getEntryNameOnly(entry);
|
|
375
|
+
const extension = getDefaultDtsExtention(format, packageType);
|
|
376
|
+
const outputRelativePath = `${outDir}/${entryName}${extension}`;
|
|
377
|
+
const outputPath = `${rootDir}/${outputRelativePath}`;
|
|
378
|
+
await Bun.write(outputPath, content);
|
|
379
|
+
const response = {
|
|
380
|
+
name,
|
|
381
|
+
success: true,
|
|
382
|
+
outputRelativePath
|
|
383
|
+
};
|
|
384
|
+
self.postMessage(response);
|
|
385
|
+
} catch (error) {
|
|
386
|
+
const response = {
|
|
387
|
+
success: false,
|
|
388
|
+
error: parseErrorMessage(error)
|
|
389
|
+
};
|
|
390
|
+
self.postMessage(response);
|
|
391
|
+
}
|
|
392
|
+
};
|
|
393
|
+
var DtsWorker = class {
|
|
394
|
+
constructor(maxWorkers = navigator.hardwareConcurrency || 4) {
|
|
395
|
+
this.workers = [];
|
|
396
|
+
this.queue = [];
|
|
397
|
+
this.busyWorkers = /* @__PURE__ */ new Set();
|
|
398
|
+
this.isShuttingDown = false;
|
|
399
|
+
this.maxWorkers = maxWorkers;
|
|
400
|
+
}
|
|
401
|
+
async process(task) {
|
|
402
|
+
if (this.isShuttingDown) {
|
|
403
|
+
throw new Error("Worker pool is shutting down");
|
|
404
|
+
}
|
|
405
|
+
return new Promise((resolve, reject) => {
|
|
406
|
+
this.queue.push({ task, resolve, reject });
|
|
407
|
+
this.processQueue();
|
|
408
|
+
});
|
|
409
|
+
}
|
|
410
|
+
processQueue() {
|
|
411
|
+
if (this.queue.length === 0 || this.isShuttingDown) return;
|
|
412
|
+
if (this.workers.length < this.maxWorkers) {
|
|
413
|
+
const worker = new Worker(path2__default.default.join(__dirname, "./dtsWorker.js"));
|
|
414
|
+
this.workers.push(worker);
|
|
415
|
+
this.assignTaskToWorker(worker);
|
|
416
|
+
} else {
|
|
417
|
+
const availableWorker = this.workers.find(
|
|
418
|
+
(w) => !this.busyWorkers.has(w)
|
|
419
|
+
);
|
|
420
|
+
if (availableWorker) {
|
|
421
|
+
this.assignTaskToWorker(availableWorker);
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
assignTaskToWorker(worker) {
|
|
426
|
+
const queueItem = this.queue.shift();
|
|
427
|
+
if (!queueItem) return;
|
|
428
|
+
const { task, resolve, reject } = queueItem;
|
|
429
|
+
this.busyWorkers.add(worker);
|
|
430
|
+
const cleanup = () => {
|
|
431
|
+
this.busyWorkers.delete(worker);
|
|
432
|
+
if (this.isShuttingDown && this.busyWorkers.size === 0) {
|
|
433
|
+
this.terminateAllWorkers();
|
|
434
|
+
} else {
|
|
435
|
+
this.processQueue();
|
|
436
|
+
}
|
|
437
|
+
};
|
|
438
|
+
worker.onmessage = (event) => {
|
|
439
|
+
if (event.data.success) {
|
|
440
|
+
logger.progress(
|
|
441
|
+
getLoggerProgressLabel("DTS", event.data.name),
|
|
442
|
+
event.data.outputRelativePath
|
|
443
|
+
);
|
|
444
|
+
resolve();
|
|
445
|
+
} else {
|
|
446
|
+
logger.error(`DTS generation failed: ${event.data.error}`);
|
|
447
|
+
reject(new Error(event.data.error));
|
|
448
|
+
}
|
|
449
|
+
cleanup();
|
|
450
|
+
};
|
|
451
|
+
worker.onerror = (error) => {
|
|
452
|
+
const errorMessage = parseErrorMessage(error);
|
|
453
|
+
logger.error(`Worker error: ${errorMessage}`);
|
|
454
|
+
reject(error);
|
|
455
|
+
cleanup();
|
|
456
|
+
};
|
|
457
|
+
worker.postMessage(task);
|
|
458
|
+
}
|
|
459
|
+
terminateAllWorkers() {
|
|
460
|
+
this.workers.forEach((worker) => {
|
|
461
|
+
try {
|
|
462
|
+
worker.terminate();
|
|
463
|
+
} catch (error) {
|
|
464
|
+
logger.error(
|
|
465
|
+
`Error terminating worker: ${parseErrorMessage(error)}`
|
|
466
|
+
);
|
|
467
|
+
}
|
|
468
|
+
});
|
|
469
|
+
this.workers = [];
|
|
470
|
+
this.busyWorkers.clear();
|
|
471
|
+
}
|
|
472
|
+
async cleanup() {
|
|
473
|
+
this.isShuttingDown = true;
|
|
474
|
+
if (this.busyWorkers.size === 0) {
|
|
475
|
+
this.terminateAllWorkers();
|
|
476
|
+
return;
|
|
477
|
+
}
|
|
478
|
+
return new Promise((resolve) => {
|
|
479
|
+
const checkInterval = setInterval(() => {
|
|
480
|
+
if (this.busyWorkers.size === 0) {
|
|
481
|
+
clearInterval(checkInterval);
|
|
482
|
+
this.terminateAllWorkers();
|
|
483
|
+
resolve();
|
|
484
|
+
}
|
|
485
|
+
}, 100);
|
|
486
|
+
setTimeout(() => {
|
|
487
|
+
clearInterval(checkInterval);
|
|
488
|
+
this.terminateAllWorkers();
|
|
489
|
+
resolve();
|
|
490
|
+
}, 5e3);
|
|
491
|
+
});
|
|
492
|
+
}
|
|
493
|
+
};
|
|
494
|
+
|
|
495
|
+
// src/plugins/external.ts
|
|
496
|
+
function externalPlugin(externalPatterns, noExternalPatterns) {
|
|
497
|
+
return {
|
|
498
|
+
name: "bunup:external-plugin",
|
|
499
|
+
setup(build2) {
|
|
500
|
+
build2.onResolve({ filter: /.*/ }, (args) => {
|
|
501
|
+
const importPath = args.path;
|
|
502
|
+
const isExternal = externalPatterns.some((re) => re.test(importPath)) && !noExternalPatterns.some((re) => re.test(importPath));
|
|
503
|
+
if (isExternal) {
|
|
504
|
+
return { path: importPath, external: true };
|
|
505
|
+
}
|
|
506
|
+
return null;
|
|
507
|
+
});
|
|
508
|
+
}
|
|
509
|
+
};
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
// src/build.ts
|
|
513
|
+
async function build(options, rootDir) {
|
|
514
|
+
if (!options.entry || options.entry.length === 0 || !options.outDir) {
|
|
515
|
+
logger.cli(
|
|
516
|
+
"Nothing to build. Please make sure you have provided a proper bunup configuration or cli arguments."
|
|
517
|
+
);
|
|
518
|
+
return;
|
|
519
|
+
}
|
|
520
|
+
const startTime = performance.now();
|
|
521
|
+
logger.cli("Build started");
|
|
522
|
+
const packageJson = loadPackageJson(rootDir);
|
|
523
|
+
const packageType = packageJson?.type;
|
|
524
|
+
const externalPatterns = getExternalPatterns(options, packageJson);
|
|
525
|
+
const noExternalPatterns = getNoExternalPatterns(options);
|
|
526
|
+
const plugins = [externalPlugin(externalPatterns, noExternalPatterns)];
|
|
527
|
+
const buildPromises = options.format.flatMap(
|
|
528
|
+
(fmt) => options.entry.map(
|
|
529
|
+
(entry) => buildEntry(options, rootDir, entry, fmt, packageType, plugins)
|
|
530
|
+
)
|
|
531
|
+
);
|
|
532
|
+
try {
|
|
533
|
+
await Promise.all(buildPromises);
|
|
534
|
+
const buildTimeMs = performance.now() - startTime;
|
|
535
|
+
const timeDisplay = formatTime(buildTimeMs);
|
|
536
|
+
logger.cli(`\u26A1 Build success in ${timeDisplay}`);
|
|
537
|
+
} catch (error) {
|
|
538
|
+
logger.error("Build process encountered errors.");
|
|
539
|
+
process.exit(1);
|
|
540
|
+
}
|
|
541
|
+
if (options.dts) {
|
|
542
|
+
const dtsStartTime = performance.now();
|
|
543
|
+
logger.progress("DTS", "Bundling types");
|
|
544
|
+
const dtsOptions = typeof options.dts === "object" ? options.dts : {};
|
|
545
|
+
const entries = dtsOptions.entry || options.entry;
|
|
546
|
+
const formatsToProcess = options.format.filter((fmt) => {
|
|
547
|
+
if (fmt === "iife" && !isModulePackage(packageType) && options.format.includes("cjs")) {
|
|
548
|
+
return false;
|
|
549
|
+
}
|
|
550
|
+
return true;
|
|
551
|
+
});
|
|
552
|
+
const dtsWorker = new DtsWorker();
|
|
553
|
+
try {
|
|
554
|
+
const dtsPromises = formatsToProcess.flatMap(
|
|
555
|
+
(fmt) => entries.map(
|
|
556
|
+
(entry) => generateDtsForEntry(
|
|
557
|
+
options,
|
|
558
|
+
rootDir,
|
|
559
|
+
entry,
|
|
560
|
+
fmt,
|
|
561
|
+
packageType,
|
|
562
|
+
dtsOptions,
|
|
563
|
+
dtsWorker
|
|
564
|
+
)
|
|
565
|
+
)
|
|
566
|
+
);
|
|
567
|
+
await Promise.all(dtsPromises);
|
|
568
|
+
const dtsTimeMs = performance.now() - dtsStartTime;
|
|
569
|
+
const dtsTimeDisplay = formatTime(dtsTimeMs);
|
|
570
|
+
logger.progress("DTS", `Bundled types in ${dtsTimeDisplay}`);
|
|
571
|
+
} catch (error) {
|
|
572
|
+
await dtsWorker.cleanup();
|
|
573
|
+
}
|
|
574
|
+
await dtsWorker.cleanup();
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
async function generateDtsForEntry(options, rootDir, entry, fmt, packageType, dtsOptions, dtsWorker) {
|
|
578
|
+
const task = {
|
|
579
|
+
name: options.name,
|
|
580
|
+
rootDir,
|
|
581
|
+
outDir: options.outDir,
|
|
582
|
+
entry,
|
|
583
|
+
format: fmt,
|
|
584
|
+
packageType,
|
|
585
|
+
dtsOptions,
|
|
586
|
+
options
|
|
587
|
+
};
|
|
588
|
+
await dtsWorker.process(task);
|
|
589
|
+
}
|
|
590
|
+
async function buildEntry(options, rootDir, entry, fmt, packageType, plugins) {
|
|
591
|
+
const extension = getDefaultOutputExtension(fmt, packageType);
|
|
592
|
+
const defaultBunBuildOptions = createDefaultBunBuildOptions(
|
|
593
|
+
options,
|
|
594
|
+
rootDir
|
|
595
|
+
);
|
|
596
|
+
const result = await Bun.build({
|
|
597
|
+
...defaultBunBuildOptions,
|
|
598
|
+
entrypoints: [`${rootDir}/${entry}`],
|
|
599
|
+
format: fmt,
|
|
600
|
+
naming: { entry: getEntryNamingFormat(extension) },
|
|
601
|
+
splitting: getResolvedSplitting(options.splitting, fmt),
|
|
602
|
+
plugins
|
|
603
|
+
});
|
|
604
|
+
const entryName = getEntryNameOnly(entry);
|
|
605
|
+
if (!result.success) {
|
|
606
|
+
result.logs.forEach((log) => {
|
|
607
|
+
if (log.level === "error") logger.error(log.message);
|
|
608
|
+
else if (log.level === "warning") logger.warn(log.message);
|
|
609
|
+
else if (log.level === "info") logger.info(log.message);
|
|
610
|
+
});
|
|
611
|
+
throw new Error(`Build failed for ${entry} (${fmt})`);
|
|
612
|
+
}
|
|
613
|
+
logger.progress(
|
|
614
|
+
getLoggerProgressLabel(fmt, options.name),
|
|
615
|
+
`${options.outDir}/${entryName}${extension}`
|
|
616
|
+
);
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
// src/cli-parse.ts
|
|
620
|
+
var cliOptionAliases = {
|
|
621
|
+
n: "name",
|
|
622
|
+
f: "format",
|
|
623
|
+
o: "outDir",
|
|
624
|
+
m: "minify",
|
|
625
|
+
w: "watch",
|
|
626
|
+
d: "dts",
|
|
627
|
+
e: "external",
|
|
628
|
+
t: "target",
|
|
629
|
+
mw: "minifyWhitespace",
|
|
630
|
+
mi: "minifyIdentifiers",
|
|
631
|
+
ms: "minifySyntax",
|
|
632
|
+
c: "clean",
|
|
633
|
+
s: "splitting",
|
|
634
|
+
ne: "noExternal"
|
|
635
|
+
};
|
|
636
|
+
var cliOptionHandlers = {
|
|
637
|
+
name: (value, args) => {
|
|
638
|
+
args.name = value;
|
|
639
|
+
},
|
|
640
|
+
format: (value, args) => {
|
|
641
|
+
args.format = value.split(",");
|
|
642
|
+
},
|
|
643
|
+
outDir: (value, args) => {
|
|
644
|
+
args.outDir = value;
|
|
645
|
+
},
|
|
646
|
+
minify: (value, args) => {
|
|
647
|
+
args.minify = !!value;
|
|
648
|
+
},
|
|
649
|
+
watch: (value, args) => {
|
|
650
|
+
args.watch = !!value;
|
|
651
|
+
},
|
|
652
|
+
dts: (value, args) => {
|
|
653
|
+
args.dts = !!value;
|
|
654
|
+
},
|
|
655
|
+
external: (value, args) => {
|
|
656
|
+
args.external = value.split(",");
|
|
657
|
+
},
|
|
658
|
+
minifyWhitespace: (value, args) => {
|
|
659
|
+
args.minifyWhitespace = !!value;
|
|
660
|
+
},
|
|
661
|
+
minifyIdentifiers: (value, args) => {
|
|
662
|
+
args.minifyIdentifiers = !!value;
|
|
663
|
+
},
|
|
664
|
+
minifySyntax: (value, args) => {
|
|
665
|
+
args.minifySyntax = !!value;
|
|
666
|
+
},
|
|
667
|
+
target: (value, args) => {
|
|
668
|
+
args.target = value;
|
|
669
|
+
},
|
|
670
|
+
clean: (value, args) => {
|
|
671
|
+
args.clean = !!value;
|
|
672
|
+
},
|
|
673
|
+
splitting: (value, args) => {
|
|
674
|
+
args.splitting = !!value;
|
|
675
|
+
},
|
|
676
|
+
noExternal: (value, args) => {
|
|
677
|
+
args.noExternal = value.split(",");
|
|
678
|
+
}
|
|
679
|
+
};
|
|
680
|
+
function parseCliOptions(argv) {
|
|
681
|
+
const cliOptions = {};
|
|
682
|
+
for (let i = 0; i < argv.length; i++) {
|
|
683
|
+
const arg = argv[i];
|
|
684
|
+
if (arg.startsWith("--") || arg.startsWith("-")) {
|
|
685
|
+
const isShortOption = arg.startsWith("-") && !arg.startsWith("--");
|
|
686
|
+
const key = isShortOption ? arg.slice(1) : arg.slice(2);
|
|
687
|
+
const resolvedKey = isShortOption ? cliOptionAliases[key] : key;
|
|
688
|
+
const handler = cliOptionHandlers[resolvedKey];
|
|
689
|
+
if (!handler) {
|
|
690
|
+
logger.error(`Unknown option: ${key}`);
|
|
691
|
+
continue;
|
|
692
|
+
}
|
|
693
|
+
const nextArg = argv[i + 1];
|
|
694
|
+
const value = nextArg && !nextArg.startsWith("-") ? nextArg : true;
|
|
695
|
+
handler(value, cliOptions);
|
|
696
|
+
if (typeof value === "string") {
|
|
697
|
+
i++;
|
|
698
|
+
}
|
|
699
|
+
} else {
|
|
700
|
+
if (!cliOptions.entry) {
|
|
701
|
+
cliOptions.entry = [];
|
|
702
|
+
}
|
|
703
|
+
cliOptions.entry.push(arg);
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
return cliOptions;
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
// src/runtime.ts
|
|
710
|
+
(() => {
|
|
711
|
+
if (typeof Bun === "undefined") {
|
|
712
|
+
logger.error(
|
|
713
|
+
"Bunup requires Bun to run.\nTo install Bun, visit https://bun.sh/docs/installation"
|
|
714
|
+
);
|
|
715
|
+
process.exit(1);
|
|
716
|
+
}
|
|
717
|
+
})();
|
|
718
|
+
async function watch(options, rootDir) {
|
|
719
|
+
const watchPaths = /* @__PURE__ */ new Set();
|
|
720
|
+
options.entry.forEach((entry) => {
|
|
721
|
+
const entryPath = path2__default.default.resolve(rootDir, entry);
|
|
722
|
+
const parentDir = path2__default.default.dirname(entryPath);
|
|
723
|
+
watchPaths.add(parentDir);
|
|
724
|
+
});
|
|
725
|
+
const watcher = chokidar__default.default.watch(Array.from(watchPaths), {
|
|
726
|
+
persistent: true,
|
|
727
|
+
ignoreInitial: true,
|
|
728
|
+
awaitWriteFinish: {
|
|
729
|
+
stabilityThreshold: 100,
|
|
730
|
+
pollInterval: 50
|
|
731
|
+
},
|
|
732
|
+
atomic: true,
|
|
733
|
+
ignorePermissionErrors: true,
|
|
734
|
+
ignored: [/[\\/]\.git[\\/]/, /[\\/]node_modules[\\/]/, options.outDir]
|
|
735
|
+
});
|
|
736
|
+
let debounceTimer = null;
|
|
737
|
+
let isRebuilding = false;
|
|
738
|
+
const triggerRebuild = async (changedFile) => {
|
|
739
|
+
if (isRebuilding) return;
|
|
740
|
+
isRebuilding = true;
|
|
741
|
+
try {
|
|
742
|
+
await build(
|
|
743
|
+
{
|
|
744
|
+
...options,
|
|
745
|
+
entry: [changedFile],
|
|
746
|
+
clean: false
|
|
747
|
+
},
|
|
748
|
+
rootDir
|
|
749
|
+
);
|
|
750
|
+
} catch (error) {
|
|
751
|
+
logger.error(`Build failed: ${error}`);
|
|
752
|
+
} finally {
|
|
753
|
+
isRebuilding = false;
|
|
754
|
+
}
|
|
755
|
+
};
|
|
756
|
+
watcher.on("change", (filePath) => {
|
|
757
|
+
const changedFile = path2__default.default.relative(rootDir, filePath);
|
|
758
|
+
logger.cli(`File changed: ${changedFile}`);
|
|
759
|
+
if (debounceTimer) {
|
|
760
|
+
clearTimeout(debounceTimer);
|
|
761
|
+
}
|
|
762
|
+
debounceTimer = setTimeout(() => triggerRebuild(changedFile), 300);
|
|
763
|
+
});
|
|
764
|
+
watcher.on("error", (error) => {
|
|
765
|
+
logger.error(`Watcher error: ${error}`);
|
|
766
|
+
});
|
|
767
|
+
}
|
|
768
|
+
|
|
769
|
+
// src/cli.ts
|
|
770
|
+
async function main(args = Bun.argv.slice(2)) {
|
|
771
|
+
const cliOptions = parseCliOptions(args);
|
|
772
|
+
const configs = await loadConfigs(process.cwd());
|
|
773
|
+
const rootDir = process.cwd();
|
|
774
|
+
if (cliOptions.watch) {
|
|
775
|
+
logger.cli("Starting watch mode");
|
|
776
|
+
logger.cli(`Watching for file changes`);
|
|
777
|
+
}
|
|
778
|
+
if (configs.length === 0) {
|
|
779
|
+
const mergedOptions = {
|
|
780
|
+
...DEFAULT_OPTIONS,
|
|
781
|
+
...cliOptions
|
|
782
|
+
};
|
|
783
|
+
if (mergedOptions.clean) cleanOutDir(rootDir, mergedOptions.outDir);
|
|
784
|
+
await handleBuild(mergedOptions, rootDir);
|
|
785
|
+
} else {
|
|
786
|
+
for (const { options, rootDir: rootDir2 } of configs) {
|
|
787
|
+
if (options.clean) cleanOutDir(rootDir2, options.outDir);
|
|
788
|
+
}
|
|
789
|
+
await Promise.all(
|
|
790
|
+
configs.map(async ({ options, rootDir: rootDir2 }) => {
|
|
791
|
+
const mergedOptions = {
|
|
792
|
+
...DEFAULT_OPTIONS,
|
|
793
|
+
...options,
|
|
794
|
+
...cliOptions
|
|
795
|
+
};
|
|
796
|
+
await handleBuild(mergedOptions, rootDir2);
|
|
797
|
+
})
|
|
798
|
+
);
|
|
799
|
+
}
|
|
800
|
+
if (!cliOptions.watch) {
|
|
801
|
+
process.exit(0);
|
|
802
|
+
}
|
|
803
|
+
}
|
|
804
|
+
async function handleBuild(options, rootDir) {
|
|
805
|
+
if (options.watch) {
|
|
806
|
+
await watch(options, rootDir);
|
|
807
|
+
} else {
|
|
808
|
+
await build(options, rootDir);
|
|
809
|
+
}
|
|
810
|
+
}
|
|
811
|
+
function cleanOutDir(rootDir, outdir) {
|
|
812
|
+
const outdirPath = path2__default.default.join(rootDir, outdir);
|
|
813
|
+
if (fs__default.default.existsSync(outdirPath)) {
|
|
814
|
+
try {
|
|
815
|
+
fs__default.default.rmSync(outdirPath, { recursive: true, force: true });
|
|
816
|
+
} catch (error) {
|
|
817
|
+
logger.error(`Failed to clean output directory: ${error}`);
|
|
818
|
+
}
|
|
819
|
+
}
|
|
820
|
+
fs__default.default.mkdirSync(outdirPath, { recursive: true });
|
|
821
|
+
}
|
|
822
|
+
main().catch((error) => {
|
|
823
|
+
handleError(error);
|
|
824
|
+
process.exit(1);
|
|
825
|
+
});
|
|
826
|
+
|
|
827
|
+
exports.main = main;
|