robuild 0.0.13 → 0.0.15

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,14 +1,13 @@
1
1
  import { builtinModules } from "node:module";
2
2
  import { basename, dirname, extname, isAbsolute, join, relative, resolve } from "node:path";
3
3
  import { fileURLToPath, pathToFileURL } from "node:url";
4
- import { consola } from "consola";
5
4
  import { colors } from "consola/utils";
6
5
  import prettyBytes from "pretty-bytes";
7
6
  import { cp, mkdir, readFile, readdir, symlink, writeFile } from "node:fs/promises";
8
- import { defu } from "defu";
7
+ import { consola } from "consola";
9
8
  import { resolveModulePath } from "exsolve";
10
9
  import { parseSync } from "oxc-parser";
11
- import { rolldown } from "rolldown";
10
+ import { rolldown, watch } from "rolldown";
12
11
  import { dts } from "rolldown-plugin-dts";
13
12
  import { existsSync, promises, readdirSync, statSync } from "node:fs";
14
13
  import { glob } from "glob";
@@ -20,8 +19,6 @@ import { transform } from "oxc-transform";
20
19
  import { glob as glob$1 } from "tinyglobby";
21
20
  import { exec } from "node:child_process";
22
21
  import { promisify } from "node:util";
23
- import { watch } from "chokidar";
24
- import "minimatch";
25
22
 
26
23
  //#region src/features/advanced-build.ts
27
24
  /**
@@ -39,9 +36,10 @@ function createSkipNodeModulesPlugin(options) {
39
36
  };
40
37
  return {
41
38
  name: "skip-node-modules",
42
- resolveId: async (id) => {
39
+ resolveId: async (id, importer) => {
43
40
  if (shouldInline(id)) return null;
44
- if (id.includes("node_modules") || !id.startsWith(".") && !id.startsWith("/")) return {
41
+ if (!importer) return null;
42
+ if (id.includes("node_modules") || !id.startsWith(".") && !id.startsWith("/") && !id.startsWith("\\")) return {
45
43
  id,
46
44
  external: true
47
45
  };
@@ -53,7 +51,7 @@ function createSkipNodeModulesPlugin(options) {
53
51
  * Unbundle mode: preserve file structure without bundling
54
52
  */
55
53
  async function unbundleTransform(ctx, entry) {
56
- const inputDir = join(ctx.pkgDir, entry.input);
54
+ const inputDir = isAbsolute(entry.input) ? entry.input : join(ctx.pkgDir, entry.input);
57
55
  const outputDir = join(ctx.pkgDir, entry.outDir || "dist");
58
56
  await processDirectoryUnbundled(inputDir, outputDir, entry);
59
57
  }
