robuild 0.0.18 → 0.0.20

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.
@@ -1,26 +1,115 @@
1
- import { RobuildPluginManager } from "./plugin-manager-w5yRGJRn.mjs";
1
+ import { t as RobuildPluginManager } from "./plugin-manager-CwMXjVtp.mjs";
2
2
  import { builtinModules } from "node:module";
3
3
  import { basename, dirname, extname, isAbsolute, join, relative, resolve } from "node:path";
4
- import { fileURLToPath, pathToFileURL } from "node:url";
5
4
  import { colors } from "consola/utils";
6
5
  import prettyBytes from "pretty-bytes";
7
- import { cp, mkdir, readFile, readdir, symlink, writeFile } from "node:fs/promises";
8
- import { consola } from "consola";
6
+ import { cp, mkdir, readFile, readdir, rm, symlink, writeFile } from "node:fs/promises";
9
7
  import { resolveModulePath } from "exsolve";
10
8
  import { parseSync } from "oxc-parser";
11
9
  import { rolldown, watch } from "rolldown";
12
10
  import { dts } from "rolldown-plugin-dts";
13
11
  import { existsSync, promises, readdirSync, statSync } from "node:fs";
14
- import { glob } from "glob";
15
- import { createHash } from "node:crypto";
12
+ import { consola } from "consola";
13
+ import { fileURLToPath, pathToFileURL } from "node:url";
16
14
  import { gzipSync } from "node:zlib";
17
15
  import { minify } from "oxc-minify";
16
+ import { glob } from "glob";
17
+ import { createHash } from "node:crypto";
18
18
  import MagicString from "magic-string";
19
19
  import { transform } from "oxc-transform";
20
20
  import { glob as glob$1 } from "tinyglobby";
21
21
  import { exec } from "node:child_process";
22
22
  import { promisify } from "node:util";
23
23
 
24
+ //#region src/features/logger.ts
25
+ /**
26
+ * Logger instance with configurable log level
27
+ */
28
+ var Logger = class {
29
+ level = "info";
30
+ warningCount = 0;
31
+ errorCount = 0;
32
+ constructor(level = "info") {
33
+ this.level = level;
34
+ this.updateConsolaLevel();
35
+ }
36
+ setLevel(level) {
37
+ this.level = level;
38
+ this.updateConsolaLevel();
39
+ }
40
+ updateConsolaLevel() {
41
+ consola.level = {
42
+ silent: 0,
43
+ error: 1,
44
+ warn: 2,
45
+ info: 3,
46
+ verbose: 4
47
+ }[this.level];
48
+ }
49
+ silent(message, ...args) {
50
+ consola.log(message, ...args);
51
+ }
52
+ error(message, ...args) {
53
+ this.errorCount++;
54
+ consola.error(message, ...args);
55
+ }
56
+ warn(message, ...args) {
57
+ this.warningCount++;
58
+ consola.warn(message, ...args);
59
+ }
60
+ info(message, ...args) {
61
+ consola.info(message, ...args);
62
+ }
63
+ verbose(message, ...args) {
64
+ if (this.level === "verbose") consola.debug(message, ...args);
65
+ }
66
+ success(message, ...args) {
67
+ consola.success(message, ...args);
68
+ }
69
+ log(message, ...args) {
70
+ consola.log(message, ...args);
71
+ }
72
+ /**
73
+ * Debug output - only visible with INSPECT_BUILD env var
74
+ */
75
+ debug(message, ...args) {
76
+ if (process.env.INSPECT_BUILD) consola.log(message, ...args);
77
+ }
78
+ getWarningCount() {
79
+ return this.warningCount;
80
+ }
81
+ getErrorCount() {
82
+ return this.errorCount;
83
+ }
84
+ resetCounts() {
85
+ this.warningCount = 0;
86
+ this.errorCount = 0;
87
+ }
88
+ shouldFailOnWarnings(failOnWarn) {
89
+ return failOnWarn && this.warningCount > 0;
90
+ }
91
+ };
92
+ const logger = new Logger();
93
+ /**
94
+ * Configure global logger
95
+ */
96
+ function configureLogger(level) {
97
+ logger.setLevel(level);
98
+ }
99
+ /**
100
+ * Reset warning and error counts
101
+ */
102
+ function resetLogCounts() {
103
+ logger.resetCounts();
104
+ }
105
+ /**
106
+ * Check if build should fail due to warnings
107
+ */
108
+ function shouldFailOnWarnings(failOnWarn) {
109
+ return logger.shouldFailOnWarnings(failOnWarn);
110
+ }
111
+
112
+ //#endregion
24
113
  //#region src/features/advanced-build.ts
25
114
  /**
26
115
  * Create skip node_modules plugin
@@ -52,9 +141,7 @@ function createSkipNodeModulesPlugin(options) {
52
141
  * Unbundle mode: preserve file structure without bundling
53
142
  */
54
143
  async function unbundleTransform(ctx, entry) {
55
- const inputDir = isAbsolute(entry.input) ? entry.input : join(ctx.pkgDir, entry.input);
56
- const outputDir = join(ctx.pkgDir, entry.outDir || "dist");
57
- await processDirectoryUnbundled(inputDir, outputDir, entry);
144
+ await processDirectoryUnbundled(isAbsolute(entry.input) ? entry.input : join(ctx.pkgDir, entry.input), join(ctx.pkgDir, entry.outDir || "dist"), entry);
58
145
  }
59
146
  /**
60
147
  * Process directory in unbundle mode
@@ -87,14 +174,13 @@ async function processFileUnbundled(inputPath, outputPath, entry) {
87
174
  ".cts"
88
175
  ].includes(ext)) return;
89
176
  try {
90
- const content = await readFile(inputPath, "utf-8");
91
- const transformedContent = transformImportsForUnbundle(content, inputPath, entry);
177
+ const transformedContent = transformImportsForUnbundle(await readFile(inputPath, "utf-8"), inputPath, entry);
92
178
  const outputExt = getUnbundleOutputExtension(ext, entry);
93
179
  const finalOutputPath = outputPath.replace(ext, outputExt);
94
180
  await mkdir(dirname(finalOutputPath), { recursive: true });
95
181
  await writeFile(finalOutputPath, transformedContent, "utf-8");
96
182
  } catch (error) {
97
- console.warn(`Failed to process file ${inputPath}:`, error);
183
+ logger.warn(`Failed to process file ${inputPath}:`, error);
98
184
  }
99
185
  }
100
186
  /**
@@ -159,10 +245,9 @@ function getUnbundleOutputExtension(inputExt, entry) {
159
245
  * Resolve banner/footer addon for specific format
160
246
  */
161
247
  function resolveChunkAddon(addon, format) {
162
- if (!addon) return void 0;
248
+ if (!addon) return;
163
249
  if (typeof addon === "string") return addon;
164
- const formatKey = format === "es" ? "js" : format;
165
- return addon[formatKey] || addon.js;
250
+ return addon[format === "es" ? "js" : format] || addon.js;
166
251
  }
