bunchee 4.4.8 → 5.0.0-beta.2

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/dist/index.js CHANGED
@@ -105,13 +105,19 @@ const nodeResolveExtensions = [
105
105
  '.node',
106
106
  '.jsx'
107
107
  ];
108
- const suffixedExportConventions = new Set([
108
+ const runtimeExportConventions = new Set([
109
109
  'react-server',
110
110
  'react-native',
111
- 'edge-light',
111
+ 'edge-light'
112
+ ]);
113
+ const optimizeConventions = new Set([
112
114
  'development',
113
115
  'production'
114
116
  ]);
117
+ const specialExportConventions = new Set([
118
+ ...runtimeExportConventions,
119
+ ...optimizeConventions
120
+ ]);
115
121
  const availableESExtensionsRegex = /\.(m|c)?[jt]sx?$/;
116
122
  const SRC = 'src';
117
123
  const dtsExtensionsMap = {
@@ -139,6 +145,7 @@ const DEFAULT_TS_CONFIG = {
139
145
  moduleResolution: 'bundler'
140
146
  }
141
147
  };
148
+ const BINARY_TAG = '$binary';
142
149
 
143
150
  const defaultColorFn = (text)=>text;
144
151
  function color(prefixColor) {
@@ -219,10 +226,15 @@ async function getSourcePathFromExportPath(cwd, exportPath, exportType) {
219
226
  if (exportPath === '.') exportPath = './index';
220
227
  // Find convention-based source file for specific export types
221
228
  // $binary represents `pkg.bin`
222
- if (suffixedExportConventions.has(exportType) && exportType !== '$binary') {
229
+ if (runtimeExportConventions.has(exportType) && exportType !== BINARY_TAG) {
223
230
  const filename = await findSourceEntryFile(cwd, exportPath, exportType, ext);
224
231
  if (filename) return filename;
225
232
  }
233
+ const [, optimizeType] = exportType.split('.');
234
+ if (optimizeConventions.has(optimizeType)) {
235
+ const filename = await findSourceEntryFile(cwd, exportPath, optimizeType, ext);
236
+ if (filename) return filename;
237
+ }
226
238
  const filename = await findSourceEntryFile(cwd, exportPath, null, ext);
227
239
  if (filename) return filename;
228
240
  }
@@ -239,11 +251,16 @@ function filePathWithoutExtension(filePath) {
239
251
  }
240
252
  return filePath;
241
253
  }
242
- const nonNullable = (n)=>Boolean(n);
243
254
  const hasAvailableExtension = (filename)=>availableExtensions.has(path__default.default.extname(filename).slice(1));
244
255
  const hasCjsExtension = (filename)=>path__default.default.extname(filename) === '.cjs';
256
+ const getMainFieldExportType = (pkg)=>{
257
+ const isEsmPkg = isESModulePackage(pkg.type);
258
+ const mainExportType = isEsmPkg && pkg.main ? hasCjsExtension(pkg.main) ? 'require' : 'import' : 'require';
259
+ return mainExportType;
260
+ };
245
261
  // TODO: add unit test
246
262
  const baseNameWithoutExtension = (filename)=>path__default.default.basename(filename, path__default.default.extname(filename));
263
+ const isTestFile = (filename)=>/\.(test|spec)$/.test(baseNameWithoutExtension(filename));
247
264
  const memoize = (fn, resolver)=>{
248
265
  const cache = new Map();
249
266
  return (...args)=>{
@@ -259,6 +276,17 @@ const memoize = (fn, resolver)=>{
259
276
  return result;
260
277
  };
261
278
  };
279
+ function joinRelativePath(...segments) {
280
+ let result = path__default.default.join(...segments);
281
+ // If the first segment starts with '.', ensure the result does too.
282
+ if (segments[0] === '.' && !result.startsWith('.')) {
283
+ result = './' + result;
284
+ }
285
+ return result;
286
+ }
287
+ function isESModulePackage(packageType) {
288
+ return packageType === 'module';
289
+ }
262
290
 
263
291
  let hasLoggedTsWarning = false;
264
292
  function resolveTypescriptHandler(cwd) {
@@ -569,268 +597,127 @@ function prependDirectives() {
569
597
  };
570
598
  }
571
599
 
572
- function getPackageTypings(pkg) {
573
- return pkg.types || pkg.typings;
574
- }
575
- // Reached the end of the export path
576
- function isExportLike(field) {
577
- if (typeof field === 'string') return true;
578
- return Object.entries(field).every(// Every value is string and key is not start with '.'
579
- ([key, value])=>typeof value === 'string' && !key.startsWith('.'));
580
- }
581
- function constructFullExportCondition(exportCondition, packageType) {
582
- let fullExportCond;
583
- if (typeof exportCondition === 'string') {
584
- const exportType = getExportTypeFromFile(exportCondition, packageType);
585
- fullExportCond = {
586
- [exportType]: exportCondition
587
- };
588
- } else {
589
- const exportTypes = Object.keys(exportCondition);
590
- fullExportCond = {};
591
- exportTypes.forEach((exportType)=>{
592
- const condition = exportCondition[exportType];
593
- // Filter out nullable value
594
- if (condition) {
595
- fullExportCond[exportType] = condition;
596
- }
597
- });
598
- }
599
- return fullExportCond;
600
- }
601
- function joinRelativePath(...segments) {
602
- let result = path.join(...segments);
603
- // If the first segment starts with '.', ensure the result does too.
604
- if (segments[0] === '.' && !result.startsWith('.')) {
605
- result = './' + result;
606
- }
607
- return result;
608
- }
609
- const getFirstExportPath = (fullExportCondition)=>{
610
- // Handle all export cond { <require|import|default>: ... }
611
- if (typeof fullExportCondition === 'object') {
612
- for (const key of Object.keys(fullExportCondition)){
613
- if (key.startsWith('.') || key === 'types') {
614
- continue;
615
- }
616
- return fullExportCondition[key];
617
- }
618
- }
619
- return fullExportCondition;
620
- };
621
- const joinExportAndCondition = (exportPath, condition)=>{
622
- return (exportPath === '.' ? '' : exportPath) + '.' + condition;
623
- };
624
- function findExport(exportPath, exportCondition, paths, packageType, currentPath) {
625
- // Skip `types` field, it cannot be the entry point
626
- if (exportPath === 'types') return;
627
- if (isExportLike(exportCondition)) {
628
- const fullExportCondition = constructFullExportCondition(exportCondition, packageType);
629
- if (exportPath.startsWith('.')) {
630
- paths[exportPath] = {
631
- ...paths[exportPath],
632
- ...fullExportCondition
633
- };
600
+ function collectExportPath(exportValue, exportKey, currentPath, exportTypes, exportToDist) {
601
+ // End of searching, export value is file path.
602
+ // <export key>: <export value> (string)
603
+ if (typeof exportValue === 'string') {
604
+ const composedTypes = new Set(exportTypes);
605
+ composedTypes.add(exportKey.startsWith('.') ? 'default' : exportKey);
606
+ const exportInfo = exportToDist.get(currentPath);
607
+ const exportCondition = Array.from(composedTypes).join('.');
608
+ if (!exportInfo) {
609
+ const outputConditionPair = [
610
+ exportValue,
611
+ exportCondition
612
+ ];
613
+ exportToDist.set(currentPath, [
614
+ outputConditionPair
615
+ ]);
634
616
  } else {
635
- const exportJsBundlePath = getFirstExportPath(fullExportCondition);
636
- if (suffixedExportConventions.has(exportPath)) {
637
- const specialPath = joinExportAndCondition(currentPath, exportPath);
638
- paths[specialPath] = {
639
- ...paths[specialPath],
640
- ...exportCondition
641
- };
642
- } else {
643
- // exportPath is exportType, import, require, ...
644
- // merge to currentPath
645
- paths[currentPath] = {
646
- ...paths[currentPath],
647
- [exportPath]: exportJsBundlePath
648
- };
649
- }
617
+ exportInfo.push([
618
+ exportValue,
619
+ exportCondition
620
+ ]);
650
621
  }
651
622
  return;
652
623
  }
653
- Object.keys(exportCondition).forEach((subpath)=>{
654
- if (subpath.startsWith('.')) {
655
- // subpath is actual export path, ./a, ./b, ...
656
- const nestedExportPath = joinRelativePath(currentPath, subpath);
657
- const nestedExportCondition = exportCondition[subpath];
658
- findExport(nestedExportPath, nestedExportCondition, paths, packageType, nestedExportPath);
659
- } else {
660
- // subpath is exportType, import, require, ...
661
- const exportType = subpath;
662
- if (typeof exportCondition[subpath] === 'object') {
663
- const defaultPath = exportCondition[subpath].default;
664
- if (defaultPath) {
665
- const nestedExportCondition = {
666
- [exportType]: defaultPath
667
- };
668
- findExport(exportPath, nestedExportCondition, paths, packageType, currentPath);
669
- }
670
- // Find special export type, such as import: { development: './dev.js', production: './prod.js' }
671
- const conditionSpecialTypes = Object.keys(exportCondition[exportType]).filter((key)=>suffixedExportConventions.has(key));
672
- if (conditionSpecialTypes.length > 0) {
673
- for (const conditionSpecialType of conditionSpecialTypes){
674
- const nestedExportConditionPath = {
675
- [exportType]: exportCondition[exportType][conditionSpecialType]
676
- };
677
- findExport(conditionSpecialType, nestedExportConditionPath, paths, packageType, currentPath);
678
- }
679
- }
680
- }
681
- const defaultPath = typeof exportCondition[subpath] === 'object' ? exportCondition[subpath].default : exportCondition[subpath];
682
- const nestedExportCondition = {
683
- [exportType]: defaultPath
684
- };
685
- findExport(exportPath, nestedExportCondition, paths, packageType, currentPath);
686
- }
687
- });
688
- }
689
- /**
690
- *
691
- * Convert package.exports field to paths mapping
692
- * Example
693
- *
694
- * Input:
695
- * {
696
- * "./sub": {
697
- * "import": {
698
- * "types": "./sub.js",
699
- * "default": "./sub.cjs",
700
- * }
701
- * }
702
- * }
703
- *
704
- * Output:
705
- * {
706
- * "./sub": {
707
- * "import": "./sub.js",
708
- * "require": "./sub.cjs",
709
- * "types": "./sub.d.ts",
710
- * }
711
- * }
712
- *
713
- */ function parseExport(exportsCondition, packageType) {
714
- const paths = {};
715
- const initialPath = '.';
716
- if (typeof exportsCondition === 'string') {
717
- paths[initialPath] = constructFullExportCondition(exportsCondition, packageType);
718
- } else if (typeof exportsCondition === 'object') {
719
- if (isExportLike(exportsCondition)) {
720
- paths[initialPath] = constructFullExportCondition(exportsCondition, packageType);
624
+ const exportKeys = Object.keys(exportValue);
625
+ for (const exportKey of exportKeys){
626
+ // Clone the set to avoid modifying the parent set
627
+ const childExports = new Set(exportTypes);
628
+ // Normalize child export value to a map
629
+ const childExportValue = exportValue[exportKey];
630
+ // Visit export path: ./subpath, ./subpath2, ...
631
+ if (exportKey.startsWith('.')) {
632
+ const childPath = joinRelativePath(currentPath, exportKey);
633
+ collectExportPath(childExportValue, exportKey, childPath, childExports, exportToDist);
721
634
  } else {
722
- Object.keys(exportsCondition).forEach((key)=>{
723
- const exportCondition = exportsCondition[key];
724
- findExport(key, exportCondition, paths, packageType, initialPath);
725
- });
635
+ // Visit export type: import, require, ...
636
+ childExports.add(exportKey);
637
+ collectExportPath(childExportValue, exportKey, currentPath, childExports, exportToDist);
726
638
  }
727
639
  }
728
- return paths;
729
640
  }
730
641
  /**
731
- * Get package exports paths
642
+ * parseExports - parse package.exports field and other fields like main,module to a map
732
643
  *
733
- * Example:
734
- *
735
- * ```json
736
- * {
737
- * "exports": {
738
- * ".": {
739
- * "require": "./dist/index.cjs",
740
- * "module": "./dist/index.esm.js",
741
- * "default": "./dist/index.esm.js"
742
- * },
743
- * "./foo": {
744
- * "require": "./dist/foo.cjs",
745
- * "module": "./dist/foo.esm.js",
746
- * "default": "./dist/foo.esm.js"
747
- * }
748
- * }
749
- *
750
- * ```
751
- *
752
- * will be parsed to:
753
- *
754
- * ```js
755
- * {
756
- * '.': {
757
- * main: './dist/index.cjs',
758
- * module: './dist/index.esm.js',
759
- * export: './dist/index.esm.js'
760
- * },
761
- * './foo': {
762
- * main: './dist/foo.cjs',
763
- * module: './dist/foo.esm.js',
764
- * export: './dist/foo.esm.js'
765
- * }
766
- *
767
- *
768
- * pkg.main and pkg.module will be added to ['.'] if exists
769
- */ function getExportPaths(pkg, resolvedWildcardExports) {
770
- var _pathsMap_;
771
- let pathsMap = {};
772
- const packageType = getPackageType(pkg);
773
- const isEsmPackage = isESModulePackage(packageType);
774
- const exportsConditions = resolvedWildcardExports != null ? resolvedWildcardExports : pkg.exports;
775
- if (exportsConditions) {
776
- const paths = parseExport(exportsConditions, packageType);
777
- pathsMap = {
778
- ...pathsMap,
779
- ...paths
780
- };
781
- }
782
- // main export '.' from main/module/typings
783
- let mainExportCondition;
784
- if (pkg.main) {
785
- const mainExportType = isEsmPackage ? hasCjsExtension(pkg.main) ? 'require' : 'import' : 'require';
786
- mainExportCondition = {
787
- [mainExportType]: pkg.main
788
- };
789
- }
790
- const defaultMainExport = constructFullExportCondition({
791
- ...mainExportCondition,
792
- module: pkg.module,
793
- types: getPackageTypings(pkg)
794
- }, packageType);
795
- if (!isEsmPackage && ((_pathsMap_ = pathsMap['.']) == null ? void 0 : _pathsMap_['require'])) {
796
- // pathsMap's exports.require are prioritized.
797
- defaultMainExport['require'] = pathsMap['.']['require'];
798
- }
799
- // Merge the main export into '.' paths
800
- const mainExport = {
801
- ...pathsMap['.'],
802
- ...defaultMainExport
803
- };
804
- // main export is not empty
805
- if (Object.keys(mainExport).length > 0) {
806
- pathsMap['.'] = {
807
- ...pathsMap['.'],
808
- ...mainExport
809
- };
810
- }
811
- return pathsMap;
812
- }
813
- function getPackageType(pkg) {
814
- return pkg.type || 'commonjs';
815
- }
816
- function isESModulePackage(packageType) {
817
- return packageType === 'module';
818
- }
819
- function constructDefaultExportCondition(value, packageType) {
820
- const isEsmPackage = isESModulePackage(packageType);
821
- let exportCondition;
822
- if (typeof value === 'string') {
823
- const types = getPackageTypings(value);
824
- exportCondition = {
825
- [isEsmPackage ? 'import' : 'require']: value,
826
- ...types && {
827
- types
644
+ * map from export path to output path and export conditions
645
+ */ function parseExports(pkg) {
646
+ var _pkg_exports;
647
+ const exportsField = (_pkg_exports = pkg.exports) != null ? _pkg_exports : {};
648
+ var _pkg_bin;
649
+ const bins = (_pkg_bin = pkg.bin) != null ? _pkg_bin : {};
650
+ const exportToDist = new Map();
651
+ const isEsmPkg = isESModulePackage(pkg.type);
652
+ const defaultCondition = isEsmPkg ? 'import' : 'require';
653
+ let currentPath = '.';
654
+ if (typeof exportsField === 'string') {
655
+ const outputConditionPair = [
656
+ exportsField,
657
+ defaultCondition
658
+ ];
659
+ exportToDist.set(currentPath, [
660
+ outputConditionPair
661
+ ]);
662
+ } else {
663
+ // keys means unknown if they're relative path or export type
664
+ const exportConditionKeys = Object.keys(exportsField);
665
+ for (const exportKey of exportConditionKeys){
666
+ const exportValue = exportsField[exportKey];
667
+ const exportTypes = new Set();
668
+ const isExportPath = exportKey.startsWith('.');
669
+ const childPath = isExportPath ? joinRelativePath(currentPath, exportKey) : currentPath;
670
+ if (!isExportPath) {
671
+ exportTypes.add(exportKey);
828
672
  }
829
- };
673
+ collectExportPath(exportValue, exportKey, childPath, exportTypes, exportToDist);
674
+ }
675
+ }
676
+ if (typeof bins === 'string') {
677
+ const outputConditionPair = [
678
+ bins,
679
+ defaultCondition
680
+ ];
681
+ exportToDist.set(BINARY_TAG, [
682
+ outputConditionPair
683
+ ]);
830
684
  } else {
831
- exportCondition = value;
685
+ for (const binName of Object.keys(bins)){
686
+ const binDistPath = bins[binName];
687
+ const exportType = getExportTypeFromFile(binDistPath, pkg.type);
688
+ const exportPath = path.posix.join(BINARY_TAG, binName);
689
+ const outputConditionPair = [
690
+ binDistPath,
691
+ exportType
692
+ ];
693
+ exportToDist.set(exportPath, [
694
+ outputConditionPair
695
+ ]);
696
+ }
832
697
  }
833
- return constructFullExportCondition(exportCondition, packageType);
698
+ // Handle package.json global exports fields
699
+ if (pkg.main || pkg.module || pkg.types) {
700
+ const mainExportPath = pkg.main;
701
+ const moduleExportPath = pkg.module;
702
+ const typesEntryPath = pkg.types;
703
+ const existingExportInfo = exportToDist.get('.');
704
+ exportToDist.set('.', [
705
+ ...existingExportInfo || [],
706
+ Boolean(mainExportPath) && [
707
+ mainExportPath,
708
+ getMainFieldExportType(pkg)
709
+ ],
710
+ Boolean(moduleExportPath) && [
711
+ moduleExportPath,
712
+ 'import'
713
+ ],
714
+ Boolean(typesEntryPath) && [
715
+ typesEntryPath,
716
+ 'types'
717
+ ]
718
+ ].filter(Boolean));
719
+ }
720
+ return exportToDist;
834
721
  }
835
722
  function isEsmExportName(name, ext) {
836
723
  return [
@@ -847,12 +734,21 @@ function isCjsExportName(pkg, exportCondition, ext) {
847
734
  const isNotEsmExportName = !isEsmExportName(exportCondition, ext);
848
735
  return !isESModule && isNotEsmExportName && (ext !== 'mjs' || isCjsCondition) || ext === 'cjs';
849
736
  }
850
- function getExportsDistFilesOfCondition(pkg, parsedExportCondition, cwd) {
737
+ function getFileExportType(composedTypes) {
738
+ return composedTypes.split('.').pop();
739
+ }
740
+ function getExportsDistFilesOfCondition(pkg, parsedExportCondition, cwd, dts) {
851
741
  const dist = [];
852
742
  const exportConditionNames = Object.keys(parsedExportCondition.export);
853
743
  const uniqueFiles = new Set();
854
744
  for (const exportCondition of exportConditionNames){
855
- if (exportCondition === 'types') {
745
+ const exportType = getFileExportType(exportCondition);
746
+ // Filter out non-types field when generating types jobs
747
+ if (dts && exportType !== 'types') {
748
+ continue;
749
+ }
750
+ // Filter out types field when generating asset jobs
751
+ if (!dts && exportType === 'types') {
856
752
  continue;
857
753
  }
858
754
  const filePath = parsedExportCondition.export[exportCondition];
@@ -866,7 +762,8 @@ function getExportsDistFilesOfCondition(pkg, parsedExportCondition, cwd) {
866
762
  uniqueFiles.add(distFile);
867
763
  dist.push({
868
764
  format,
869
- file: distFile
765
+ file: distFile,
766
+ exportCondition
870
767
  });
871
768
  }
872
769
  return dist;
@@ -886,17 +783,9 @@ function getExportTypeFromFile(filename, pkgType) {
886
783
  return exportType;
887
784
  }
888
785
 
889
- const swcMinifyOptions = {
890
- compress: true,
891
- format: {
892
- comments: 'some'
893
- },
894
- mangle: {
895
- toplevel: true
896
- }
897
- };
898
- // return { 'process.env.<key>': '<value>' }
899
- function getDefinedInlineVariables(envs, parsedExportCondition) {
786
+ /**
787
+ * @return {Record<string, string>} env { 'process.env.<key>': '<value>' }
788
+ */ function getDefinedInlineVariables(envs, parsedExportCondition) {
900
789
  if (!envs.includes('NODE_ENV')) {
901
790
  envs.push('NODE_ENV');
902
791
  }
@@ -907,9 +796,14 @@ function getDefinedInlineVariables(envs, parsedExportCondition) {
907
796
  }
908
797
  return acc;
909
798
  }, {});
910
- // handle .development, .production
911
- const condName = parsedExportCondition.name.startsWith('.') ? parsedExportCondition.name.slice(1) : parsedExportCondition.name;
912
- const exportConditionNames = new Set(Object.keys(parsedExportCondition.export).concat(condName));
799
+ const exportConditionNames = Object.keys(parsedExportCondition.export).reduce((acc, key)=>{
800
+ // key could be 'require' or 'import.development' etc.
801
+ const exportTypes = key.split('.');
802
+ for (const exportType of exportTypes){
803
+ acc.add(exportType);
804
+ }
805
+ return acc;
806
+ }, new Set());
913
807
  // For development and production convention, we override the NODE_ENV value
914
808
  if (exportConditionNames.has('development')) {
915
809
  envVars['process.env.NODE_ENV'] = JSON.stringify('development');
@@ -921,17 +815,245 @@ function getDefinedInlineVariables(envs, parsedExportCondition) {
921
815
  }
922
816
  return envVars;
923
817
  }
818
+
819
+ // shared.ts -> ./shared
820
+ // shared.<export condition>.ts -> ./shared
821
+ // index.ts -> ./index
822
+ // index.development.ts -> ./index.development
823
+ function sourceFilenameToExportPath(filename) {
824
+ const baseName = baseNameWithoutExtension(filename);
825
+ let exportPath = baseName;
826
+ return relativify(exportPath);
827
+ }
828
+ async function collectEntriesFromParsedExports(cwd, parsedExportsInfo, sourceFile) {
829
+ const entries = {};
830
+ if (sourceFile) {
831
+ const defaultExport = parsedExportsInfo.get('.')[0];
832
+ entries['.'] = {
833
+ source: sourceFile,
834
+ name: '.',
835
+ export: {
836
+ default: defaultExport[0]
837
+ }
838
+ };
839
+ }
840
+ // Find source files
841
+ const { bins, exportsEntries } = await collectSourceEntries(path.join(cwd, SRC));
842
+ // A mapping between each export path and its related special export conditions,
843
+ // excluding the 'default' export condition.
844
+ // { '.' => Set('development') }
845
+ const pathSpecialConditionsMap = {};
846
+ for (const [exportPath] of exportsEntries){
847
+ const normalizedExportPath = normalizeExportPath(exportPath);
848
+ if (!pathSpecialConditionsMap[normalizedExportPath]) {
849
+ pathSpecialConditionsMap[normalizedExportPath] = new Set();
850
+ }
851
+ const exportType = getExportTypeFromExportPath(exportPath);
852
+ if (exportType !== 'default') {
853
+ pathSpecialConditionsMap[normalizedExportPath].add(exportType);
854
+ }
855
+ }
856
+ // Traverse source files and try to match the entries
857
+ // Find exports from parsed exports info
858
+ // entryExportPath can be: '.', './index.development', './shared.edge-light', etc.
859
+ for (const [entryExportPath, sourceFilesMap] of exportsEntries){
860
+ const normalizedExportPath = normalizeExportPath(entryExportPath);
861
+ const entryExportPathType = getExportTypeFromExportPath(entryExportPath);
862
+ const outputExports = parsedExportsInfo.get(normalizedExportPath);
863
+ if (!outputExports) {
864
+ continue;
865
+ }
866
+ for (const [outputPath, composedExportType] of outputExports){
867
+ const matchedExportType = getSpecialExportTypeFromExportPath(composedExportType);
868
+ // export type can be: default, development, react-server, etc.
869
+ const sourceFile = sourceFilesMap[matchedExportType] || sourceFilesMap.default;
870
+ if (!sourceFile) {
871
+ continue;
872
+ }
873
+ if (!entries[entryExportPath]) {
874
+ entries[entryExportPath] = {
875
+ source: sourceFile,
876
+ name: normalizedExportPath,
877
+ export: {}
878
+ };
879
+ }
880
+ const exportMap = entries[entryExportPath].export;
881
+ if (entryExportPathType === 'default' && matchedExportType !== 'default' && pathSpecialConditionsMap[normalizedExportPath].size > 0) {
882
+ continue;
883
+ }
884
+ exportMap[composedExportType] = outputPath;
885
+ }
886
+ }
887
+ // Handling binaries
888
+ for (const [exportPath, sourceFile] of bins){
889
+ const normalizedExportPath = normalizeExportPath(exportPath);
890
+ const outputExports = parsedExportsInfo.get(normalizedExportPath);
891
+ if (!outputExports) {
892
+ continue;
893
+ }
894
+ for (const [outputPath, exportType] of outputExports){
895
+ entries[exportPath] = {
896
+ source: sourceFile,
897
+ name: exportPath,
898
+ export: {
899
+ [exportType]: outputPath
900
+ }
901
+ };
902
+ }
903
+ }
904
+ return entries;
905
+ }
906
+ // ./index -> import|require|default
907
+ // ./index.development -> development
908
+ // ./index.react-server -> react-server
909
+ function getExportTypeFromExportPath(exportPath) {
910
+ // Skip the first two segments: `.` and `index`
911
+ const exportTypes = exportPath.split('.').slice(2);
912
+ return getExportTypeFromExportTypes(exportTypes);
913
+ }
914
+ function getSpecialExportTypeFromExportPath(composedExportType) {
915
+ const exportTypes = composedExportType.split('.');
916
+ for (const exportType of exportTypes){
917
+ if (specialExportConventions.has(exportType)) {
918
+ return exportType;
919
+ }
920
+ }
921
+ return 'default';
922
+ }
923
+ function getExportTypeFromExportTypes(types) {
924
+ let exportType = 'default';
925
+ new Set(types).forEach((value)=>{
926
+ if (specialExportConventions.has(value)) {
927
+ exportType = value;
928
+ } else if (value === 'import' || value === 'require' || value === 'types') {
929
+ exportType = value;
930
+ }
931
+ });
932
+ return exportType;
933
+ }
934
+ // ./index -> .
935
+ // ./index.development -> .
936
+ // ./index.react-server -> .
937
+ // ./shared -> ./shared
938
+ // ./shared.development -> ./shared
939
+ // $binary -> $binary
940
+ // $binary/index -> $binary
941
+ // $binary/foo -> $binary/foo
942
+ function normalizeExportPath(exportPath) {
943
+ if (exportPath.startsWith(BINARY_TAG)) {
944
+ if (exportPath === `${BINARY_TAG}/index`) {
945
+ exportPath = BINARY_TAG;
946
+ }
947
+ return exportPath;
948
+ }
949
+ const baseName = exportPath.split('.').slice(0, 2).join('.');
950
+ if (baseName === './index') {
951
+ return '.';
952
+ }
953
+ return baseName;
954
+ }
955
+ async function collectSourceEntries(sourceFolderPath) {
956
+ const bins = new Map();
957
+ const exportsEntries = new Map();
958
+ if (!fs__default.default.existsSync(sourceFolderPath)) {
959
+ return {
960
+ bins,
961
+ exportsEntries
962
+ };
963
+ }
964
+ const entryFileDirentList = await fsp__default.default.readdir(sourceFolderPath, {
965
+ withFileTypes: true
966
+ });
967
+ for (const dirent of entryFileDirentList){
968
+ if (dirent.isDirectory()) {
969
+ if (dirent.name === 'bin') {
970
+ const binDirentList = await fsp__default.default.readdir(path__default.default.join(sourceFolderPath, dirent.name), {
971
+ withFileTypes: true
972
+ });
973
+ for (const binDirent of binDirentList){
974
+ if (binDirent.isFile()) {
975
+ const binFileAbsolutePath = path__default.default.join(sourceFolderPath, dirent.name, binDirent.name);
976
+ const binExportPath = sourceFilenameToExportPath(binDirent.name);
977
+ if (fs__default.default.existsSync(binFileAbsolutePath)) {
978
+ bins.set(path.posix.join(BINARY_TAG, binExportPath), binFileAbsolutePath);
979
+ }
980
+ }
981
+ }
982
+ } else {
983
+ // Search folder/index.<ext> convention entries
984
+ for (const extension of availableExtensions){
985
+ const indexAbsoluteFile = path__default.default.join(dirent.path, dirent.name, `index.${extension}`);
986
+ // Search folder/index.<special type>.<ext> convention entries
987
+ for (const specialExportType of runtimeExportConventions){
988
+ const indexSpecialAbsoluteFile = path__default.default.join(dirent.path, dirent.name, `index.${specialExportType}.${extension}`);
989
+ if (fs__default.default.existsSync(indexSpecialAbsoluteFile)) {
990
+ // Add special export path
991
+ // { ./<export path>.<special cond>: { <special cond>: 'index.<special cond>.<ext>' } }
992
+ const exportPath = sourceFilenameToExportPath(dirent.name);
993
+ const specialExportPath = exportPath + '.' + specialExportType;
994
+ const sourceFilesMap = exportsEntries.get(specialExportPath) || {};
995
+ sourceFilesMap[specialExportType] = indexSpecialAbsoluteFile;
996
+ exportsEntries.set(specialExportPath, sourceFilesMap);
997
+ }
998
+ }
999
+ if (fs__default.default.existsSync(indexAbsoluteFile) && !isTestFile(indexAbsoluteFile)) {
1000
+ const exportPath = sourceFilenameToExportPath(dirent.name);
1001
+ const sourceFilesMap = exportsEntries.get(exportPath) || {};
1002
+ const exportType = getExportTypeFromExportPath(exportPath);
1003
+ sourceFilesMap[exportType] = indexAbsoluteFile;
1004
+ exportsEntries.set(exportPath, sourceFilesMap);
1005
+ break;
1006
+ }
1007
+ }
1008
+ }
1009
+ } else if (dirent.isFile()) {
1010
+ const isAvailableExtension = availableExtensions.has(path__default.default.extname(dirent.name).slice(1));
1011
+ if (isAvailableExtension) {
1012
+ const exportPath = sourceFilenameToExportPath(dirent.name);
1013
+ const isBinFile = exportPath === './bin';
1014
+ const fullPath = path__default.default.join(sourceFolderPath, dirent.name);
1015
+ if (isBinFile) {
1016
+ bins.set(BINARY_TAG, fullPath);
1017
+ } else {
1018
+ if (hasAvailableExtension(dirent.name) && !isTestFile(dirent.name)) {
1019
+ const sourceFilesMap = exportsEntries.get(exportPath) || {};
1020
+ const exportType = getExportTypeFromExportPath(exportPath);
1021
+ sourceFilesMap[exportType] = fullPath;
1022
+ exportsEntries.set(exportPath, sourceFilesMap);
1023
+ }
1024
+ }
1025
+ }
1026
+ }
1027
+ }
1028
+ return {
1029
+ bins,
1030
+ exportsEntries
1031
+ };
1032
+ }
1033
+
1034
+ const swcMinifyOptions = {
1035
+ compress: {
1036
+ directives: false
1037
+ },
1038
+ format: {
1039
+ comments: 'some'
1040
+ },
1041
+ mangle: {
1042
+ toplevel: true
1043
+ }
1044
+ };
924
1045
  /**
925
1046
  * return {
926
1047
  * <absolute source path>: <pkg>/<export>
927
1048
  * }
928
- */ function getReversedAlias(entries) {
1049
+ */ function getReversedAlias({ entries, name }) {
929
1050
  const alias = {};
930
- for (const [entryImportPath, exportCondition] of Object.entries(entries)){
931
- const exportType = entryImportPath.split('.')[1] // e.g. index.react-server, pick react-server
932
- ;
933
- if (!exportType) {
934
- alias[exportCondition.source] = entryImportPath;
1051
+ for (const [entryExportPath, exportCondition] of Object.entries(entries)){
1052
+ const normalizedExportPath = normalizeExportPath(entryExportPath);
1053
+ // entryExportPath format: ./index, ./shared, etc.
1054
+ const specialExportType = getSpecialExportTypeFromExportPath(entryExportPath);
1055
+ if (specialExportType === 'default') {
1056
+ alias[exportCondition.source] = path.posix.join(name || '', normalizedExportPath);
935
1057
  }
936
1058
  }
937
1059
  return alias;
@@ -949,7 +1071,7 @@ async function buildInputConfig(entry, bundleConfig, exportCondition, buildConte
949
1071
  for (const [exportImportPath, exportCondition] of Object.entries(entries)){
950
1072
  const entryFilePath = exportCondition.source;
951
1073
  if (entryFilePath !== entry) {
952
- externals.push(exportImportPath);
1074
+ externals.push(path.posix.join(pkg.name || '', normalizeExportPath(exportImportPath)));
953
1075
  externals.push(entryFilePath);
954
1076
  }
955
1077
  }
@@ -1083,25 +1205,13 @@ async function buildInputConfig(entry, bundleConfig, exportCondition, buildConte
1083
1205
  if (dts && code === 'EMPTY_BUNDLE') return;
1084
1206
  if (disabledWarnings.has(code)) return;
1085
1207
  // If the circular dependency warning is from node_modules, ignore it
1086
- if (code === 'CIRCULAR_DEPENDENCY' && /Circular dependency: node_modules/.test(warning.message)) {
1208
+ if (code === 'CIRCULAR_DEPENDENCY' && /Circular dependency:(\s|\S)*node_modules/.test(warning.message)) {
1087
1209
  return;
1088
1210
  }
1089
1211
  warn(warning);
1090
1212
  }
1091
1213
  };
1092
1214
  }
1093
- function hasEsmExport(exportPaths, tsCompilerOptions) {
1094
- let hasEsm = false;
1095
- for(const key in exportPaths){
1096
- const exportInfo = exportPaths[key];
1097
- const exportInfoEntries = Object.entries(exportInfo);
1098
- if (exportInfoEntries.some(([exportType, file])=>isEsmExportName(exportType, file))) {
1099
- hasEsm = true;
1100
- break;
1101
- }
1102
- }
1103
- return Boolean(hasEsm || (tsCompilerOptions == null ? void 0 : tsCompilerOptions.esModuleInterop));
1104
- }
1105
1215
  function getModuleLater(moduleMeta) {
1106
1216
  const directives = (moduleMeta.preserveDirectives || {
1107
1217
  directives: []
@@ -1109,6 +1219,18 @@ function getModuleLater(moduleMeta) {
1109
1219
  const moduleLayer = directives[0];
1110
1220
  return moduleLayer;
1111
1221
  }
1222
+ function getCustomModuleLayer(moduleId) {
1223
+ const segments = path__default.default.basename(moduleId).split('.');
1224
+ if (segments.length >= 2) {
1225
+ const [layerSegment, ext] = segments.slice(-2);
1226
+ const match = layerSegment.match(/^(\w+)-runtime$/);
1227
+ const layer = match && match[1];
1228
+ if (availableExtensions.has(ext) && layer && layer.length > 0) {
1229
+ return layer;
1230
+ }
1231
+ }
1232
+ return undefined;
1233
+ }
1112
1234
  // dependencyGraphMap: Map<subModuleId, Set<entryParentId>>
1113
1235
  function createSplitChunks(dependencyGraphMap, entryFiles) {
1114
1236
  // If there's existing chunk being splitted, and contains a layer { <id>: <chunkGroup> }
@@ -1141,6 +1263,15 @@ function createSplitChunks(dependencyGraphMap, entryFiles) {
1141
1263
  }
1142
1264
  }
1143
1265
  }
1266
+ if (!isEntry) {
1267
+ const cachedCustomModuleLayer = splitChunksGroupMap.get(id);
1268
+ if (cachedCustomModuleLayer) return cachedCustomModuleLayer;
1269
+ const customModuleLayer = getCustomModuleLayer(id);
1270
+ if (customModuleLayer) {
1271
+ splitChunksGroupMap.set(id, customModuleLayer);
1272
+ return customModuleLayer;
1273
+ }
1274
+ }
1144
1275
  // If current module has a layer, and it's not an entry
1145
1276
  if (moduleLayer && !isEntry) {
1146
1277
  // If the module is imported by the entry:
@@ -1176,9 +1307,11 @@ function createSplitChunks(dependencyGraphMap, entryFiles) {
1176
1307
  }
1177
1308
  async function buildOutputConfigs(entry, bundleConfig, exportCondition, buildContext, dts) {
1178
1309
  const { format } = bundleConfig;
1179
- const { entries, pkg, exportPaths, cwd, tsOptions: { tsCompilerOptions }, pluginContext } = buildContext;
1310
+ const { entries, pkg, // exportPaths,
1311
+ cwd, tsOptions: { tsCompilerOptions }, pluginContext } = buildContext;
1180
1312
  // Add esm mark and interop helper if esm export is detected
1181
- const useEsModuleMark = hasEsmExport(exportPaths, tsCompilerOptions);
1313
+ const useEsModuleMark = tsCompilerOptions == null ? void 0 : tsCompilerOptions.esModuleInterop // hasEsmExport(exportPaths, tsCompilerOptions)
1314
+ ;
1182
1315
  const absoluteOutputFile = path.resolve(cwd, bundleConfig.file);
1183
1316
  const outputFileExtension = path.extname(absoluteOutputFile);
1184
1317
  const name = filePathWithoutExtension(absoluteOutputFile);
@@ -1223,168 +1356,81 @@ async function buildEntryConfig(bundleConfig, pluginContext, dts) {
1223
1356
  }
1224
1357
  return configs;
1225
1358
  }
1226
- async function collectEntry(// export type, e.g. react-server, edge-light those special cases required suffix
1227
- exportType, options) {
1228
- const { cwd, pkg, entries, entryPath, exportCondRef, entryExport: originEntryExport } = options;
1229
- let entryExport = originEntryExport;
1230
- let exportCondForType = {
1231
- ...exportCondRef
1232
- };
1233
- // Special cases of export type, only pass down the exportPaths for the type
1234
- if (suffixedExportConventions.has(exportType)) {
1235
- exportCondForType = {
1236
- [exportType]: exportCondRef[exportType]
1237
- };
1238
- } else if (exportType[0] === '.' && suffixedExportConventions.has(exportType.slice(1))) {
1239
- // e.g. .development, .production that has both esm and cjs export
1240
- exportCondForType = exportCondRef;
1241
- exportType = exportType.slice(1);
1242
- entryExport = entryExport.replace(exportType, '');
1243
- } else {
1244
- // Basic export type, pass down the exportPaths with erasing the special ones
1245
- for (const exportType of suffixedExportConventions){
1246
- delete exportCondForType[exportType];
1247
- }
1248
- }
1249
- let source = entryPath;
1250
- if (source) {
1251
- source = resolveSourceFile(cwd, source);
1252
- } else {
1253
- source = await getSourcePathFromExportPath(cwd, entryExport, exportType);
1254
- }
1255
- if (!source) {
1256
- return;
1257
- }
1258
- const exportCondition = {
1259
- source,
1260
- name: originEntryExport,
1261
- export: exportCondForType
1262
- };
1263
- const nameWithExportPath = pkg.name ? path__default.default.join(pkg.name, exportCondition.name) : exportCondition.name;
1264
- const needsDelimiter = !nameWithExportPath.endsWith('.') && exportType;
1265
- const entryImportPath = nameWithExportPath + (needsDelimiter ? '.' : '') + exportType;
1266
- entries[entryImportPath] = exportCondition;
1267
- }
1268
- /*
1269
- * build configs for each entry from package exports
1270
- *
1271
- * return { <pkg>/<export>: { input: InputOptions, output: OutputOptions[] }
1272
- */ async function collectEntries(pkg, entryPath, exportPaths, cwd) {
1273
- const entries = {};
1274
- const binaryExports = pkg.bin;
1275
- if (binaryExports) {
1276
- // binDistPaths: [ [ 'bin1', './dist/bin1.js'], [ 'bin2', './dist/bin2.js'] ]
1277
- const binPairs = typeof binaryExports === 'string' ? [
1278
- [
1279
- 'bin',
1280
- binaryExports
1281
- ]
1282
- ] : Object.keys(binaryExports).map((key)=>[
1283
- path.join('bin', key),
1284
- binaryExports[key]
1285
- ]);
1286
- const binExportPaths = binPairs.reduce((acc, [binName, binDistPath])=>{
1287
- const exportType = getExportTypeFromFile(binDistPath, pkg.type);
1288
- acc[binName] = {
1289
- [exportType]: binDistPath
1290
- };
1291
- return acc;
1292
- }, {});
1293
- for (const [binName] of binPairs){
1294
- const source = await getSourcePathFromExportPath(cwd, binName, '$binary');
1295
- if (!source) {
1296
- logger.warn(`Cannot find source file for ${binName}`);
1297
- continue;
1298
- }
1299
- const binEntryPath = await resolveSourceFile(cwd, source);
1300
- entries[binName] = {
1301
- source: binEntryPath,
1302
- name: binName,
1303
- export: binExportPaths[binName]
1304
- };
1305
- }
1306
- }
1307
- const collectEntriesPromises = Object.keys(exportPaths).map(async (entryExport)=>{
1308
- const exportCond = exportPaths[entryExport];
1309
- const collectEntryOptions = {
1310
- cwd,
1311
- pkg,
1312
- entries,
1313
- entryPath,
1314
- exportCondRef: exportCond,
1315
- entryExport
1316
- };
1317
- if (entryExport.startsWith('.')) {
1318
- await collectEntry('', collectEntryOptions);
1319
- for (const exportCondType of suffixedExportConventions){
1320
- if (exportCond[exportCondType]) {
1321
- await collectEntry(exportCondType, collectEntryOptions);
1322
- } else if (entryExport === '.' + exportCondType) {
1323
- await collectEntry(entryExport, collectEntryOptions);
1324
- }
1325
- }
1326
- }
1327
- });
1328
- await Promise.all(collectEntriesPromises);
1329
- return entries;
1330
- }
1331
1359
  async function buildConfig(bundleConfig, exportCondition, pluginContext, dts) {
1332
1360
  const { file } = bundleConfig;
1333
1361
  const { pkg, cwd } = pluginContext;
1334
1362
  const entry = exportCondition.source;
1335
- const outputExports = getExportsDistFilesOfCondition(pkg, exportCondition, cwd);
1363
+ const outputExports = getExportsDistFilesOfCondition(pkg, exportCondition, cwd, dts);
1336
1364
  // If there's nothing found, give a default output
1337
1365
  if (outputExports.length === 0 && !pkg.bin) {
1338
- const defaultFormat = isESModulePackage(pkg.type) ? 'esm' : 'cjs';
1366
+ const isEsmPkg = isESModulePackage(pkg.type);
1367
+ const defaultFormat = isEsmPkg ? 'esm' : 'cjs';
1339
1368
  outputExports.push({
1340
1369
  format: defaultFormat,
1341
- file: path.join(cwd, 'dist/index.js')
1370
+ file: path.join(cwd, 'dist/index.js'.replace('/', path__default.default.sep)),
1371
+ exportCondition: 'default'
1342
1372
  });
1343
1373
  }
1344
1374
  let bundleOptions = [];
1345
- // multi outputs with specified format
1346
- // CLI output option is always prioritized
1347
1375
  if (file) {
1348
- var _outputExports_;
1349
- const fallbackFormat = (_outputExports_ = outputExports[0]) == null ? void 0 : _outputExports_.format;
1350
- bundleOptions = [
1351
- {
1352
- resolvedFile: path.resolve(cwd, file),
1353
- format: bundleConfig.format || fallbackFormat
1354
- }
1355
- ];
1376
+ const absoluteFile = path.resolve(cwd, file);
1377
+ const absoluteTypeFile = getExportFileTypePath(absoluteFile);
1378
+ if (dts) {
1379
+ bundleOptions = [
1380
+ {
1381
+ file: absoluteTypeFile,
1382
+ format: 'esm',
1383
+ exportCondition: 'types'
1384
+ }
1385
+ ];
1386
+ } else {
1387
+ const fallbackExport = outputExports[0];
1388
+ bundleOptions = [
1389
+ {
1390
+ file: absoluteFile,
1391
+ format: bundleConfig.format || fallbackExport.format,
1392
+ exportCondition: fallbackExport.exportCondition
1393
+ }
1394
+ ];
1395
+ }
1356
1396
  } else {
1357
- bundleOptions = outputExports.map((exportDist)=>{
1358
- return {
1359
- resolvedFile: path.resolve(cwd, exportDist.file),
1360
- format: exportDist.format
1361
- };
1362
- });
1363
- }
1364
- if (dts) {
1365
- // types could have duplicates, dedupe them
1366
- // e.g. { types, import, .. } use the same `types` condition with all conditions
1367
- const uniqTypes = new Set();
1368
- bundleOptions.forEach((bundleOption)=>{
1369
- if (exportCondition.export.types) {
1370
- uniqTypes.add(path.resolve(cwd, exportCondition.export.types));
1371
- }
1372
- const typeForExtension = getExportFileTypePath(bundleOption.resolvedFile);
1373
- uniqTypes.add(typeForExtension);
1374
- });
1375
- bundleOptions = Array.from(uniqTypes).map((typeFile)=>{
1376
- return {
1377
- resolvedFile: typeFile,
1378
- format: 'esm'
1379
- };
1380
- });
1397
+ // CLI output option is always prioritized
1398
+ if (dts) {
1399
+ // types could have duplicates, dedupe them
1400
+ // e.g. { types, import, .. } use the same `types` condition with all conditions
1401
+ const uniqTypes = new Set();
1402
+ outputExports.forEach((exportDist)=>{
1403
+ uniqTypes.add(path.resolve(cwd, exportDist.file));
1404
+ });
1405
+ bundleOptions = Array.from(uniqTypes).map((typeFile)=>{
1406
+ return {
1407
+ file: typeFile,
1408
+ format: 'esm',
1409
+ exportCondition: 'types'
1410
+ };
1411
+ });
1412
+ } else {
1413
+ bundleOptions = outputExports.map((exportDist)=>{
1414
+ return {
1415
+ file: path.resolve(cwd, exportDist.file),
1416
+ format: exportDist.format,
1417
+ exportCondition: exportDist.exportCondition
1418
+ };
1419
+ });
1420
+ }
1381
1421
  }
1382
1422
  const outputConfigs = bundleOptions.map(async (bundleOption)=>{
1423
+ const targetExportCondition = {
1424
+ ...exportCondition,
1425
+ export: {
1426
+ [bundleOption.exportCondition]: bundleOption.exportCondition === 'types' ? bundleOption.file : exportCondition.export[bundleOption.exportCondition]
1427
+ }
1428
+ };
1383
1429
  return await buildOutputConfigs(entry, {
1384
1430
  ...bundleConfig,
1385
- file: bundleOption.resolvedFile,
1431
+ file: bundleOption.file,
1386
1432
  format: bundleOption.format
1387
- }, exportCondition, pluginContext, dts);
1433
+ }, targetExportCondition, pluginContext, dts);
1388
1434
  });
1389
1435
  return Promise.all(outputConfigs);
1390
1436
  }
@@ -1393,17 +1439,21 @@ async function buildConfig(bundleConfig, exportCondition, pluginContext, dts) {
1393
1439
  const removeScope = (exportPath)=>exportPath.replace(/^@[^/]+\//, '');
1394
1440
  function createOutputState({ entries }) {
1395
1441
  const sizeStats = new Map();
1442
+ const uniqFiles = new Set();
1396
1443
  function addSize({ fileName, size, sourceFileName, exportPath }) {
1397
1444
  if (!sizeStats.has(exportPath)) {
1398
1445
  sizeStats.set(exportPath, []);
1399
1446
  }
1400
1447
  const distFilesStats = sizeStats.get(exportPath);
1401
- if (distFilesStats) {
1402
- distFilesStats.push([
1403
- fileName,
1404
- sourceFileName,
1405
- size
1406
- ]);
1448
+ if (!uniqFiles.has(fileName)) {
1449
+ uniqFiles.add(fileName);
1450
+ if (distFilesStats) {
1451
+ distFilesStats.push([
1452
+ fileName,
1453
+ sourceFileName,
1454
+ size
1455
+ ]);
1456
+ }
1407
1457
  }
1408
1458
  }
1409
1459
  const reversedMapping = new Map();
@@ -1440,7 +1490,7 @@ function createOutputState({ entries }) {
1440
1490
  };
1441
1491
  }
1442
1492
  function isBin(filename) {
1443
- return filename === 'bin' || filename.startsWith('bin/');
1493
+ return filename === BINARY_TAG || filename.startsWith(BINARY_TAG + '/');
1444
1494
  }
1445
1495
  function isTypeFile(filename) {
1446
1496
  return filename.endsWith('.d.ts') || filename.endsWith('.d.mts') || filename.endsWith('.d.cts');
@@ -1448,26 +1498,15 @@ function isTypeFile(filename) {
1448
1498
  function normalizeExportName(exportName) {
1449
1499
  const isBinary = isBin(exportName);
1450
1500
  let result = exportName;
1451
- const isSubpathExport = exportName.includes('/');
1452
- const isSpecialExport = exportName.includes('.');
1453
1501
  if (isBinary) {
1454
- result = (exportName.replace(/bin(\/|$)/, '') || '.') + ' (bin)';
1455
- } else if (isSubpathExport || isSpecialExport) {
1456
- const subExportName = exportName.split('/')[1] || exportName;
1457
- if (subExportName.includes('.') && subExportName !== '.') {
1458
- const [originExportName, specialCondition] = subExportName.split('.');
1459
- result = (isSubpathExport ? relativify(originExportName) : '.') + ' (' + specialCondition + ')';
1460
- } else {
1461
- result = isSubpathExport ? relativify(subExportName) : '.';
1462
- }
1502
+ result = (exportName.replace(new RegExp(`^\\${BINARY_TAG}\\/?`), '') || '.') + ' (bin)';
1463
1503
  } else {
1464
- result = '.';
1504
+ const normalizedExportPath = normalizeExportPath(exportName);
1505
+ const specialConditionName = getSpecialExportTypeFromExportPath(exportName);
1506
+ result = normalizedExportPath + (specialConditionName !== 'default' ? ` (${specialConditionName})` : '');
1465
1507
  }
1466
1508
  return result;
1467
1509
  }
1468
- function getExportNameWithoutExportCondition(exportName) {
1469
- return exportName.includes('.') ? exportName.split('.')[0] : exportName;
1470
- }
1471
1510
  function logOutputState(sizeCollector) {
1472
1511
  const stats = sizeCollector.getSizeStats();
1473
1512
  if (stats.size === 0) {
@@ -1479,7 +1518,7 @@ function logOutputState(sizeCollector) {
1479
1518
  const statsArray = [
1480
1519
  ...stats.entries()
1481
1520
  ].sort(([a], [b])=>{
1482
- const comp = getExportNameWithoutExportCondition(a).length - getExportNameWithoutExportCondition(b).length;
1521
+ const comp = normalizeExportPath(a).length - normalizeExportPath(b).length;
1483
1522
  return comp === 0 ? a.localeCompare(b) : comp;
1484
1523
  });
1485
1524
  const maxLengthOfExportName = Math.max(...statsArray.map(([exportName])=>normalizeExportName(exportName).length));
@@ -1512,61 +1551,6 @@ function logOutputState(sizeCollector) {
1512
1551
  });
1513
1552
  }
1514
1553
 
1515
- // TODO: support nested wildcard exportsCondition (e.g. './foo/*')
1516
- const getWildcardExports = (exportsCondition)=>{
1517
- return {
1518
- './*': exportsCondition['./*']
1519
- };
1520
- };
1521
- const isExportable = async (dirent, pathname)=>{
1522
- if (dirent.isDirectory()) {
1523
- const innerDirents = await fsp__default.default.readdir(path__default.default.join(pathname, dirent.name), {
1524
- withFileTypes: true
1525
- });
1526
- return innerDirents.some(({ name })=>name.startsWith('index') && hasAvailableExtension(name));
1527
- }
1528
- return dirent.isFile() && !dirent.name.startsWith('index') && hasAvailableExtension(dirent.name);
1529
- };
1530
- async function getExportables(cwd, excludeKeys) {
1531
- const pathname = path__default.default.resolve(cwd, SRC);
1532
- const dirents = await fsp__default.default.readdir(pathname, {
1533
- withFileTypes: true
1534
- });
1535
- const exportables = await Promise.all(dirents.map(async (dirent)=>await isExportable(dirent, pathname) && !excludeKeys.includes(dirent.name) ? dirent.name : undefined));
1536
- return exportables.filter(nonNullable);
1537
- }
1538
- function mapWildcard(wildcardExports, exportables) {
1539
- return exportables.map((exportable)=>{
1540
- const isFile = exportable.includes('.');
1541
- const filename = isFile ? filePathWithoutExtension(exportable) : exportable;
1542
- return {
1543
- [`./${filename}`]: Object.fromEntries(Object.entries(wildcardExports['./*']).map(([key, value])=>[
1544
- key,
1545
- value.replace(/\*/g, isFile ? filename : `${filename}/index`)
1546
- ]))
1547
- };
1548
- });
1549
- }
1550
- async function resolveWildcardExports(exportsCondition, cwd) {
1551
- if (!exportsCondition || typeof exportsCondition === 'string') return undefined;
1552
- const hasWildcard = !!exportsCondition['./*'];
1553
- if (hasWildcard) {
1554
- logger.warn(`The wildcard export "./*" is experimental and may change or be removed at any time.\n` + 'To open an issue, please visit https://github.com/huozhi/bunchee/issues' + '.\n');
1555
- // './foo' -> ['foo']; './foo/bar' -> ['bar']
1556
- // will contain '*' also but it's not a problem
1557
- const excludeKeys = Object.keys(exportsCondition).map((key)=>key.split('/').pop());
1558
- const exportables = await getExportables(cwd, excludeKeys);
1559
- if (exportables.length > 0) {
1560
- const wildcardExports = getWildcardExports(exportsCondition);
1561
- const resolvedWildcardExports = mapWildcard(wildcardExports, exportables);
1562
- const resolvedExports = Object.assign({}, exportsCondition, ...resolvedWildcardExports);
1563
- delete resolvedExports['./*'];
1564
- return resolvedExports;
1565
- }
1566
- }
1567
- return undefined;
1568
- }
1569
-
1570
1554
  function assignDefault(options, name, defaultValue) {
1571
1555
  if (!(name in options) || options[name] == null) {
1572
1556
  options[name] = defaultValue;
@@ -1582,12 +1566,11 @@ async function bundle(cliEntryPath, { cwd: _cwd, ...options } = {}) {
1582
1566
  assignDefault(options, 'minify', false);
1583
1567
  assignDefault(options, 'target', 'es2015');
1584
1568
  const pkg = await getPackageMeta(cwd);
1585
- const resolvedWildcardExports = await resolveWildcardExports(pkg.exports, cwd);
1586
- const packageType = getPackageType(pkg);
1587
- const exportPaths = getExportPaths(pkg, resolvedWildcardExports);
1588
- const isMultiEntries = hasMultiEntryExport(exportPaths) // exportPathsLength > 1
1589
- ;
1569
+ const parsedExportsInfo = parseExports(pkg);
1570
+ const isMultiEntries = hasMultiEntryExport(parsedExportsInfo);
1590
1571
  const hasBin = Boolean(pkg.bin);
1572
+ // Original input file path, client path might change later
1573
+ const inputFile = cliEntryPath;
1591
1574
  const isFromCli = Boolean(cliEntryPath);
1592
1575
  let tsConfig = resolveTsConfig(cwd);
1593
1576
  let hasTsConfig = Boolean(tsConfig == null ? void 0 : tsConfig.tsConfigPath);
@@ -1602,21 +1585,27 @@ async function bundle(cliEntryPath, { cwd: _cwd, ...options } = {}) {
1602
1585
  cliEntryPath = cliEntryPath || await getSourcePathFromExportPath(cwd, '.', 'default') || '';
1603
1586
  }
1604
1587
  // Handle CLI input
1605
- if (cliEntryPath) {
1606
- let mainEntryPath;
1607
- let typesEntryPath;
1588
+ let mainExportPath;
1589
+ let typesEntryPath;
1590
+ if (isFromCli) {
1608
1591
  // with -o option
1609
1592
  if (options.file) {
1610
- mainEntryPath = options.file;
1593
+ mainExportPath = options.file;
1611
1594
  }
1612
- if (mainEntryPath) {
1595
+ if (mainExportPath) {
1613
1596
  if (options.dts) {
1614
- typesEntryPath = getExportFileTypePath(mainEntryPath);
1597
+ typesEntryPath = getExportFileTypePath(mainExportPath);
1615
1598
  }
1616
- exportPaths['.'] = constructDefaultExportCondition({
1617
- main: mainEntryPath,
1618
- types: typesEntryPath
1619
- }, packageType);
1599
+ parsedExportsInfo.set('.', [
1600
+ [
1601
+ mainExportPath,
1602
+ 'default'
1603
+ ],
1604
+ Boolean(typesEntryPath) && [
1605
+ typesEntryPath,
1606
+ 'types'
1607
+ ]
1608
+ ].filter(Boolean));
1620
1609
  }
1621
1610
  }
1622
1611
  const bundleOrWatch = async (rollupConfig)=>{
@@ -1646,7 +1635,7 @@ async function bundle(cliEntryPath, { cwd: _cwd, ...options } = {}) {
1646
1635
  }
1647
1636
  }
1648
1637
  }
1649
- const entries = await collectEntries(pkg, cliEntryPath, exportPaths, cwd);
1638
+ const entries = await collectEntriesFromParsedExports(cwd, parsedExportsInfo, inputFile);
1650
1639
  const hasTypeScriptFiles = Object.values(entries).some((entry)=>isTypescriptFile(entry.source));
1651
1640
  if (hasTypeScriptFiles && !hasTsConfig) {
1652
1641
  const tsConfigPath = path.resolve(cwd, 'tsconfig.json');
@@ -1657,11 +1646,13 @@ async function bundle(cliEntryPath, { cwd: _cwd, ...options } = {}) {
1657
1646
  const sizeCollector = createOutputState({
1658
1647
  entries
1659
1648
  });
1660
- const entriesAlias = getReversedAlias(entries);
1649
+ const entriesAlias = getReversedAlias({
1650
+ entries,
1651
+ name: pkg.name
1652
+ });
1661
1653
  const buildContext = {
1662
1654
  entries,
1663
1655
  pkg,
1664
- exportPaths,
1665
1656
  cwd,
1666
1657
  tsOptions: defaultTsOptions,
1667
1658
  useTypeScript: hasTsConfig,
@@ -1673,7 +1664,7 @@ async function bundle(cliEntryPath, { cwd: _cwd, ...options } = {}) {
1673
1664
  };
1674
1665
  const buildConfigs = await buildEntryConfig(options, buildContext, false);
1675
1666
  const assetsJobs = buildConfigs.map((rollupConfig)=>bundleOrWatch(rollupConfig));
1676
- const typesJobs = hasTsConfig ? (await buildEntryConfig(options, buildContext, true)).map((rollupConfig)=>bundleOrWatch(rollupConfig)) : [];
1667
+ const typesJobs = hasTsConfig && options.dts !== false ? (await buildEntryConfig(options, buildContext, true)).map((rollupConfig)=>bundleOrWatch(rollupConfig)) : [];
1677
1668
  const totalJobs = assetsJobs.concat(typesJobs);
1678
1669
  const result = await Promise.all(totalJobs);
1679
1670
  if (result.length === 0) {