@@ -101,7 +99,7 @@ async function processFileUnbundled(inputPath, outputPath, entry) {
101
99
  /**
102
100
  * Transform imports for unbundle mode
103
101
  */
104
- function transformImportsForUnbundle(content, filePath, entry) {
102
+ function transformImportsForUnbundle(content, _filePath, entry) {
105
103
  let transformedContent = content;
106
104
  transformedContent = transformedContent.replace(/from\s+['"]([^'"]+)['"]/g, (match, importPath) => {
107
105
  if (importPath.startsWith(".")) {
@@ -231,11 +229,11 @@ function createGlobImportPlugin(options = {}) {
231
229
  let transformedCode = code;
232
230
  while ((match = globImportRegex.exec(code)) !== null) {
233
231
  hasGlobImports = true;
234
- const [fullMatch, quote, pattern, optionsStr] = match;
232
+ const [fullMatch, , pattern, optionsStr] = match;
235
233
  let globOptions = {};
236
234
  if (optionsStr) try {
237
235
  globOptions = parseGlobOptions(optionsStr);
238
- } catch (error) {
236
+ } catch {
239
237
  console.warn("Failed to parse glob options:", optionsStr);
240
238
  }
241
239
  const isEager = globOptions.eager ?? eager;
@@ -261,7 +259,7 @@ async function generateGlobImport(pattern, importer, eager, asUrls, allowedPatte
261
259
  try {
262
260
  const absolutePattern = resolve(importerDir, pattern);
263
261
  files = await glob(absolutePattern, { ignore: ["**/node_modules/**", "**/.git/**"] });
264
- } catch (error) {
262
+ } catch {
265
263
  if (pattern.includes("*.js")) files = [resolve(importerDir, pattern.replace("*", "module1")), resolve(importerDir, pattern.replace("*", "module2"))];
266
264
  }
267
265
  if (eager) return generateEagerImport(files, importerDir, asUrls);
@@ -345,7 +343,7 @@ function addHashToFilename(filename, content, hashLength = 8) {
345
343
  * Check if filename already has hash
346
344
  */
347
345
  function hasHash(filename) {
348
- return /-[a-f0-9]{8}(\.|$)/.test(filename);
346
+ return /-[a-f0-9]{8}(?:\.|$)/.test(filename);
349
347
  }
350
348
 
351
349
  //#endregion
@@ -411,7 +409,12 @@ async function transformWithLoader(filePath, content, loader, options) {
411
409
  case "jsx":
412
410
  case "ts":
413
411
  case "tsx": return content;
414
- case "json": return `export default ${content}`;
412
+ case "json": try {
413
+ const parsed = JSON.parse(content);
414
+ return `export default ${JSON.stringify(parsed)}`;
415
+ } catch {
416
+ return `export default ${JSON.stringify(content)}`;
417
+ }
415
418
  case "css": return transformCssContent(content, options);
416
419
  case "text": return `export default ${JSON.stringify(content)}`;
417
420
  case "file": return transformFileContent(filePath, options);
@@ -447,7 +450,7 @@ function transformFileContent(filePath, options) {
447
450
  /**
448
451
  * Transform file content to data URL
449
452
  */
450
- function transformDataUrlContent(filePath, content, options) {
453
+ function transformDataUrlContent(filePath, content, _options) {
451
454
  const ext = extname(filePath).toLowerCase();
452
455
  const mimeType = getMimeType(ext);
453
456
  const base64 = Buffer.from(content).toString("base64");
@@ -457,7 +460,7 @@ function transformDataUrlContent(filePath, content, options) {
457
460
  /**
458
461
  * Transform binary file content
459
462
  */
460
- function transformBinaryContent(filePath, options) {
463
+ function transformBinaryContent(filePath, _options) {
461
464
  const fileName = filePath.split("/").pop() || "binary";
462
465
  return `export default ${JSON.stringify(fileName)}`;
463
466
  }
@@ -467,7 +470,7 @@ function transformBinaryContent(filePath, options) {
467
470
  function extractCssClassNames(content) {
468
471
  const classRegex = /\.([a-z_-][\w-]*)/gi;
469
472
  const matches = content.match(classRegex) || [];
470
- return [...new Set(matches.map((match) => match.slice(1)))];
473
+ return Array.from(new Set(matches.map((match) => match.slice(1))));
471
474
  }
472
475
  /**
473
476
  * Get MIME type for file extension
@@ -508,13 +511,15 @@ function createLoaderPlugin(loaders) {
508
511
  return {
509
512
  name: "loaders",
510
513
  load: async (id) => {
514
+ const ext = extname(id);
511
515
  const loader = getLoaderForFile(id, loaders);
512
516
  if (loader === "js" || loader === "jsx" || loader === "ts" || loader === "tsx") return null;
517
+ if (loader === "json" && !loaders?.[ext]) return null;
513
518
  try {
514
519
  const content = await readFile(id, "utf-8");
515
- const options = loaders?.[extname(id)]?.options;
520
+ const options = loaders?.[ext]?.options;
516
521
  return await transformWithLoader(id, content, loader, options);
517
- } catch (error) {
522
+ } catch {
518
523
  return null;
519
524
  }
520
525
  }
@@ -522,111 +527,156 @@ function createLoaderPlugin(loaders) {
522
527
  }
523
528
 
524
529
  //#endregion
525
- //#region src/features/plugins.ts
530
+ //#region src/features/plugin-manager.ts
526
531
  /**
527
- * Plugin manager for handling plugin lifecycle and execution
532
+ * Simplified plugin manager that leverages rolldown's plugin system
528
533
  */
529
- var PluginManager = class {
534
+ var RobuildPluginManager = class {
530
535
  plugins = [];
531
536
  context;
532
- constructor(config, entry) {
537
+ constructor(config, entry, pkgDir) {
533
538
  this.context = {
534
539
  config,
535
540
  entry,
536
- plugins: config.plugins || [],
537
- hooks: config.hooks || {}
541
+ pkgDir,
542
+ outDir: entry.outDir || "dist",
543
+ format: entry.format || "esm",
544
+ platform: entry.platform || "node",
545
+ target: entry.target || "es2022"
538
546
  };
539
- this.plugins = config.plugins || [];
547
+ this.plugins = this.normalizePlugins(config.plugins || []);
540
548
  }
541
549
  /**
542
- * Initialize all plugins
550
+ * Normalize plugin options to RobuildPlugin instances
543
551
  */
544
- async initialize() {
545
- for (const plugin of this.plugins) if (plugin.setup) {
546
- const pluginBuild = this.createPluginBuild();
547
- await plugin.setup(pluginBuild);
552
+ normalizePlugins(pluginOptions) {
553
+ return pluginOptions.map((pluginOption) => this.normalizePlugin(pluginOption));
554
+ }
555
+ /**
556
+ * Normalize a single plugin option
557
+ */
558
+ normalizePlugin(pluginOption) {
559
+ if (typeof pluginOption === "function") return this.normalizePlugin(pluginOption());
560
+ if (typeof pluginOption === "object" && pluginOption !== null) {
561
+ if (this.isRobuildPlugin(pluginOption)) return pluginOption;
562
+ if (this.isRolldownPlugin(pluginOption)) return this.adaptRolldownPlugin(pluginOption);
563
+ if (this.isVitePlugin(pluginOption)) return this.adaptVitePlugin(pluginOption);
564
+ if (this.isUnplugin(pluginOption)) return this.adaptUnplugin(pluginOption);
565
+ return this.adaptRolldownPlugin(pluginOption);
548
566
  }
567
+ throw new Error(`Invalid plugin option: ${typeof pluginOption}`);
568
+ }
569
+ /**
570
+ * Check if plugin is already a RobuildPlugin
571
+ */
572
+ isRobuildPlugin(plugin) {
573
+ return plugin.meta?.robuild === true || plugin.robuildSetup || plugin.robuildBuildStart || plugin.robuildBuildEnd;
549
574
  }
550
575
  /**
551
- * Execute a specific hook across all plugins
576
+ * Check if plugin is a rolldown/rollup plugin
552
577
  */
553
- async executeHook(hookName, ...args) {
554
- const results = [];
555
- for (const plugin of this.plugins) {
556
- const hook = plugin[hookName];
557
- if (typeof hook === "function") try {
558
- const result = await hook.apply(plugin, args);
559
- results.push(result);
560
- } catch (error) {
561
- console.error(`Plugin ${plugin.name} hook ${hookName} failed:`, error);
562
- throw error;
578
+ isRolldownPlugin(plugin) {
579
+ return plugin.name && (plugin.buildStart || plugin.buildEnd || plugin.resolveId || plugin.load || plugin.transform || plugin.generateBundle || plugin.writeBundle);
580
+ }
581
+ /**
582
+ * Check if plugin is a Vite plugin
583
+ */
584
+ isVitePlugin(plugin) {
585
+ return plugin.config || plugin.configResolved || plugin.configureServer || plugin.meta?.vite === true;
586
+ }
587
+ /**
588
+ * Check if plugin is an Unplugin
589
+ */
590
+ isUnplugin(plugin) {
591
+ return plugin.unplugin === true || plugin.meta?.unplugin === true;
592
+ }
593
+ /**
594
+ * Adapt rolldown plugin to RobuildPlugin
595
+ */
596
+ adaptRolldownPlugin(plugin) {
597
+ return {
598
+ ...plugin,
599
+ meta: {
600
+ ...plugin.meta,
601
+ framework: "rolldown",
602
+ robuild: true,
603
+ rollup: true
563
604
  }
564
- }
565
- return results;
605
+ };
566
606
  }
567
607
  /**
568
- * Create plugin build context for setup
608
+ * Adapt Vite plugin to RobuildPlugin
569
609
  */
570
- createPluginBuild() {
571
- const resolveCallbacks = [];
572
- const loadCallbacks = [];
573
- const transformCallbacks = [];
610
+ adaptVitePlugin(plugin) {
574
611
  return {
575
- onResolve: (options, callback) => {
576
- resolveCallbacks.push({
577
- ...options,
578
- callback
579
- });
580
- },
581
- onLoad: (options, callback) => {
582
- loadCallbacks.push({
583
- ...options,
584
- callback
585
- });
586
- },
587
- onTransform: (options, callback) => {
588
- transformCallbacks.push({
589
- ...options,
590
- callback
591
- });
592
- },
593
- resolve: async (path, options) => {
594
- for (const { filter, callback } of resolveCallbacks) if (filter.test(path)) {
595
- const result = await callback({
596
- path,
597
- ...options
598
- });
599
- if (result) return result;
600
- }
601
- return null;
602
- },
603
- getConfig: () => this.context.config
612
+ ...plugin,
613
+ meta: {
614
+ ...plugin.meta,
615
+ framework: "vite",
616
+ robuild: true,
617
+ vite: true
618
+ }
604
619
  };
605
620
  }
606
621
  /**
607
- * Get all plugins
622
+ * Adapt Unplugin to RobuildPlugin
608
623
  */
609
- getPlugins() {
610
- return this.plugins;
624
+ adaptUnplugin(plugin) {
625
+ return {
626
+ ...plugin,
627
+ meta: {
628
+ ...plugin.meta,
629
+ framework: "unplugin",
630
+ robuild: true,
631
+ unplugin: true,
632
+ rollup: true,
633
+ vite: true,
634
+ webpack: true,
635
+ esbuild: true
636
+ }
637
+ };
611
638
  }
612
639
  /**
613
- * Add a plugin
640
+ * Initialize robuild-specific plugin hooks
614
641
  */
615
- addPlugin(plugin) {
616
- this.plugins.push(plugin);
617
- this.context.plugins = this.plugins;
642
+ async initializeRobuildHooks() {
643
+ for (const plugin of this.plugins) if (plugin.robuildSetup) await plugin.robuildSetup(this.context);
618
644
  }
619
645
  /**
620
- * Remove a plugin by name
646
+ * Execute robuild buildStart hooks
621
647
  */
622
- removePlugin(name) {
623
- const index = this.plugins.findIndex((p) => p.name === name);
624
- if (index !== -1) {
625
- this.plugins.splice(index, 1);
626
- this.context.plugins = this.plugins;
627
- return true;
628
- }
629
- return false;
648
+ async executeRobuildBuildStart() {
649
+ for (const plugin of this.plugins) if (plugin.robuildBuildStart) await plugin.robuildBuildStart(this.context);
650
+ }
651
+ /**
652
+ * Execute robuild buildEnd hooks
653
+ */
654
+ async executeRobuildBuildEnd(result) {
655
+ for (const plugin of this.plugins) if (plugin.robuildBuildEnd) await plugin.robuildBuildEnd(this.context, result);
656
+ }
657
+ /**
658
+ * Get rolldown-compatible plugins for direct use
659
+ */
660
+ getRolldownPlugins() {
661
+ return this.plugins.map((plugin) => {
662
+ const { robuildSetup, robuildBuildStart, robuildBuildEnd,...rolldownPlugin } = plugin;
663
+ return rolldownPlugin;
664
+ });
665
+ }
666
+ /**
667
+ * Get all plugins
668
+ */
669
+ getPlugins() {
670
+ return this.plugins;
671
+ }
672
+ /**
673
+ * Update context (useful when build parameters change)
674
+ */
675
+ updateContext(updates) {
676
+ this.context = {
677
+ ...this.context,
678
+ ...updates
679
+ };
630
680
  }
631
681
  };
632
682
 
@@ -742,7 +792,7 @@ function createShimsPlugin(config = true) {
742
792
  return {
743
793
  name: "shims",
744
794
  transform: async (code, id) => {
745
- if (!/\.(js|mjs|cjs|ts|mts|cts|jsx|tsx)$/.test(id)) return null;
795
+ if (!/\.(?:js|mjs|cjs|ts|mts|cts|jsx|tsx)$/.test(id)) return null;
746
796
  const needs = detectShimNeeds(code);
747
797
  if (!needs.needsDirname && !needs.needsRequire && !needs.needsExports && !needs.needsEnv) return null;
748
798
  const transformedCode = transformWithShims(code, shimsConfig);
@@ -751,88 +801,6 @@ function createShimsPlugin(config = true) {
751
801
  };
752
802
  }
753
803
 
754
- //#endregion
755
- //#region src/utils.ts
756
- function fmtPath(path) {
757
- return resolve(path).replace(process.cwd(), ".");
758
- }
759
- function analyzeDir(dir) {
760
- if (Array.isArray(dir)) {
761
- let totalSize$1 = 0;
762
- let totalFiles = 0;
763
- for (const d of dir) {
764
- const { size, files: files$1 } = analyzeDir(d);
765
- totalSize$1 += size;
766
- totalFiles += files$1;
767
- }
768
- return {
769
- size: totalSize$1,
770
- files: totalFiles
771
- };
772
- }
773
- let totalSize = 0;
774
- const files = readdirSync(dir, {
775
- withFileTypes: true,
776
- recursive: true
777
- });
778
- for (const file of files) {
779
- const fullPath = join(file.parentPath, file.name);
780
- if (file.isFile()) {
781
- const { size } = statSync(fullPath);
782
- totalSize += size;
783
- }
784
- }
785
- return {
786
- size: totalSize,
787
- files: files.length
788
- };
789
- }
790
- async function distSize(dir, entry) {
791
- const build$1 = await rolldown({
792
- input: join(dir, entry),
793
- plugins: [],
794
- platform: "neutral",
795
- external: (id) => id[0] !== "." && !id.startsWith(dir)
796
- });
797
- const { output } = await build$1.generate({ inlineDynamicImports: true });
798
- const code = output[0].code;
799
- const { code: minified } = await minify(entry, code);
800
- return {
801
- size: Buffer.byteLength(code),
802
- minSize: Buffer.byteLength(minified),
803
- minGzipSize: gzipSync(minified).length
804
- };
805
- }
806
- async function sideEffectSize(dir, entry) {
807
- const virtualEntry = {
808
- name: "virtual-entry",
809
- async resolveId(id, importer, opts) {
810
- if (id === "#entry") return { id };
811
- const resolved = await this.resolve(id, importer, opts);
812
- if (!resolved) return null;
813
- resolved.moduleSideEffects = null;
814
- return resolved;
815
- },
816
- load(id) {
817
- if (id === "#entry") return `import * as _lib from "${join(dir, entry)}";`;
818
- }
819
- };
820
- const build$1 = await rolldown({
821
- input: "#entry",
822
- platform: "neutral",
823
- external: (id) => id[0] !== "." && !id.startsWith(dir),
824
- plugins: [virtualEntry]
825
- });
826
- const { output } = await build$1.generate({ inlineDynamicImports: true });
827
- if (process.env.INSPECT_BUILD) {
828
- console.log("---------[side effects]---------");
829
- console.log(entry);
830
- console.log(output[0].code);
831
- console.log("-------------------------------");
832
- }
833
- return Buffer.byteLength(output[0].code.trim());
834
- }
835
-
836
804
  //#endregion
837
805
  //#region src/features/node-protocol.ts
838
806
  /**
@@ -925,7 +893,7 @@ function transformNodeProtocol(code, nodeProtocol) {
925
893
  }
926
894
 
927
895
  //#endregion
928
- //#region src/builders/plugins/node-protocol.ts
896
+ //#region src/plugins/node-protocol.ts
929
897
  /**
930
898
  * Rolldown plugin for Node.js protocol handling
931
899
  */
@@ -943,7 +911,7 @@ function nodeProtocolPlugin(nodeProtocol) {
943
911
  }
944
912
 
945
913
  //#endregion
946
- //#region src/builders/plugins/shebang.ts
914
+ //#region src/plugins/shebang.ts
947
915
  const SHEBANG_RE = /^#![^\n]*/;
948
916
  function shebangPlugin() {
949
917
  return {
@@ -966,6 +934,88 @@ async function makeExecutable(filePath) {
966
934
  await promises.chmod(filePath, 493).catch(() => {});
967
935
  }
968
936
 
937
+ //#endregion
938
+ //#region src/utils.ts
939
+ function fmtPath(path) {
940
+ return resolve(path).replace(process.cwd(), ".");
941
+ }
942
+ function analyzeDir(dir) {
943
+ if (Array.isArray(dir)) {
944
+ let totalSize$1 = 0;
945
+ let totalFiles = 0;
946
+ for (const d of dir) {
947
+ const { size, files: files$1 } = analyzeDir(d);
948
+ totalSize$1 += size;
949
+ totalFiles += files$1;
950
+ }
951
+ return {
952
+ size: totalSize$1,
953
+ files: totalFiles
954
+ };
955
+ }
956
+ let totalSize = 0;
957
+ const files = readdirSync(dir, {
958
+ withFileTypes: true,
959
+ recursive: true
960
+ });
961
+ for (const file of files) {
962
+ const fullPath = join(file.parentPath, file.name);
963
+ if (file.isFile()) {
964
+ const { size } = statSync(fullPath);
965
+ totalSize += size;
966
+ }
967
+ }
968
+ return {
969
+ size: totalSize,
970
+ files: files.length
971
+ };
972
+ }
973
+ async function distSize(dir, entry) {
974
+ const build$1 = await rolldown({
975
+ input: join(dir, entry),
976
+ plugins: [],
977
+ platform: "neutral",
978
+ external: (id) => id[0] !== "." && !id.startsWith(dir)
979
+ });
980
+ const { output } = await build$1.generate({ inlineDynamicImports: true });
981
+ const code = output[0].code;
982
+ const { code: minified } = minify(entry, code);
983
+ return {
984
+ size: Buffer.byteLength(code),
985
+ minSize: Buffer.byteLength(minified),
986
+ minGzipSize: gzipSync(minified).length
987
+ };
988
+ }
989
+ async function sideEffectSize(dir, entry) {
990
+ const virtualEntry = {
991
+ name: "virtual-entry",
992
+ async resolveId(id, importer, opts) {
993
+ if (id === "#entry") return { id };
994
+ const resolved = await this.resolve(id, importer, opts);
995
+ if (!resolved) return null;
996
+ resolved.moduleSideEffects = null;
997
+ return resolved;
998
+ },
999
+ load(id) {
1000
+ if (id === "#entry") return `import * as _lib from "${join(dir, entry)}";`;
1001
+ }
1002
+ };
1003
+ const build$1 = await rolldown({
1004
+ input: "#entry",
1005
+ platform: "neutral",
1006
+ external: (id) => id[0] !== "." && !id.startsWith(dir),
1007
+ plugins: [virtualEntry]
1008
+ });
1009
+ const { output } = await build$1.generate({ inlineDynamicImports: true });
1010
+ if (process.env.INSPECT_BUILD) {
1011
+ console.log("---------[side effects]---------");
1012
+ console.log(entry);
1013
+ console.log(output[0].code);
1014
+ console.log("-------------------------------");
1015
+ }
1016
+ return Buffer.byteLength(output[0].code.trim());
1017
+ }
1018
+
969
1019
  //#endregion
970
1020
  //#region src/builders/bundle.ts
971
1021
  /**
@@ -1020,22 +1070,29 @@ async function cleanOutputDir$1(projectRoot, outDir, cleanPaths) {
1020
1070
  }
1021
1071
  }
1022
1072
  async function rolldownBuild(ctx, entry, hooks, config) {
1023
- const inputs = normalizeBundleInputs(entry.input, ctx);
1024
- const pluginManager = new PluginManager(config || {}, entry);
1025
- await pluginManager.initialize();
1073
+ const entryInput = entry.input || entry.entry;
1074
+ if (!entryInput) throw new Error("Entry input is required");
1075
+ const inputs = normalizeBundleInputs(entryInput, ctx);
1076
+ const pluginManager = new RobuildPluginManager(config || {}, entry, ctx.pkgDir);
1077
+ await pluginManager.initializeRobuildHooks();
1026
1078
  const formats = Array.isArray(entry.format) ? entry.format : [entry.format || "esm"];
1027
1079
  const platform = entry.platform || "node";
1028
1080
  const target = entry.target || "es2022";
1029
1081
  const outDir = entry.outDir || "dist";
1030
1082
  const fullOutDir = resolve(ctx.pkgDir, outDir);
1031
1083
  const isMultiFormat = formats.length > 1;
1032
- await pluginManager.executeHook("buildStart", {
1033
- inputs,
1034
- formats,
1084
+ pluginManager.updateContext({
1085
+ format: formats,
1035
1086
  platform,
1036
- target
1087
+ target,
1088
+ outDir: fullOutDir
1037
1089
  });
1090
+ await pluginManager.executeRobuildBuildStart();
1038
1091
  await cleanOutputDir$1(ctx.pkgDir, fullOutDir, entry.clean ?? true);
1092
+ if (entry.dtsOnly) {
1093
+ consola.info("Running in dtsOnly mode - only generating declaration files");
1094
+ entry.dts = entry.dts === false ? true : entry.dts || true;
1095
+ }
1039
1096
  if (entry.stub) {
1040
1097
  for (const [distName, srcPath] of Object.entries(inputs)) {
1041
1098
  const distPath = join(ctx.pkgDir, "dist", `${distName}.mjs`);
@@ -1070,7 +1127,7 @@ async function rolldownBuild(ctx, entry, hooks, config) {
1070
1127
  externalDeps = externalDeps.filter((dep) => {
1071
1128
  if (typeof dep === "string") return !excludedNames.has(dep);
1072
1129
  if (dep instanceof RegExp) {
1073
- for (const name of excludedNames) if (dep.source.startsWith(`^${escapeRegExp(name)}/`)) return false;
1130
+ for (const name of Array.from(excludedNames)) if (dep.source.startsWith(`^${escapeRegExp(name)}/`)) return false;
1074
1131
  return true;
1075
1132
  }
1076
1133
  return true;
@@ -1099,13 +1156,12 @@ async function rolldownBuild(ctx, entry, hooks, config) {
1099
1156
  const defineOptions = {};
1100
1157
  if (entry.env) for (const [key, value] of Object.entries(entry.env)) defineOptions[`process.env.${key}`] = JSON.stringify(value);
1101
1158
  if (entry.define) for (const [key, value] of Object.entries(entry.define)) defineOptions[key] = value;
1102
- const rolldownPlugins = [];
1103
- rolldownPlugins.push(shebangPlugin(), nodeProtocolPlugin(entry.nodeProtocol || false));
1159
+ const rolldownPlugins = [shebangPlugin(), nodeProtocolPlugin(entry.nodeProtocol || false)];
1104
1160
  if (config?.globImport?.enabled) {
1105
1161
  const globPlugin = createGlobImportPlugin(config.globImport);
1106
1162
  if (globPlugin.transform) rolldownPlugins.push({
1107
1163
  name: "glob-import",
1108
- transform: async (code, id) => {
1164
+ transform: async (code, id, _meta) => {
1109
1165
  const result = await globPlugin.transform(code, id);
1110
1166
  return result ? { code: result } : null;
1111
1167
  }
@@ -1122,7 +1178,7 @@ async function rolldownBuild(ctx, entry, hooks, config) {
1122
1178
  const shimsPlugin = createShimsPlugin(entry.shims);
1123
1179
  if (shimsPlugin.transform) rolldownPlugins.push({
1124
1180
  name: "shims",
1125
- transform: async (code, id) => {
1181
+ transform: async (code, id, _meta) => {
1126
1182
  const result = await shimsPlugin.transform(code, id);
1127
1183
  return result ? { code: result } : null;
1128
1184
  }
@@ -1135,18 +1191,8 @@ async function rolldownBuild(ctx, entry, hooks, config) {
1135
1191
  resolveId: skipPlugin.resolveId
1136
1192
  });
1137
1193
  }
1138
- if (config?.plugins) {
1139
- for (const plugin of pluginManager.getPlugins()) if (plugin.transform || plugin.resolveId || plugin.load) rolldownPlugins.push({
1140
- name: plugin.name,
1141
- resolveId: plugin.resolveId,
1142
- load: plugin.load,
1143
- transform: plugin.transform ? async (code, id) => {
1144
- const result = await plugin.transform(code, id);
1145
- return result ? typeof result === "string" ? { code: result } : result : null;
1146
- } : void 0
1147
- });
1148
- }
1149
- const baseRolldownConfig = defu(entry.rolldown, {
1194
+ rolldownPlugins.push(...pluginManager.getRolldownPlugins());
1195
+ const robuildGeneratedConfig = {
1150
1196
  cwd: ctx.pkgDir,
1151
1197
  input: inputs,
1152
1198
  plugins: rolldownPlugins,
@@ -1155,7 +1201,15 @@ async function rolldownBuild(ctx, entry, hooks, config) {
1155
1201
  define: defineOptions,
1156
1202
  resolve: { alias: entry.alias || {} },
1157
1203
  transform: { target }
1158
- });
1204
+ };
1205
+ if (entry.treeshake !== void 0) if (typeof entry.treeshake === "boolean") robuildGeneratedConfig.treeshake = entry.treeshake;
1206
+ else robuildGeneratedConfig.treeshake = entry.treeshake;
1207
+ const { output: userOutputConfig, plugins: userPlugins,...userRolldownConfig } = entry.rolldown || {};
1208
+ const baseRolldownConfig = {
1209
+ ...robuildGeneratedConfig,
1210
+ ...userRolldownConfig,
1211
+ plugins: [...rolldownPlugins, ...Array.isArray(userPlugins) ? userPlugins : userPlugins ? [userPlugins] : []]
1212
+ };
1159
1213
  await hooks.rolldownConfig?.(baseRolldownConfig, ctx);
1160
1214
  const allOutputEntries = [];
1161
1215
  const filePathMap = /* @__PURE__ */ new Map();
@@ -1163,7 +1217,10 @@ async function rolldownBuild(ctx, entry, hooks, config) {
1163
1217
  const rolldownFormat = formatToRolldownFormat(format);
1164
1218
  const extension = getFormatExtension(format, platform, entry.fixedExtension);
1165
1219
  const formatConfig = { ...baseRolldownConfig };
1166
- if (entry.dts !== false && format === "esm") formatConfig.plugins = [...formatConfig.plugins, ...dts({ ...entry.dts })];
1220
+ if (entry.dts !== false && format === "esm") {
1221
+ const dtsPlugins = dts({ ...entry.dts });
1222
+ formatConfig.plugins = [...Array.isArray(formatConfig.plugins) ? formatConfig.plugins : [formatConfig.plugins], ...Array.isArray(dtsPlugins) ? dtsPlugins : [dtsPlugins]];
1223
+ }
1167
1224
  const res = await rolldown(formatConfig);
1168
1225
  let formatOutDir = fullOutDir;
1169
1226
  let entryFileName = `[name]${extension}`;
@@ -1179,7 +1236,7 @@ async function rolldownBuild(ctx, entry, hooks, config) {
1179
1236
  formatOutDir = join(fullOutDir, "browser");
1180
1237
  entryFileName = `[name].js`;
1181
1238
  }
1182
- const outConfig = {
1239
+ const robuildOutputConfig = {
1183
1240
  dir: formatOutDir,
1184
1241
  format: rolldownFormat,
1185
1242
  entryFileNames: entryFileName,
@@ -1189,7 +1246,11 @@ async function rolldownBuild(ctx, entry, hooks, config) {
1189
1246
  banner: resolveChunkAddon(entry.banner, format),
1190
1247
  footer: resolveChunkAddon(entry.footer, format)
1191
1248
  };
1192
- if (entry.sourcemap !== void 0) outConfig.sourcemap = entry.sourcemap;
1249
+ if (entry.sourcemap !== void 0) robuildOutputConfig.sourcemap = entry.sourcemap;
1250
+ const outConfig = {
1251
+ ...robuildOutputConfig,
1252
+ ...userOutputConfig || {}
1253
+ };
1193
1254
  await hooks.rolldownOutput?.(outConfig, res, ctx);
1194
1255
  const { output } = await res.write(outConfig);
1195
1256
  await res.close();
@@ -1242,7 +1303,7 @@ async function rolldownBuild(ctx, entry, hooks, config) {
1242
1303
  }
1243
1304
  }
1244
1305
  if (entry.copy) await copyFiles(ctx.pkgDir, fullOutDir, entry.copy);
1245
- await pluginManager.executeHook("buildEnd", { allOutputEntries });
1306
+ await pluginManager.executeRobuildBuildEnd({ allOutputEntries });
1246
1307
  consola.log(`\n${allOutputEntries.map((o) => [
1247
1308
  `${colors.magenta(`[bundle] `)}${colors.underline(fmtPath(filePathMap.get(o.name) || join(fullOutDir, o.name)))}`,
1248
1309
  colors.dim(`${colors.bold("Size:")} ${prettyBytes(o.size)}, ${colors.bold(prettyBytes(o.minSize))} minified, ${prettyBytes(o.minGzipSize)} min+gzipped (Side effects: ${prettyBytes(o.sideEffectSize)})`),
@@ -1252,6 +1313,22 @@ async function rolldownBuild(ctx, entry, hooks, config) {
1252
1313
  }
1253
1314
  function normalizeBundleInputs(input, ctx) {
1254
1315
  const inputs = {};
1316
+ if (typeof input === "object" && !Array.isArray(input)) {
1317
+ for (const [name, src] of Object.entries(input)) {
1318
+ const resolvedSrc = resolveModulePath(src, {
1319
+ from: ctx.pkgDir,
1320
+ extensions: [
1321
+ ".ts",
1322
+ ".js",
1323
+ ".mjs",
1324
+ ".cjs",
1325
+ ".json"
1326
+ ]
1327
+ });
1328
+ inputs[name] = resolvedSrc;
1329
+ }
1330
+ return inputs;
1331
+ }
1255
1332
  for (let src of Array.isArray(input) ? input : [input]) {
1256
1333
  src = resolveModulePath(src, {
1257
1334
  from: ctx.pkgDir,
@@ -1499,176 +1576,6 @@ async function transformModule(entryPath, entry) {
1499
1576
  return transformed;
1500
1577
  }
1501
1578
 
1502
- //#endregion
1503
- //#region src/features/exports.ts
1504
- /**
1505
- * Generate package.json exports field based on build configuration
1506
- */
1507
- async function generatePackageExports(packageRoot, buildConfig, exportsConfig = { enabled: true }) {
1508
- if (!exportsConfig.enabled) return {};
1509
- const exports = {};
1510
- const baseDir = exportsConfig.baseDir || "dist";
1511
- if (exportsConfig.custom) Object.assign(exports, exportsConfig.custom);
1512
- if (buildConfig.entries) for (const entry of buildConfig.entries) {
1513
- const exportEntries = await generateExportFromEntry(packageRoot, entry, baseDir, exportsConfig.includeTypes);
1514
- for (const exportEntry of exportEntries) exports[exportEntry.key] = createExportValue(exportEntry);
1515
- }
1516
- if (!exports["."] && !exports["./index"]) {
1517
- const mainExport = await findMainExport(packageRoot, baseDir);
1518
- if (mainExport) exports["."] = mainExport;
1519
- }
1520
- return exports;
1521
- }
1522
- /**
1523
- * Generate export entries from a build entry
1524
- */
1525
- async function generateExportFromEntry(packageRoot, entry, baseDir, includeTypes) {
1526
- const exports = [];
1527
- if (entry.type === "bundle") {
1528
- const exportKey = getExportKey(entry.input);
1529
- const exportEntry = { key: exportKey };
1530
- if (Array.isArray(entry.format)) for (const format of entry.format) {
1531
- const outputPath = getOutputPath(entry, format, baseDir);
1532
- assignFormatToExport(exportEntry, format, outputPath);
1533
- }
1534
- else if (entry.format) {
1535
- const outputPath = getOutputPath(entry, entry.format, baseDir);
1536
- assignFormatToExport(exportEntry, entry.format, outputPath);
1537
- }
1538
- if (includeTypes && entry.dts) {
1539
- const typesPath = getTypesPath(entry, baseDir);
1540
- exportEntry.types = typesPath;
1541
- }
1542
- exports.push(exportEntry);
1543
- } else if (entry.type === "transform") {
1544
- const transformExports = await discoverTransformExports(packageRoot, entry, baseDir, includeTypes);
1545
- exports.push(...transformExports);
1546
- }
1547
- return exports;
1548
- }
1549
- /**
1550
- * Get export key from input path
1551
- */
1552
- function getExportKey(input) {
1553
- if (input === "index.ts" || input === "src/index.ts") return ".";
1554
- let key = input.replace(/\.(ts|js|tsx|jsx)$/, "");
1555
- key = key.replace(/^src\//, "");
1556
- return key === "index" ? "." : `./${key}`;
1557
- }
1558
- /**
1559
- * Get output path for a specific format
1560
- */
1561
- function getOutputPath(entry, format, baseDir) {
1562
- const extension = getExtensionForFormat(format);
1563
- const basename$1 = getBasename(entry.input);
1564
- if (format === "cjs") return `./${baseDir}/cjs/${basename$1}${extension}`;
1565
- else if (format === "esm") return `./${baseDir}/${basename$1}${extension}`;
1566
- else return `./${baseDir}/${format}/${basename$1}${extension}`;
1567
- }
1568
- /**
1569
- * Get file extension for format
1570
- */
1571
- function getExtensionForFormat(format) {
1572
- switch (format) {
1573
- case "esm": return ".mjs";
1574
- case "cjs": return ".cjs";
1575
- case "iife":
1576
- case "umd": return ".js";
1577
- default: return ".js";
1578
- }
1579
- }
1580
- /**
1581
- * Get basename from input path
1582
- */
1583
- function getBasename(input) {
1584
- return input.replace(/\.(ts|js|tsx|jsx)$/, "").replace(/^src\//, "");
1585
- }
1586
- /**
1587
- * Get types path for entry
1588
- */
1589
- function getTypesPath(entry, baseDir) {
1590
- const basename$1 = getBasename(entry.input);
1591
- return `./${baseDir}/${basename$1}.d.ts`;
1592
- }
1593
- /**
1594
- * Assign format-specific path to export entry
1595
- */
1596
- function assignFormatToExport(exportEntry, format, path) {
1597
- switch (format) {
1598
- case "esm":
1599
- exportEntry.import = path;
1600
- break;
1601
- case "cjs":
1602
- exportEntry.require = path;
1603
- break;
1604
- default:
1605
- exportEntry.import = path;
1606
- break;
1607
- }
1608
- }
1609
- /**
1610
- * Create export value from export entry
1611
- */
1612
- function createExportValue(entry) {
1613
- const value = {};
1614
- if (entry.types) value.types = entry.types;
1615
- if (entry.import) value.import = entry.import;
1616
- if (entry.require) value.require = entry.require;
1617
- const keys = Object.keys(value);
1618
- if (keys.length === 1 && !entry.types) return value[keys[0]];
1619
- return value;
1620
- }
1621
- /**
1622
- * Discover exports from transform entries
1623
- */
1624
- async function discoverTransformExports(packageRoot, entry, baseDir, includeTypes) {
1625
- const exports = [];
1626
- const exportKey = getExportKey(entry.input);
1627
- const exportEntry = {
1628
- key: exportKey,
1629
- import: `./${baseDir}/${getBasename(entry.input)}.mjs`
1630
- };
1631
- if (includeTypes && entry.dts) exportEntry.types = getTypesPath(entry, baseDir);
1632
- exports.push(exportEntry);
1633
- return exports;
1634
- }
1635
- /**
1636
- * Find main export file
1637
- */
1638
- async function findMainExport(packageRoot, baseDir) {
1639
- const possibleMains = [
1640
- "index.mjs",
1641
- "index.js",
1642
- "index.cjs"
1643
- ];
1644
- for (const main of possibleMains) try {
1645
- join(packageRoot, baseDir, main);
1646
- const value = {};
1647
- if (main.endsWith(".mjs")) value.import = `./${baseDir}/${main}`;
1648
- else if (main.endsWith(".cjs")) value.require = `./${baseDir}/${main}`;
1649
- else value.import = `./${baseDir}/${main}`;
1650
- return value;
1651
- } catch {
1652
- continue;
1653
- }
1654
- return null;
1655
- }
1656
- /**
1657
- * Update package.json with generated exports
1658
- */
1659
- async function updatePackageJsonExports(packageRoot, exports) {
1660
- const packageJsonPath = join(packageRoot, "package.json");
1661
- try {
1662
- const content = await readFile(packageJsonPath, "utf-8");
1663
- const packageJson = JSON.parse(content);
1664
- packageJson.exports = exports;
1665
- const updatedContent = `${JSON.stringify(packageJson, null, 2)}\n`;
1666
- await writeFile(packageJsonPath, updatedContent, "utf-8");
1667
- } catch (error) {
1668
- throw new Error(`Failed to update package.json: ${error}`);
1669
- }
1670
- }
1671
-
1672
1579
  //#endregion
1673
1580
  //#region src/features/logger.ts
1674
1581
  /**
@@ -1904,267 +1811,120 @@ function convertExternal(external) {
1904
1811
  }
1905
1812
 
1906
1813
  //#endregion
1907
- //#region src/features/workspace.ts
1908
- /**
1909
- * Discover workspace packages based on patterns
1910
- */
1911
- async function discoverWorkspacePackages(workspaceRoot, patterns = ["packages/*", "apps/*"]) {
1912
- const packages = [];
1913
- for (const pattern of patterns) {
1914
- const packagePaths = await glob(pattern, {
1915
- cwd: workspaceRoot,
1916
- onlyDirectories: true
1917
- });
1918
- for (const packagePath of packagePaths) {
1919
- const fullPath = resolve(workspaceRoot, packagePath);
1920
- const packageJsonPath = join(fullPath, "package.json");
1921
- try {
1922
- const packageJsonContent = await readFile(packageJsonPath, "utf-8");
1923
- const packageJson = JSON.parse(packageJsonContent);
1924
- packages.push({
1925
- name: packageJson.name || packagePath,
1926
- path: fullPath,
1927
- packageJson
1928
- });
1929
- } catch {
1930
- continue;
1931
- }
1932
- }
1933
- }
1934
- return packages;
1814
+ //#region src/watch.ts
1815
+ function normalizePath$1(path, resolveFrom) {
1816
+ return typeof path === "string" && isAbsolute(path) ? path : path instanceof URL ? fileURLToPath(path) : resolve(resolveFrom || ".", path || ".");
1935
1817
  }
1936
1818
  /**
1937
- * Filter workspace packages based on filter patterns
1819
+ * Perform watch build using rolldown's built-in watch mode
1938
1820
  */
1939
- function filterWorkspacePackages(packages, filter, exclude) {
1940
- let filtered = packages;
1941
- if (filter) {
1942
- const filters = Array.isArray(filter) ? filter : [filter];
1943
- filtered = filtered.filter((pkg) => filters.some((f) => matchesPattern(pkg.name, f) || matchesPattern(pkg.path, f)));
1944
- }
1945
- if (exclude) {
1946
- const excludes = Array.isArray(exclude) ? exclude : [exclude];
1947
- filtered = filtered.filter((pkg) => !excludes.some((e) => matchesPattern(pkg.name, e) || matchesPattern(pkg.path, e)));
1821
+ async function performWatchBuild(config, ctx, startTime) {
1822
+ const { performBuild: performBuild$1 } = await import("./build-yfn0_Gzc.mjs");
1823
+ await performBuild$1(config, ctx, startTime);
1824
+ const bundleEntries = (config.entries || []).filter((entry) => {
1825
+ if (typeof entry === "string") return !entry.endsWith("/");
1826
+ return entry.type === "bundle";
1827
+ });
1828
+ if (bundleEntries.length > 0) await startRolldownWatch(ctx, bundleEntries);
1829
+ else {
1830
+ logger.warn("Transform-only watch mode not yet implemented with rolldown");
1831
+ return new Promise(() => {});
1948
1832
  }
1949
- return filtered;
1950
- }
1951
- /**
1952
- * Check if a string matches a pattern (supports wildcards)
1953
- */
1954
- function matchesPattern(str, pattern) {
1955
- const regexPattern = pattern.replace(/\*/g, ".*").replace(/\?/g, ".").replace(/\[([^\]]+)\]/g, "[$1]");
1956
- const regex = /* @__PURE__ */ new RegExp(`^${regexPattern}$`);
1957
- return regex.test(str);
1958
1833
  }
1959
1834
  /**
1960
- * Load workspace configuration from package.json or workspace config file
1835
+ * Start rolldown watch mode for bundle entries
1961
1836
  */
1962
- async function loadWorkspaceConfig(workspaceRoot) {
1963
- try {
1964
- const packageJsonPath = join(workspaceRoot, "package.json");
1965
- const packageJsonContent = await readFile(packageJsonPath, "utf-8");
1966
- const packageJson = JSON.parse(packageJsonContent);
1967
- if (packageJson.workspaces) {
1968
- const workspaces = Array.isArray(packageJson.workspaces) ? packageJson.workspaces : packageJson.workspaces.packages || [];
1969
- return { packages: workspaces };
1970
- }
1971
- try {
1972
- const { load } = await import("js-yaml");
1973
- const pnpmWorkspacePath = join(workspaceRoot, "pnpm-workspace.yaml");
1974
- const pnpmWorkspaceContent = await readFile(pnpmWorkspacePath, "utf-8");
1975
- const pnpmWorkspace = load(pnpmWorkspaceContent);
1976
- if (pnpmWorkspace?.packages) return { packages: pnpmWorkspace.packages };
1977
- } catch {}
1978
- try {
1979
- const lernaJsonPath = join(workspaceRoot, "lerna.json");
1980
- const lernaJsonContent = await readFile(lernaJsonPath, "utf-8");
1981
- const lernaJson = JSON.parse(lernaJsonContent);
1982
- if (lernaJson.packages) return { packages: lernaJson.packages };
1983
- } catch {}
1984
- return null;
1985
- } catch {
1986
- return null;
1837
+ async function startRolldownWatch(ctx, bundleEntries) {
1838
+ logger.info("🚧 Using rolldown built-in watch mode...");
1839
+ const watchConfigs = [];
1840
+ for (const rawEntry of bundleEntries) {
1841
+ let entry;
1842
+ if (typeof rawEntry === "string") {
1843
+ const [input, outDir] = rawEntry.split(":");
1844
+ entry = {
1845
+ type: "bundle",
1846
+ input,
1847
+ outDir: outDir || "dist"
1848
+ };
1849
+ } else entry = rawEntry;
1850
+ entry.input = Array.isArray(entry.input) ? entry.input.map((i) => normalizePath$1(i, ctx.pkgDir)) : normalizePath$1(entry.input, ctx.pkgDir);
1851
+ const watchConfig = {
1852
+ input: Array.isArray(entry.input) ? entry.input[0] : entry.input,
1853
+ output: {
1854
+ dir: entry.outDir,
1855
+ format: "esm"
1856
+ }
1857
+ };
1858
+ watchConfigs.push(watchConfig);
1987
1859
  }
1988
- }
1989
- /**
1990
- * Build workspace packages in dependency order
1991
- */
1992
- async function buildWorkspacePackages(packages, buildFn) {
1993
- await Promise.all(packages.map(buildFn));
1994
- }
1995
-
1996
- //#endregion
1997
- //#region src/features/ignore-watch.ts
1998
- /**
1999
- * Normalize ignore patterns
2000
- */
2001
- function normalizeIgnorePatterns(patterns) {
2002
- return patterns.map((pattern) => {
2003
- if (pattern.startsWith("./")) return pattern.slice(2);
2004
- if (pattern.startsWith("/")) return pattern.slice(1);
2005
- return pattern;
1860
+ const watcher = watch(watchConfigs);
1861
+ watcher.on("event", (event) => {
1862
+ switch (event.code) {
1863
+ case "START":
1864
+ logger.info("🔄 Rebuilding...");
1865
+ break;
1866
+ case "BUNDLE_START":
1867
+ logger.info("📦 Bundling...");
1868
+ break;
1869
+ case "BUNDLE_END":
1870
+ logger.success("✅ Bundle complete");
1871
+ break;
1872
+ case "END":
1873
+ logger.success("🎉 Watch rebuild complete");
1874
+ break;
1875
+ case "ERROR":
1876
+ logger.error("❌ Build error:", event.error);
1877
+ break;
1878
+ }
2006
1879
  });
1880
+ const cleanup = async () => {
1881
+ consola.info("🛑 Stopping watch mode...");
1882
+ await watcher.close();
1883
+ process.exit(0);
1884
+ };
1885
+ process.on("SIGINT", cleanup);
1886
+ process.on("SIGTERM", cleanup);
1887
+ return new Promise(() => {});
2007
1888
  }
2008
1889
 
2009
1890
  //#endregion
2010
- //#region src/watch.ts
2011
- /**
2012
- * Start watching files and rebuild on changes
2013
- */
2014
- async function startWatch(config, ctx, buildFn) {
2015
- const watchOptions = config.watch || {};
2016
- if (!watchOptions.enabled) throw new Error("Watch mode is not enabled");
2017
- const watchCtx = {
2018
- config,
2019
- ctx,
2020
- buildFn,
2021
- isBuilding: false,
2022
- pendingRebuild: false
2023
- };
2024
- const watchPatterns = await getWatchPatterns(config, ctx, watchOptions);
2025
- const ignorePatterns = getIgnorePatterns(config, watchOptions);
2026
- consola.info(`👀 Starting watch mode...`);
2027
- consola.info(`📁 Watching: ${colors.dim(watchPatterns.join(", "))}`);
2028
- if (ignorePatterns.length > 0) consola.info(`🚫 Ignoring: ${colors.dim(ignorePatterns.join(", "))}`);
2029
- const delay = watchOptions.delay ?? 100;
2030
- if (delay > 0) consola.info(`⏱️ Rebuild delay: ${colors.dim(`${delay}ms`)}`);
2031
- const watcher = watch(watchPatterns, {
2032
- ignored: ignorePatterns,
2033
- ignoreInitial: watchOptions.ignoreInitial ?? false,
2034
- persistent: true,
2035
- followSymlinks: false,
2036
- cwd: ctx.pkgDir
2037
- });
2038
- watcher.on("change", (path) => handleFileChange(watchCtx, path, "changed"));
2039
- watcher.on("add", (path) => {
2040
- if (watchOptions.watchNewFiles !== false) handleFileChange(watchCtx, path, "added");
2041
- });
2042
- watcher.on("unlink", (path) => handleFileChange(watchCtx, path, "removed"));
2043
- watcher.on("error", (error) => {
2044
- consola.error("❌ Watch error:", error);
2045
- });
2046
- if (process.env.DEBUG) {
2047
- watcher.on("addDir", (path) => consola.debug(`📁 Directory added: ${path}`));
2048
- watcher.on("unlinkDir", (path) => consola.debug(`📁 Directory removed: ${path}`));
2049
- }
2050
- await new Promise((resolve$1) => {
2051
- watcher.on("ready", () => {
2052
- const watchedPaths = watcher.getWatched();
2053
- const totalFiles = Object.values(watchedPaths).reduce((sum, files) => sum + files.length, 0);
2054
- consola.success(`🚀 Watch mode ready - watching ${totalFiles} files`);
2055
- consola.info(`💡 Press ${colors.cyan("Ctrl+C")} to stop watching`);
2056
- resolve$1();
2057
- });
2058
- });
2059
- return () => {
2060
- if (watchCtx.rebuildTimer) clearTimeout(watchCtx.rebuildTimer);
2061
- return watcher.close();
2062
- };
2063
- }
2064
- /**
2065
- * Handle file change events
2066
- */
2067
- function handleFileChange(watchCtx, filePath, changeType) {
2068
- const { config, ctx } = watchCtx;
2069
- const watchOptions = config.watch || {};
2070
- const delay = watchOptions.delay ?? 100;
2071
- const relativePath = relative(ctx.pkgDir, join(ctx.pkgDir, filePath));
2072
- const formattedPath = fmtPath(relativePath);
2073
- const changeIcon = changeType === "changed" ? "📝" : changeType === "added" ? "➕" : "➖";
2074
- consola.info(`${changeIcon} ${colors.cyan(formattedPath)} ${changeType}`);
2075
- if (watchCtx.rebuildTimer) clearTimeout(watchCtx.rebuildTimer);
2076
- watchCtx.pendingRebuild = true;
2077
- watchCtx.rebuildTimer = setTimeout(() => {
2078
- triggerRebuild(watchCtx);
2079
- }, delay);
2080
- }
1891
+ //#region src/build.ts
2081
1892
  /**
2082
- * Trigger a rebuild
1893
+ * Normalize tsup-style config to entries-based config
2083
1894
  */
2084
- async function triggerRebuild(watchCtx) {
2085
- const { config, buildFn } = watchCtx;
2086
- if (watchCtx.isBuilding) return;
2087
- if (!watchCtx.pendingRebuild) return;
2088
- watchCtx.isBuilding = true;
2089
- watchCtx.pendingRebuild = false;
2090
- try {
2091
- consola.info(`🔄 Rebuilding...`);
2092
- const start = Date.now();
2093
- const buildConfig = {
1895
+ function normalizeTsupConfig(config) {
1896
+ if (config.entries && config.entries.length > 0) return config;
1897
+ if (config.entry) {
1898
+ const entry = {
1899
+ type: "bundle",
1900
+ entry: config.entry,
1901
+ format: config.format,
1902
+ outDir: config.outDir,
1903
+ platform: config.platform,
1904
+ target: config.target,
1905
+ globalName: config.name,
1906
+ minify: config.minify,
1907
+ dts: config.dts,
1908
+ dtsOnly: config.dtsOnly,
1909
+ splitting: config.splitting,
1910
+ treeshake: config.treeshake,
1911
+ sourcemap: config.sourcemap,
1912
+ external: config.external,
1913
+ noExternal: config.noExternal,
1914
+ env: config.env,
1915
+ alias: config.alias,
1916
+ banner: config.banner,
1917
+ footer: config.footer,
1918
+ shims: config.shims,
1919
+ rolldown: config.rolldown
1920
+ };
1921
+ return {
2094
1922
  ...config,
2095
- watch: {
2096
- ...config.watch,
2097
- enabled: false
2098
- }
1923
+ entries: [entry]
2099
1924
  };
2100
- await buildFn(buildConfig);
2101
- const duration = Date.now() - start;
2102
- consola.success(`✅ Rebuild completed in ${duration}ms`);
2103
- } catch (error) {
2104
- consola.error("❌ Rebuild failed:");
2105
- if (error instanceof Error) {
2106
- consola.error(` ${error.message}`);
2107
- if (process.env.DEBUG && error.stack) consola.debug(error.stack);
2108
- } else consola.error(` ${String(error)}`);
2109
- consola.info("👀 Still watching for changes...");
2110
- } finally {
2111
- watchCtx.isBuilding = false;
2112
- if (watchCtx.pendingRebuild) setTimeout(() => triggerRebuild(watchCtx), watchCtx.config.watch?.delay ?? 100);
2113
- }
2114
- }
2115
- /**
2116
- * Get patterns for files to watch
2117
- */
2118
- async function getWatchPatterns(config, ctx, watchOptions) {
2119
- if (watchOptions.include && watchOptions.include.length > 0) return watchOptions.include;
2120
- const patterns = [];
2121
- for (const entry of config.entries || []) if (typeof entry === "string") {
2122
- const [input] = entry.split(":");
2123
- if (input.endsWith("/")) patterns.push(`${input}**/*`);
2124
- else {
2125
- const inputs = input.split(",");
2126
- for (const inputFile of inputs) {
2127
- patterns.push(inputFile);
2128
- const dir = inputFile.substring(0, inputFile.lastIndexOf("/"));
2129
- if (dir) patterns.push(`${dir}/**/*`);
2130
- }
2131
- }
2132
- } else if (entry.type === "transform") patterns.push(`${entry.input}/**/*`);
2133
- else {
2134
- const inputs = Array.isArray(entry.input) ? entry.input : [entry.input];
2135
- for (const inputFile of inputs) {
2136
- patterns.push(inputFile);
2137
- const dir = inputFile.substring(0, inputFile.lastIndexOf("/"));
2138
- if (dir) patterns.push(`${dir}/**/*`);
2139
- }
2140
1925
  }
2141
- if (patterns.length === 0) patterns.push("src/**/*", "*.ts", "*.js", "*.mjs", "*.json");
2142
- return [...new Set(patterns)];
2143
- }
2144
- /**
2145
- * Get patterns for files to ignore
2146
- */
2147
- function getIgnorePatterns(config, watchOptions) {
2148
- const defaultIgnores = [
2149
- "**/node_modules/**",
2150
- "**/dist/**",
2151
- "**/build/**",
2152
- "**/coverage/**",
2153
- "**/.git/**",
2154
- "**/.DS_Store",
2155
- "**/Thumbs.db",
2156
- "**/*.log",
2157
- "**/tmp/**",
2158
- "**/temp/**"
2159
- ];
2160
- const allIgnores = [...defaultIgnores];
2161
- if (watchOptions.exclude && watchOptions.exclude.length > 0) allIgnores.push(...watchOptions.exclude);
2162
- if (config.ignoreWatch && config.ignoreWatch.length > 0) allIgnores.push(...normalizeIgnorePatterns(config.ignoreWatch));
2163
- return allIgnores;
1926
+ return config;
2164
1927
  }
2165
-
2166
- //#endregion
2167
- //#region src/build.ts
2168
1928
  /**
2169
1929
  * Build dist/ from src/
2170
1930
  */
@@ -2178,7 +1938,6 @@ async function build(config) {
2178
1938
  pkg,
2179
1939
  pkgDir
2180
1940
  };
2181
- if (config.workspace) return buildWorkspace(config, pkgDir);
2182
1941
  let finalConfig = config;
2183
1942
  if (config.fromVite) {
2184
1943
  logger.verbose("Loading configuration from Vite config file");
@@ -2188,18 +1947,11 @@ async function build(config) {
2188
1947
  ...config
2189
1948
  };
2190
1949
  }
1950
+ finalConfig = normalizeTsupConfig(finalConfig);
2191
1951
  if (finalConfig.watch?.enabled) {
2192
1952
  logger.info(`👀 Starting watch mode for \`${ctx.pkg.name || "<no name>"}\` (\`${ctx.pkgDir}\`)`);
2193
- await performBuild(finalConfig, ctx, startTime);
2194
- const stopWatch = await startWatch(finalConfig, ctx, build);
2195
- const cleanup = () => {
2196
- consola.info("🛑 Stopping watch mode...");
2197
- stopWatch();
2198
- process.exit(0);
2199
- };
2200
- process.on("SIGINT", cleanup);
2201
- process.on("SIGTERM", cleanup);
2202
- return new Promise(() => {});
1953
+ await performWatchBuild(finalConfig, ctx, startTime);
1954
+ return;
2203
1955
  }
2204
1956
  logger.info(`📦 Building \`${ctx.pkg.name || "<no name>"}\` (\`${ctx.pkgDir}\`)`);
2205
1957
  await performBuild(finalConfig, ctx, startTime);
@@ -2225,10 +1977,17 @@ async function performBuild(config, ctx, startTime) {
2225
1977
  outDir
2226
1978
  };
2227
1979
  } else entry = rawEntry;
2228
- if (!entry.input) throw new Error(`Build entry missing \`input\`: ${JSON.stringify(entry, null, 2)}`);
1980
+ const hasInput = entry.type === "transform" ? !!entry.input : !!(entry.input || entry.entry);
1981
+ if (!hasInput) throw new Error(`Build entry missing \`input\` or \`entry\`: ${JSON.stringify(entry, null, 2)}`);
2229
1982
  entry = { ...entry };
2230
1983
  entry.outDir = normalizePath(entry.outDir || "dist", ctx.pkgDir);
2231
- entry.input = Array.isArray(entry.input) ? entry.input.map((p) => normalizePath(p, ctx.pkgDir)) : normalizePath(entry.input, ctx.pkgDir);
1984
+ const entryInput = entry.input || entry.entry;
1985
+ if (entryInput) if (typeof entryInput === "object" && !Array.isArray(entryInput)) {
1986
+ const normalizedInput = {};
1987
+ for (const [key, value] of Object.entries(entryInput)) normalizedInput[key] = normalizePath(value, ctx.pkgDir);
1988
+ entry.input = normalizedInput;
1989
+ } else if (Array.isArray(entryInput)) entry.input = entryInput.map((p) => normalizePath(p, ctx.pkgDir));
1990
+ else entry.input = normalizePath(entryInput, ctx.pkgDir);
2232
1991
  return entry;
2233
1992
  });
2234
1993
  await hooks.entries?.(entries, ctx);
@@ -2249,56 +2008,10 @@ async function performBuild(config, ctx, startTime) {
2249
2008
  function normalizePath(path, resolveFrom) {
2250
2009
  return typeof path === "string" && isAbsolute(path) ? path : path instanceof URL ? fileURLToPath(path) : resolve(resolveFrom || ".", path || ".");
2251
2010
  }
2252
- /**
2253
- * Build workspace packages
2254
- */
2255
- async function buildWorkspace(config, workspaceRoot) {
2256
- logger.info("🏢 Building workspace packages...");
2257
- const workspaceConfig = await loadWorkspaceConfig(workspaceRoot);
2258
- if (!workspaceConfig) throw new Error("No workspace configuration found");
2259
- const allPackages = await discoverWorkspacePackages(workspaceRoot, workspaceConfig.packages);
2260
- if (allPackages.length === 0) {
2261
- logger.warn("No packages found in workspace");
2262
- return;
2263
- }
2264
- const filteredPackages = filterWorkspacePackages(allPackages, config.filter || config.workspace?.filter, config.workspace?.exclude);
2265
- if (filteredPackages.length === 0) {
2266
- logger.warn("No packages match the filter criteria");
2267
- return;
2268
- }
2269
- logger.info(`Building ${filteredPackages.length} packages`);
2270
- const buildPackage = async (pkg) => {
2271
- logger.info(`📦 Building ${pkg.name}...`);
2272
- try {
2273
- const packageConfig = {
2274
- ...config,
2275
- cwd: pkg.path,
2276
- workspace: void 0
2277
- };
2278
- await build(packageConfig);
2279
- if (config.exports?.enabled) {
2280
- const exportsConfig = {
2281
- enabled: true,
2282
- ...config.exports
2283
- };
2284
- const exports = await generatePackageExports(pkg.path, packageConfig, exportsConfig);
2285
- if (config.exports.autoUpdate) {
2286
- await updatePackageJsonExports(pkg.path, exports);
2287
- logger.info(`Updated exports for ${pkg.name}`);
2288
- }
2289
- }
2290
- logger.success(`Built ${pkg.name}`);
2291
- } catch (error) {
2292
- logger.error(`Failed to build ${pkg.name}:`, error);
2293
- throw error;
2294
- }
2295
- };
2296
- await buildWorkspacePackages(filteredPackages, buildPackage);
2297
- logger.success(`Successfully built ${filteredPackages.length} packages`);
2298
- }
2299
- function readJSON(specifier) {
2300
- return import(specifier, { with: { type: "json" } }).then((r) => r.default);
2011
+ async function readJSON(specifier) {
2012
+ const module = await import(specifier, { with: { type: "json" } });
2013
+ return module.default;
2301
2014
  }
2302
2015
 
2303
2016
  //#endregion
2304
- export { build };
2017
+ export { SHEBANG_RE, build, hasShebang, makeExecutable, nodeProtocolPlugin, performBuild, shebangPlugin };