167
252
  /**
168
253
  * Add banner to content
@@ -188,6 +273,136 @@ function addBannerFooter(content, banner, footer) {
188
273
  return result;
189
274
  }
190
275
 
276
+ //#endregion
277
+ //#region src/utils.ts
278
+ /**
279
+ * Normalize a path to an absolute path.
280
+ * Handles string paths, URL objects, and undefined values.
281
+ *
282
+ * @param path - The path to normalize (string, URL, or undefined)
283
+ * @param resolveFrom - The base directory to resolve relative paths from
284
+ * @returns The normalized absolute path
285
+ */
286
+ function normalizePath(path, resolveFrom) {
287
+ return typeof path === "string" && isAbsolute(path) ? path : path instanceof URL ? fileURLToPath(path) : resolve(resolveFrom || ".", path || ".");
288
+ }
289
+ function fmtPath(path) {
290
+ return resolve(path).replace(process.cwd(), ".");
291
+ }
292
+ function analyzeDir(dir) {
293
+ if (Array.isArray(dir)) {
294
+ let totalSize = 0;
295
+ let totalFiles = 0;
296
+ for (const d of dir) {
297
+ const { size, files } = analyzeDir(d);
298
+ totalSize += size;
299
+ totalFiles += files;
300
+ }
301
+ return {
302
+ size: totalSize,
303
+ files: totalFiles
304
+ };
305
+ }
306
+ let totalSize = 0;
307
+ try {
308
+ const files = readdirSync(dir, {
309
+ withFileTypes: true,
310
+ recursive: true
311
+ });
312
+ for (const file of files) {
313
+ const fullPath = join(file.parentPath, file.name);
314
+ if (file.isFile()) {
315
+ const { size } = statSync(fullPath);
316
+ totalSize += size;
317
+ }
318
+ }
319
+ return {
320
+ size: totalSize,
321
+ files: files.length
322
+ };
323
+ } catch (error) {
324
+ if (error.code === "ENOENT" || error.code === "ENOTDIR") return {
325
+ size: 0,
326
+ files: 0
327
+ };
328
+ throw error;
329
+ }
330
+ }
331
+ async function distSize(dir, entry) {
332
+ const { output } = await (await rolldown({
333
+ input: join(dir, entry),
334
+ plugins: [],
335
+ platform: "neutral",
336
+ external: (id) => id[0] !== "." && !id.startsWith(dir)
337
+ })).generate({ inlineDynamicImports: true });
338
+ const code = output[0].code;
339
+ const { code: minified } = await minify(entry, code);
340
+ return {
341
+ size: Buffer.byteLength(code),
342
+ minSize: Buffer.byteLength(minified),
343
+ minGzipSize: gzipSync(minified).length
344
+ };
345
+ }
346
+ async function sideEffectSize(dir, entry) {
347
+ const { output } = await (await rolldown({
348
+ input: "#entry",
349
+ platform: "neutral",
350
+ external: (id) => id[0] !== "." && !id.startsWith(dir),
351
+ plugins: [{
352
+ name: "virtual-entry",
353
+ async resolveId(id, importer, opts) {
354
+ if (id === "#entry") return { id };
355
+ const resolved = await this.resolve(id, importer, opts);
356
+ if (!resolved) return null;
357
+ resolved.moduleSideEffects = null;
358
+ return resolved;
359
+ },
360
+ load(id) {
361
+ if (id === "#entry") return `import * as _lib from "${join(dir, entry)}";`;
362
+ }
363
+ }]
364
+ })).generate({ inlineDynamicImports: true });
365
+ if (process.env.INSPECT_BUILD) {
366
+ logger.debug("---------[side effects]---------");
367
+ logger.debug(entry);
368
+ logger.debug(output[0].code);
369
+ logger.debug("-------------------------------");
370
+ }
371
+ return Buffer.byteLength(output[0].code.trim());
372
+ }
373
+
374
+ //#endregion
375
+ //#region src/features/clean.ts
376
+ /**
377
+ * Clean output directory or specific paths.
378
+ * Used by both bundle and transform builders.
379
+ *
380
+ * @param projectRoot - The project root directory
381
+ * @param outDir - The output directory to clean
382
+ * @param cleanPaths - true to clean outDir, or array of specific paths to clean
383
+ */
384
+ async function cleanOutputDir(projectRoot, outDir, cleanPaths) {
385
+ if (!cleanPaths) return;
386
+ if (cleanPaths === true) {
387
+ if (existsSync(outDir)) {
388
+ logger.log(colors.dim(`Cleaning ${fmtPath(outDir)}`));
389
+ await rm(outDir, {
390
+ recursive: true,
391
+ force: true
392
+ });
393
+ }
394
+ } else if (Array.isArray(cleanPaths)) for (const path of cleanPaths) {
395
+ const fullPath = resolve(projectRoot, path);
396
+ if (existsSync(fullPath)) {
397
+ logger.log(colors.dim(`Cleaning ${fmtPath(fullPath)}`));
398
+ await rm(fullPath, {
399
+ recursive: true,
400
+ force: true
401
+ });
402
+ }
403
+ }
404
+ }
405
+
191
406
  //#endregion
192
407
  //#region src/features/copy.ts
193
408
  /**
@@ -195,7 +410,7 @@ function addBannerFooter(content, banner, footer) {
195
410
  */
196
411
  async function copyFiles(cwd, outDir, copyOptions) {
197
412
  if (!copyOptions || copyOptions.length === 0) return;
198
- consola.debug("📁 Copying files...");
413
+ logger.verbose("Copying files...");
199
414
  await Promise.all(copyOptions.map(async (entry) => {
200
415
  const from = typeof entry === "string" ? entry : entry.from;
201
416
  const to = typeof entry === "string" ? resolve(outDir, basename(from)) : resolve(cwd, entry.to);
@@ -205,12 +420,231 @@ async function copyFiles(cwd, outDir, copyOptions) {
205
420
  recursive: true,
206
421
  force: true
207
422
  });
208
- consola.debug(` ${from} → ${to}`);
423
+ logger.verbose(` ${from} → ${to}`);
209
424
  } catch (error) {
210
- consola.warn(`Failed to copy ${from} to ${to}:`, error);
425
+ logger.warn(`Failed to copy ${from} to ${to}:`, error);
211
426
  }
212
427
  }));
213
- consola.debug("Files copied successfully");
428
+ logger.verbose("Files copied successfully");
429
+ }
430
+
431
+ //#endregion
432
+ //#region src/features/entry-resolver.ts
433
+ /**
434
+ * Parse a string entry format like "src/index.ts:dist" or "src/:dist"
435
+ *
436
+ * @param rawEntry - The raw string entry
437
+ * @returns Parsed entry object
438
+ */
439
+ function parseEntryString(rawEntry) {
440
+ const [input, outDir] = rawEntry.split(":");
441
+ if (input.endsWith("/")) return {
442
+ type: "transform",
443
+ input,
444
+ outDir: outDir || "dist"
445
+ };
446
+ return {
447
+ type: "bundle",
448
+ input: input.includes(",") ? input.split(",") : input,
449
+ outDir: outDir || "dist"
450
+ };
451
+ }
452
+ /**
453
+ * Normalize entry input paths to absolute paths.
454
+ * Handles string, array, and object (named entries) formats.
455
+ *
456
+ * @param entryInput - The entry input (string, array, or object)
457
+ * @param pkgDir - The package directory to resolve paths from
458
+ * @returns Normalized input
459
+ */
460
+ function normalizeEntryInput(entryInput, pkgDir) {
461
+ if (typeof entryInput === "object" && !Array.isArray(entryInput)) {
462
+ const normalizedInput = {};
463
+ for (const [key, value] of Object.entries(entryInput)) normalizedInput[key] = normalizePath(value, pkgDir);
464
+ return normalizedInput;
465
+ }
466
+ if (Array.isArray(entryInput)) return entryInput.map((p) => normalizePath(p, pkgDir));
467
+ return normalizePath(entryInput, pkgDir);
468
+ }
469
+ /**
470
+ * Get the entry input from a BundleEntry, supporting both 'input' and 'entry' fields.
471
+ * This provides tsup compatibility.
472
+ *
473
+ * @param entry - The bundle entry
474
+ * @returns The entry input or undefined
475
+ */
476
+ function getBundleEntryInput(entry) {
477
+ return entry.input || entry.entry;
478
+ }
479
+ /**
480
+ * Check if an entry has valid input.
481
+ *
482
+ * @param entry - The entry to check
483
+ * @returns true if the entry has valid input
484
+ */
485
+ function hasValidInput(entry) {
486
+ if (entry.type === "transform") return !!entry.input;
487
+ return !!getBundleEntryInput(entry);
488
+ }
489
+
490
+ //#endregion
491
+ //#region src/features/extensions.ts
492
+ /**
493
+ * Get file extension for a given format (with leading dot).
494
+ * This is the unified function used by bundle, watch, and transform modes.
495
+ *
496
+ * @param format - The module format (es, cjs, iife, umd, etc.)
497
+ * @param platform - The target platform
498
+ * @param fixedExtension - Whether to force .cjs/.mjs extensions
499
+ * @returns The file extension with leading dot (e.g., '.mjs', '.cjs', '.js')
500
+ */
501
+ function getFormatExtension(format, platform = "node", fixedExtension = false) {
502
+ if (fixedExtension) return format === "cjs" || format === "commonjs" ? ".cjs" : ".mjs";
503
+ switch (format) {
504
+ case "es":
505
+ case "esm":
506
+ case "module": return ".mjs";
507
+ case "cjs":
508
+ case "commonjs": return platform === "node" ? ".cjs" : ".js";
509
+ case "iife":
510
+ case "umd": return ".js";
511
+ default: return ".js";
512
+ }
513
+ }
514
+ /**
515
+ * Resolve JavaScript output extension (without leading dot).
516
+ * @deprecated Use getFormatExtension() instead for consistency
517
+ */
518
+ function resolveJsOutputExtension(format, platform = "node", fixedExtension = false) {
519
+ if (fixedExtension) return format === "cjs" ? "cjs" : "mjs";
520
+ switch (format) {
521
+ case "es": return platform === "browser" ? "js" : "mjs";
522
+ case "cjs": return platform === "browser" ? "js" : "cjs";
523
+ case "iife":
524
+ case "umd": return "js";
525
+ default: return "js";
526
+ }
527
+ }
528
+ /**
529
+ * Resolve DTS output extension
530
+ */
531
+ function resolveDtsOutputExtension(format, fixedExtension = false) {
532
+ if (fixedExtension) return format === "cjs" ? "d.cts" : "d.mts";
533
+ switch (format) {
534
+ case "es": return "d.mts";
535
+ case "cjs": return "d.cts";
536
+ default: return "d.ts";
537
+ }
538
+ }
539
+ /**
540
+ * Apply custom output extensions
541
+ */
542
+ function applyOutExtensions(format, outExtensions) {
543
+ const defaultJs = resolveJsOutputExtension(format);
544
+ const defaultDts = resolveDtsOutputExtension(format);
545
+ if (!outExtensions) return {
546
+ js: defaultJs,
547
+ dts: defaultDts
548
+ };
549
+ const custom = outExtensions(format);
550
+ return {
551
+ js: custom.js || defaultJs,
552
+ dts: custom.dts || defaultDts
553
+ };
554
+ }
555
+ /**
556
+ * Create filename with proper extension
557
+ */
558
+ function createFilename(basename, format, isDts = false, options = {}) {
559
+ const { platform, fixedExtension, outExtensions } = options;
560
+ if (outExtensions) {
561
+ const extensions = applyOutExtensions(format, outExtensions);
562
+ return `${basename}.${isDts ? extensions.dts : extensions.js}`;
563
+ }
564
+ if (isDts) return `${basename}.${resolveDtsOutputExtension(format, fixedExtension)}`;
565
+ return `${basename}.${resolveJsOutputExtension(format, platform, fixedExtension)}`;
566
+ }
567
+
568
+ //#endregion
569
+ //#region src/features/external.ts
570
+ /**
571
+ * Build external dependencies list from package.json dependencies and peerDependencies.
572
+ * This is the shared logic used by both bundle and watch modes.
573
+ */
574
+ function buildExternalDeps(ctx) {
575
+ return [
576
+ ...builtinModules,
577
+ ...builtinModules.map((m) => `node:${m}`),
578
+ ...[...Object.keys(ctx.pkg.dependencies || {}), ...Object.keys(ctx.pkg.peerDependencies || {})].flatMap((p) => [p, new RegExp(`^${p}/`)])
579
+ ];
580
+ }
581
+ /**
582
+ * Escape special regex characters in a string
583
+ */
584
+ function escapeRegExp(s) {
585
+ return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
586
+ }
587
+ /**
588
+ * Apply noExternal configuration to filter out dependencies that should be bundled.
589
+ * Supports both function and array forms.
590
+ */
591
+ function applyNoExternal(externalDeps, noExternal, ctx) {
592
+ if (!noExternal) return externalDeps;
593
+ if (typeof noExternal === "function") {
594
+ const predicate = noExternal;
595
+ const depNames = [...Object.keys(ctx.pkg.dependencies || {}), ...Object.keys(ctx.pkg.peerDependencies || {})];
596
+ const excludedNames = /* @__PURE__ */ new Set();
597
+ for (const name of depNames) try {
598
+ if (predicate(name)) excludedNames.add(name);
599
+ } catch {}
600
+ return externalDeps.filter((dep) => {
601
+ if (typeof dep === "string") return !excludedNames.has(dep);
602
+ if (dep instanceof RegExp) {
603
+ for (const name of Array.from(excludedNames)) if (dep.source.startsWith(`^${escapeRegExp(name)}/`)) return false;
604
+ return true;
605
+ }
606
+ return true;
607
+ });
608
+ }
609
+ if (Array.isArray(noExternal)) {
610
+ const rules = noExternal;
611
+ return externalDeps.filter((dep) => {
612
+ for (const rule of rules) if (typeof rule === "string") {
613
+ if (typeof dep === "string") {
614
+ if (dep === rule) return false;
615
+ } else if (dep instanceof RegExp) {
616
+ if (dep.source.startsWith(`^${escapeRegExp(rule)}/`)) return false;
617
+ }
618
+ } else if (rule instanceof RegExp) {
619
+ if (typeof dep === "string") {
620
+ if (rule.test(dep)) return false;
621
+ } else if (dep instanceof RegExp) {
622
+ if (dep.source === rule.source && dep.flags === rule.flags) return false;
623
+ }
624
+ }
625
+ return true;
626
+ });
627
+ }
628
+ return externalDeps;
629
+ }
630
+ /**
631
+ * Add custom external dependencies to the list.
632
+ * Only handles array form; function form is handled separately.
633
+ */
634
+ function addCustomExternal(externalDeps, external) {
635
+ if (external && typeof external !== "function") return [...externalDeps, ...external];
636
+ return externalDeps;
637
+ }
638
+ /**
639
+ * Build the complete external configuration for rolldown.
640
+ * Returns either a function (if entry.external is a function) or the processed array.
641
+ */
642
+ function resolveExternalConfig(ctx, options) {
643
+ let externalDeps = buildExternalDeps(ctx);
644
+ externalDeps = applyNoExternal(externalDeps, options.noExternal, ctx);
645
+ externalDeps = addCustomExternal(externalDeps, options.external);
646
+ if (typeof options.external === "function") return options.external;
647
+ return externalDeps;
214
648
  }
