bunchee 6.6.2 → 6.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -317,6 +317,23 @@ bunchee --no-external
317
317
 
318
318
  This will include all dependencies within your output bundle.
319
319
 
320
+ #### TypeScript-Go Compiler
321
+
322
+ TypeScript-Go (`@typescript/native-preview`) is a high-performance, Go-based implementation of the TypeScript compiler that can significantly speed up type declaration file generation.
323
+
324
+ To use TypeScript-Go for type generation, use the `--tsgo` flag:
325
+
326
+ ```sh
327
+ bunchee --tsgo
328
+ ```
329
+
330
+ **Note**: This requires `@typescript/native-preview` to be installed as a dev dependency. If it's not installed, bunchee will exit with an error.
331
+
332
+ ```sh
333
+ pnpm add -D bunchee @typescript/native-preview
334
+ bunchee --tsgo
335
+ ```
336
+
320
337
  #### Build Successful Command
321
338
 
322
339
  A command to be executed after a build is successful can be specified using the `--success` option, which is useful for development watching mode:
@@ -365,6 +382,14 @@ Then use use the [exports field in package.json](https://nodejs.org/api/packages
365
382
 
366
383
  If you're building a TypeScript library, separate the types from the main entry file and specify the types path in package.json. Types exports need to stay on the top of each export with `types` condition, and you can use `default` condition for the JS bundle file.
367
384
 
385
+ **bunchee** supports using the TypeScript-Go compiler (`@typescript/native-preview`) for faster type generation. To enable it, use the `--tsgo` flag:
386
+
387
+ ```sh
388
+ bunchee --tsgo
389
+ ```
390
+
391
+ Note: This requires `@typescript/native-preview` to be installed as a dev dependency. If it's not installed, bunchee will fall back to the regular TypeScript compiler with a warning.
392
+
368
393
  ```json5
369
394
  {
370
395
  "files": ["dist"],
@@ -545,6 +570,7 @@ await bundle(path.resolve('./src/index.ts'), {
545
570
  cwd: process.cwd(), // string
546
571
  clean: true, // boolean
547
572
  tsconfig: 'tsconfig.json', // string
573
+ tsgo: false, // Boolean - use TypeScript-Go compiler for type generation
548
574
  })
549
575
  ```
550
576
 
package/dist/bin/cli.js CHANGED
@@ -198,7 +198,7 @@ function validateEntryFiles(entryFiles) {
198
198
 
199
199
  function exit(err) {
200
200
  logger.error(err);
201
- process.exit(1);
201
+ throw typeof err === 'string' ? new Error(err) : err;
202
202
  }
203
203
  function hasPackageJson(cwd) {
204
204
  return fileExists(path__default.default.resolve(cwd, 'package.json'));
@@ -437,13 +437,13 @@ function validateFilesField(packageJson) {
437
437
  '*'
438
438
  ];
439
439
  const exportsField = packageJson.exports || {};
440
- const resolveExportsPaths = (exports)=>{
440
+ const resolveExportsPaths = (exports$1)=>{
441
441
  const paths = [];
442
- if (typeof exports === 'string') {
443
- paths.push(exports);
444
- } else if (typeof exports === 'object') {
445
- for(const key in exports){
446
- paths.push(...resolveExportsPaths(exports[key]));
442
+ if (typeof exports$1 === 'string') {
443
+ paths.push(exports$1);
444
+ } else if (typeof exports$1 === 'object') {
445
+ for(const key in exports$1){
446
+ paths.push(...resolveExportsPaths(exports$1[key]));
447
447
  }
448
448
  }
449
449
  return paths;
@@ -470,7 +470,7 @@ function validateFilesField(packageJson) {
470
470
  return state;
471
471
  }
472
472
  function lint$1(pkg) {
473
- const { name, main, exports } = pkg;
473
+ const { name, main, exports: exports$1 } = pkg;
474
474
  const isESM = isESModulePackage(pkg.type);
475
475
  const parsedExports = parseExports(pkg);
476
476
  if (!name) {
@@ -500,12 +500,12 @@ function lint$1(pkg) {
500
500
  };
501
501
  // Validate ESM package
502
502
  if (isESM) {
503
- if (exports) {
504
- if (typeof exports === 'string') {
505
- if (hasCjsExtension(exports)) {
503
+ if (exports$1) {
504
+ if (typeof exports$1 === 'string') {
505
+ if (hasCjsExtension(exports$1)) {
506
506
  exportsState.badMainExport = true;
507
507
  }
508
- } else if (typeof exports !== 'object') {
508
+ } else if (typeof exports$1 !== 'object') {
509
509
  exportsState.invalidExportsFieldType = true;
510
510
  } else {
511
511
  parsedExports.forEach((outputPairs)=>{
@@ -548,12 +548,12 @@ function lint$1(pkg) {
548
548
  if (main && path__default.default.extname(main) === '.mjs') {
549
549
  exportsState.badMainExtension = true;
550
550
  }
551
- if (exports) {
552
- if (typeof exports === 'string') {
553
- if (path__default.default.extname(exports) === '.mjs') {
551
+ if (exports$1) {
552
+ if (typeof exports$1 === 'string') {
553
+ if (path__default.default.extname(exports$1) === '.mjs') {
554
554
  exportsState.badMainExport = true;
555
555
  }
556
- } else if (typeof exports !== 'object') {
556
+ } else if (typeof exports$1 !== 'object') {
557
557
  exportsState.invalidExportsFieldType = true;
558
558
  } else {
559
559
  parsedExports.forEach((outputPairs)=>{
@@ -647,11 +647,12 @@ function lint$1(pkg) {
647
647
  }
648
648
  }
649
649
 
650
- var version = "6.6.2";
650
+ var version = "6.8.0";
651
651
 
652
- async function writeDefaultTsconfig(tsConfigPath) {
652
+ async function writeDefaultTsconfig(tsConfigPath, useTsGo) {
653
653
  await fs.promises.writeFile(tsConfigPath, JSON.stringify(DEFAULT_TS_CONFIG, null, 2), 'utf-8');
654
- logger.log(`Detected using TypeScript but tsconfig.json is missing, created a ${pc.blue('tsconfig.json')} for you.`);
654
+ const compilerName = 'TypeScript';
655
+ logger.log(`Detected using ${compilerName} but tsconfig.json is missing, created a ${pc.blue('tsconfig.json')} for you.`);
655
656
  }
656
657
 
657
658
  // ./index -> default
@@ -826,7 +827,7 @@ const normalizeBaseNameToExportName = (name)=>{
826
827
  const baseName = stripeBinaryTag(name);
827
828
  return /^\.\/index(\.|$)/.test(baseName) ? '.' : posixRelativify(baseName);
828
829
  };
829
- function createExportCondition(exportName, sourceFile, moduleType) {
830
+ function createExportCondition(exportName, sourceFile, moduleType, esmOnly = false) {
830
831
  const isTsSourceFile = isTypescriptFile(sourceFile);
831
832
  let cjsExtension = 'js';
832
833
  let esmExtension = 'mjs';
@@ -838,23 +839,31 @@ function createExportCondition(exportName, sourceFile, moduleType) {
838
839
  exportName = 'index';
839
840
  }
840
841
  if (isTsSourceFile) {
842
+ const importCondition = {
843
+ types: getDistPath('es', `${exportName}.${dtsExtensionsMap[esmExtension]}`),
844
+ default: getDistPath('es', `${exportName}.${esmExtension}`)
845
+ };
846
+ if (esmOnly) {
847
+ return importCondition;
848
+ }
841
849
  return {
842
- import: {
843
- types: getDistPath('es', `${exportName}.${dtsExtensionsMap[esmExtension]}`),
844
- default: getDistPath('es', `${exportName}.${esmExtension}`)
845
- },
850
+ import: importCondition,
846
851
  require: {
847
852
  types: getDistPath('cjs', `${exportName}.${dtsExtensionsMap[cjsExtension]}`),
848
853
  default: getDistPath('cjs', `${exportName}.${cjsExtension}`)
849
854
  }
850
855
  };
851
856
  }
857
+ const importPath = getDistPath(`${exportName}.${esmExtension}`);
858
+ if (esmOnly) {
859
+ return importPath;
860
+ }
852
861
  return {
853
- import: getDistPath(`${exportName}.mjs`),
862
+ import: importPath,
854
863
  require: getDistPath(`${exportName}.${cjsExtension}`)
855
864
  };
856
865
  }
857
- function createExportConditionPair(exportName, sourceFile, moduleType) {
866
+ function createExportConditionPair(exportName, sourceFile, moduleType, esmOnly = false) {
858
867
  // <exportName>.<specialCondition>
859
868
  let specialCondition;
860
869
  const specialConditionName = getSpecialExportTypeFromComposedExportPath(exportName);
@@ -872,13 +881,39 @@ function createExportConditionPair(exportName, sourceFile, moduleType) {
872
881
  specialCondition
873
882
  ];
874
883
  }
875
- const exportCond = createExportCondition(exportName, sourceFile, moduleType);
884
+ const exportCond = createExportCondition(exportName, sourceFile, moduleType, esmOnly);
876
885
  return [
877
886
  normalizedExportPath,
878
887
  exportCond
879
888
  ];
880
889
  }
881
- async function prepare(cwd) {
890
+ function detectPackageManager(cwd) {
891
+ if (fs__default.default.existsSync(path__default.default.join(cwd, 'pnpm-lock.yaml'))) {
892
+ return 'pnpm';
893
+ }
894
+ if (fs__default.default.existsSync(path__default.default.join(cwd, 'yarn.lock'))) {
895
+ return 'yarn';
896
+ }
897
+ if (fs__default.default.existsSync(path__default.default.join(cwd, 'package-lock.json'))) {
898
+ return 'npm';
899
+ }
900
+ // Default to npm if no lock file found
901
+ return 'npm';
902
+ }
903
+ function addBuildScripts(pkgJson, cwd) {
904
+ if (!pkgJson.scripts) {
905
+ pkgJson.scripts = {};
906
+ }
907
+ const packageManager = detectPackageManager(cwd);
908
+ const buildCmd = packageManager === 'pnpm' ? 'pnpm build' : `${packageManager} run build`;
909
+ if (!pkgJson.scripts.build) {
910
+ pkgJson.scripts.build = 'bunchee';
911
+ }
912
+ if (!pkgJson.scripts.prepublishOnly) {
913
+ pkgJson.scripts.prepublishOnly = buildCmd;
914
+ }
915
+ }
916
+ async function prepare(cwd, options) {
882
917
  const sourceFolder = path__default.default.resolve(cwd, SRC);
883
918
  if (!fs__default.default.existsSync(sourceFolder)) {
884
919
  logger.error(`Source folder ${sourceFolder} does not exist. Cannot proceed to configure \`exports\` field.`);
@@ -920,7 +955,8 @@ async function prepare(cwd) {
920
955
  }
921
956
  }
922
957
  // Configure as ESM package by default if there's no package.json
923
- if (!hasPackageJson) {
958
+ // OR if --esm flag is explicitly set
959
+ if (!hasPackageJson || (options == null ? void 0 : options.esm)) {
924
960
  pkgJson.type = 'module';
925
961
  }
926
962
  if (bins.size > 0) {
@@ -950,24 +986,40 @@ async function prepare(cwd) {
950
986
  }
951
987
  }
952
988
  const pkgExports = {};
989
+ const esmOnly = (options == null ? void 0 : options.esm) === true;
953
990
  for (const [exportName, sourceFilesMap] of exportsEntries.entries()){
954
991
  for (const sourceFile of Object.values(sourceFilesMap)){
955
- const [normalizedExportPath, conditions] = createExportConditionPair(exportName, sourceFile, pkgJson.type);
956
- pkgExports[normalizedExportPath] = {
957
- ...conditions,
958
- ...pkgExports[normalizedExportPath]
959
- };
992
+ const [normalizedExportPath, conditions] = createExportConditionPair(exportName, sourceFile, pkgJson.type, esmOnly);
993
+ // When esmOnly is true, conditions is either a string or an object with types/default (no import/require)
994
+ // When esmOnly is false, conditions is an object with import/require
995
+ if (esmOnly || typeof conditions === 'string' || typeof conditions === 'object' && conditions !== null && !('import' in conditions || 'require' in conditions)) {
996
+ pkgExports[normalizedExportPath] = conditions;
997
+ } else {
998
+ pkgExports[normalizedExportPath] = {
999
+ ...conditions,
1000
+ ...pkgExports[normalizedExportPath]
1001
+ };
1002
+ }
960
1003
  }
961
1004
  }
962
1005
  // Configure node10 module resolution
963
1006
  if (exportsEntries.has('./index')) {
964
1007
  const isESM = pkgJson.type === 'module';
965
1008
  const mainExport = pkgExports['.'];
966
- const mainCondition = isESM ? 'import' : 'require';
967
- pkgJson.main = isUsingTs ? mainExport[mainCondition].default : mainExport[mainCondition];
968
- pkgJson.module = isUsingTs ? mainExport.import.default : mainExport.import;
969
- if (isUsingTs) {
970
- pkgJson.types = mainExport[mainCondition].types;
1009
+ if (esmOnly) {
1010
+ // When esmOnly is true, mainExport is either a string or an object with types/default
1011
+ pkgJson.main = isUsingTs ? typeof mainExport === 'object' ? mainExport.default : mainExport : typeof mainExport === 'string' ? mainExport : mainExport.default;
1012
+ pkgJson.module = isUsingTs ? typeof mainExport === 'object' ? mainExport.default : mainExport : typeof mainExport === 'string' ? mainExport : mainExport.default;
1013
+ if (isUsingTs && typeof mainExport === 'object') {
1014
+ pkgJson.types = mainExport.types;
1015
+ }
1016
+ } else {
1017
+ const mainCondition = isESM ? 'import' : 'require';
1018
+ pkgJson.main = isUsingTs ? mainExport[mainCondition].default : mainExport[mainCondition];
1019
+ pkgJson.module = isUsingTs ? mainExport.import.default : mainExport.import;
1020
+ if (isUsingTs) {
1021
+ pkgJson.types = mainExport[mainCondition].types;
1022
+ }
971
1023
  }
972
1024
  }
973
1025
  // Assign the properties by order: files, main, module, types, exports
@@ -987,6 +1039,10 @@ async function prepare(cwd) {
987
1039
  }
988
1040
  }
989
1041
  }
1042
+ // Additional setup when --esm flag is set
1043
+ if (options == null ? void 0 : options.esm) {
1044
+ addBuildScripts(pkgJson, cwd);
1045
+ }
990
1046
  await fsp__default.default.writeFile(pkgJsonPath, JSON.stringify(pkgJson, null, 2) + '\n');
991
1047
  logger.info('Configured `exports` in package.json');
992
1048
  }
@@ -1082,7 +1138,7 @@ Options:
1082
1138
  -o, --output <file> specify output filename
1083
1139
  -f, --format <format> type of output (esm, amd, cjs, iife, umd, system), default: esm
1084
1140
  -h, --help output usage information
1085
-
1141
+
1086
1142
  --external <mod> specify an external dependency, separate by comma
1087
1143
  --no-external do not bundle external dependencies
1088
1144
  --no-clean do not clean dist folder before building, default: false
@@ -1095,6 +1151,7 @@ Options:
1095
1151
  --tsconfig path to tsconfig file, default: tsconfig.json
1096
1152
  --dts-bundle bundle type declaration files, default: false
1097
1153
  --success <cmd> run command after build success
1154
+ --tsgo use TypeScript-Go compiler for type generation
1098
1155
  `;
1099
1156
  function help() {
1100
1157
  logger.log(helpMessage);
@@ -1172,8 +1229,18 @@ async function parseCliArgs(argv) {
1172
1229
  }).option('success', {
1173
1230
  type: 'string',
1174
1231
  description: 'run command after build success'
1175
- }).command('prepare', 'auto configure package.json exports for building', ()=>{}, (argv)=>{
1176
- return prepare(argv.cwd || process.cwd());
1232
+ }).option('tsgo', {
1233
+ type: 'boolean',
1234
+ description: 'use TypeScript-Go compiler for type generation'
1235
+ }).command('prepare', 'auto configure package.json exports for building', (yargs)=>{
1236
+ return yargs.option('esm', {
1237
+ type: 'boolean',
1238
+ description: 'configure package as ESM (sets type: "module")'
1239
+ });
1240
+ }, (argv)=>{
1241
+ return prepare(argv.cwd || process.cwd(), {
1242
+ esm: argv.esm
1243
+ });
1177
1244
  }).command('lint', 'lint package.json', ()=>{}, (argv)=>{
1178
1245
  return lint(argv.cwd || process.cwd());
1179
1246
  }).version(version).help('help', 'output usage information').showHelpOnFail(true).parse();
@@ -1207,7 +1274,8 @@ async function parseCliArgs(argv) {
1207
1274
  clean: args['clean'] !== false,
1208
1275
  env: args['env'],
1209
1276
  tsconfig: args['tsconfig'],
1210
- onSuccess: args['success']
1277
+ onSuccess: args['success'],
1278
+ tsgo: args['tsgo']
1211
1279
  };
1212
1280
  // When minify is enabled, sourcemap should be enabled by default, unless explicitly opted out
1213
1281
  if (parsedArgs.minify && typeof args['sourcemap'] === 'undefined') {
@@ -1236,9 +1304,22 @@ async function run(args) {
1236
1304
  env: (env == null ? void 0 : env.split(',')) || [],
1237
1305
  clean,
1238
1306
  tsconfig,
1239
- onSuccess
1307
+ onSuccess,
1308
+ tsgo: args.tsgo
1240
1309
  };
1241
1310
  const cliEntry = source ? path__default.default.resolve(cwd, source) : '';
1311
+ // Check if ts-go is available when requested (before any build operations)
1312
+ if (args.tsgo) {
1313
+ try {
1314
+ require.resolve('@typescript/native-preview', {
1315
+ paths: [
1316
+ cwd
1317
+ ]
1318
+ });
1319
+ } catch {
1320
+ exit('--tsgo flag was specified but @typescript/native-preview is not installed. Please install it as a dev dependency: pnpm add -D @typescript/native-preview');
1321
+ }
1322
+ }
1242
1323
  // lint package by default
1243
1324
  await lint(cwd);
1244
1325
  const spinnerInstance = process.stdout.isTTY ? nanospinner.createSpinner('Building...\n\n', {
@@ -1389,4 +1470,7 @@ function logError(err) {
1389
1470
  const error = normalizeError(err);
1390
1471
  logger.error(error);
1391
1472
  }
1392
- main().catch(exit);
1473
+ main().catch((error)=>{
1474
+ logError(error);
1475
+ process.exit(1);
1476
+ });
package/dist/index.d.ts CHANGED
@@ -22,6 +22,7 @@ type BundleConfig = {
22
22
  clean?: boolean;
23
23
  tsconfig?: string;
24
24
  onSuccess?: string | (() => void | Promise<void>);
25
+ tsgo?: boolean;
25
26
  _callbacks?: {
26
27
  onBuildStart?: (state: any) => void;
27
28
  onBuildEnd?: (assetJobs: any) => void;
@@ -36,6 +37,7 @@ type PackageMetadata = {
36
37
  files?: string[];
37
38
  type?: 'commonjs' | 'module';
38
39
  dependencies?: Record<string, string>;
40
+ optionalDependencies?: Record<string, string>;
39
41
  peerDependencies?: Record<string, string>;
40
42
  peerDependenciesMeta?: Record<string, Record<string, string>>;
41
43
  exports?: string | Record<string, ExportCondition>;
package/dist/index.js CHANGED
@@ -299,7 +299,7 @@ function validateEntryFiles(entryFiles) {
299
299
 
300
300
  function exit(err) {
301
301
  logger.error(err);
302
- process.exit(1);
302
+ throw typeof err === 'string' ? new Error(err) : err;
303
303
  }
304
304
  async function getPackageMeta(cwd) {
305
305
  const pkgFilePath = path__default.default.resolve(cwd, 'package.json');
@@ -1034,7 +1034,42 @@ const memoizeByKey = (fn)=>{
1034
1034
  const memoize = (fn)=>createMemoize(fn);
1035
1035
 
1036
1036
  let hasLoggedTsWarning = false;
1037
- function resolveTypescript(cwd) {
1037
+ let hasLoggedTsGoWarning = false;
1038
+ function resolveTsGo(cwd) {
1039
+ let tsgo;
1040
+ const m = new module$1.Module('', undefined);
1041
+ m.paths = module$1.Module._nodeModulePaths(cwd);
1042
+ try {
1043
+ // Bun does not yet support the `Module` class properly.
1044
+ if (typeof (m == null ? void 0 : m.require) === 'undefined') {
1045
+ const tsgoPath = require.resolve('@typescript/native-preview', {
1046
+ paths: [
1047
+ cwd
1048
+ ]
1049
+ });
1050
+ tsgo = require(tsgoPath);
1051
+ } else {
1052
+ tsgo = m.require('@typescript/native-preview');
1053
+ }
1054
+ // ts-go exports the TypeScript API as default or named export
1055
+ return tsgo.default || tsgo;
1056
+ } catch (e) {
1057
+ if (!hasLoggedTsGoWarning) {
1058
+ hasLoggedTsGoWarning = true;
1059
+ logger.warn('Could not load TypeScript-Go compiler. Make sure `@typescript/native-preview` is installed as a dev dependency.');
1060
+ }
1061
+ return null;
1062
+ }
1063
+ }
1064
+ function resolveTypescript(cwd, useTsGo) {
1065
+ if (useTsGo) {
1066
+ const tsgo = resolveTsGo(cwd);
1067
+ if (tsgo) {
1068
+ return tsgo;
1069
+ }
1070
+ // Error if ts-go is requested but not available
1071
+ exit('TypeScript-Go compiler not found. Please install @typescript/native-preview as a dev dependency: pnpm add -D @typescript/native-preview');
1072
+ }
1038
1073
  let ts;
1039
1074
  const m = new module$1.Module('', undefined);
1040
1075
  m.paths = module$1.Module._nodeModulePaths(cwd);
@@ -1064,11 +1099,11 @@ const resolveTsConfigPath = memoize((cwd, tsconfigFileName = 'tsconfig.json')=>{
1064
1099
  tsConfigPath = path.resolve(cwd, tsconfigFileName);
1065
1100
  return fileExists(tsConfigPath) ? tsConfigPath : undefined;
1066
1101
  });
1067
- function resolveTsConfigHandler(cwd, tsConfigPath) {
1102
+ function resolveTsConfigHandler(cwd, tsConfigPath, useTsGo) {
1068
1103
  let tsCompilerOptions = {};
1069
1104
  if (tsConfigPath) {
1070
1105
  // Use the original ts handler to avoid memory leak
1071
- const ts = resolveTypescript(cwd);
1106
+ const ts = resolveTypescript(cwd, useTsGo);
1072
1107
  const basePath = tsConfigPath ? path.dirname(tsConfigPath) : cwd;
1073
1108
  const tsconfigJSON = ts.readConfigFile(tsConfigPath, ts.sys.readFile).config;
1074
1109
  tsCompilerOptions = ts.parseJsonConfigFileContent(tsconfigJSON, ts.sys, basePath).options;
@@ -1080,15 +1115,28 @@ function resolveTsConfigHandler(cwd, tsConfigPath) {
1080
1115
  tsConfigPath
1081
1116
  };
1082
1117
  }
1083
- const resolveTsConfig = memoize(resolveTsConfigHandler);
1084
- async function convertCompilerOptions(cwd, json) {
1118
+ // Note: We can't memoize resolveTsConfigHandler directly with useTsGo parameter
1119
+ // because memoize doesn't handle optional parameters well. Instead, we'll create
1120
+ // a wrapper that handles the memoization per useTsGo value.
1121
+ const resolveTsConfigCache = new Map();
1122
+ function resolveTsConfig(cwd, tsConfigPath, useTsGo) {
1123
+ const cacheKey = `${cwd}:${tsConfigPath || ''}:${useTsGo ? 'tsgo' : 'ts'}`;
1124
+ if (resolveTsConfigCache.has(cacheKey)) {
1125
+ return resolveTsConfigCache.get(cacheKey);
1126
+ }
1127
+ const result = resolveTsConfigHandler(cwd, tsConfigPath, useTsGo);
1128
+ resolveTsConfigCache.set(cacheKey, result);
1129
+ return result;
1130
+ }
1131
+ async function convertCompilerOptions(cwd, json, useTsGo) {
1085
1132
  // Use the original ts handler to avoid memory leak
1086
- const ts = resolveTypescript(cwd);
1133
+ const ts = resolveTypescript(cwd, useTsGo);
1087
1134
  return ts.convertCompilerOptionsFromJson(json, './');
1088
1135
  }
1089
- async function writeDefaultTsconfig(tsConfigPath) {
1136
+ async function writeDefaultTsconfig(tsConfigPath, useTsGo) {
1090
1137
  await fs.promises.writeFile(tsConfigPath, JSON.stringify(DEFAULT_TS_CONFIG, null, 2), 'utf-8');
1091
- logger.log(`Detected using TypeScript but tsconfig.json is missing, created a ${pc.blue('tsconfig.json')} for you.`);
1138
+ const compilerName = useTsGo ? 'TypeScript-Go' : 'TypeScript';
1139
+ logger.log(`Detected using ${compilerName} but tsconfig.json is missing, created a ${pc.blue('tsconfig.json')} for you.`);
1092
1140
  }
1093
1141
 
1094
1142
  /**
@@ -1327,6 +1375,89 @@ function rawContent({ exclude }) {
1327
1375
  };
1328
1376
  }
1329
1377
 
1378
+ const NATIVE_ADDON_SUFFIX = '\0native-addon:';
1379
+ /**
1380
+ * Plugin to handle native Node.js addon (.node) files.
1381
+ * Copies the binary to the output directory and rewrites import paths.
1382
+ */ function nativeAddon() {
1383
+ // Map from virtual module id to original absolute path
1384
+ const nativeAddonMap = new Map();
1385
+ return {
1386
+ name: 'native-addon',
1387
+ resolveId (source, importer) {
1388
+ // Check if this is a .node file import
1389
+ if (!source.endsWith('.node')) {
1390
+ return null;
1391
+ }
1392
+ // Resolve the absolute path of the .node file
1393
+ let absolutePath;
1394
+ if (path__default.default.isAbsolute(source)) {
1395
+ absolutePath = source;
1396
+ } else if (importer) {
1397
+ absolutePath = path__default.default.resolve(path__default.default.dirname(importer), source);
1398
+ } else {
1399
+ return null;
1400
+ }
1401
+ // Create a virtual module id for this native addon
1402
+ const virtualId = NATIVE_ADDON_SUFFIX + absolutePath;
1403
+ // Track the mapping
1404
+ nativeAddonMap.set(virtualId, absolutePath);
1405
+ return virtualId;
1406
+ },
1407
+ async load (id) {
1408
+ if (!id.startsWith(NATIVE_ADDON_SUFFIX)) {
1409
+ return null;
1410
+ }
1411
+ const absolutePath = nativeAddonMap.get(id);
1412
+ if (!absolutePath) {
1413
+ return null;
1414
+ }
1415
+ // Get the filename for the output
1416
+ const fileName = path__default.default.basename(absolutePath);
1417
+ // Return code that will require the binary from the same directory
1418
+ // Using __dirname to get the directory of the output file at runtime
1419
+ return {
1420
+ code: `
1421
+ import { createRequire } from 'module';
1422
+ import { fileURLToPath } from 'url';
1423
+ import { dirname, join } from 'path';
1424
+
1425
+ const __filename = fileURLToPath(import.meta.url);
1426
+ const __dirname = dirname(__filename);
1427
+ const require = createRequire(import.meta.url);
1428
+
1429
+ const nativeAddon = require(join(__dirname, ${JSON.stringify(fileName)}));
1430
+ export default nativeAddon;
1431
+ `,
1432
+ map: null,
1433
+ meta: {
1434
+ nativeAddon: {
1435
+ absolutePath,
1436
+ fileName
1437
+ }
1438
+ }
1439
+ };
1440
+ },
1441
+ async generateBundle () {
1442
+ // Copy all tracked native addon files to the output directory
1443
+ for (const [_, absolutePath] of nativeAddonMap){
1444
+ const fileName = path__default.default.basename(absolutePath);
1445
+ try {
1446
+ const fileContent = await fsp.readFile(absolutePath);
1447
+ // Emit the binary file as an asset
1448
+ this.emitFile({
1449
+ type: 'asset',
1450
+ fileName,
1451
+ source: fileContent
1452
+ });
1453
+ } catch (error) {
1454
+ this.error(`[bunchee] Failed to read native addon file: ${absolutePath}`);
1455
+ }
1456
+ }
1457
+ }
1458
+ };
1459
+ }
1460
+
1330
1461
  function hasNoSpecialCondition(conditionNames) {
1331
1462
  return [
1332
1463
  ...conditionNames
@@ -1457,6 +1588,51 @@ const prependShebang = (entry)=>({
1457
1588
  }
1458
1589
  });
1459
1590
 
1591
+ function hasSwcHelpersDeclared(pkg) {
1592
+ var _pkg_dependencies, _pkg_peerDependencies, _pkg_optionalDependencies;
1593
+ return Boolean(((_pkg_dependencies = pkg.dependencies) == null ? void 0 : _pkg_dependencies['@swc/helpers']) || ((_pkg_peerDependencies = pkg.peerDependencies) == null ? void 0 : _pkg_peerDependencies['@swc/helpers']) || ((_pkg_optionalDependencies = pkg.optionalDependencies) == null ? void 0 : _pkg_optionalDependencies['@swc/helpers']));
1594
+ }
1595
+ function isSwcHelpersInstalled(cwd) {
1596
+ try {
1597
+ // Use Node's resolver (supports pnpm/yarn PnP) and resolve from the user's project.
1598
+ const req = module$1.createRequire(path__default.default.join(cwd, 'package.json'));
1599
+ req.resolve('@swc/helpers/package.json');
1600
+ return true;
1601
+ } catch {
1602
+ return false;
1603
+ }
1604
+ }
1605
+ function chunkUsesSwcHelpers(chunk) {
1606
+ const allImports = chunk.imports.concat(chunk.dynamicImports);
1607
+ return allImports.some((id)=>id === '@swc/helpers' || id.startsWith('@swc/helpers/'));
1608
+ }
1609
+ function swcHelpersWarningPlugin({ cwd, pkg }) {
1610
+ let hasWarned = false;
1611
+ const swcHelpersImportFiles = new Set();
1612
+ return {
1613
+ name: 'bunchee:swc-helpers-warning',
1614
+ writeBundle (options, bundle) {
1615
+ if (hasWarned) return;
1616
+ const outputDir = options.dir || path__default.default.dirname(options.file);
1617
+ for (const [fileName, item] of Object.entries(bundle)){
1618
+ if (item.type !== 'chunk') continue;
1619
+ if (!chunkUsesSwcHelpers(item)) continue;
1620
+ swcHelpersImportFiles.add(path__default.default.relative(cwd, path__default.default.join(outputDir, fileName)));
1621
+ }
1622
+ if (swcHelpersImportFiles.size === 0) return;
1623
+ if (isSwcHelpersInstalled(cwd)) return;
1624
+ hasWarned = true;
1625
+ const exampleFiles = Array.from(swcHelpersImportFiles).slice(0, 3).join(', ');
1626
+ const declaredHint = hasSwcHelpersDeclared(pkg) ? '' : ' (and add it to your dependencies)';
1627
+ logger.warn([
1628
+ `Your build output imports "@swc/helpers" due to transform, but it isn't installed in this project.`,
1629
+ `Install it as a runtime dependency${declaredHint} (e.g. "pnpm add @swc/helpers").`,
1630
+ exampleFiles ? `Detected in: ${exampleFiles}` : ''
1631
+ ].filter(Boolean).join('\n'));
1632
+ }
1633
+ };
1634
+ }
1635
+
1460
1636
  const swcMinifyOptions = {
1461
1637
  compress: {
1462
1638
  directives: false
@@ -1468,7 +1644,7 @@ const swcMinifyOptions = {
1468
1644
  toplevel: true
1469
1645
  }
1470
1646
  };
1471
- async function createDtsPlugin(tsCompilerOptions, tsConfigPath, respectExternal, cwd) {
1647
+ async function createDtsPlugin(tsCompilerOptions, tsConfigPath, respectExternal, cwd, useTsGo) {
1472
1648
  const enableIncrementalWithoutBuildInfo = (tsCompilerOptions == null ? void 0 : tsCompilerOptions.incremental) && !(tsCompilerOptions == null ? void 0 : tsCompilerOptions.tsBuildInfoFile);
1473
1649
  const incrementalOptions = enableIncrementalWithoutBuildInfo ? {
1474
1650
  incremental: false
@@ -1495,7 +1671,33 @@ async function createDtsPlugin(tsCompilerOptions, tsConfigPath, respectExternal,
1495
1671
  ...incrementalOptions,
1496
1672
  // error TS6379: Composite projects may not disable incremental compilation.
1497
1673
  ...compositeOptions
1498
- });
1674
+ }, useTsGo);
1675
+ // If useTsGo is enabled, we need to make ts-go available to rollup-plugin-dts
1676
+ // rollup-plugin-dts uses require('typescript') internally, so we need to
1677
+ // temporarily override the module cache to use ts-go
1678
+ if (useTsGo) {
1679
+ const tsgo = resolveTsGo(cwd);
1680
+ if (tsgo) {
1681
+ try {
1682
+ // First, try to resolve typescript to get its path
1683
+ const tsPath = require.resolve('typescript', {
1684
+ paths: [
1685
+ cwd
1686
+ ]
1687
+ });
1688
+ // Make ts-go available as 'typescript' for rollup-plugin-dts
1689
+ // This overrides the module cache so rollup-plugin-dts will use ts-go
1690
+ require.cache[tsPath] = {
1691
+ id: tsPath,
1692
+ exports: tsgo,
1693
+ loaded: true
1694
+ };
1695
+ } catch (e) {
1696
+ // If typescript cannot be resolved, we can't override it
1697
+ // rollup-plugin-dts will try to require it and may fail
1698
+ }
1699
+ }
1700
+ }
1499
1701
  const dtsPlugin = require('rollup-plugin-dts').default({
1500
1702
  tsconfig: tsConfigPath,
1501
1703
  compilerOptions: overrideResolvedTsOptions,
@@ -1589,8 +1791,8 @@ async function buildInputConfig(entry, bundleConfig, exportCondition, buildConte
1589
1791
  // Each process should be unique
1590
1792
  // Each package build should be unique
1591
1793
  // Composing above factors into a unique cache key to retrieve the memoized dts plugin with tsconfigs
1592
- const uniqueProcessId = 'dts-plugin:' + process.pid + tsConfigPath;
1593
- const dtsPlugin = await memoizeDtsPluginByKey(uniqueProcessId)(tsCompilerOptions, tsConfigPath, bundleConfig.dts && bundleConfig.dts.respectExternal, cwd);
1794
+ const uniqueProcessId = 'dts-plugin:' + process.pid + tsConfigPath + (buildContext.useTsGo ? ':tsgo' : '');
1795
+ const dtsPlugin = await memoizeDtsPluginByKey(uniqueProcessId)(tsCompilerOptions, tsConfigPath, bundleConfig.dts && bundleConfig.dts.respectExternal, cwd, buildContext.useTsGo);
1594
1796
  typesPlugins.push(dtsPlugin);
1595
1797
  }
1596
1798
  const plugins = (dts ? typesPlugins : [
@@ -1603,6 +1805,7 @@ async function buildInputConfig(entry, bundleConfig, exportCondition, buildConte
1603
1805
  rawContent({
1604
1806
  exclude: /node_modules/
1605
1807
  }),
1808
+ nativeAddon(),
1606
1809
  isBinEntry && prependShebang(entry),
1607
1810
  replace__default.default({
1608
1811
  values: inlineDefinedValues,
@@ -1627,6 +1830,11 @@ async function buildInputConfig(entry, bundleConfig, exportCondition, buildConte
1627
1830
  // For relative paths, the module will be bundled;
1628
1831
  // For external libraries, the module will not be bundled.
1629
1832
  transformMixedEsModules: true
1833
+ }),
1834
+ // If SWC emits @swc/helpers imports, warn when it's not installed.
1835
+ swcHelpersWarningPlugin({
1836
+ cwd,
1837
+ pkg
1630
1838
  })
1631
1839
  ]).filter(isNotNull);
1632
1840
  return {
@@ -1668,6 +1876,62 @@ function getModuleLayer(moduleMeta) {
1668
1876
  const moduleLayer = directives[0];
1669
1877
  return moduleLayer ? hashTo3Char(moduleLayer) : undefined;
1670
1878
  }
1879
+ /**
1880
+ * Get the effective layer of a module by walking up the importer chain.
1881
+ * A module inherits the layer of its importer if it doesn't have its own layer.
1882
+ */ function getEffectiveModuleLayer(id, getModuleInfo, visited = new Set()) {
1883
+ if (visited.has(id)) {
1884
+ return undefined;
1885
+ }
1886
+ visited.add(id);
1887
+ const moduleInfo = getModuleInfo(id);
1888
+ if (!moduleInfo) {
1889
+ return undefined;
1890
+ }
1891
+ // If this module has its own layer, return it
1892
+ const ownLayer = getModuleLayer(moduleInfo.meta);
1893
+ if (ownLayer) {
1894
+ return ownLayer;
1895
+ }
1896
+ // Otherwise, inherit layer from importers
1897
+ for (const importerId of moduleInfo.importers){
1898
+ const importerLayer = getEffectiveModuleLayer(importerId, getModuleInfo, visited);
1899
+ if (importerLayer) {
1900
+ return importerLayer;
1901
+ }
1902
+ }
1903
+ return undefined;
1904
+ }
1905
+ /**
1906
+ * Check if a module is imported by modules with different boundary layers.
1907
+ * Returns the set of unique layers if there are multiple, otherwise undefined.
1908
+ */ function getImporterLayers(id, getModuleInfo) {
1909
+ const moduleInfo = getModuleInfo(id);
1910
+ if (!moduleInfo) {
1911
+ return new Set();
1912
+ }
1913
+ const layers = new Set();
1914
+ for (const importerId of moduleInfo.importers){
1915
+ const importerInfo = getModuleInfo(importerId);
1916
+ if (!importerInfo) {
1917
+ continue;
1918
+ }
1919
+ // Get the importer's own layer first
1920
+ const importerOwnLayer = getModuleLayer(importerInfo.meta);
1921
+ if (importerOwnLayer) {
1922
+ layers.add(importerOwnLayer);
1923
+ } else {
1924
+ // If the importer doesn't have a layer, get its effective layer
1925
+ const effectiveLayer = getEffectiveModuleLayer(importerId, getModuleInfo, new Set([
1926
+ id
1927
+ ]));
1928
+ if (effectiveLayer) {
1929
+ layers.add(effectiveLayer);
1930
+ }
1931
+ }
1932
+ }
1933
+ return layers;
1934
+ }
1671
1935
  // dependencyGraphMap: Map<subModuleId, Set<entryParentId>>
1672
1936
  function createSplitChunks(dependencyGraphMap, entryFiles) {
1673
1937
  // If there's existing chunk being splitted, and contains a layer { <id>: <chunkGroup> }
@@ -1704,6 +1968,24 @@ function createSplitChunks(dependencyGraphMap, entryFiles) {
1704
1968
  }
1705
1969
  }
1706
1970
  }
1971
+ // Check if this module (without its own directive) is imported by multiple boundaries.
1972
+ // If so, split it into a separate shared chunk to prevent boundary crossing issues.
1973
+ if (!moduleLayer && !isEntry) {
1974
+ const importerLayers = getImporterLayers(id, ctx.getModuleInfo);
1975
+ // If this module is imported by modules with different layers (e.g., both client and server),
1976
+ // split it into a separate chunk that can be safely imported by both boundaries.
1977
+ if (importerLayers.size > 1) {
1978
+ if (splitChunksGroupMap.has(id)) {
1979
+ return splitChunksGroupMap.get(id);
1980
+ }
1981
+ const chunkName = path__default.default.basename(id, path__default.default.extname(id));
1982
+ // Create a unique suffix based on all the layers that import this module
1983
+ const layersSuffix = Array.from(importerLayers).sort().join('-');
1984
+ const chunkGroup = `${chunkName}-${hashTo3Char(layersSuffix)}`;
1985
+ splitChunksGroupMap.set(id, chunkGroup);
1986
+ return chunkGroup;
1987
+ }
1988
+ }
1707
1989
  // If current module has a layer, and it's not an entry
1708
1990
  if (moduleLayer && !isEntry) {
1709
1991
  // If the module is imported by the entry:
@@ -1987,8 +2269,17 @@ async function bundle(cliEntryPath, { cwd: _cwd, onSuccess, ...options } = {}) {
1987
2269
  // Original input file path, client path might change later
1988
2270
  const inputFile = cliEntryPath;
1989
2271
  const isFromCli = Boolean(cliEntryPath);
2272
+ const useTsGo = options.tsgo === true;
2273
+ // Check if ts-go is available when requested (before resolving tsconfig)
2274
+ let tsgoInstance = null;
2275
+ if (useTsGo) {
2276
+ tsgoInstance = resolveTsGo(cwd);
2277
+ if (!tsgoInstance) {
2278
+ exit('--tsgo flag was specified but @typescript/native-preview is not installed. Please install it as a dev dependency: pnpm add -D @typescript/native-preview');
2279
+ }
2280
+ }
1990
2281
  const tsConfigPath = resolveTsConfigPath(cwd, options.tsconfig);
1991
- let tsConfig = resolveTsConfig(cwd, tsConfigPath);
2282
+ let tsConfig = resolveTsConfig(cwd, tsConfigPath, useTsGo);
1992
2283
  let hasTsConfig = Boolean(tsConfig == null ? void 0 : tsConfig.tsConfigPath);
1993
2284
  const defaultTsOptions = {
1994
2285
  tsConfigPath: tsConfig == null ? void 0 : tsConfig.tsConfigPath,
@@ -2058,7 +2349,7 @@ async function bundle(cliEntryPath, { cwd: _cwd, onSuccess, ...options } = {}) {
2058
2349
  // Otherwise, use the existing one.
2059
2350
  const defaultTsConfigPath = path.resolve(cwd, 'tsconfig.json');
2060
2351
  if (!fileExists(defaultTsConfigPath)) {
2061
- await writeDefaultTsconfig(defaultTsConfigPath);
2352
+ await writeDefaultTsconfig(defaultTsConfigPath, useTsGo);
2062
2353
  }
2063
2354
  defaultTsOptions.tsConfigPath = defaultTsConfigPath;
2064
2355
  hasTsConfig = true;
@@ -2070,12 +2361,15 @@ async function bundle(cliEntryPath, { cwd: _cwd, onSuccess, ...options } = {}) {
2070
2361
  const outputState = createOutputState({
2071
2362
  entries
2072
2363
  });
2364
+ // Use ts-go if it was successfully resolved earlier
2365
+ const useTsGoInContext = Boolean(useTsGo && hasTsConfig && tsgoInstance);
2073
2366
  const buildContext = {
2074
2367
  entries,
2075
2368
  pkg,
2076
2369
  cwd,
2077
2370
  tsOptions: defaultTsOptions,
2078
2371
  useTypeScript: hasTsConfig,
2372
+ useTsGo: useTsGoInContext,
2079
2373
  browserslistConfig,
2080
2374
  pluginContext: {
2081
2375
  outputState,
package/package.json CHANGED
@@ -1,10 +1,29 @@
1
1
  {
2
2
  "name": "bunchee",
3
- "version": "6.6.2",
3
+ "version": "6.8.0",
4
4
  "description": "zero config bundler for js/ts/jsx libraries",
5
5
  "bin": "./dist/bin/cli.js",
6
6
  "main": "./dist/index.js",
7
7
  "types": "./dist/index.d.ts",
8
+ "scripts": {
9
+ "test": "vitest run",
10
+ "test:update": "TEST_UPDATE_SNAPSHOT=1 pnpm test",
11
+ "test:post": "cross-env POST_BUILD=1 pnpm test test/compile.test.ts test/integration.test.ts",
12
+ "site": "next dev docs",
13
+ "docs:build": "next build docs",
14
+ "clean": "rm -rf ./dist",
15
+ "new-test": "node ./scripts/new-test.js",
16
+ "typecheck": "tsc --noEmit && tsc -p test/tsconfig.json --noEmit",
17
+ "prepare-release": "pnpm clean && pnpm build",
18
+ "publish-local": "pnpm prepare-release && pnpm test && pnpm publish",
19
+ "prepublishOnly": "pnpm prepare-release && chmod +x ./dist/bin/cli.js",
20
+ "run-ts": "cross-env SWC_NODE_IGNORE_DYNAMIC=1 node -r @swc-node/register",
21
+ "ts-bunchee": "pnpm run-ts ./src/bin/index.ts",
22
+ "build-dir": "pnpm ts-bunchee --cwd",
23
+ "build": "pnpm run-ts ./src/bin/index.ts --runtime node",
24
+ "format": "prettier --write .",
25
+ "prepare": "husky"
26
+ },
8
27
  "type": "commonjs",
9
28
  "keywords": [
10
29
  "bundler",
@@ -37,7 +56,7 @@
37
56
  },
38
57
  "license": "MIT",
39
58
  "dependencies": {
40
- "@rollup/plugin-commonjs": "^28.0.6",
59
+ "@rollup/plugin-commonjs": "^29.0.0",
41
60
  "@rollup/plugin-json": "^6.1.0",
42
61
  "@rollup/plugin-node-resolve": "^16.0.1",
43
62
  "@rollup/plugin-replace": "^6.0.2",
@@ -51,7 +70,7 @@
51
70
  "picomatch": "^4.0.2",
52
71
  "pretty-bytes": "^5.6.0",
53
72
  "rollup": "^4.52.4",
54
- "rollup-plugin-dts": "^6.2.3",
73
+ "rollup-plugin-dts": "^6.3.0",
55
74
  "rollup-plugin-swc3": "^0.11.1",
56
75
  "rollup-preserve-directives": "^1.1.3",
57
76
  "tinyglobby": "^0.2.14",
@@ -59,7 +78,8 @@
59
78
  "yargs": "^17.7.2"
60
79
  },
61
80
  "peerDependencies": {
62
- "typescript": "^4.1 || ^5.0"
81
+ "typescript": "^4.1 || ^5.0",
82
+ "@typescript/native-preview": "*"
63
83
  },
64
84
  "peerDependenciesMeta": {
65
85
  "typescript": {
@@ -67,6 +87,9 @@
67
87
  },
68
88
  "@swc/helpers": {
69
89
  "optional": true
90
+ },
91
+ "@typescript/native-preview": {
92
+ "optional": true
70
93
  }
71
94
  },
72
95
  "devDependencies": {
@@ -79,16 +102,17 @@
79
102
  "@types/picomatch": "^3.0.1",
80
103
  "@types/react": "^19.0.9",
81
104
  "@types/yargs": "^17.0.33",
105
+ "@typescript/native-preview": "*",
82
106
  "bunchee": "link:./",
83
107
  "cross-env": "^7.0.3",
84
108
  "husky": "^9.0.11",
85
109
  "lint-staged": "^15.2.2",
86
- "next": "16.0.0-canary.1",
110
+ "next": "16.0.10",
87
111
  "picocolors": "^1.0.0",
88
112
  "postcss": "^8.5.4",
89
113
  "prettier": "3.4.2",
90
- "react": "^19.0.0",
91
- "react-dom": "^19.0.0",
114
+ "react": "^19.2.1",
115
+ "react-dom": "^19.2.1",
92
116
  "tailwindcss": "^4.1.8",
93
117
  "typescript": "^5.9.2",
94
118
  "vitest": "^3.0.4"
@@ -96,21 +120,5 @@
96
120
  "lint-staged": {
97
121
  "*.{js,jsx,ts,tsx,md,json,yml,yaml}": "prettier --write"
98
122
  },
99
- "scripts": {
100
- "test": "vitest run",
101
- "test:update": "TEST_UPDATE_SNAPSHOT=1 pnpm test",
102
- "test:post": "cross-env POST_BUILD=1 pnpm test test/compile.test.ts test/integration.test.ts",
103
- "docs:dev": "next dev docs",
104
- "docs:build": "next build docs",
105
- "clean": "rm -rf ./dist",
106
- "new-test": "node ./scripts/new-test.js",
107
- "typecheck": "tsc --noEmit && tsc -p test/tsconfig.json --noEmit",
108
- "prepare-release": "pnpm clean && pnpm build",
109
- "publish-local": "pnpm prepare-release && pnpm test && pnpm publish",
110
- "run-ts": "cross-env SWC_NODE_IGNORE_DYNAMIC=1 node -r @swc-node/register",
111
- "ts-bunchee": "pnpm run-ts ./src/bin/index.ts",
112
- "build-dir": "pnpm ts-bunchee --cwd",
113
- "build": "pnpm run-ts ./src/bin/index.ts --runtime node",
114
- "format": "prettier --write ."
115
- }
116
- }
123
+ "packageManager": "pnpm@9.4.0"
124
+ }