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 CHANGED
@@ -1,7 +1,18 @@
1
- # Bunup [WIP]
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
- ![demo](/demo.gif)
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 fs = require('fs');
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 fs__default = /*#__PURE__*/_interopDefault(fs);
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 (!fs__default.default.existsSync(filePath)) continue;
206
+ if (!fs2__default.default.existsSync(filePath)) continue;
210
207
  let content;
211
208
  if (ext === ".json" || ext === ".jsonc") {
212
- const text = fs__default.default.readFileSync(filePath, "utf8");
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 (!fs__default.default.existsSync(packageJsonPath)) {
245
+ if (!fs2__default.default.existsSync(packageJsonPath)) {
249
246
  return null;
250
247
  }
251
- const text = fs__default.default.readFileSync(packageJsonPath, "utf8");
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
- function loadTsconfig(tsconfigPath) {
262
- if (!fs__default.default.existsSync(tsconfigPath)) {
263
- return {};
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 content = fs__default.default.readFileSync(tsconfigPath, "utf8");
267
- const json = JSON.parse(content);
268
- return json || {};
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
- `Failed to parse tsconfig at ${tsconfigPath}: ${parseErrorMessage(error)}`
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
- // 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);
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: absoluteEntry,
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
- return handler(warning);
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
- result = output[0].code;
322
- } catch (rollupError) {
323
- throw new Error(
324
- `Rollup bundling failed: ${parseErrorMessage(rollupError)}`
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 (!fs__default.default.existsSync(absoluteRootDir)) {
425
+ if (!fs2__default.default.existsSync(absoluteRootDir)) {
338
426
  throw new Error(`Root directory does not exist: ${absoluteRootDir}`);
339
427
  }
340
- if (!fs__default.default.existsSync(absoluteEntry)) {
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}/${entryName}${extension}`;
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) => options.entry.map(
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) => entries.map(
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, dtsOptions, dtsWorker) {
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}/${entryName}${extension}`
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.forEach((entry) => {
721
- const entryPath = path2__default.default.resolve(rootDir, entry);
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 (fs__default.default.existsSync(outdirPath)) {
916
+ if (fs2__default.default.existsSync(outdirPath)) {
814
917
  try {
815
- fs__default.default.rmSync(outdirPath, { recursive: true, force: true });
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
- fs__default.default.mkdirSync(outdirPath, { recursive: true });
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 fs from 'fs';
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 (!fs.existsSync(filePath)) continue;
196
+ if (!fs2.existsSync(filePath)) continue;
200
197
  let content;
201
198
  if (ext === ".json" || ext === ".jsonc") {
202
- const text = fs.readFileSync(filePath, "utf8");
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 (!fs.existsSync(packageJsonPath)) {
235
+ if (!fs2.existsSync(packageJsonPath)) {
239
236
  return null;
240
237
  }
241
- const text = fs.readFileSync(packageJsonPath, "utf8");
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
- function loadTsconfig(tsconfigPath) {
252
- if (!fs.existsSync(tsconfigPath)) {
253
- return {};
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 content = fs.readFileSync(tsconfigPath, "utf8");
257
- const json = JSON.parse(content);
258
- return json || {};
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
- `Failed to parse tsconfig at ${tsconfigPath}: ${parseErrorMessage(error)}`
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
- // src/dts/index.ts
268
- async function generateDts(rootDir, entry, format, options, dtsOptions = {}) {
269
- const { absoluteRootDir, absoluteEntry } = validateInputs(rootDir, entry);
270
- const tsconfigPath = dtsOptions.preferredTsconfigPath ? path2.resolve(dtsOptions.preferredTsconfigPath) : path2.join(absoluteRootDir, "tsconfig.json");
271
- const tsconfig = await loadTsconfig(tsconfigPath);
272
- const compilerOptions = tsconfig.compilerOptions;
273
- const packageJson = loadPackageJson(absoluteRootDir);
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: absoluteEntry,
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
- return handler(warning);
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
- result = output[0].code;
312
- } catch (rollupError) {
313
- throw new Error(
314
- `Rollup bundling failed: ${parseErrorMessage(rollupError)}`
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 (!fs.existsSync(absoluteRootDir)) {
415
+ if (!fs2.existsSync(absoluteRootDir)) {
328
416
  throw new Error(`Root directory does not exist: ${absoluteRootDir}`);
329
417
  }
330
- if (!fs.existsSync(absoluteEntry)) {
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}/${entryName}${extension}`;
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) => options.entry.map(
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) => entries.map(
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, dtsOptions, dtsWorker) {
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}/${entryName}${extension}`
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.forEach((entry) => {
711
- const entryPath = path2.resolve(rootDir, entry);
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 (fs.existsSync(outdirPath)) {
906
+ if (fs2.existsSync(outdirPath)) {
804
907
  try {
805
- fs.rmSync(outdirPath, { recursive: true, force: true });
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
- fs.mkdirSync(outdirPath, { recursive: true });
913
+ fs2.mkdirSync(outdirPath, { recursive: true });
811
914
  }
812
915
  main().catch((error) => {
813
916
  handleError(error);
@@ -1 +1 @@
1
- 'use strict';var h=require('path'),E=require('fs'),rollup=require('rollup'),L=require('rollup-plugin-dts'),j=require('typescript');function _interopDefault(e){return e&&e.__esModule?e:{default:e}}var h__default=/*#__PURE__*/_interopDefault(h);var E__default=/*#__PURE__*/_interopDefault(E);var L__default=/*#__PURE__*/_interopDefault(L);var j__default=/*#__PURE__*/_interopDefault(j);var a=e=>e instanceof Error?e.message:String(e);function D(e){return e.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}function b(e,t){switch(e){case "esm":return ".d.mts";case "cjs":return F(t)?".d.cts":".d.ts";case "iife":return ".d.ts"}}function F(e){return e==="module"}function O(e){return e.split("/").pop()?.split(".").slice(0,-1).join(".")||""}function $(e){return e?Array.from(new Set([...Object.keys(e.dependencies||{}),...Object.keys(e.peerDependencies||{})])):[]}function v(e){return e.map(t=>typeof t=="string"?new RegExp(`^${D(t)}($|\\/|\\\\)`):t)}function S(e,t){return v(e.external||[]).concat($(t).map(r=>new RegExp(`^${D(r)}($|\\/|\\\\)`)))}function W(e){return v(e.noExternal||[])}var l={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(e,t,r){let o=" ".repeat(Math.max(0,this.MAX_LABEL_LENGTH-t.length));return `\x1B[38;5;${e}m[${t}]\x1B[0m ${o}${r}`},cli(e){let t=this.labels.cli;console.log(this.formatMessage(this.colors.cli,t,e));},info(e){let t=this.labels.info;console.log(this.formatMessage(this.colors.info,t,e));},warn(e){let t=this.labels.warn;console.warn(this.formatMessage(this.colors.warn,t,e));},error(e){let t=this.labels.error;console.error(this.formatMessage(this.colors.error,t,e));},progress(e,t){let r=String(e),o=this.colors.default;for(let[i,n]of Object.entries(this.colors.progress))if(r.includes(i)){o=n;break}console.log(this.formatMessage(o,r,t));}};function R(e,t){return `${t?`${t.replace(/-/g,"_")}_`:""}${e}`.toUpperCase()}function P(e){let t=h__default.default.join(e,"package.json");try{if(!E__default.default.existsSync(t))return null;let r=E__default.default.readFileSync(t,"utf8");return JSON.parse(r)}catch(r){return l.error(`Failed to load package.json at ${t}: ${a(r)}`),null}}function T(e){if(!E__default.default.existsSync(e))return {};try{let t=E__default.default.readFileSync(e,"utf8");return JSON.parse(t)||{}}catch(t){return l.warn(`Failed to parse tsconfig at ${e}: ${a(t)}`),{}}}async function B(e,t,r,o,i={}){let{absoluteRootDir:n,absoluteEntry:u}=_(e,t),s=i.preferredTsconfigPath?h__default.default.resolve(i.preferredTsconfigPath):h__default.default.join(n,"tsconfig.json"),p=(await T(s)).compilerOptions,f=P(n),k=S(o,f),x=W(o),g,m;try{g=await rollup.rollup({input:u,onwarn(c,d){if(!(c.code==="UNRESOLVED_IMPORT"||c.code==="CIRCULAR_DEPENDENCY"||c.code==="EMPTY_BUNDLE"))return d(c)},plugins:[L__default.default({tsconfig:s,compilerOptions:{...p?j__default.default.parseJsonConfigFileContent({compilerOptions:p},j__default.default.sys,"./").options:{},declaration:!0,noEmit:!1,emitDeclarationOnly:!0,noEmitOnError:!0,checkJs:!1,declarationMap:!1,skipLibCheck:!0,preserveSymlinks:!1,target:j.ScriptTarget.ESNext}})],external:c=>k.some(d=>d.test(c))&&!x.some(d=>d.test(c))});let{output:w}=await g.generate({format:r});m=w[0].code;}catch(w){throw new Error(`Rollup bundling failed: ${a(w)}`)}finally{g&&await g.close();}if(!m)throw new Error("Failed to generate bundled DTS content");return m}function _(e,t){let r=h__default.default.resolve(e),o=h__default.default.resolve(r,t);if(!E__default.default.existsSync(r))throw new Error(`Root directory does not exist: ${r}`);if(!E__default.default.existsSync(o))throw new Error(`Entry file does not exist: ${o}`);if(!o.endsWith(".ts"))throw new Error(`Entry file must be a TypeScript file (.ts): ${o}`);if(h__default.default.relative(r,o).startsWith(".."))throw new Error(`Entry file must be within rootDir: ${o}`);return {absoluteRootDir:r,absoluteEntry:o}}self.onmessage=async e=>{let{name:t,rootDir:r,outDir:o,entry:i,format:n,packageType:u,dtsOptions:s,options:y}=e.data;try{let p=await B(r,i,n,y,s),f=O(i),k=b(n,u),x=`${o}/${f}${k}`,g=`${r}/${x}`;await Bun.write(g,p);let m={name:t,success:!0,outputRelativePath:x};self.postMessage(m);}catch(p){let f={success:false,error:a(p)};self.postMessage(f);}};var C=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((r,o)=>{this.queue.push({task:t,resolve:r,reject:o}),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(r=>!this.busyWorkers.has(r));t&&this.assignTaskToWorker(t);}}assignTaskToWorker(t){let r=this.queue.shift();if(!r)return;let{task:o,resolve:i,reject:n}=r;this.busyWorkers.add(t);let u=()=>{this.busyWorkers.delete(t),this.isShuttingDown&&this.busyWorkers.size===0?this.terminateAllWorkers():this.processQueue();};t.onmessage=s=>{s.data.success?(l.progress(R("DTS",s.data.name),s.data.outputRelativePath),i()):(l.error(`DTS generation failed: ${s.data.error}`),n(new Error(s.data.error))),u();},t.onerror=s=>{let y=a(s);l.error(`Worker error: ${y}`),n(s),u();},t.postMessage(o);}terminateAllWorkers(){this.workers.forEach(t=>{try{t.terminate();}catch(r){l.error(`Error terminating worker: ${a(r)}`);}}),this.workers=[],this.busyWorkers.clear();}async cleanup(){if(this.isShuttingDown=true,this.busyWorkers.size===0){this.terminateAllWorkers();return}return new Promise(t=>{let r=setInterval(()=>{this.busyWorkers.size===0&&(clearInterval(r),this.terminateAllWorkers(),t());},100);setTimeout(()=>{clearInterval(r),this.terminateAllWorkers(),t();},5e3);})}};exports.DtsWorker=C;
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
- interface DtsOptions {
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: string[];
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 | DtsOptions;
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
- interface DtsOptions {
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: string[];
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 | DtsOptions;
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",
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.3"
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
  },