bunup 0.1.3 → 0.1.5
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 +13 -2
- package/build/cli.js +202 -99
- package/build/cli.mjs +200 -97
- package/build/dtsWorker.js +1 -1
- package/build/index.d.mts +3 -15
- package/build/index.d.ts +3 -15
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -1,7 +1,18 @@
|
|
|
1
|
-
# Bunup
|
|
1
|
+
# Bunup
|
|
2
2
|
|
|
3
3
|
> A extremely fast, zero-config bundler for TypeScript & JavaScript, powered by Bun.
|
|
4
4
|
|
|
5
5
|
Built with speed in mind, Bunup aims to provide the fastest bundling experience possible. This project is currently a work in progress and will be ready for production use soon.
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
## Benchmarks
|
|
8
|
+
|
|
9
|
+
Bunup outperforms other popular bundlers by a significant margin:
|
|
10
|
+
|
|
11
|
+
| Bundler | Format | Build Time | Relative Speed |
|
|
12
|
+
| ------------- | -------- | ------------ | ---------------- |
|
|
13
|
+
| bunup | esm, cjs | **3.65ms** | **16.0x faster** |
|
|
14
|
+
| bunup (+ dts) | esm, cjs | **149.51ms** | **5.0x faster** |
|
|
15
|
+
| tsup | esm, cjs | 58.36ms | baseline |
|
|
16
|
+
| tsup (+ dts) | esm, cjs | 745.23ms | baseline |
|
|
17
|
+
|
|
18
|
+
_Lower build time is better. Benchmark run on the same code with identical output formats._
|
package/build/cli.js
CHANGED
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
2
|
'use strict';
|
|
3
3
|
|
|
4
|
-
var
|
|
4
|
+
var fs2 = require('fs');
|
|
5
5
|
var path2 = require('path');
|
|
6
|
+
var oxc = require('oxc-transform');
|
|
6
7
|
var rollup = require('rollup');
|
|
7
8
|
var dtsPlugin = require('rollup-plugin-dts');
|
|
8
|
-
var ts = require('typescript');
|
|
9
9
|
var chokidar = require('chokidar');
|
|
10
10
|
|
|
11
11
|
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
12
12
|
|
|
13
|
-
var
|
|
13
|
+
var fs2__default = /*#__PURE__*/_interopDefault(fs2);
|
|
14
14
|
var path2__default = /*#__PURE__*/_interopDefault(path2);
|
|
15
|
+
var oxc__default = /*#__PURE__*/_interopDefault(oxc);
|
|
15
16
|
var dtsPlugin__default = /*#__PURE__*/_interopDefault(dtsPlugin);
|
|
16
|
-
var ts__default = /*#__PURE__*/_interopDefault(ts);
|
|
17
17
|
var chokidar__default = /*#__PURE__*/_interopDefault(chokidar);
|
|
18
18
|
|
|
19
19
|
// src/errors.ts
|
|
@@ -38,6 +38,9 @@ var handleError = (error, context) => {
|
|
|
38
38
|
function escapeRegExp(string) {
|
|
39
39
|
return string.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
40
40
|
}
|
|
41
|
+
function generateRandomSuffix(length = 8) {
|
|
42
|
+
return Math.random().toString(36).substring(2, 2 + length);
|
|
43
|
+
}
|
|
41
44
|
function getDefaultOutputExtension(format, packageType) {
|
|
42
45
|
switch (format) {
|
|
43
46
|
case "esm":
|
|
@@ -58,15 +61,9 @@ function getDefaultDtsExtention(format, packageType) {
|
|
|
58
61
|
return ".d.ts";
|
|
59
62
|
}
|
|
60
63
|
}
|
|
61
|
-
function getEntryNamingFormat(extension) {
|
|
62
|
-
return `[dir]/[name]${extension}`;
|
|
63
|
-
}
|
|
64
64
|
function isModulePackage(packageType) {
|
|
65
65
|
return packageType === "module";
|
|
66
66
|
}
|
|
67
|
-
function getEntryNameOnly(entry) {
|
|
68
|
-
return entry.split("/").pop()?.split(".").slice(0, -1).join(".") || "";
|
|
69
|
-
}
|
|
70
67
|
function formatTime(ms) {
|
|
71
68
|
return ms >= 1e3 ? `${(ms / 1e3).toFixed(2)}s` : `${Math.round(ms)}ms`;
|
|
72
69
|
}
|
|
@@ -206,10 +203,10 @@ async function loadConfigs(cwd) {
|
|
|
206
203
|
]) {
|
|
207
204
|
const filePath = path2__default.default.join(cwd, `bunup.config${ext}`);
|
|
208
205
|
try {
|
|
209
|
-
if (!
|
|
206
|
+
if (!fs2__default.default.existsSync(filePath)) continue;
|
|
210
207
|
let content;
|
|
211
208
|
if (ext === ".json" || ext === ".jsonc") {
|
|
212
|
-
const text =
|
|
209
|
+
const text = fs2__default.default.readFileSync(filePath, "utf8");
|
|
213
210
|
content = JSON.parse(text);
|
|
214
211
|
} else {
|
|
215
212
|
const imported = await import(`file://${filePath}`);
|
|
@@ -245,10 +242,10 @@ async function loadConfigs(cwd) {
|
|
|
245
242
|
function loadPackageJson(cwd) {
|
|
246
243
|
const packageJsonPath = path2__default.default.join(cwd, "package.json");
|
|
247
244
|
try {
|
|
248
|
-
if (!
|
|
245
|
+
if (!fs2__default.default.existsSync(packageJsonPath)) {
|
|
249
246
|
return null;
|
|
250
247
|
}
|
|
251
|
-
const text =
|
|
248
|
+
const text = fs2__default.default.readFileSync(packageJsonPath, "utf8");
|
|
252
249
|
const content = JSON.parse(text);
|
|
253
250
|
return content;
|
|
254
251
|
} catch (error) {
|
|
@@ -258,86 +255,177 @@ function loadPackageJson(cwd) {
|
|
|
258
255
|
return null;
|
|
259
256
|
}
|
|
260
257
|
}
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
258
|
+
|
|
259
|
+
// src/dts/index.ts
|
|
260
|
+
async function generateDts(rootDir, entry, format, options) {
|
|
261
|
+
const { absoluteRootDir, absoluteEntry } = validateInputs(rootDir, entry);
|
|
262
|
+
const tsFiles = await collectTsFiles(absoluteEntry);
|
|
263
|
+
const dtsMap = await generateDtsContent(tsFiles);
|
|
264
|
+
return bundleDtsContent(
|
|
265
|
+
absoluteEntry,
|
|
266
|
+
dtsMap,
|
|
267
|
+
format,
|
|
268
|
+
options,
|
|
269
|
+
absoluteRootDir
|
|
270
|
+
);
|
|
271
|
+
}
|
|
272
|
+
async function collectTsFiles(entry) {
|
|
273
|
+
const visited = /* @__PURE__ */ new Set();
|
|
274
|
+
const toVisit = [entry];
|
|
275
|
+
while (toVisit.length > 0) {
|
|
276
|
+
const current = toVisit.pop();
|
|
277
|
+
if (!current || visited.has(current)) continue;
|
|
278
|
+
visited.add(current);
|
|
279
|
+
try {
|
|
280
|
+
const sourceText = await fs2__default.default.promises.readFile(current, "utf8");
|
|
281
|
+
const relativeImports = extractRelativeImports(sourceText);
|
|
282
|
+
for (const relImport of relativeImports) {
|
|
283
|
+
const importDir = path2__default.default.dirname(current);
|
|
284
|
+
const absImport = path2__default.default.resolve(importDir, relImport);
|
|
285
|
+
const possiblePaths = [
|
|
286
|
+
absImport,
|
|
287
|
+
`${absImport}.ts`,
|
|
288
|
+
`${absImport}.tsx`,
|
|
289
|
+
`${absImport}/index.ts`,
|
|
290
|
+
`${absImport}/index.tsx`
|
|
291
|
+
];
|
|
292
|
+
for (const tsFile of possiblePaths) {
|
|
293
|
+
if (fs2__default.default.existsSync(tsFile) && tsFile.endsWith(".ts") && !visited.has(tsFile)) {
|
|
294
|
+
toVisit.push(tsFile);
|
|
295
|
+
break;
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
} catch (error) {
|
|
300
|
+
logger.warn(
|
|
301
|
+
`Error processing ${current}: ${error instanceof Error ? error.message : String(error)}`
|
|
302
|
+
);
|
|
303
|
+
}
|
|
264
304
|
}
|
|
305
|
+
return visited;
|
|
306
|
+
}
|
|
307
|
+
function extractRelativeImports(sourceText) {
|
|
308
|
+
const imports = /* @__PURE__ */ new Set();
|
|
265
309
|
try {
|
|
266
|
-
const
|
|
267
|
-
|
|
268
|
-
|
|
310
|
+
const importExportRegex = /(?:import|export)(?:(?:[\s\n]*(?:type[\s\n]+)?(?:\*|\{[^}]*\}|[\w$]+)[\s\n]+from[\s\n]*)|[\s\n]+)(["'`])([^'"]+)\1/g;
|
|
311
|
+
let match;
|
|
312
|
+
while ((match = importExportRegex.exec(sourceText)) !== null) {
|
|
313
|
+
const importPath = match[2];
|
|
314
|
+
if (importPath.startsWith(".")) {
|
|
315
|
+
imports.add(importPath);
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
const sideEffectImportRegex = /import\s+(["'`])([^'"]+)\1\s*;?/g;
|
|
319
|
+
while ((match = sideEffectImportRegex.exec(sourceText)) !== null) {
|
|
320
|
+
const importPath = match[2];
|
|
321
|
+
if (importPath.startsWith(".")) {
|
|
322
|
+
imports.add(importPath);
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
const dynamicImportRegex = /import\s*\(\s*(["'`])([^'"]+)\1\s*\)/g;
|
|
326
|
+
while ((match = dynamicImportRegex.exec(sourceText)) !== null) {
|
|
327
|
+
const importPath = match[2];
|
|
328
|
+
if (importPath.startsWith(".")) {
|
|
329
|
+
imports.add(importPath);
|
|
330
|
+
}
|
|
331
|
+
}
|
|
269
332
|
} catch (error) {
|
|
270
333
|
logger.warn(
|
|
271
|
-
`
|
|
334
|
+
`Error extracting imports: ${error instanceof Error ? error.message : String(error)}`
|
|
272
335
|
);
|
|
273
|
-
return {};
|
|
274
336
|
}
|
|
337
|
+
return Array.from(imports);
|
|
275
338
|
}
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
339
|
+
async function generateDtsContent(tsFiles) {
|
|
340
|
+
const dtsMap = /* @__PURE__ */ new Map();
|
|
341
|
+
await Promise.all(
|
|
342
|
+
Array.from(tsFiles).map(async (tsFile) => {
|
|
343
|
+
try {
|
|
344
|
+
const dtsPath = tsFile.replace(/\.tsx?$/, ".d.ts");
|
|
345
|
+
const sourceText = await fs2__default.default.promises.readFile(tsFile, "utf8");
|
|
346
|
+
const { code: declaration } = oxc__default.default.isolatedDeclaration(
|
|
347
|
+
tsFile,
|
|
348
|
+
sourceText
|
|
349
|
+
);
|
|
350
|
+
if (declaration) {
|
|
351
|
+
dtsMap.set(dtsPath, declaration);
|
|
352
|
+
}
|
|
353
|
+
} catch (error) {
|
|
354
|
+
logger.warn(
|
|
355
|
+
`Failed to generate declaration for ${tsFile}: ${error instanceof Error ? error.message : String(error)}`
|
|
356
|
+
);
|
|
357
|
+
}
|
|
358
|
+
})
|
|
359
|
+
);
|
|
360
|
+
return dtsMap;
|
|
361
|
+
}
|
|
362
|
+
async function bundleDtsContent(entryFile, dtsMap, format, options, rootDir) {
|
|
363
|
+
const VIRTUAL_PREFIX = "\0virtual:";
|
|
364
|
+
const entryDtsPath = entryFile.replace(/\.tsx?$/, ".d.ts");
|
|
365
|
+
const virtualEntry = `${VIRTUAL_PREFIX}${entryDtsPath}`;
|
|
366
|
+
const virtualPlugin = {
|
|
367
|
+
name: "virtual-dts",
|
|
368
|
+
resolveId(source, importer) {
|
|
369
|
+
if (source.startsWith(VIRTUAL_PREFIX)) {
|
|
370
|
+
return source;
|
|
371
|
+
}
|
|
372
|
+
if (importer?.startsWith(VIRTUAL_PREFIX)) {
|
|
373
|
+
const importerPath = importer.slice(VIRTUAL_PREFIX.length);
|
|
374
|
+
const importerDir = path2__default.default.dirname(importerPath);
|
|
375
|
+
if (source.startsWith(".")) {
|
|
376
|
+
const resolvedPath = path2__default.default.resolve(importerDir, source);
|
|
377
|
+
for (const ext of ["", ".d.ts", "/index.d.ts"]) {
|
|
378
|
+
const fullPath = `${resolvedPath}${ext}`;
|
|
379
|
+
if (dtsMap.has(fullPath)) {
|
|
380
|
+
return `${VIRTUAL_PREFIX}${fullPath}`;
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
return null;
|
|
386
|
+
},
|
|
387
|
+
load(id) {
|
|
388
|
+
if (id.startsWith(VIRTUAL_PREFIX)) {
|
|
389
|
+
const actualPath = id.slice(VIRTUAL_PREFIX.length);
|
|
390
|
+
return dtsMap.get(actualPath) || null;
|
|
391
|
+
}
|
|
392
|
+
return null;
|
|
393
|
+
}
|
|
394
|
+
};
|
|
395
|
+
const packageJson = loadPackageJson(rootDir);
|
|
284
396
|
const externalPatterns = getExternalPatterns(options, packageJson);
|
|
285
397
|
const noExternalPatterns = getNoExternalPatterns(options);
|
|
286
398
|
let bundle;
|
|
287
|
-
let result;
|
|
288
399
|
try {
|
|
289
400
|
bundle = await rollup.rollup({
|
|
290
|
-
input:
|
|
401
|
+
input: virtualEntry,
|
|
291
402
|
onwarn(warning, handler) {
|
|
292
403
|
if (warning.code === "UNRESOLVED_IMPORT" || warning.code === "CIRCULAR_DEPENDENCY" || warning.code === "EMPTY_BUNDLE") {
|
|
293
404
|
return;
|
|
294
405
|
}
|
|
295
|
-
|
|
406
|
+
handler(warning);
|
|
296
407
|
},
|
|
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
|
-
],
|
|
408
|
+
plugins: [virtualPlugin, dtsPlugin__default.default()],
|
|
318
409
|
external: (source) => externalPatterns.some((re) => re.test(source)) && !noExternalPatterns.some((re) => re.test(source))
|
|
319
410
|
});
|
|
320
411
|
const { output } = await bundle.generate({ format });
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
412
|
+
if (!output[0]?.code) {
|
|
413
|
+
throw new Error("Generated bundle is empty");
|
|
414
|
+
}
|
|
415
|
+
return output[0].code;
|
|
416
|
+
} catch (error) {
|
|
417
|
+
throw new Error(`DTS bundling failed: ${parseErrorMessage(error)}`);
|
|
326
418
|
} finally {
|
|
327
419
|
if (bundle) await bundle.close();
|
|
328
420
|
}
|
|
329
|
-
if (!result) {
|
|
330
|
-
throw new Error("Failed to generate bundled DTS content");
|
|
331
|
-
}
|
|
332
|
-
return result;
|
|
333
421
|
}
|
|
334
422
|
function validateInputs(rootDir, entry) {
|
|
335
423
|
const absoluteRootDir = path2__default.default.resolve(rootDir);
|
|
336
424
|
const absoluteEntry = path2__default.default.resolve(absoluteRootDir, entry);
|
|
337
|
-
if (!
|
|
425
|
+
if (!fs2__default.default.existsSync(absoluteRootDir)) {
|
|
338
426
|
throw new Error(`Root directory does not exist: ${absoluteRootDir}`);
|
|
339
427
|
}
|
|
340
|
-
if (!
|
|
428
|
+
if (!fs2__default.default.existsSync(absoluteEntry)) {
|
|
341
429
|
throw new Error(`Entry file does not exist: ${absoluteEntry}`);
|
|
342
430
|
}
|
|
343
431
|
if (!absoluteEntry.endsWith(".ts")) {
|
|
@@ -353,27 +441,11 @@ function validateInputs(rootDir, entry) {
|
|
|
353
441
|
|
|
354
442
|
// src/dts/worker.ts
|
|
355
443
|
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;
|
|
444
|
+
const { name, rootDir, outDir, entry, format, packageType, options } = event.data;
|
|
366
445
|
try {
|
|
367
|
-
const content = await generateDts(
|
|
368
|
-
rootDir,
|
|
369
|
-
entry,
|
|
370
|
-
format,
|
|
371
|
-
options,
|
|
372
|
-
dtsOptions
|
|
373
|
-
);
|
|
374
|
-
const entryName = getEntryNameOnly(entry);
|
|
446
|
+
const content = await generateDts(rootDir, entry.path, format, options);
|
|
375
447
|
const extension = getDefaultDtsExtention(format, packageType);
|
|
376
|
-
const outputRelativePath = `${outDir}/${
|
|
448
|
+
const outputRelativePath = `${outDir}/${entry.name}${extension}`;
|
|
377
449
|
const outputPath = `${rootDir}/${outputRelativePath}`;
|
|
378
450
|
await Bun.write(outputPath, content);
|
|
379
451
|
const response = {
|
|
@@ -492,6 +564,38 @@ var DtsWorker = class {
|
|
|
492
564
|
}
|
|
493
565
|
};
|
|
494
566
|
|
|
567
|
+
// src/helpers/entry.ts
|
|
568
|
+
function getEntryNameOnly(entry) {
|
|
569
|
+
return entry.split("/").pop()?.split(".").slice(0, -1).join(".") || "";
|
|
570
|
+
}
|
|
571
|
+
function normalizeEntryToProcessableEntries(entries) {
|
|
572
|
+
const result = [];
|
|
573
|
+
const usedNames = /* @__PURE__ */ new Set();
|
|
574
|
+
function addEntry(name, path6) {
|
|
575
|
+
if (usedNames.has(name)) {
|
|
576
|
+
const randomSuffix = generateRandomSuffix();
|
|
577
|
+
result.push({ name: `${name}_${randomSuffix}`, path: path6 });
|
|
578
|
+
} else {
|
|
579
|
+
result.push({ name, path: path6 });
|
|
580
|
+
usedNames.add(name);
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
for (const entry of entries) {
|
|
584
|
+
if (typeof entry === "string") {
|
|
585
|
+
const name = getEntryNameOnly(entry);
|
|
586
|
+
addEntry(name, entry);
|
|
587
|
+
} else {
|
|
588
|
+
Object.entries(entry).forEach(([name, path6]) => {
|
|
589
|
+
addEntry(name, path6);
|
|
590
|
+
});
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
return result;
|
|
594
|
+
}
|
|
595
|
+
function getEntryNamingFormat(extension) {
|
|
596
|
+
return `[dir]/[name]${extension}`;
|
|
597
|
+
}
|
|
598
|
+
|
|
495
599
|
// src/plugins/external.ts
|
|
496
600
|
function externalPlugin(externalPatterns, noExternalPatterns) {
|
|
497
601
|
return {
|
|
@@ -524,8 +628,11 @@ async function build(options, rootDir) {
|
|
|
524
628
|
const externalPatterns = getExternalPatterns(options, packageJson);
|
|
525
629
|
const noExternalPatterns = getNoExternalPatterns(options);
|
|
526
630
|
const plugins = [externalPlugin(externalPatterns, noExternalPatterns)];
|
|
631
|
+
const processableEntries = normalizeEntryToProcessableEntries(
|
|
632
|
+
options.entry
|
|
633
|
+
);
|
|
527
634
|
const buildPromises = options.format.flatMap(
|
|
528
|
-
(fmt) =>
|
|
635
|
+
(fmt) => processableEntries.map(
|
|
529
636
|
(entry) => buildEntry(options, rootDir, entry, fmt, packageType, plugins)
|
|
530
637
|
)
|
|
531
638
|
);
|
|
@@ -541,8 +648,6 @@ async function build(options, rootDir) {
|
|
|
541
648
|
if (options.dts) {
|
|
542
649
|
const dtsStartTime = performance.now();
|
|
543
650
|
logger.progress("DTS", "Bundling types");
|
|
544
|
-
const dtsOptions = typeof options.dts === "object" ? options.dts : {};
|
|
545
|
-
const entries = dtsOptions.entry || options.entry;
|
|
546
651
|
const formatsToProcess = options.format.filter((fmt) => {
|
|
547
652
|
if (fmt === "iife" && !isModulePackage(packageType) && options.format.includes("cjs")) {
|
|
548
653
|
return false;
|
|
@@ -552,14 +657,13 @@ async function build(options, rootDir) {
|
|
|
552
657
|
const dtsWorker = new DtsWorker();
|
|
553
658
|
try {
|
|
554
659
|
const dtsPromises = formatsToProcess.flatMap(
|
|
555
|
-
(fmt) =>
|
|
660
|
+
(fmt) => processableEntries.map(
|
|
556
661
|
(entry) => generateDtsForEntry(
|
|
557
662
|
options,
|
|
558
663
|
rootDir,
|
|
559
664
|
entry,
|
|
560
665
|
fmt,
|
|
561
666
|
packageType,
|
|
562
|
-
dtsOptions,
|
|
563
667
|
dtsWorker
|
|
564
668
|
)
|
|
565
669
|
)
|
|
@@ -574,7 +678,7 @@ async function build(options, rootDir) {
|
|
|
574
678
|
await dtsWorker.cleanup();
|
|
575
679
|
}
|
|
576
680
|
}
|
|
577
|
-
async function generateDtsForEntry(options, rootDir, entry, fmt, packageType,
|
|
681
|
+
async function generateDtsForEntry(options, rootDir, entry, fmt, packageType, dtsWorker) {
|
|
578
682
|
const task = {
|
|
579
683
|
name: options.name,
|
|
580
684
|
rootDir,
|
|
@@ -582,7 +686,6 @@ async function generateDtsForEntry(options, rootDir, entry, fmt, packageType, dt
|
|
|
582
686
|
entry,
|
|
583
687
|
format: fmt,
|
|
584
688
|
packageType,
|
|
585
|
-
dtsOptions,
|
|
586
689
|
options
|
|
587
690
|
};
|
|
588
691
|
await dtsWorker.process(task);
|
|
@@ -595,13 +698,12 @@ async function buildEntry(options, rootDir, entry, fmt, packageType, plugins) {
|
|
|
595
698
|
);
|
|
596
699
|
const result = await Bun.build({
|
|
597
700
|
...defaultBunBuildOptions,
|
|
598
|
-
entrypoints: [`${rootDir}/${entry}`],
|
|
701
|
+
entrypoints: [`${rootDir}/${entry.path}`],
|
|
599
702
|
format: fmt,
|
|
600
703
|
naming: { entry: getEntryNamingFormat(extension) },
|
|
601
704
|
splitting: getResolvedSplitting(options.splitting, fmt),
|
|
602
705
|
plugins
|
|
603
706
|
});
|
|
604
|
-
const entryName = getEntryNameOnly(entry);
|
|
605
707
|
if (!result.success) {
|
|
606
708
|
result.logs.forEach((log) => {
|
|
607
709
|
if (log.level === "error") logger.error(log.message);
|
|
@@ -612,7 +714,7 @@ async function buildEntry(options, rootDir, entry, fmt, packageType, plugins) {
|
|
|
612
714
|
}
|
|
613
715
|
logger.progress(
|
|
614
716
|
getLoggerProgressLabel(fmt, options.name),
|
|
615
|
-
`${options.outDir}/${
|
|
717
|
+
`${options.outDir}/${entry.name}${extension}`
|
|
616
718
|
);
|
|
617
719
|
}
|
|
618
720
|
|
|
@@ -717,8 +819,9 @@ function parseCliOptions(argv) {
|
|
|
717
819
|
})();
|
|
718
820
|
async function watch(options, rootDir) {
|
|
719
821
|
const watchPaths = /* @__PURE__ */ new Set();
|
|
720
|
-
options.entry
|
|
721
|
-
|
|
822
|
+
const normalizedEntry = normalizeEntryToProcessableEntries(options.entry);
|
|
823
|
+
normalizedEntry.forEach((entry) => {
|
|
824
|
+
const entryPath = path2__default.default.resolve(rootDir, entry.path);
|
|
722
825
|
const parentDir = path2__default.default.dirname(entryPath);
|
|
723
826
|
watchPaths.add(parentDir);
|
|
724
827
|
});
|
|
@@ -810,14 +913,14 @@ async function handleBuild(options, rootDir) {
|
|
|
810
913
|
}
|
|
811
914
|
function cleanOutDir(rootDir, outdir) {
|
|
812
915
|
const outdirPath = path2__default.default.join(rootDir, outdir);
|
|
813
|
-
if (
|
|
916
|
+
if (fs2__default.default.existsSync(outdirPath)) {
|
|
814
917
|
try {
|
|
815
|
-
|
|
918
|
+
fs2__default.default.rmSync(outdirPath, { recursive: true, force: true });
|
|
816
919
|
} catch (error) {
|
|
817
920
|
logger.error(`Failed to clean output directory: ${error}`);
|
|
818
921
|
}
|
|
819
922
|
}
|
|
820
|
-
|
|
923
|
+
fs2__default.default.mkdirSync(outdirPath, { recursive: true });
|
|
821
924
|
}
|
|
822
925
|
main().catch((error) => {
|
|
823
926
|
handleError(error);
|
package/build/cli.mjs
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
|
-
import
|
|
2
|
+
import fs2 from 'fs';
|
|
3
3
|
import path2 from 'path';
|
|
4
|
+
import oxc from 'oxc-transform';
|
|
4
5
|
import { rollup } from 'rollup';
|
|
5
6
|
import dtsPlugin from 'rollup-plugin-dts';
|
|
6
|
-
import ts, { ScriptTarget } from 'typescript';
|
|
7
7
|
import chokidar from 'chokidar';
|
|
8
8
|
|
|
9
9
|
// src/errors.ts
|
|
@@ -28,6 +28,9 @@ var handleError = (error, context) => {
|
|
|
28
28
|
function escapeRegExp(string) {
|
|
29
29
|
return string.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
30
30
|
}
|
|
31
|
+
function generateRandomSuffix(length = 8) {
|
|
32
|
+
return Math.random().toString(36).substring(2, 2 + length);
|
|
33
|
+
}
|
|
31
34
|
function getDefaultOutputExtension(format, packageType) {
|
|
32
35
|
switch (format) {
|
|
33
36
|
case "esm":
|
|
@@ -48,15 +51,9 @@ function getDefaultDtsExtention(format, packageType) {
|
|
|
48
51
|
return ".d.ts";
|
|
49
52
|
}
|
|
50
53
|
}
|
|
51
|
-
function getEntryNamingFormat(extension) {
|
|
52
|
-
return `[dir]/[name]${extension}`;
|
|
53
|
-
}
|
|
54
54
|
function isModulePackage(packageType) {
|
|
55
55
|
return packageType === "module";
|
|
56
56
|
}
|
|
57
|
-
function getEntryNameOnly(entry) {
|
|
58
|
-
return entry.split("/").pop()?.split(".").slice(0, -1).join(".") || "";
|
|
59
|
-
}
|
|
60
57
|
function formatTime(ms) {
|
|
61
58
|
return ms >= 1e3 ? `${(ms / 1e3).toFixed(2)}s` : `${Math.round(ms)}ms`;
|
|
62
59
|
}
|
|
@@ -196,10 +193,10 @@ async function loadConfigs(cwd) {
|
|
|
196
193
|
]) {
|
|
197
194
|
const filePath = path2.join(cwd, `bunup.config${ext}`);
|
|
198
195
|
try {
|
|
199
|
-
if (!
|
|
196
|
+
if (!fs2.existsSync(filePath)) continue;
|
|
200
197
|
let content;
|
|
201
198
|
if (ext === ".json" || ext === ".jsonc") {
|
|
202
|
-
const text =
|
|
199
|
+
const text = fs2.readFileSync(filePath, "utf8");
|
|
203
200
|
content = JSON.parse(text);
|
|
204
201
|
} else {
|
|
205
202
|
const imported = await import(`file://${filePath}`);
|
|
@@ -235,10 +232,10 @@ async function loadConfigs(cwd) {
|
|
|
235
232
|
function loadPackageJson(cwd) {
|
|
236
233
|
const packageJsonPath = path2.join(cwd, "package.json");
|
|
237
234
|
try {
|
|
238
|
-
if (!
|
|
235
|
+
if (!fs2.existsSync(packageJsonPath)) {
|
|
239
236
|
return null;
|
|
240
237
|
}
|
|
241
|
-
const text =
|
|
238
|
+
const text = fs2.readFileSync(packageJsonPath, "utf8");
|
|
242
239
|
const content = JSON.parse(text);
|
|
243
240
|
return content;
|
|
244
241
|
} catch (error) {
|
|
@@ -248,86 +245,177 @@ function loadPackageJson(cwd) {
|
|
|
248
245
|
return null;
|
|
249
246
|
}
|
|
250
247
|
}
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
248
|
+
|
|
249
|
+
// src/dts/index.ts
|
|
250
|
+
async function generateDts(rootDir, entry, format, options) {
|
|
251
|
+
const { absoluteRootDir, absoluteEntry } = validateInputs(rootDir, entry);
|
|
252
|
+
const tsFiles = await collectTsFiles(absoluteEntry);
|
|
253
|
+
const dtsMap = await generateDtsContent(tsFiles);
|
|
254
|
+
return bundleDtsContent(
|
|
255
|
+
absoluteEntry,
|
|
256
|
+
dtsMap,
|
|
257
|
+
format,
|
|
258
|
+
options,
|
|
259
|
+
absoluteRootDir
|
|
260
|
+
);
|
|
261
|
+
}
|
|
262
|
+
async function collectTsFiles(entry) {
|
|
263
|
+
const visited = /* @__PURE__ */ new Set();
|
|
264
|
+
const toVisit = [entry];
|
|
265
|
+
while (toVisit.length > 0) {
|
|
266
|
+
const current = toVisit.pop();
|
|
267
|
+
if (!current || visited.has(current)) continue;
|
|
268
|
+
visited.add(current);
|
|
269
|
+
try {
|
|
270
|
+
const sourceText = await fs2.promises.readFile(current, "utf8");
|
|
271
|
+
const relativeImports = extractRelativeImports(sourceText);
|
|
272
|
+
for (const relImport of relativeImports) {
|
|
273
|
+
const importDir = path2.dirname(current);
|
|
274
|
+
const absImport = path2.resolve(importDir, relImport);
|
|
275
|
+
const possiblePaths = [
|
|
276
|
+
absImport,
|
|
277
|
+
`${absImport}.ts`,
|
|
278
|
+
`${absImport}.tsx`,
|
|
279
|
+
`${absImport}/index.ts`,
|
|
280
|
+
`${absImport}/index.tsx`
|
|
281
|
+
];
|
|
282
|
+
for (const tsFile of possiblePaths) {
|
|
283
|
+
if (fs2.existsSync(tsFile) && tsFile.endsWith(".ts") && !visited.has(tsFile)) {
|
|
284
|
+
toVisit.push(tsFile);
|
|
285
|
+
break;
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
} catch (error) {
|
|
290
|
+
logger.warn(
|
|
291
|
+
`Error processing ${current}: ${error instanceof Error ? error.message : String(error)}`
|
|
292
|
+
);
|
|
293
|
+
}
|
|
254
294
|
}
|
|
295
|
+
return visited;
|
|
296
|
+
}
|
|
297
|
+
function extractRelativeImports(sourceText) {
|
|
298
|
+
const imports = /* @__PURE__ */ new Set();
|
|
255
299
|
try {
|
|
256
|
-
const
|
|
257
|
-
|
|
258
|
-
|
|
300
|
+
const importExportRegex = /(?:import|export)(?:(?:[\s\n]*(?:type[\s\n]+)?(?:\*|\{[^}]*\}|[\w$]+)[\s\n]+from[\s\n]*)|[\s\n]+)(["'`])([^'"]+)\1/g;
|
|
301
|
+
let match;
|
|
302
|
+
while ((match = importExportRegex.exec(sourceText)) !== null) {
|
|
303
|
+
const importPath = match[2];
|
|
304
|
+
if (importPath.startsWith(".")) {
|
|
305
|
+
imports.add(importPath);
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
const sideEffectImportRegex = /import\s+(["'`])([^'"]+)\1\s*;?/g;
|
|
309
|
+
while ((match = sideEffectImportRegex.exec(sourceText)) !== null) {
|
|
310
|
+
const importPath = match[2];
|
|
311
|
+
if (importPath.startsWith(".")) {
|
|
312
|
+
imports.add(importPath);
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
const dynamicImportRegex = /import\s*\(\s*(["'`])([^'"]+)\1\s*\)/g;
|
|
316
|
+
while ((match = dynamicImportRegex.exec(sourceText)) !== null) {
|
|
317
|
+
const importPath = match[2];
|
|
318
|
+
if (importPath.startsWith(".")) {
|
|
319
|
+
imports.add(importPath);
|
|
320
|
+
}
|
|
321
|
+
}
|
|
259
322
|
} catch (error) {
|
|
260
323
|
logger.warn(
|
|
261
|
-
`
|
|
324
|
+
`Error extracting imports: ${error instanceof Error ? error.message : String(error)}`
|
|
262
325
|
);
|
|
263
|
-
return {};
|
|
264
326
|
}
|
|
327
|
+
return Array.from(imports);
|
|
265
328
|
}
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
329
|
+
async function generateDtsContent(tsFiles) {
|
|
330
|
+
const dtsMap = /* @__PURE__ */ new Map();
|
|
331
|
+
await Promise.all(
|
|
332
|
+
Array.from(tsFiles).map(async (tsFile) => {
|
|
333
|
+
try {
|
|
334
|
+
const dtsPath = tsFile.replace(/\.tsx?$/, ".d.ts");
|
|
335
|
+
const sourceText = await fs2.promises.readFile(tsFile, "utf8");
|
|
336
|
+
const { code: declaration } = oxc.isolatedDeclaration(
|
|
337
|
+
tsFile,
|
|
338
|
+
sourceText
|
|
339
|
+
);
|
|
340
|
+
if (declaration) {
|
|
341
|
+
dtsMap.set(dtsPath, declaration);
|
|
342
|
+
}
|
|
343
|
+
} catch (error) {
|
|
344
|
+
logger.warn(
|
|
345
|
+
`Failed to generate declaration for ${tsFile}: ${error instanceof Error ? error.message : String(error)}`
|
|
346
|
+
);
|
|
347
|
+
}
|
|
348
|
+
})
|
|
349
|
+
);
|
|
350
|
+
return dtsMap;
|
|
351
|
+
}
|
|
352
|
+
async function bundleDtsContent(entryFile, dtsMap, format, options, rootDir) {
|
|
353
|
+
const VIRTUAL_PREFIX = "\0virtual:";
|
|
354
|
+
const entryDtsPath = entryFile.replace(/\.tsx?$/, ".d.ts");
|
|
355
|
+
const virtualEntry = `${VIRTUAL_PREFIX}${entryDtsPath}`;
|
|
356
|
+
const virtualPlugin = {
|
|
357
|
+
name: "virtual-dts",
|
|
358
|
+
resolveId(source, importer) {
|
|
359
|
+
if (source.startsWith(VIRTUAL_PREFIX)) {
|
|
360
|
+
return source;
|
|
361
|
+
}
|
|
362
|
+
if (importer?.startsWith(VIRTUAL_PREFIX)) {
|
|
363
|
+
const importerPath = importer.slice(VIRTUAL_PREFIX.length);
|
|
364
|
+
const importerDir = path2.dirname(importerPath);
|
|
365
|
+
if (source.startsWith(".")) {
|
|
366
|
+
const resolvedPath = path2.resolve(importerDir, source);
|
|
367
|
+
for (const ext of ["", ".d.ts", "/index.d.ts"]) {
|
|
368
|
+
const fullPath = `${resolvedPath}${ext}`;
|
|
369
|
+
if (dtsMap.has(fullPath)) {
|
|
370
|
+
return `${VIRTUAL_PREFIX}${fullPath}`;
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
return null;
|
|
376
|
+
},
|
|
377
|
+
load(id) {
|
|
378
|
+
if (id.startsWith(VIRTUAL_PREFIX)) {
|
|
379
|
+
const actualPath = id.slice(VIRTUAL_PREFIX.length);
|
|
380
|
+
return dtsMap.get(actualPath) || null;
|
|
381
|
+
}
|
|
382
|
+
return null;
|
|
383
|
+
}
|
|
384
|
+
};
|
|
385
|
+
const packageJson = loadPackageJson(rootDir);
|
|
274
386
|
const externalPatterns = getExternalPatterns(options, packageJson);
|
|
275
387
|
const noExternalPatterns = getNoExternalPatterns(options);
|
|
276
388
|
let bundle;
|
|
277
|
-
let result;
|
|
278
389
|
try {
|
|
279
390
|
bundle = await rollup({
|
|
280
|
-
input:
|
|
391
|
+
input: virtualEntry,
|
|
281
392
|
onwarn(warning, handler) {
|
|
282
393
|
if (warning.code === "UNRESOLVED_IMPORT" || warning.code === "CIRCULAR_DEPENDENCY" || warning.code === "EMPTY_BUNDLE") {
|
|
283
394
|
return;
|
|
284
395
|
}
|
|
285
|
-
|
|
396
|
+
handler(warning);
|
|
286
397
|
},
|
|
287
|
-
plugins: [
|
|
288
|
-
dtsPlugin({
|
|
289
|
-
tsconfig: tsconfigPath,
|
|
290
|
-
compilerOptions: {
|
|
291
|
-
...compilerOptions ? ts.parseJsonConfigFileContent(
|
|
292
|
-
{ compilerOptions },
|
|
293
|
-
ts.sys,
|
|
294
|
-
"./"
|
|
295
|
-
).options : {},
|
|
296
|
-
declaration: true,
|
|
297
|
-
noEmit: false,
|
|
298
|
-
emitDeclarationOnly: true,
|
|
299
|
-
noEmitOnError: true,
|
|
300
|
-
checkJs: false,
|
|
301
|
-
declarationMap: false,
|
|
302
|
-
skipLibCheck: true,
|
|
303
|
-
preserveSymlinks: false,
|
|
304
|
-
target: ScriptTarget.ESNext
|
|
305
|
-
}
|
|
306
|
-
})
|
|
307
|
-
],
|
|
398
|
+
plugins: [virtualPlugin, dtsPlugin()],
|
|
308
399
|
external: (source) => externalPatterns.some((re) => re.test(source)) && !noExternalPatterns.some((re) => re.test(source))
|
|
309
400
|
});
|
|
310
401
|
const { output } = await bundle.generate({ format });
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
402
|
+
if (!output[0]?.code) {
|
|
403
|
+
throw new Error("Generated bundle is empty");
|
|
404
|
+
}
|
|
405
|
+
return output[0].code;
|
|
406
|
+
} catch (error) {
|
|
407
|
+
throw new Error(`DTS bundling failed: ${parseErrorMessage(error)}`);
|
|
316
408
|
} finally {
|
|
317
409
|
if (bundle) await bundle.close();
|
|
318
410
|
}
|
|
319
|
-
if (!result) {
|
|
320
|
-
throw new Error("Failed to generate bundled DTS content");
|
|
321
|
-
}
|
|
322
|
-
return result;
|
|
323
411
|
}
|
|
324
412
|
function validateInputs(rootDir, entry) {
|
|
325
413
|
const absoluteRootDir = path2.resolve(rootDir);
|
|
326
414
|
const absoluteEntry = path2.resolve(absoluteRootDir, entry);
|
|
327
|
-
if (!
|
|
415
|
+
if (!fs2.existsSync(absoluteRootDir)) {
|
|
328
416
|
throw new Error(`Root directory does not exist: ${absoluteRootDir}`);
|
|
329
417
|
}
|
|
330
|
-
if (!
|
|
418
|
+
if (!fs2.existsSync(absoluteEntry)) {
|
|
331
419
|
throw new Error(`Entry file does not exist: ${absoluteEntry}`);
|
|
332
420
|
}
|
|
333
421
|
if (!absoluteEntry.endsWith(".ts")) {
|
|
@@ -343,27 +431,11 @@ function validateInputs(rootDir, entry) {
|
|
|
343
431
|
|
|
344
432
|
// src/dts/worker.ts
|
|
345
433
|
self.onmessage = async (event) => {
|
|
346
|
-
const {
|
|
347
|
-
name,
|
|
348
|
-
rootDir,
|
|
349
|
-
outDir,
|
|
350
|
-
entry,
|
|
351
|
-
format,
|
|
352
|
-
packageType,
|
|
353
|
-
dtsOptions,
|
|
354
|
-
options
|
|
355
|
-
} = event.data;
|
|
434
|
+
const { name, rootDir, outDir, entry, format, packageType, options } = event.data;
|
|
356
435
|
try {
|
|
357
|
-
const content = await generateDts(
|
|
358
|
-
rootDir,
|
|
359
|
-
entry,
|
|
360
|
-
format,
|
|
361
|
-
options,
|
|
362
|
-
dtsOptions
|
|
363
|
-
);
|
|
364
|
-
const entryName = getEntryNameOnly(entry);
|
|
436
|
+
const content = await generateDts(rootDir, entry.path, format, options);
|
|
365
437
|
const extension = getDefaultDtsExtention(format, packageType);
|
|
366
|
-
const outputRelativePath = `${outDir}/${
|
|
438
|
+
const outputRelativePath = `${outDir}/${entry.name}${extension}`;
|
|
367
439
|
const outputPath = `${rootDir}/${outputRelativePath}`;
|
|
368
440
|
await Bun.write(outputPath, content);
|
|
369
441
|
const response = {
|
|
@@ -482,6 +554,38 @@ var DtsWorker = class {
|
|
|
482
554
|
}
|
|
483
555
|
};
|
|
484
556
|
|
|
557
|
+
// src/helpers/entry.ts
|
|
558
|
+
function getEntryNameOnly(entry) {
|
|
559
|
+
return entry.split("/").pop()?.split(".").slice(0, -1).join(".") || "";
|
|
560
|
+
}
|
|
561
|
+
function normalizeEntryToProcessableEntries(entries) {
|
|
562
|
+
const result = [];
|
|
563
|
+
const usedNames = /* @__PURE__ */ new Set();
|
|
564
|
+
function addEntry(name, path6) {
|
|
565
|
+
if (usedNames.has(name)) {
|
|
566
|
+
const randomSuffix = generateRandomSuffix();
|
|
567
|
+
result.push({ name: `${name}_${randomSuffix}`, path: path6 });
|
|
568
|
+
} else {
|
|
569
|
+
result.push({ name, path: path6 });
|
|
570
|
+
usedNames.add(name);
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
for (const entry of entries) {
|
|
574
|
+
if (typeof entry === "string") {
|
|
575
|
+
const name = getEntryNameOnly(entry);
|
|
576
|
+
addEntry(name, entry);
|
|
577
|
+
} else {
|
|
578
|
+
Object.entries(entry).forEach(([name, path6]) => {
|
|
579
|
+
addEntry(name, path6);
|
|
580
|
+
});
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
return result;
|
|
584
|
+
}
|
|
585
|
+
function getEntryNamingFormat(extension) {
|
|
586
|
+
return `[dir]/[name]${extension}`;
|
|
587
|
+
}
|
|
588
|
+
|
|
485
589
|
// src/plugins/external.ts
|
|
486
590
|
function externalPlugin(externalPatterns, noExternalPatterns) {
|
|
487
591
|
return {
|
|
@@ -514,8 +618,11 @@ async function build(options, rootDir) {
|
|
|
514
618
|
const externalPatterns = getExternalPatterns(options, packageJson);
|
|
515
619
|
const noExternalPatterns = getNoExternalPatterns(options);
|
|
516
620
|
const plugins = [externalPlugin(externalPatterns, noExternalPatterns)];
|
|
621
|
+
const processableEntries = normalizeEntryToProcessableEntries(
|
|
622
|
+
options.entry
|
|
623
|
+
);
|
|
517
624
|
const buildPromises = options.format.flatMap(
|
|
518
|
-
(fmt) =>
|
|
625
|
+
(fmt) => processableEntries.map(
|
|
519
626
|
(entry) => buildEntry(options, rootDir, entry, fmt, packageType, plugins)
|
|
520
627
|
)
|
|
521
628
|
);
|
|
@@ -531,8 +638,6 @@ async function build(options, rootDir) {
|
|
|
531
638
|
if (options.dts) {
|
|
532
639
|
const dtsStartTime = performance.now();
|
|
533
640
|
logger.progress("DTS", "Bundling types");
|
|
534
|
-
const dtsOptions = typeof options.dts === "object" ? options.dts : {};
|
|
535
|
-
const entries = dtsOptions.entry || options.entry;
|
|
536
641
|
const formatsToProcess = options.format.filter((fmt) => {
|
|
537
642
|
if (fmt === "iife" && !isModulePackage(packageType) && options.format.includes("cjs")) {
|
|
538
643
|
return false;
|
|
@@ -542,14 +647,13 @@ async function build(options, rootDir) {
|
|
|
542
647
|
const dtsWorker = new DtsWorker();
|
|
543
648
|
try {
|
|
544
649
|
const dtsPromises = formatsToProcess.flatMap(
|
|
545
|
-
(fmt) =>
|
|
650
|
+
(fmt) => processableEntries.map(
|
|
546
651
|
(entry) => generateDtsForEntry(
|
|
547
652
|
options,
|
|
548
653
|
rootDir,
|
|
549
654
|
entry,
|
|
550
655
|
fmt,
|
|
551
656
|
packageType,
|
|
552
|
-
dtsOptions,
|
|
553
657
|
dtsWorker
|
|
554
658
|
)
|
|
555
659
|
)
|
|
@@ -564,7 +668,7 @@ async function build(options, rootDir) {
|
|
|
564
668
|
await dtsWorker.cleanup();
|
|
565
669
|
}
|
|
566
670
|
}
|
|
567
|
-
async function generateDtsForEntry(options, rootDir, entry, fmt, packageType,
|
|
671
|
+
async function generateDtsForEntry(options, rootDir, entry, fmt, packageType, dtsWorker) {
|
|
568
672
|
const task = {
|
|
569
673
|
name: options.name,
|
|
570
674
|
rootDir,
|
|
@@ -572,7 +676,6 @@ async function generateDtsForEntry(options, rootDir, entry, fmt, packageType, dt
|
|
|
572
676
|
entry,
|
|
573
677
|
format: fmt,
|
|
574
678
|
packageType,
|
|
575
|
-
dtsOptions,
|
|
576
679
|
options
|
|
577
680
|
};
|
|
578
681
|
await dtsWorker.process(task);
|
|
@@ -585,13 +688,12 @@ async function buildEntry(options, rootDir, entry, fmt, packageType, plugins) {
|
|
|
585
688
|
);
|
|
586
689
|
const result = await Bun.build({
|
|
587
690
|
...defaultBunBuildOptions,
|
|
588
|
-
entrypoints: [`${rootDir}/${entry}`],
|
|
691
|
+
entrypoints: [`${rootDir}/${entry.path}`],
|
|
589
692
|
format: fmt,
|
|
590
693
|
naming: { entry: getEntryNamingFormat(extension) },
|
|
591
694
|
splitting: getResolvedSplitting(options.splitting, fmt),
|
|
592
695
|
plugins
|
|
593
696
|
});
|
|
594
|
-
const entryName = getEntryNameOnly(entry);
|
|
595
697
|
if (!result.success) {
|
|
596
698
|
result.logs.forEach((log) => {
|
|
597
699
|
if (log.level === "error") logger.error(log.message);
|
|
@@ -602,7 +704,7 @@ async function buildEntry(options, rootDir, entry, fmt, packageType, plugins) {
|
|
|
602
704
|
}
|
|
603
705
|
logger.progress(
|
|
604
706
|
getLoggerProgressLabel(fmt, options.name),
|
|
605
|
-
`${options.outDir}/${
|
|
707
|
+
`${options.outDir}/${entry.name}${extension}`
|
|
606
708
|
);
|
|
607
709
|
}
|
|
608
710
|
|
|
@@ -707,8 +809,9 @@ function parseCliOptions(argv) {
|
|
|
707
809
|
})();
|
|
708
810
|
async function watch(options, rootDir) {
|
|
709
811
|
const watchPaths = /* @__PURE__ */ new Set();
|
|
710
|
-
options.entry
|
|
711
|
-
|
|
812
|
+
const normalizedEntry = normalizeEntryToProcessableEntries(options.entry);
|
|
813
|
+
normalizedEntry.forEach((entry) => {
|
|
814
|
+
const entryPath = path2.resolve(rootDir, entry.path);
|
|
712
815
|
const parentDir = path2.dirname(entryPath);
|
|
713
816
|
watchPaths.add(parentDir);
|
|
714
817
|
});
|
|
@@ -800,14 +903,14 @@ async function handleBuild(options, rootDir) {
|
|
|
800
903
|
}
|
|
801
904
|
function cleanOutDir(rootDir, outdir) {
|
|
802
905
|
const outdirPath = path2.join(rootDir, outdir);
|
|
803
|
-
if (
|
|
906
|
+
if (fs2.existsSync(outdirPath)) {
|
|
804
907
|
try {
|
|
805
|
-
|
|
908
|
+
fs2.rmSync(outdirPath, { recursive: true, force: true });
|
|
806
909
|
} catch (error) {
|
|
807
910
|
logger.error(`Failed to clean output directory: ${error}`);
|
|
808
911
|
}
|
|
809
912
|
}
|
|
810
|
-
|
|
913
|
+
fs2.mkdirSync(outdirPath, { recursive: true });
|
|
811
914
|
}
|
|
812
915
|
main().catch((error) => {
|
|
813
916
|
handleError(error);
|
package/build/dtsWorker.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
'use strict';var h=require('path'),
|
|
1
|
+
'use strict';var h=require('path'),y=require('fs'),I=require('oxc-transform'),rollup=require('rollup'),N=require('rollup-plugin-dts');function _interopDefault(e){return e&&e.__esModule?e:{default:e}}var h__default=/*#__PURE__*/_interopDefault(h);var y__default=/*#__PURE__*/_interopDefault(y);var I__default=/*#__PURE__*/_interopDefault(I);var N__default=/*#__PURE__*/_interopDefault(N);var f=r=>r instanceof Error?r.message:String(r);function w(r){return r.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}function b(r,t){switch(r){case "esm":return ".d.mts";case "cjs":return j(t)?".d.cts":".d.ts";case "iife":return ".d.ts"}}function j(r){return r==="module"}function $(r){return r?Array.from(new Set([...Object.keys(r.dependencies||{}),...Object.keys(r.peerDependencies||{})])):[]}function D(r){return r.map(t=>typeof t=="string"?new RegExp(`^${w(t)}($|\\/|\\\\)`):t)}function v(r,t){return D(r.external||[]).concat($(t).map(e=>new RegExp(`^${w(e)}($|\\/|\\\\)`)))}function P(r){return D(r.noExternal||[])}var p={MAX_LABEL_LENGTH:5,colors:{cli:"183",info:"240",warn:"221",error:"203",progress:{ESM:"214",CJS:"114",IIFE:"105",DTS:"75"},default:"255"},labels:{cli:"BUNUP",info:"INFO",warn:"WARN",error:"ERROR"},formatMessage(r,t,e){let s=" ".repeat(Math.max(0,this.MAX_LABEL_LENGTH-t.length));return `\x1B[38;5;${r}m[${t}]\x1B[0m ${s}${e}`},cli(r){let t=this.labels.cli;console.log(this.formatMessage(this.colors.cli,t,r));},info(r){let t=this.labels.info;console.log(this.formatMessage(this.colors.info,t,r));},warn(r){let t=this.labels.warn;console.warn(this.formatMessage(this.colors.warn,t,r));},error(r){let t=this.labels.error;console.error(this.formatMessage(this.colors.error,t,r));},progress(r,t){let e=String(r),s=this.colors.default;for(let[n,o]of Object.entries(this.colors.progress))if(e.includes(n)){s=o;break}console.log(this.formatMessage(s,e,t));}};function S(r,t){return `${t?`${t.replace(/-/g,"_")}_`:""}${r}`.toUpperCase()}function R(r){let t=h__default.default.join(r,"package.json");try{if(!y__default.default.existsSync(t))return null;let e=y__default.default.readFileSync(t,"utf8");return JSON.parse(e)}catch(e){return p.error(`Failed to load package.json at ${t}: ${f(e)}`),null}}async function M(r,t,e,s){let{absoluteRootDir:n,absoluteEntry:o}=q(r,t),i=await L(o),a=await U(i);return J(o,a,e,s,n)}async function L(r){let t=new Set,e=[r];for(;e.length>0;){let s=e.pop();if(!(!s||t.has(s))){t.add(s);try{let n=await y__default.default.promises.readFile(s,"utf8"),o=_(n);for(let i of o){let a=h__default.default.dirname(s),c=h__default.default.resolve(a,i),m=[c,`${c}.ts`,`${c}.tsx`,`${c}/index.ts`,`${c}/index.tsx`];for(let g of m)if(y__default.default.existsSync(g)&&g.endsWith(".ts")&&!t.has(g)){e.push(g);break}}}catch(n){p.warn(`Error processing ${s}: ${n instanceof Error?n.message:String(n)}`);}}}return t}function _(r){let t=new Set;try{let e=/(?:import|export)(?:(?:[\s\n]*(?:type[\s\n]+)?(?:\*|\{[^}]*\}|[\w$]+)[\s\n]+from[\s\n]*)|[\s\n]+)(["'`])([^'"]+)\1/g,s;for(;(s=e.exec(r))!==null;){let i=s[2];i.startsWith(".")&&t.add(i);}let n=/import\s+(["'`])([^'"]+)\1\s*;?/g;for(;(s=n.exec(r))!==null;){let i=s[2];i.startsWith(".")&&t.add(i);}let o=/import\s*\(\s*(["'`])([^'"]+)\1\s*\)/g;for(;(s=o.exec(r))!==null;){let i=s[2];i.startsWith(".")&&t.add(i);}}catch(e){p.warn(`Error extracting imports: ${e instanceof Error?e.message:String(e)}`);}return Array.from(t)}async function U(r){let t=new Map;return await Promise.all(Array.from(r).map(async e=>{try{let s=e.replace(/\.tsx?$/,".d.ts"),n=await y__default.default.promises.readFile(e,"utf8"),{code:o}=I__default.default.isolatedDeclaration(e,n);o&&t.set(s,o);}catch(s){p.warn(`Failed to generate declaration for ${e}: ${s instanceof Error?s.message:String(s)}`);}})),t}async function J(r,t,e,s,n){let o="\0virtual:",i=r.replace(/\.tsx?$/,".d.ts"),a=`${o}${i}`,c={name:"virtual-dts",resolveId(l,u){if(l.startsWith(o))return l;if(u?.startsWith(o)){let d=u.slice(o.length),B=h__default.default.dirname(d);if(l.startsWith(".")){let T=h__default.default.resolve(B,l);for(let F of ["",".d.ts","/index.d.ts"]){let k=`${T}${F}`;if(t.has(k))return `${o}${k}`}}}return null},load(l){if(l.startsWith(o)){let u=l.slice(o.length);return t.get(u)||null}return null}},m=R(n),g=v(s,m),E=P(s),x;try{x=await rollup.rollup({input:a,onwarn(u,d){u.code==="UNRESOLVED_IMPORT"||u.code==="CIRCULAR_DEPENDENCY"||u.code==="EMPTY_BUNDLE"||d(u);},plugins:[c,N__default.default()],external:u=>g.some(d=>d.test(u))&&!E.some(d=>d.test(u))});let{output:l}=await x.generate({format:e});if(!l[0]?.code)throw new Error("Generated bundle is empty");return l[0].code}catch(l){throw new Error(`DTS bundling failed: ${f(l)}`)}finally{x&&await x.close();}}function q(r,t){let e=h__default.default.resolve(r),s=h__default.default.resolve(e,t);if(!y__default.default.existsSync(e))throw new Error(`Root directory does not exist: ${e}`);if(!y__default.default.existsSync(s))throw new Error(`Entry file does not exist: ${s}`);if(!s.endsWith(".ts"))throw new Error(`Entry file must be a TypeScript file (.ts): ${s}`);if(h__default.default.relative(e,s).startsWith(".."))throw new Error(`Entry file must be within rootDir: ${s}`);return {absoluteRootDir:e,absoluteEntry:s}}self.onmessage=async r=>{let{name:t,rootDir:e,outDir:s,entry:n,format:o,packageType:i,options:a}=r.data;try{let c=await M(e,n.path,o,a),m=b(o,i),g=`${s}/${n.name}${m}`,E=`${e}/${g}`;await Bun.write(E,c);let x={name:t,success:!0,outputRelativePath:g};self.postMessage(x);}catch(c){let m={success:false,error:f(c)};self.postMessage(m);}};var O=class{constructor(t=navigator.hardwareConcurrency||4){this.workers=[];this.queue=[];this.busyWorkers=new Set;this.isShuttingDown=false;this.maxWorkers=t;}async process(t){if(this.isShuttingDown)throw new Error("Worker pool is shutting down");return new Promise((e,s)=>{this.queue.push({task:t,resolve:e,reject:s}),this.processQueue();})}processQueue(){if(!(this.queue.length===0||this.isShuttingDown))if(this.workers.length<this.maxWorkers){let t=new Worker(h__default.default.join(__dirname,"./dtsWorker.js"));this.workers.push(t),this.assignTaskToWorker(t);}else {let t=this.workers.find(e=>!this.busyWorkers.has(e));t&&this.assignTaskToWorker(t);}}assignTaskToWorker(t){let e=this.queue.shift();if(!e)return;let{task:s,resolve:n,reject:o}=e;this.busyWorkers.add(t);let i=()=>{this.busyWorkers.delete(t),this.isShuttingDown&&this.busyWorkers.size===0?this.terminateAllWorkers():this.processQueue();};t.onmessage=a=>{a.data.success?(p.progress(S("DTS",a.data.name),a.data.outputRelativePath),n()):(p.error(`DTS generation failed: ${a.data.error}`),o(new Error(a.data.error))),i();},t.onerror=a=>{let c=f(a);p.error(`Worker error: ${c}`),o(a),i();},t.postMessage(s);}terminateAllWorkers(){this.workers.forEach(t=>{try{t.terminate();}catch(e){p.error(`Error terminating worker: ${f(e)}`);}}),this.workers=[],this.busyWorkers.clear();}async cleanup(){if(this.isShuttingDown=true,this.busyWorkers.size===0){this.terminateAllWorkers();return}return new Promise(t=>{let e=setInterval(()=>{this.busyWorkers.size===0&&(clearInterval(e),this.terminateAllWorkers(),t());},100);setTimeout(()=>{clearInterval(e),this.terminateAllWorkers(),t();},5e3);})}};exports.DtsWorker=O;
|
package/build/index.d.mts
CHANGED
|
@@ -3,18 +3,7 @@ type WithOptional<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>>;
|
|
|
3
3
|
type Format = 'esm' | 'cjs' | 'iife';
|
|
4
4
|
type Target = 'bun' | 'node' | 'browser';
|
|
5
5
|
type External = string[];
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* Entry files to generate declaration files for
|
|
9
|
-
* If not specified, the main entry points will be used
|
|
10
|
-
*/
|
|
11
|
-
entry?: string[];
|
|
12
|
-
/**
|
|
13
|
-
* Path to a specific tsconfig.json file to use for declaration generation
|
|
14
|
-
* If not specified, the default tsconfig.json will be used
|
|
15
|
-
*/
|
|
16
|
-
preferredTsconfigPath?: string;
|
|
17
|
-
}
|
|
6
|
+
type Entry = string | Record<string, string>;
|
|
18
7
|
interface BunupOptions {
|
|
19
8
|
/**
|
|
20
9
|
* Name of the build configuration
|
|
@@ -25,7 +14,7 @@ interface BunupOptions {
|
|
|
25
14
|
* Entry point files for the build
|
|
26
15
|
* These are the files that will be processed and bundled
|
|
27
16
|
*/
|
|
28
|
-
entry:
|
|
17
|
+
entry: Entry[];
|
|
29
18
|
/**
|
|
30
19
|
* Output directory for the bundled files
|
|
31
20
|
* Defaults to 'dist' if not specified
|
|
@@ -68,9 +57,8 @@ interface BunupOptions {
|
|
|
68
57
|
watch?: boolean;
|
|
69
58
|
/**
|
|
70
59
|
* Whether to generate TypeScript declaration files (.d.ts)
|
|
71
|
-
* Can be a boolean or a DtsOptions object for more control
|
|
72
60
|
*/
|
|
73
|
-
dts?: boolean
|
|
61
|
+
dts?: boolean;
|
|
74
62
|
/**
|
|
75
63
|
* External packages that should not be bundled
|
|
76
64
|
* Useful for dependencies that should be kept as external imports
|
package/build/index.d.ts
CHANGED
|
@@ -3,18 +3,7 @@ type WithOptional<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>>;
|
|
|
3
3
|
type Format = 'esm' | 'cjs' | 'iife';
|
|
4
4
|
type Target = 'bun' | 'node' | 'browser';
|
|
5
5
|
type External = string[];
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* Entry files to generate declaration files for
|
|
9
|
-
* If not specified, the main entry points will be used
|
|
10
|
-
*/
|
|
11
|
-
entry?: string[];
|
|
12
|
-
/**
|
|
13
|
-
* Path to a specific tsconfig.json file to use for declaration generation
|
|
14
|
-
* If not specified, the default tsconfig.json will be used
|
|
15
|
-
*/
|
|
16
|
-
preferredTsconfigPath?: string;
|
|
17
|
-
}
|
|
6
|
+
type Entry = string | Record<string, string>;
|
|
18
7
|
interface BunupOptions {
|
|
19
8
|
/**
|
|
20
9
|
* Name of the build configuration
|
|
@@ -25,7 +14,7 @@ interface BunupOptions {
|
|
|
25
14
|
* Entry point files for the build
|
|
26
15
|
* These are the files that will be processed and bundled
|
|
27
16
|
*/
|
|
28
|
-
entry:
|
|
17
|
+
entry: Entry[];
|
|
29
18
|
/**
|
|
30
19
|
* Output directory for the bundled files
|
|
31
20
|
* Defaults to 'dist' if not specified
|
|
@@ -68,9 +57,8 @@ interface BunupOptions {
|
|
|
68
57
|
watch?: boolean;
|
|
69
58
|
/**
|
|
70
59
|
* Whether to generate TypeScript declaration files (.d.ts)
|
|
71
|
-
* Can be a boolean or a DtsOptions object for more control
|
|
72
60
|
*/
|
|
73
|
-
dts?: boolean
|
|
61
|
+
dts?: boolean;
|
|
74
62
|
/**
|
|
75
63
|
* External packages that should not be bundled
|
|
76
64
|
* Useful for dependencies that should be kept as external imports
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "bunup",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.5",
|
|
4
4
|
"description": "A extremely fast, zero-config bundler for TypeScript & JavaScript, powered by Bun.",
|
|
5
5
|
"main": "./build/index.js",
|
|
6
6
|
"types": "./build/index.d.ts",
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
"tsup": "^8.0.2",
|
|
25
25
|
"typescript": "^5.4.3",
|
|
26
26
|
"vitest": "^2.0.5",
|
|
27
|
-
"bunup": "0.1.
|
|
27
|
+
"bunup": "0.1.5"
|
|
28
28
|
},
|
|
29
29
|
"peerDependencies": {
|
|
30
30
|
"typescript": ">=4.5.0"
|
|
@@ -54,6 +54,7 @@
|
|
|
54
54
|
"author": "Arshad Yaseen <m@arshadyaseen.com> (https://arshadyaseen.com)",
|
|
55
55
|
"dependencies": {
|
|
56
56
|
"chokidar": "^4.0.3",
|
|
57
|
+
"oxc-transform": "^0.58.1",
|
|
57
58
|
"rollup": "^4.35.0",
|
|
58
59
|
"rollup-plugin-dts": "^6.1.1"
|
|
59
60
|
},
|