215
649
 
216
650
  //#endregion
@@ -235,7 +669,7 @@ function createGlobImportPlugin(options = {}) {
235
669
  if (optionsStr) try {
236
670
  globOptions = parseGlobOptions(optionsStr);
237
671
  } catch {
238
- console.warn("Failed to parse glob options:", optionsStr);
672
+ logger.warn("Failed to parse glob options:", optionsStr);
239
673
  }
240
674
  const isEager = globOptions.eager ?? eager;
241
675
  const isAsUrls = globOptions.as === "url" || asUrls;
@@ -243,7 +677,7 @@ function createGlobImportPlugin(options = {}) {
243
677
  const replacement = await generateGlobImport(pattern, id, isEager, isAsUrls, patterns);
244
678
  transformedCode = transformedCode.replace(fullMatch, replacement);
245
679
  } catch (error) {
246
- console.error(`Failed to process glob import ${pattern}:`, error);
680
+ logger.error(`Failed to process glob import ${pattern}:`, error);
247
681
  }
248
682
  }
249
683
  return hasGlobImports ? transformedCode : null;
@@ -258,8 +692,7 @@ async function generateGlobImport(pattern, importer, eager, asUrls, allowedPatte
258
692
  if (!isPatternAllowed(pattern, allowedPatterns)) throw new Error(`Glob pattern "${pattern}" is not allowed`);
259
693
  let files = [];
260
694
  try {
261
- const absolutePattern = resolve(importerDir, pattern);
262
- files = await glob(absolutePattern, { ignore: ["**/node_modules/**", "**/.git/**"] });
695
+ files = await glob(resolve(importerDir, pattern), { ignore: ["**/node_modules/**", "**/.git/**"] });
263
696
  } catch {
264
697
  if (pattern.includes("*.js")) files = [resolve(importerDir, pattern.replace("*", "module1")), resolve(importerDir, pattern.replace("*", "module2"))];
265
698
  }
@@ -336,9 +769,7 @@ function addHashToFilename(filename, content, hashLength = 8) {
336
769
  const hash = generateContentHash(content, hashLength);
337
770
  const dotIndex = filename.lastIndexOf(".");
338
771
  if (dotIndex === -1) return `${filename}-${hash}`;
339
- const name = filename.slice(0, dotIndex);
340
- const ext = filename.slice(dotIndex);
341
- return `${name}-${hash}${ext}`;
772
+ return `${filename.slice(0, dotIndex)}-${hash}${filename.slice(dotIndex)}`;
342
773
  }
343
774
  /**
344
775
  * Check if filename already has hash
@@ -402,15 +833,11 @@ if (typeof exports === 'undefined') {
402
833
  */
403
834
  function detectShimNeeds(code) {
404
835
  const cleanCode = removeCommentsAndStrings(code);
405
- const needsDirname = /\b__dirname\b/.test(cleanCode) || /\b__filename\b/.test(cleanCode);
406
- const needsRequire = /\brequire\s*\(/.test(cleanCode);
407
- const needsExports = /\bmodule\.exports\b/.test(cleanCode) || /\bexports\.\w+/.test(cleanCode);
408
- const needsEnv = /\bprocess\.env\b/.test(cleanCode);
409
836
  return {
410
- needsDirname,
411
- needsRequire,
412
- needsExports,
413
- needsEnv
837
+ needsDirname: /\b__dirname\b/.test(cleanCode) || /\b__filename\b/.test(cleanCode),
838
+ needsRequire: /\brequire\s*\(/.test(cleanCode),
839
+ needsExports: /\bmodule\.exports\b/.test(cleanCode) || /\bexports\.\w+/.test(cleanCode),
840
+ needsEnv: /\bprocess\.env\b/.test(cleanCode)
414
841
  };
415
842
  }
416
843
  /**
@@ -438,8 +865,7 @@ const require = createRequire(import.meta.url)
438
865
  * Transform code to use shims
439
866
  */
440
867
  function transformWithShims(code, config) {
441
- const needs = detectShimNeeds(code);
442
- const shims = generateShims(config, needs);
868
+ const shims = generateShims(config, detectShimNeeds(code));
443
869
  if (!shims) return code;
444
870
  return `${shims}\n${code}`;
445
871
  }
@@ -552,191 +978,55 @@ function processNodeProtocol(id, nodeProtocol) {
552
978
  */
553
979
  function transformNodeProtocol(code, nodeProtocol) {
554
980
  if (!nodeProtocol) return code;
555
- const importRegex = /(?:import|export)(?:\s[^'"]*)?\s['"]([^'"]+)['"]/g;
556
- return code.replace(importRegex, (match, moduleId) => {
981
+ return code.replace(/(?:import|export)(?:\s[^'"]*)?\s['"]([^'"]+)['"]/g, (match, moduleId) => {
557
982
  const processedId = processNodeProtocol(moduleId, nodeProtocol);
558
- return match.replace(moduleId, processedId);
559
- });
560
- }
561
-
562
- //#endregion
563
- //#region src/plugins/node-protocol.ts
564
- /**
565
- * Rolldown plugin for Node.js protocol handling
566
- */
567
- function nodeProtocolPlugin(nodeProtocol) {
568
- if (!nodeProtocol) return { name: "node-protocol-noop" };
569
- return {
570
- name: "node-protocol",
571
- renderChunk(code) {
572
- return {
573
- code: transformNodeProtocol(code, nodeProtocol),
574
- map: null
575
- };
576
- }
577
- };
578
- }
579
-
580
- //#endregion
581
- //#region src/plugins/shebang.ts
582
- const SHEBANG_RE = /^#![^\n]*/;
583
- function shebangPlugin() {
584
- return {
585
- name: "robuild-shebang",
586
- async writeBundle(options, bundle) {
587
- for (const [fileName, output] of Object.entries(bundle)) {
588
- if (output.type !== "chunk") continue;
589
- if (hasShebang(output.code)) {
590
- const outFile = resolve(options.dir, fileName);
591
- await makeExecutable(outFile);
592
- }
593
- }
594
- }
595
- };
596
- }
597
- function hasShebang(code) {
598
- return SHEBANG_RE.test(code);
599
- }
600
- async function makeExecutable(filePath) {
601
- await promises.chmod(filePath, 493).catch(() => {});
602
- }
603
-
604
- //#endregion
605
- //#region src/utils.ts
606
- function fmtPath(path) {
607
- return resolve(path).replace(process.cwd(), ".");
608
- }
609
- function analyzeDir(dir) {
610
- if (Array.isArray(dir)) {
611
- let totalSize$1 = 0;
612
- let totalFiles = 0;
613
- for (const d of dir) {
614
- const { size, files } = analyzeDir(d);
615
- totalSize$1 += size;
616
- totalFiles += files;
617
- }
618
- return {
619
- size: totalSize$1,
620
- files: totalFiles
621
- };
622
- }
623
- let totalSize = 0;
624
- try {
625
- const files = readdirSync(dir, {
626
- withFileTypes: true,
627
- recursive: true
628
- });
629
- for (const file of files) {
630
- const fullPath = join(file.parentPath, file.name);
631
- if (file.isFile()) {
632
- const { size } = statSync(fullPath);
633
- totalSize += size;
634
- }
635
- }
636
- return {
637
- size: totalSize,
638
- files: files.length
639
- };
640
- } catch (error) {
641
- if (error.code === "ENOENT" || error.code === "ENOTDIR") return {
642
- size: 0,
643
- files: 0
644
- };
645
- throw error;
646
- }
647
- }
648
- async function distSize(dir, entry) {
649
- const build$1 = await rolldown({
650
- input: join(dir, entry),
651
- plugins: [],
652
- platform: "neutral",
653
- external: (id) => id[0] !== "." && !id.startsWith(dir)
654
- });
655
- const { output } = await build$1.generate({ inlineDynamicImports: true });
656
- const code = output[0].code;
657
- const { code: minified } = await minify(entry, code);
658
- return {
659
- size: Buffer.byteLength(code),
660
- minSize: Buffer.byteLength(minified),
661
- minGzipSize: gzipSync(minified).length
662
- };
663
- }
664
- async function sideEffectSize(dir, entry) {
665
- const virtualEntry = {
666
- name: "virtual-entry",
667
- async resolveId(id, importer, opts) {
668
- if (id === "#entry") return { id };
669
- const resolved = await this.resolve(id, importer, opts);
670
- if (!resolved) return null;
671
- resolved.moduleSideEffects = null;
672
- return resolved;
673
- },
674
- load(id) {
675
- if (id === "#entry") return `import * as _lib from "${join(dir, entry)}";`;
676
- }
677
- };
678
- const build$1 = await rolldown({
679
- input: "#entry",
680
- platform: "neutral",
681
- external: (id) => id[0] !== "." && !id.startsWith(dir),
682
- plugins: [virtualEntry]
983
+ return match.replace(moduleId, processedId);
683
984
  });
684
- const { output } = await build$1.generate({ inlineDynamicImports: true });
685
- if (process.env.INSPECT_BUILD) {
686
- console.log("---------[side effects]---------");
687
- console.log(entry);
688
- console.log(output[0].code);
689
- console.log("-------------------------------");
690
- }
691
- return Buffer.byteLength(output[0].code.trim());
692
985
  }
693
986
 
694
987
  //#endregion
695
- //#region src/builders/bundle.ts
696
- /**
697
- * Get file extension for format
698
- */
699
- function getFormatExtension(format, platform, fixedExtension = false) {
700
- if (fixedExtension) return format === "cjs" ? ".cjs" : ".mjs";
701
- switch (format) {
702
- case "es":
703
- case "esm":
704
- case "module": return ".mjs";
705
- case "cjs":
706
- case "commonjs": return platform === "node" ? ".cjs" : ".js";
707
- case "iife":
708
- case "umd": return ".js";
709
- default: return ".js";
710
- }
711
- }
988
+ //#region src/plugins/node-protocol.ts
712
989
  /**
713
- * Clean output directory
990
+ * Rolldown plugin for Node.js protocol handling
714
991
  */
715
- async function cleanOutputDir$1(projectRoot, outDir, cleanPaths) {
716
- if (!cleanPaths) return;
717
- const { rm } = await import("node:fs/promises");
718
- const { existsSync: existsSync$1 } = await import("node:fs");
719
- if (cleanPaths === true) {
720
- if (existsSync$1(outDir)) {
721
- consola.log(`🧻 Cleaning up ${fmtPath(outDir)}`);
722
- await rm(outDir, {
723
- recursive: true,
724
- force: true
725
- });
992
+ function nodeProtocolPlugin(nodeProtocol) {
993
+ if (!nodeProtocol) return { name: "node-protocol-noop" };
994
+ return {
995
+ name: "node-protocol",
996
+ renderChunk(code) {
997
+ return {
998
+ code: transformNodeProtocol(code, nodeProtocol),
999
+ map: null
1000
+ };
726
1001
  }
727
- } else if (Array.isArray(cleanPaths)) for (const path of cleanPaths) {
728
- const fullPath = resolve(projectRoot, path);
729
- if (existsSync$1(fullPath)) {
730
- consola.log(`🧻 Cleaning up ${fmtPath(fullPath)}`);
731
- await rm(fullPath, {
732
- recursive: true,
733
- force: true
734
- });
1002
+ };
1003
+ }
1004
+
1005
+ //#endregion
1006
+ //#region src/plugins/shebang.ts
1007
+ const SHEBANG_RE = /^#![^\n]*/;
1008
+ function shebangPlugin() {
1009
+ return {
1010
+ name: "robuild-shebang",
1011
+ async writeBundle(options, bundle) {
1012
+ for (const [fileName, output] of Object.entries(bundle)) {
1013
+ if (output.type !== "chunk") continue;
1014
+ if (hasShebang(output.code)) await makeExecutable(resolve(options.dir, fileName));
1015
+ }
735
1016
  }
736
- }
1017
+ };
737
1018
  }
1019
+ function hasShebang(code) {
1020
+ return SHEBANG_RE.test(code);
1021
+ }
1022
+ async function makeExecutable(filePath) {
1023
+ await promises.chmod(filePath, 493).catch(() => {});
1024
+ }
1025
+
1026
+ //#endregion
1027
+ //#region src/builders/bundle.ts
738
1028
  async function rolldownBuild(ctx, entry, hooks, config) {
739
- const entryInput = entry.input || entry.entry;
1029
+ const entryInput = getBundleEntryInput(entry);
740
1030
  if (!entryInput) throw new Error("Entry input is required");
741
1031
  const inputs = normalizeBundleInputs(entryInput, ctx);
742
1032
  const pluginManager = new RobuildPluginManager(config || {}, entry, ctx.pkgDir);
@@ -754,20 +1044,18 @@ async function rolldownBuild(ctx, entry, hooks, config) {
754
1044
  outDir: fullOutDir
755
1045
  });
756
1046
  await pluginManager.executeRobuildBuildStart();
757
- await cleanOutputDir$1(ctx.pkgDir, fullOutDir, entry.clean ?? true);
1047
+ await cleanOutputDir(ctx.pkgDir, fullOutDir, entry.clean ?? true);
758
1048
  if (entry.dtsOnly) {
759
- consola.info("Running in dtsOnly mode - only generating declaration files");
1049
+ logger.info("Running in dtsOnly mode - only generating declaration files");
760
1050
  entry.dts = entry.dts === false ? true : entry.dts || true;
761
1051
  }
762
1052
  if (entry.stub) {
763
1053
  for (const [distName, srcPath] of Object.entries(inputs)) {
764
1054
  const distPath = join(ctx.pkgDir, "dist", `${distName}.mjs`);
765
1055
  await mkdir(dirname(distPath), { recursive: true });
766
- consola.log(`${colors.magenta("[stub bundle] ")} ${colors.underline(fmtPath(distPath))}`);
1056
+ logger.log(`${colors.cyan("Stub")} ${colors.green(fmtPath(distPath))}`);
767
1057
  const srcContents = await readFile(srcPath, "utf8");
768
- const parsed = parseSync(srcPath, srcContents);
769
- const exportNames = parsed.module.staticExports.flatMap((e) => e.entries.map((e$1) => e$1.exportName.kind === "Default" ? "default" : e$1.exportName.name));
770
- const hasDefaultExport = exportNames.includes("default");
1058
+ const hasDefaultExport = parseSync(srcPath, srcContents).module.staticExports.flatMap((e) => e.entries.map((e) => e.exportName.kind === "Default" ? "default" : e.exportName.name)).includes("default");
771
1059
  const firstLine = srcContents.split("\n")[0];
772
1060
  const hasShebangLine = firstLine.startsWith("#!");
773
1061
  await writeFile(distPath, `${hasShebangLine ? `${firstLine}\n` : ""}export * from "${srcPath}";\n${hasDefaultExport ? `export { default } from "${srcPath}";\n` : ""}`, "utf8");
@@ -776,49 +1064,10 @@ async function rolldownBuild(ctx, entry, hooks, config) {
776
1064
  }
777
1065
  return;
778
1066
  }
779
- let externalDeps = [
780
- ...builtinModules,
781
- ...builtinModules.map((m) => `node:${m}`),
782
- ...[...Object.keys(ctx.pkg.dependencies || {}), ...Object.keys(ctx.pkg.peerDependencies || {})].flatMap((p) => [p, /* @__PURE__ */ new RegExp(`^${p}/`)])
783
- ];
784
- if (entry.noExternal) {
785
- const escapeRegExp = (s) => s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
786
- if (typeof entry.noExternal === "function") {
787
- const predicate = entry.noExternal;
788
- const depNames = [...Object.keys(ctx.pkg.dependencies || {}), ...Object.keys(ctx.pkg.peerDependencies || {})];
789
- const excludedNames = /* @__PURE__ */ new Set();
790
- for (const name of depNames) try {
791
- if (predicate(name)) excludedNames.add(name);
792
- } catch {}
793
- externalDeps = externalDeps.filter((dep) => {
794
- if (typeof dep === "string") return !excludedNames.has(dep);
795
- if (dep instanceof RegExp) {
796
- for (const name of Array.from(excludedNames)) if (dep.source.startsWith(`^${escapeRegExp(name)}/`)) return false;
797
- return true;
798
- }
799
- return true;
800
- });
801
- } else if (Array.isArray(entry.noExternal)) {
802
- const rules = entry.noExternal;
803
- externalDeps = externalDeps.filter((dep) => {
804
- for (const rule of rules) if (typeof rule === "string") {
805
- if (typeof dep === "string") {
806
- if (dep === rule) return false;
807
- } else if (dep instanceof RegExp) {
808
- if (dep.source.startsWith(`^${escapeRegExp(rule)}/`)) return false;
809
- }
810
- } else if (rule instanceof RegExp) {
811
- if (typeof dep === "string") {
812
- if (rule.test(dep)) return false;
813
- } else if (dep instanceof RegExp) {
814
- if (dep.source === rule.source && dep.flags === rule.flags) return false;
815
- }
816
- }
817
- return true;
818
- });
819
- }
820
- }
821
- if (entry.external) if (typeof entry.external === "function") {} else externalDeps.push(...entry.external);
1067
+ const externalConfig = resolveExternalConfig(ctx, {
1068
+ external: entry.external,
1069
+ noExternal: entry.noExternal
1070
+ });
822
1071
  const defineOptions = {};
823
1072
  if (entry.env) for (const [key, value] of Object.entries(entry.env)) defineOptions[`process.env.${key}`] = JSON.stringify(value);
824
1073
  if (entry.define) for (const [key, value] of Object.entries(entry.define)) defineOptions[key] = value;
@@ -852,21 +1101,23 @@ async function rolldownBuild(ctx, entry, hooks, config) {
852
1101
  }
853
1102
  rolldownPlugins.push(...pluginManager.getRolldownPlugins());
854
1103
  const moduleTypes = {};
855
- if (entry.loaders) for (const [ext, config$1] of Object.entries(entry.loaders)) moduleTypes[ext] = config$1.loader;
1104
+ if (entry.loaders) for (const [ext, config] of Object.entries(entry.loaders)) moduleTypes[ext] = config.loader;
856
1105
  const robuildGeneratedConfig = {
857
1106
  cwd: ctx.pkgDir,
858
1107
  input: inputs,
859
1108
  plugins: rolldownPlugins,
860
1109
  platform: platform === "node" ? "node" : "neutral",
861
- external: typeof entry.external === "function" ? entry.external : externalDeps,
862
- define: defineOptions,
1110
+ external: externalConfig,
863
1111
  resolve: { alias: entry.alias || {} },
864
- transform: { target },
1112
+ transform: {
1113
+ target,
1114
+ define: defineOptions
1115
+ },
865
1116
  ...Object.keys(moduleTypes).length > 0 ? { moduleTypes } : {}
866
1117
  };
867
1118
  if (entry.treeshake !== void 0) if (typeof entry.treeshake === "boolean") robuildGeneratedConfig.treeshake = entry.treeshake;
868
1119
  else robuildGeneratedConfig.treeshake = entry.treeshake;
869
- const { output: userOutputConfig, plugins: userPlugins,...userRolldownConfig } = entry.rolldown || {};
1120
+ const { output: userOutputConfig, plugins: userPlugins, ...userRolldownConfig } = entry.rolldown || {};
870
1121
  const baseRolldownConfig = {
871
1122
  ...robuildGeneratedConfig,
872
1123
  ...userRolldownConfig,
@@ -894,7 +1145,10 @@ async function rolldownBuild(ctx, entry, hooks, config) {
894
1145
  dir: formatOutDir,
895
1146
  format,
896
1147
  entryFileNames: entryFileName,
897
- chunkFileNames: `_chunks/[name]-[hash]${extension}`,
1148
+ chunkFileNames: (chunk) => {
1149
+ if (chunk.name.endsWith(".d") || chunk.name.includes(".d.")) return `[name].mjs`;
1150
+ return `_chunks/[name]-[hash]${extension}`;
1151
+ },
898
1152
  minify: entry.minify,
899
1153
  name: entry.globalName,
900
1154
  banner: resolveChunkAddon(entry.banner, format),
@@ -938,9 +1192,7 @@ async function rolldownBuild(ctx, entry, hooks, config) {
938
1192
  const { rename } = await import("node:fs/promises");
939
1193
  await rename(finalFilePath, hashedFilePath);
940
1194
  try {
941
- const mapOld = `${finalFilePath}.map`;
942
- const mapNew = `${hashedFilePath}.map`;
943
- await rename(mapOld, mapNew);
1195
+ await rename(`${finalFilePath}.map`, `${hashedFilePath}.map`);
944
1196
  } catch {}
945
1197
  finalFileName = hashedFileName;
946
1198
  finalFilePath = hashedFilePath;
@@ -958,29 +1210,26 @@ async function rolldownBuild(ctx, entry, hooks, config) {
958
1210
  }
959
1211
  if (entry.copy) await copyFiles(ctx.pkgDir, fullOutDir, entry.copy);
960
1212
  await pluginManager.executeRobuildBuildEnd({ allOutputEntries });
961
- consola.log(`\n${allOutputEntries.map((o) => [
962
- `${colors.magenta(`[bundle] `)}${colors.underline(fmtPath(filePathMap.get(o.name) || join(fullOutDir, o.name)))}`,
963
- colors.dim(`${colors.bold("Size:")} ${prettyBytes(o.size)}, ${colors.bold(prettyBytes(o.minSize))} minified, ${prettyBytes(o.minGzipSize)} min+gzipped (Side effects: ${prettyBytes(o.sideEffectSize)})`),
964
- o.exports.some((e) => e !== "default") ? colors.dim(`${colors.bold("Exports:")} ${o.exports.map((e) => e).join(", ")}`) : "",
965
- o.deps.length > 0 ? colors.dim(`${colors.bold("Dependencies:")} ${o.deps.join(", ")}`) : ""
1213
+ logger.log(`\n${allOutputEntries.map((o) => [
1214
+ `${colors.cyan("Bundle")} ${colors.green(fmtPath(filePathMap.get(o.name) || join(fullOutDir, o.name)))}`,
1215
+ colors.dim(` ${prettyBytes(o.size)} / minified: ${prettyBytes(o.minSize)} / gzip: ${prettyBytes(o.minGzipSize)}`),
1216
+ o.exports.some((e) => e !== "default") ? colors.dim(` Exports: ${o.exports.map((e) => e).join(", ")}`) : "",
1217
+ o.deps.length > 0 ? colors.dim(` Dependencies: ${o.deps.join(", ")}`) : ""
966
1218
  ].filter(Boolean).join("\n")).join("\n\n")}`);
967
1219
  }
968
1220
  function normalizeBundleInputs(input, ctx) {
969
1221
  const inputs = {};
970
1222
  if (typeof input === "object" && !Array.isArray(input)) {
971
- for (const [name, src] of Object.entries(input)) {
972
- const resolvedSrc = resolveModulePath(src, {
973
- from: ctx.pkgDir,
974
- extensions: [
975
- ".ts",
976
- ".js",
977
- ".mjs",
978
- ".cjs",
979
- ".json"
980
- ]
981
- });
982
- inputs[name] = resolvedSrc;
983
- }
1223
+ for (const [name, src] of Object.entries(input)) inputs[name] = resolveModulePath(src, {
1224
+ from: ctx.pkgDir,
1225
+ extensions: [
1226
+ ".ts",
1227
+ ".js",
1228
+ ".mjs",
1229
+ ".cjs",
1230
+ ".json"
1231
+ ]
1232
+ });
984
1233
  return inputs;
985
1234
  }
986
1235
  for (let src of Array.isArray(input) ? input : [input]) {
@@ -1004,122 +1253,39 @@ function normalizeBundleInputs(input, ctx) {
1004
1253
  return inputs;
1005
1254
  }
1006
1255
 
1007
- //#endregion
1008
- //#region src/features/extensions.ts
1009
- /**
1010
- * Resolve JavaScript output extension
1011
- */
1012
- function resolveJsOutputExtension(format, platform = "node", fixedExtension = false) {
1013
- if (fixedExtension) return format === "cjs" ? "cjs" : "mjs";
1014
- switch (format) {
1015
- case "es": return platform === "browser" ? "js" : "mjs";
1016
- case "cjs": return platform === "browser" ? "js" : "cjs";
1017
- case "iife":
1018
- case "umd": return "js";
1019
- default: return "js";
1020
- }
1021
- }
1022
- /**
1023
- * Resolve DTS output extension
1024
- */
1025
- function resolveDtsOutputExtension(format, fixedExtension = false) {
1026
- if (fixedExtension) return format === "cjs" ? "d.cts" : "d.mts";
1027
- switch (format) {
1028
- case "es": return "d.mts";
1029
- case "cjs": return "d.cts";
1030
- default: return "d.ts";
1031
- }
1032
- }
1033
- /**
1034
- * Apply custom output extensions
1035
- */
1036
- function applyOutExtensions(format, outExtensions) {
1037
- const defaultJs = resolveJsOutputExtension(format);
1038
- const defaultDts = resolveDtsOutputExtension(format);
1039
- if (!outExtensions) return {
1040
- js: defaultJs,
1041
- dts: defaultDts
1042
- };
1043
- const custom = outExtensions(format);
1044
- return {
1045
- js: custom.js || defaultJs,
1046
- dts: custom.dts || defaultDts
1047
- };
1048
- }
1049
- /**
1050
- * Create filename with proper extension
1051
- */
1052
- function createFilename(basename$1, format, isDts = false, options = {}) {
1053
- const { platform, fixedExtension, outExtensions } = options;
1054
- if (outExtensions) {
1055
- const extensions = applyOutExtensions(format, outExtensions);
1056
- return `${basename$1}.${isDts ? extensions.dts : extensions.js}`;
1057
- }
1058
- if (isDts) return `${basename$1}.${resolveDtsOutputExtension(format, fixedExtension)}`;
1059
- return `${basename$1}.${resolveJsOutputExtension(format, platform, fixedExtension)}`;
1060
- }
1061
-
1062
1256
  //#endregion
1063
1257
  //#region src/builders/transform.ts
1064
1258
  /**
1065
- * Clean output directory for transform entries
1066
- */
1067
- async function cleanOutputDir(projectRoot, outDir, cleanPaths) {
1068
- if (!cleanPaths) return;
1069
- const { rm } = await import("node:fs/promises");
1070
- const { existsSync: existsSync$1 } = await import("node:fs");
1071
- if (cleanPaths === true) {
1072
- if (existsSync$1(outDir)) {
1073
- consola.log(`🧻 Cleaning up ${fmtPath(outDir)}`);
1074
- await rm(outDir, {
1075
- recursive: true,
1076
- force: true
1077
- });
1078
- }
1079
- } else if (Array.isArray(cleanPaths)) for (const path of cleanPaths) {
1080
- const fullPath = resolve(projectRoot, path);
1081
- if (existsSync$1(fullPath)) {
1082
- consola.log(`🧻 Cleaning up ${fmtPath(fullPath)}`);
1083
- await rm(fullPath, {
1084
- recursive: true,
1085
- force: true
1086
- });
1087
- }
1088
- }
1089
- }
1090
- /**
1091
1259
  * Transform all .ts modules in a directory using oxc-transform.
1092
1260
  */
1093
1261
  async function transformDir(ctx, entry) {
1094
1262
  if (entry.stub) {
1095
- consola.log(`${colors.magenta("[stub transform] ")} ${colors.underline(`${fmtPath(entry.outDir)}/`)}`);
1263
+ logger.log(`${colors.cyan("Stub")} ${colors.green(`${fmtPath(entry.outDir)}/`)}`);
1096
1264
  await symlink(entry.input, entry.outDir, "junction");
1097
1265
  return;
1098
1266
  }
1099
1267
  if (entry.unbundle) {
1100
- consola.log(`${colors.magenta("[unbundle] ")} ${colors.underline(`${fmtPath(entry.outDir)}/`)}`);
1268
+ logger.log(`${colors.cyan("Unbundle")} ${colors.green(`${fmtPath(entry.outDir)}/`)}`);
1101
1269
  await unbundleTransform(ctx, entry);
1102
1270
  return;
1103
1271
  }
1104
- const fullOutDir = resolve(ctx.pkgDir, entry.outDir);
1272
+ const fullOutDir = normalizePath(entry.outDir || "dist", ctx.pkgDir);
1105
1273
  await cleanOutputDir(ctx.pkgDir, fullOutDir, entry.clean ?? true);
1106
- const { statSync: statSync$1 } = await import("node:fs");
1274
+ const { statSync } = await import("node:fs");
1107
1275
  let inputDir = entry.input;
1108
1276
  try {
1109
- const stats = statSync$1(inputDir);
1110
- if (stats.isFile()) {
1277
+ if (statSync(inputDir).isFile()) {
1111
1278
  inputDir = dirname(inputDir);
1112
- consola.warn(`Transform input should be a directory, not a file. Using directory: ${fmtPath(inputDir)}`);
1279
+ logger.warn(`Transform input should be a directory, not a file. Using directory: ${fmtPath(inputDir)}`);
1113
1280
  }
1114
1281
  } catch (error) {
1115
1282
  if (error.code !== "ENOENT") throw error;
1116
1283
  }
1117
- const promises$1 = [];
1284
+ const promises = [];
1118
1285
  const files = await glob$1("**/*.*", { cwd: inputDir });
1119
- for await (const entryName of files) promises$1.push((async () => {
1286
+ for await (const entryName of files) promises.push((async () => {
1120
1287
  const entryPath = join(inputDir, entryName);
1121
- const ext = extname(entryPath);
1122
- switch (ext) {
1288
+ switch (extname(entryPath)) {
1123
1289
  case ".ts":
1124
1290
  case ".tsx":
1125
1291
  case ".jsx": {
@@ -1133,11 +1299,7 @@ async function transformDir(ctx, entry) {
1133
1299
  let entryDistPath = join(entry.outDir, outputFileName);
1134
1300
  await mkdir(dirname(entryDistPath), { recursive: true });
1135
1301
  await writeFile(entryDistPath, transformed.code, "utf8");
1136
- if (entry.sourcemap && transformed.map) {
1137
- const mapPath = `${entryDistPath}.map`;
1138
- const mapContent = typeof transformed.map === "string" ? transformed.map : JSON.stringify(transformed.map);
1139
- await writeFile(mapPath, mapContent, "utf8");
1140
- }
1302
+ if (entry.sourcemap && transformed.map) await writeFile(`${entryDistPath}.map`, typeof transformed.map === "string" ? transformed.map : JSON.stringify(transformed.map), "utf8");
1141
1303
  if (entry.hash && !hasHash(entryDistPath)) {
1142
1304
  const hashedPath = addHashToFilename(entryDistPath, transformed.code);
1143
1305
  const { rename } = await import("node:fs/promises");
@@ -1151,8 +1313,7 @@ async function transformDir(ctx, entry) {
1151
1313
  fixedExtension: entry.fixedExtension,
1152
1314
  outExtensions: entry.outExtensions
1153
1315
  });
1154
- const dtsPath = join(entry.outDir, dtsFileName);
1155
- await writeFile(dtsPath, transformed.declaration, "utf8");
1316
+ await writeFile(join(entry.outDir, dtsFileName), transformed.declaration, "utf8");
1156
1317
  }
1157
1318
  return entryDistPath;
1158
1319
  }
@@ -1166,9 +1327,9 @@ async function transformDir(ctx, entry) {
1166
1327
  }
1167
1328
  }
1168
1329
  })());
1169
- const writtenFiles = await Promise.all(promises$1);
1330
+ const writtenFiles = await Promise.all(promises);
1170
1331
  if (entry.copy) await copyFiles(ctx.pkgDir, fullOutDir, entry.copy);
1171
- consola.log(`\n${colors.magenta("[transform] ")}${colors.underline(`${fmtPath(entry.outDir)}/`)}\n${writtenFiles.map((f) => colors.dim(fmtPath(f))).join("\n\n")}`);
1332
+ logger.log(`\n${colors.cyan("Transform")} ${colors.green(`${fmtPath(entry.outDir)}/`)} ${colors.dim(`(${writtenFiles.length} files)`)}`);
1172
1333
  }
1173
1334
  /**
1174
1335
  * Transform a .ts module using oxc-transform.
@@ -1176,9 +1337,8 @@ async function transformDir(ctx, entry) {
1176
1337
  async function transformModule(entryPath, entry) {
1177
1338
  let sourceText = await readFile(entryPath, "utf8");
1178
1339
  const ext = extname(entryPath);
1179
- const lang = ext === ".tsx" || ext === ".jsx" ? "tsx" : "ts";
1180
1340
  const sourceOptions = {
1181
- lang,
1341
+ lang: ext === ".tsx" || ext === ".jsx" ? "tsx" : "ts",
1182
1342
  sourceType: "module"
1183
1343
  };
1184
1344
  const parsed = parseSync(entryPath, sourceText, { ...sourceOptions });
@@ -1254,90 +1414,6 @@ async function transformModule(entryPath, entry) {
1254
1414
  return transformed;
1255
1415
  }
1256
1416
 
1257
- //#endregion
1258
- //#region src/features/logger.ts
1259
- /**
1260
- * Logger instance with configurable log level
1261
- */
1262
- var Logger = class {
1263
- level = "info";
1264
- warningCount = 0;
1265
- errorCount = 0;
1266
- constructor(level = "info") {
1267
- this.level = level;
1268
- this.updateConsolaLevel();
1269
- }
1270
- setLevel(level) {
1271
- this.level = level;
1272
- this.updateConsolaLevel();
1273
- }
1274
- updateConsolaLevel() {
1275
- const levelMap = {
1276
- silent: 0,
1277
- error: 1,
1278
- warn: 2,
1279
- info: 3,
1280
- verbose: 4
1281
- };
1282
- consola.level = levelMap[this.level];
1283
- }
1284
- silent(message, ...args) {
1285
- console.log(message, ...args);
1286
- }
1287
- error(message, ...args) {
1288
- this.errorCount++;
1289
- consola.error(message, ...args);
1290
- }
1291
- warn(message, ...args) {
1292
- this.warningCount++;
1293
- consola.warn(message, ...args);
1294
- }
1295
- info(message, ...args) {
1296
- consola.info(message, ...args);
1297
- }
1298
- verbose(message, ...args) {
1299
- if (this.level === "verbose") consola.debug(message, ...args);
1300
- }
1301
- success(message, ...args) {
1302
- consola.success(message, ...args);
1303
- }
1304
- log(message, ...args) {
1305
- consola.log(message, ...args);
1306
- }
1307
- getWarningCount() {
1308
- return this.warningCount;
1309
- }
1310
- getErrorCount() {
1311
- return this.errorCount;
1312
- }
1313
- resetCounts() {
1314
- this.warningCount = 0;
1315
- this.errorCount = 0;
1316
- }
1317
- shouldFailOnWarnings(failOnWarn) {
1318
- return failOnWarn && this.warningCount > 0;
1319
- }
1320
- };
1321
- const logger = new Logger();
1322
- /**
1323
- * Configure global logger
1324
- */
1325
- function configureLogger(level) {
1326
- logger.setLevel(level);
1327
- }
1328
- /**
1329
- * Reset warning and error counts
1330
- */
1331
- function resetLogCounts() {
1332
- logger.resetCounts();
1333
- }
1334
- /**
1335
- * Check if build should fail due to warnings
1336
- */
1337
- function shouldFailOnWarnings(failOnWarn) {
1338
- return logger.shouldFailOnWarnings(failOnWarn);
1339
- }
1340
-
1341
1417
  //#endregion
1342
1418
  //#region src/features/on-success.ts
1343
1419
  const execAsync = promisify(exec);
@@ -1404,8 +1480,7 @@ async function loadViteConfig(cwd) {
1404
1480
  try {
1405
1481
  logger.verbose(`Loading Vite config from: ${configPath}`);
1406
1482
  const configModule = await import(configPath);
1407
- const viteConfig = configModule.default || configModule;
1408
- return convertViteConfig(viteConfig);
1483
+ return convertViteConfig(configModule.default || configModule);
1409
1484
  } catch (error) {
1410
1485
  logger.error(`Failed to load Vite config from ${configPath}:`, error);
1411
1486
  return {};
@@ -1468,7 +1543,7 @@ function convertFormats(formats) {
1468
1543
  */
1469
1544
  function convertTarget(target) {
1470
1545
  if (!target) return void 0;
1471
- const targetMap = {
1546
+ return {
1472
1547
  es2015: "es2015",
1473
1548
  es2016: "es2016",
1474
1549
  es2017: "es2017",
@@ -1478,8 +1553,7 @@ function convertTarget(target) {
1478
1553
  es2021: "es2021",
1479
1554
  es2022: "es2022",
1480
1555
  esnext: "esnext"
1481
- };
1482
- return targetMap[target] || void 0;
1556
+ }[target] || void 0;
1483
1557
  }
1484
1558
  /**
1485
1559
  * Convert Vite external config to robuild external
@@ -1490,15 +1564,12 @@ function convertExternal(external) {
1490
1564
 
1491
1565
  //#endregion
1492
1566
  //#region src/watch.ts
1493
- function normalizePath$1(path, resolveFrom) {
1494
- return typeof path === "string" && isAbsolute(path) ? path : path instanceof URL ? fileURLToPath(path) : resolve(resolveFrom || ".", path || ".");
1495
- }
1496
1567
  /**
1497
1568
  * Perform watch build using rolldown's built-in watch mode
1498
1569
  */
1499
1570
  async function performWatchBuild(config, ctx, startTime) {
1500
- const { performBuild: performBuild$1 } = await import("./build-S2eglIZn.mjs");
1501
- await performBuild$1(config, ctx, startTime);
1571
+ const { performBuild } = await import("./build-B-lzI2ff.mjs");
1572
+ await performBuild(config, ctx, startTime);
1502
1573
  const bundleEntries = (config.entries || []).filter((entry) => {
1503
1574
  if (typeof entry === "string") return !entry.endsWith("/");
1504
1575
  return entry.type === "bundle";
@@ -1517,45 +1588,22 @@ async function performWatchBuild(config, ctx, startTime) {
1517
1588
  * The watch mode then monitors for file changes and triggers rebuilds.
1518
1589
  */
1519
1590
  async function startRolldownWatch(config, ctx, bundleEntries) {
1520
- logger.info("🚧 Using rolldown built-in watch mode...");
1521
- const { RobuildPluginManager: RobuildPluginManager$1 } = await import("./plugin-manager-WN1-NA--.mjs");
1591
+ logger.info("Watching for changes...");
1592
+ const { RobuildPluginManager } = await import("./plugin-manager-pCQvlo7q.mjs");
1522
1593
  const watchConfigs = [];
1523
1594
  for (const rawEntry of bundleEntries) {
1524
- let entry;
1525
- if (typeof rawEntry === "string") {
1526
- const [input, outDir] = rawEntry.split(":");
1527
- entry = {
1528
- type: "bundle",
1529
- input,
1530
- outDir: outDir || "dist"
1531
- };
1532
- } else entry = rawEntry;
1533
- const entryInput = entry.input || entry.entry;
1595
+ const entry = typeof rawEntry === "string" ? parseEntryString(rawEntry) : rawEntry;
1596
+ const entryInput = getBundleEntryInput(entry);
1534
1597
  if (!entryInput) {
1535
1598
  logger.warn("Skipping entry without input:", entry);
1536
1599
  continue;
1537
1600
  }
1538
- let normalizedInput;
1539
- if (typeof entryInput === "object" && !Array.isArray(entryInput)) {
1540
- const normalizedObj = {};
1541
- for (const [key, value] of Object.entries(entryInput)) normalizedObj[key] = normalizePath$1(value, ctx.pkgDir);
1542
- normalizedInput = normalizedObj;
1543
- } else if (Array.isArray(entryInput)) normalizedInput = entryInput.map((i) => normalizePath$1(i, ctx.pkgDir));
1544
- else normalizedInput = normalizePath$1(entryInput, ctx.pkgDir);
1601
+ const normalizedInput = normalizeEntryInput(entryInput, ctx.pkgDir);
1545
1602
  const target = entry.target || "es2022";
1546
1603
  const platform = entry.platform || "node";
1547
1604
  const format = entry.format || "es";
1548
- const getExtension = (fmt) => {
1549
- switch (fmt) {
1550
- case "es": return ".mjs";
1551
- case "cjs": return ".cjs";
1552
- case "iife":
1553
- case "umd": return ".js";
1554
- default: return ".mjs";
1555
- }
1556
- };
1557
- const extension = getExtension(Array.isArray(format) ? format[0] : format);
1558
1605
  const rolldownFormat = Array.isArray(format) ? format[0] : format;
1606
+ const extension = getFormatExtension(rolldownFormat, platform);
1559
1607
  const formatMap = {
1560
1608
  esm: "es",
1561
1609
  cjs: "cjs",
@@ -1566,8 +1614,11 @@ async function startRolldownWatch(config, ctx, bundleEntries) {
1566
1614
  if (Array.isArray(normalizedInput)) rolldownInput = normalizedInput[0];
1567
1615
  else if (typeof normalizedInput === "object") rolldownInput = normalizedInput;
1568
1616
  else rolldownInput = normalizedInput;
1569
- const pluginManager = new RobuildPluginManager$1(config, entry, ctx.pkgDir);
1570
- const rolldownPlugins = [...pluginManager.getRolldownPlugins(), ...entry.rolldown?.plugins || []];
1617
+ const rolldownPlugins = [...new RobuildPluginManager(config, entry, ctx.pkgDir).getRolldownPlugins(), ...entry.rolldown?.plugins || []];
1618
+ const externalConfig = resolveExternalConfig(ctx, {
1619
+ external: entry.external,
1620
+ noExternal: entry.noExternal
1621
+ });
1571
1622
  const watchConfig = {
1572
1623
  input: rolldownInput,
1573
1624
  output: {
@@ -1577,6 +1628,7 @@ async function startRolldownWatch(config, ctx, bundleEntries) {
1577
1628
  sourcemap: entry.sourcemap
1578
1629
  },
1579
1630
  platform: platform === "node" ? "node" : "neutral",
1631
+ external: externalConfig,
1580
1632
  transform: { target },
1581
1633
  plugins: rolldownPlugins
1582
1634
  };
@@ -1586,24 +1638,20 @@ async function startRolldownWatch(config, ctx, bundleEntries) {
1586
1638
  watcher.on("event", (event) => {
1587
1639
  switch (event.code) {
1588
1640
  case "START":
1589
- logger.info("🔄 Rebuilding...");
1590
- break;
1591
- case "BUNDLE_START":
1592
- logger.info("📦 Bundling...");
1593
- break;
1594
- case "BUNDLE_END":
1595
- logger.success("✅ Bundle complete");
1641
+ logger.info("Rebuilding...");
1596
1642
  break;
1643
+ case "BUNDLE_START": break;
1644
+ case "BUNDLE_END": break;
1597
1645
  case "END":
1598
- logger.success("🎉 Watch rebuild complete");
1646
+ logger.success("Rebuilt");
1599
1647
  break;
1600
1648
  case "ERROR":
1601
- logger.error("Build error:", event.error);
1649
+ logger.error("Build error:", event.error);
1602
1650
  break;
1603
1651
  }
1604
1652
  });
1605
1653
  const cleanup = async () => {
1606
- consola.info("🛑 Stopping watch mode...");
1654
+ logger.info("Stopping watch mode...");
1607
1655
  await watcher.close();
1608
1656
  process.exit(0);
1609
1657
  };
@@ -1675,27 +1723,25 @@ async function build(config) {
1675
1723
  if (config.logLevel) configureLogger(config.logLevel);
1676
1724
  resetLogCounts();
1677
1725
  const pkgDir = normalizePath(config.cwd);
1678
- const pkg = await readJSON(join(pkgDir, "package.json")).catch(() => ({}));
1679
1726
  const ctx = {
1680
- pkg,
1727
+ pkg: await readJSON(join(pkgDir, "package.json")).catch(() => ({})),
1681
1728
  pkgDir
1682
1729
  };
1683
1730
  let finalConfig = config;
1684
1731
  if (config.fromVite) {
1685
1732
  logger.verbose("Loading configuration from Vite config file");
1686
- const viteConfig = await loadViteConfig(pkgDir);
1687
1733
  finalConfig = {
1688
- ...viteConfig,
1734
+ ...await loadViteConfig(pkgDir),
1689
1735
  ...config
1690
1736
  };
1691
1737
  }
1692
1738
  finalConfig = normalizeTsupConfig(finalConfig);
1693
1739
  if (finalConfig.watch?.enabled) {
1694
- logger.info(`👀 Starting watch mode for \`${ctx.pkg.name || "<no name>"}\` (\`${ctx.pkgDir}\`)`);
1740
+ logger.info(`Watching ${colors.cyan(ctx.pkg.name || "<unnamed>")}`);
1695
1741
  await performWatchBuild(finalConfig, ctx, startTime);
1696
1742
  return;
1697
1743
  }
1698
- logger.info(`📦 Building \`${ctx.pkg.name || "<no name>"}\` (\`${ctx.pkgDir}\`)`);
1744
+ logger.info(`Building ${colors.cyan(ctx.pkg.name || "<unnamed>")}`);
1699
1745
  await performBuild(finalConfig, ctx, startTime);
1700
1746
  }
1701
1747
  /**
@@ -1706,35 +1752,17 @@ async function performBuild(config, ctx, startTime) {
1706
1752
  const hooks = config.hooks || {};
1707
1753
  await hooks.start?.(ctx);
1708
1754
  const entries = (config.entries || []).map((rawEntry) => {
1709
- let entry;
1710
- if (typeof rawEntry === "string") {
1711
- const [input, outDir] = rawEntry.split(":");
1712
- entry = input.endsWith("/") ? {
1713
- type: "transform",
1714
- input,
1715
- outDir
1716
- } : {
1717
- type: "bundle",
1718
- input: input.split(","),
1719
- outDir
1720
- };
1721
- } else entry = rawEntry;
1755
+ let entry = typeof rawEntry === "string" ? parseEntryString(rawEntry) : rawEntry;
1722
1756
  if (entry.type === "bundle") entry = inheritConfig(entry, config);
1723
1757
  else if (entry.type === "transform") entry = inheritConfig(entry, config);
1724
- const hasInput = entry.type === "transform" ? !!entry.input : !!(entry.input || entry.entry);
1725
- if (!hasInput) throw new Error(`Build entry missing \`input\` or \`entry\`: ${JSON.stringify(entry, null, 2)}`);
1758
+ if (!hasValidInput(entry)) throw new Error(`Build entry missing \`input\` or \`entry\`: ${JSON.stringify(entry, null, 2)}`);
1726
1759
  entry = { ...entry };
1727
1760
  entry.outDir = normalizePath(entry.outDir || "dist", ctx.pkgDir);
1728
1761
  if (entry.type === "transform") {
1729
1762
  if (entry.input) entry.input = normalizePath(entry.input, ctx.pkgDir);
1730
1763
  } else {
1731
- const entryInput = entry.input || entry.entry;
1732
- if (entryInput) if (typeof entryInput === "object" && !Array.isArray(entryInput)) {
1733
- const normalizedInput = {};
1734
- for (const [key, value] of Object.entries(entryInput)) normalizedInput[key] = normalizePath(value, ctx.pkgDir);
1735
- entry.input = normalizedInput;
1736
- } else if (Array.isArray(entryInput)) entry.input = entryInput.map((p) => normalizePath(p, ctx.pkgDir));
1737
- else entry.input = normalizePath(entryInput, ctx.pkgDir);
1764
+ const entryInput = getBundleEntryInput(entry);
1765
+ if (entryInput) entry.input = normalizeEntryInput(entryInput, ctx.pkgDir);
1738
1766
  }
1739
1767
  return entry;
1740
1768
  });
@@ -1745,21 +1773,17 @@ async function performBuild(config, ctx, startTime) {
1745
1773
  await hooks.end?.(ctx);
1746
1774
  if (shouldFailOnWarnings(config.failOnWarn || false)) throw new Error("Build failed due to warnings");
1747
1775
  const dirSize = analyzeDir(outDirs);
1748
- logger.info(colors.dim(`\nΣ Total dist byte size: ${colors.underline(prettyBytes(dirSize.size))} (${colors.underline(dirSize.files)} files)`));
1776
+ logger.info(colors.dim(`\nTotal: ${colors.bold(prettyBytes(dirSize.size))} (${dirSize.files} files)`));
1749
1777
  const duration = Date.now() - start;
1750
- logger.success(`\n✅ robuild finished in ${duration}ms`);
1778
+ logger.success(`\nBuild succeeded in ${colors.bold(`${duration}ms`)}`);
1751
1779
  if (config.onSuccess) {
1752
1780
  const buildResult = createBuildResult([], startTime);
1753
1781
  await executeOnSuccess(config.onSuccess, buildResult, ctx.pkgDir);
1754
1782
  }
1755
1783
  }
1756
- function normalizePath(path, resolveFrom) {
1757
- return typeof path === "string" && isAbsolute(path) ? path : path instanceof URL ? fileURLToPath(path) : resolve(resolveFrom || ".", path || ".");
1758
- }
1759
1784
  async function readJSON(specifier) {
1760
- const module = await import(specifier, { with: { type: "json" } });
1761
- return module.default;
1785
+ return (await import(specifier, { with: { type: "json" } })).default;
1762
1786
  }
1763
1787
 
1764
1788
  //#endregion
1765
- export { SHEBANG_RE, build, hasShebang, makeExecutable, nodeProtocolPlugin, performBuild, shebangPlugin };
1789
+ export { makeExecutable as a, configureLogger as c, hasShebang as i, logger as l, performBuild as n, shebangPlugin as o, SHEBANG_RE as r, nodeProtocolPlugin as s, build as t };