bunchee 4.1.1 → 4.2.1

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/bin/cli.js CHANGED
@@ -2,6 +2,7 @@
2
2
  var path = require('path');
3
3
  var arg = require('arg');
4
4
  var fs = require('fs/promises');
5
+ var bunchee = require('bunchee');
5
6
 
6
7
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
7
8
 
@@ -54,7 +55,7 @@ async function fileExists(filePath) {
54
55
  }
55
56
  }
56
57
 
57
- var version = "4.1.1";
58
+ var version = "4.2.1";
58
59
 
59
60
  const helpMessage = `
60
61
  Usage: bunchee [options]
@@ -165,11 +166,10 @@ async function run(args) {
165
166
  return help();
166
167
  }
167
168
  const entry = source ? path__default.default.resolve(cwd, source) : '';
168
- const bundle = require('../index').bundle;
169
169
  let timeStart = Date.now();
170
170
  let timeEnd;
171
171
  try {
172
- await bundle(entry, bundleConfig);
172
+ await bunchee.bundle(entry, bundleConfig);
173
173
  timeEnd = Date.now();
174
174
  } catch (err) {
175
175
  if (err.name === 'NOT_EXISTED') {
package/dist/index.js CHANGED
@@ -10,9 +10,9 @@ var json = require('@rollup/plugin-json');
10
10
  var pluginNodeResolve = require('@rollup/plugin-node-resolve');
11
11
  var replace = require('@rollup/plugin-replace');
12
12
  var esmShim = require('@rollup/plugin-esm-shim');
13
- var prettyBytes = require('pretty-bytes');
14
- var pluginutils = require('@rollup/pluginutils');
15
13
  var preserveDirectives = require('rollup-preserve-directives');
14
+ var pluginutils = require('@rollup/pluginutils');
15
+ var prettyBytes = require('pretty-bytes');
16
16
  var module$1 = require('module');
17
17
 
18
18
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
@@ -23,96 +23,8 @@ var commonjs__default = /*#__PURE__*/_interopDefault(commonjs);
23
23
  var json__default = /*#__PURE__*/_interopDefault(json);
24
24
  var replace__default = /*#__PURE__*/_interopDefault(replace);
25
25
  var esmShim__default = /*#__PURE__*/_interopDefault(esmShim);
26
- var prettyBytes__default = /*#__PURE__*/_interopDefault(prettyBytes);
27
26
  var preserveDirectives__default = /*#__PURE__*/_interopDefault(preserveDirectives);
28
-
29
- const availableExtensions = [
30
- 'js',
31
- 'cjs',
32
- 'mjs',
33
- 'jsx',
34
- 'ts',
35
- 'tsx',
36
- 'cts',
37
- 'mts'
38
- ];
39
- const availableExportConventions = [
40
- 'react-server',
41
- 'react-native',
42
- 'edge-light'
43
- ];
44
- const availableESExtensionsRegex = /\.(m|c)?[jt]sx?$/;
45
- const dtsExtensionRegex = /\.d\.(m|c)?ts$/;
46
- const SRC = 'src';
47
- const dtsExtensions = {
48
- js: '.d.ts',
49
- cjs: '.d.cts',
50
- mjs: '.d.mts'
51
- };
52
-
53
- const logger = {
54
- log (...arg) {
55
- console.log(' ', ...arg);
56
- },
57
- warn (...arg) {
58
- console.warn(' ⚠️', ...arg);
59
- },
60
- error (...arg) {
61
- console.error(' ⨯', ...arg);
62
- },
63
- info (...arg) {
64
- console.log(' ✓', ...arg);
65
- }
66
- };
67
-
68
- function createChunkSizeCollector() {
69
- const sizes = new Map();
70
- function addSize(name, size) {
71
- sizes.set(name, size);
72
- }
73
- return {
74
- plugin: (cwd)=>{
75
- return {
76
- name: 'collect-sizes',
77
- augmentChunkHash () {
78
- // Do nothing, but use the hook to keep the plugin instance alive
79
- },
80
- renderChunk (code, chunk, options) {
81
- const dir = options.dir || options.file && path__default.default.dirname(options.file);
82
- let fileName = chunk.fileName;
83
- if (dir) {
84
- const filePath = path__default.default.join(dir, fileName);
85
- fileName = filePath.startsWith(cwd) ? path__default.default.relative(cwd, filePath) : filePath;
86
- }
87
- addSize(fileName, code.length);
88
- return null;
89
- }
90
- };
91
- },
92
- getSizeStats () {
93
- const sizeStats = [];
94
- sizes.forEach((size, name)=>{
95
- sizeStats.push([
96
- name,
97
- prettyBytes__default.default(size),
98
- size
99
- ]);
100
- });
101
- return sizeStats;
102
- }
103
- };
104
- }
105
- // This can also be passed down as stats from top level
106
- const sizeCollector = createChunkSizeCollector();
107
- function logSizeStats() {
108
- const stats = sizeCollector.getSizeStats();
109
- const maxLength = Math.max(...stats.map(([filename])=>filename.length));
110
- stats.forEach(([filename, prettiedSize])=>{
111
- const padding = ' '.repeat(maxLength - filename.length);
112
- const action = dtsExtensionRegex.test(filename) ? 'Typed' : 'Built';
113
- logger.info(`${action} ${filename}${padding} - ${prettiedSize}`);
114
- });
115
- }
27
+ var prettyBytes__default = /*#__PURE__*/_interopDefault(prettyBytes);
116
28
 
117
29
  function minifyCSS(content) {
118
30
  return content.replace(/\/\*[\s\S]*?\*\/|([^:]|^)\/\/.*$|(?:^|\s)(\s+)|\s*([\{\};,:])\s*|\s+(!)\s+/g, (match, p1, p2, p3, p4)=>{
@@ -206,6 +118,31 @@ function rawContent({ exclude }) {
206
118
  };
207
119
  }
208
120
 
121
+ // Alias entries to import path
122
+ // e.g.
123
+ // For a resolved file, if it's one of the entries,
124
+ // aliases it as export path, such as <absolute file> -> <pkg>/<export path>
125
+ function aliasEntries({ entries }) {
126
+ return {
127
+ name: 'alias',
128
+ resolveId: {
129
+ async handler (source, importer, options) {
130
+ const resolvedId = await this.resolve(source, importer, options);
131
+ if (resolvedId != null) {
132
+ const aliasedId = entries[resolvedId.id];
133
+ if (aliasedId != null) {
134
+ return {
135
+ id: aliasedId,
136
+ external: true
137
+ };
138
+ }
139
+ }
140
+ return null;
141
+ }
142
+ }
143
+ };
144
+ }
145
+
209
146
  function prependDirectives() {
210
147
  return {
211
148
  name: 'prependDirective',
@@ -231,6 +168,53 @@ function prependDirectives() {
231
168
  };
232
169
  }
233
170
 
171
+ const availableExtensions = [
172
+ 'js',
173
+ 'cjs',
174
+ 'mjs',
175
+ 'jsx',
176
+ 'ts',
177
+ 'tsx',
178
+ 'cts',
179
+ 'mts'
180
+ ];
181
+ const nodeResolveExtensions = [
182
+ '.mjs',
183
+ '.cjs',
184
+ '.js',
185
+ '.json',
186
+ '.node',
187
+ '.jsx'
188
+ ];
189
+ const availableExportConventions = [
190
+ 'react-server',
191
+ 'react-native',
192
+ 'edge-light'
193
+ ];
194
+ const availableESExtensionsRegex = /\.(m|c)?[jt]sx?$/;
195
+ const dtsExtensionRegex = /\.d\.(m|c)?ts$/;
196
+ const SRC = 'src';
197
+ const dtsExtensions = {
198
+ js: '.d.ts',
199
+ cjs: '.d.cts',
200
+ mjs: '.d.mts'
201
+ };
202
+
203
+ const logger = {
204
+ log (...arg) {
205
+ console.log(' ', ...arg);
206
+ },
207
+ warn (...arg) {
208
+ console.warn(' ⚠️', ...arg);
209
+ },
210
+ error (...arg) {
211
+ console.error(' ⨯', ...arg);
212
+ },
213
+ info (...arg) {
214
+ console.log(' ✓', ...arg);
215
+ }
216
+ };
217
+
234
218
  function exit(err) {
235
219
  logger.error(err);
236
220
  process.exit(1);
@@ -521,28 +505,29 @@ function isEsmExportName(name, ext) {
521
505
  'module'
522
506
  ].includes(name) || ext === 'mjs';
523
507
  }
524
- function isCjsExportName(name, ext) {
525
- return [
508
+ function isCjsExportName(pkg, name, ext) {
509
+ const isESModule = isESModulePackage(pkg.type);
510
+ return !isESModule && [
526
511
  'require',
527
512
  'main',
528
513
  'node',
529
514
  'default'
530
- ].includes(name) || ext === 'cjs';
515
+ ].includes(name) && ext !== 'mjs' || ext === 'cjs';
531
516
  }
532
517
  function getExportConditionDist(pkg, parsedExportCondition, cwd) {
533
518
  const dist = [];
534
519
  const existed = new Set();
535
520
  const exportTypes = Object.keys(parsedExportCondition.export);
536
- for (const key of exportTypes){
537
- if (key === 'types') {
521
+ for (const exportType of exportTypes){
522
+ if (exportType === 'types') {
538
523
  continue;
539
524
  }
540
- const filePath = parsedExportCondition.export[key];
525
+ const filePath = parsedExportCondition.export[exportType];
541
526
  const ext = path.extname(filePath).slice(1);
542
- const relativePath = parsedExportCondition.export[key];
527
+ const relativePath = parsedExportCondition.export[exportType];
543
528
  const distFile = getDistPath(relativePath, cwd);
544
529
  let format = 'esm';
545
- if (isCjsExportName(key, ext)) {
530
+ if (isCjsExportName(pkg, exportType, ext)) {
546
531
  format = 'cjs';
547
532
  }
548
533
  // Deduplicate the same path jobs
@@ -595,16 +580,36 @@ function getBuildEnv(envs) {
595
580
  }, {});
596
581
  return envVars;
597
582
  }
598
- async function buildInputConfig(entry, pkg, options, cwd, { tsConfigPath, tsCompilerOptions }, dts) {
583
+ /**
584
+ * return { '<pkg>/<export>': '<absolute source path> }
585
+ */ function getEntriesAlias(entries) {
586
+ const alias = {};
587
+ for (const [entryImportPath, exportCondition] of Object.entries(entries)){
588
+ alias[entryImportPath] = exportCondition.source;
589
+ }
590
+ return alias;
591
+ }
592
+ async function buildInputConfig(entry, entries, pkg, options, cwd, { tsConfigPath, tsCompilerOptions }, pluginContext, dts) {
593
+ const entriesAlias = getEntriesAlias(entries);
594
+ const reversedAlias = {};
595
+ for (const [key, value] of Object.entries(entriesAlias)){
596
+ if (value !== entry) {
597
+ reversedAlias[value] = key;
598
+ }
599
+ }
599
600
  const hasNoExternal = options.external === null;
600
601
  var _options_external;
601
602
  const externals = hasNoExternal ? [] : [
602
603
  pkg.peerDependencies,
603
604
  pkg.dependencies,
604
605
  pkg.peerDependenciesMeta
605
- ].filter((n)=>Boolean(n)).map((o)=>Object.keys(o)).reduce((a, b)=>a.concat(b), []).concat(((_options_external = options.external) != null ? _options_external : []).concat(pkg.name ? [
606
- pkg.name
607
- ] : []));
606
+ ].filter((n)=>Boolean(n)).map((o)=>Object.keys(o)).reduce((a, b)=>a.concat(b), []).concat((_options_external = options.external) != null ? _options_external : []);
607
+ for (const [exportImportPath, entryFilePath] of Object.entries(entriesAlias)){
608
+ if (entryFilePath !== entry) {
609
+ externals.push(exportImportPath);
610
+ externals.push(entryFilePath);
611
+ }
612
+ }
608
613
  const { useTypescript, runtime, target: jscTarget, minify: shouldMinify } = options;
609
614
  const hasSpecifiedTsTarget = Boolean(tsCompilerOptions.target && tsConfigPath);
610
615
  const swcParserConfig = {
@@ -631,10 +636,13 @@ async function buildInputConfig(entry, pkg, options, cwd, { tsConfigPath, tsComp
631
636
  inlineSourcesContent: false,
632
637
  isModule: true
633
638
  };
634
- const sizePlugin = sizeCollector.plugin(cwd);
639
+ const sizePlugin = pluginContext.sizeCollector.plugin(cwd);
635
640
  // common plugins for both dts and ts assets that need to be processed
636
641
  const commonPlugins = [
637
- sizePlugin
642
+ sizePlugin,
643
+ aliasEntries({
644
+ entries: reversedAlias
645
+ })
638
646
  ];
639
647
  const baseResolvedTsOptions = {
640
648
  declaration: true,
@@ -692,14 +700,7 @@ async function buildInputConfig(entry, pkg, options, cwd, { tsConfigPath, tsComp
692
700
  }),
693
701
  pluginNodeResolve.nodeResolve({
694
702
  preferBuiltins: runtime === 'node',
695
- extensions: [
696
- '.mjs',
697
- '.cjs',
698
- '.js',
699
- '.json',
700
- '.node',
701
- '.jsx'
702
- ]
703
+ extensions: nodeResolveExtensions
703
704
  }),
704
705
  commonjs__default.default({
705
706
  exclude: options.external || null
@@ -731,7 +732,8 @@ async function buildInputConfig(entry, pkg, options, cwd, { tsConfigPath, tsComp
731
732
  'MIXED_EXPORTS',
732
733
  'PREFER_NAMED_EXPORTS',
733
734
  'UNRESOLVED_IMPORT',
734
- 'THIS_IS_UNDEFINED'
735
+ 'THIS_IS_UNDEFINED',
736
+ 'INVALID_ANNOTATION'
735
737
  ].includes(code)) return;
736
738
  // If the circular dependency warning is from node_modules, ignore it
737
739
  if (code === 'CIRCULAR_DEPENDENCY' && /Circular dependency: node_modules/.test(warning.message)) {
@@ -753,23 +755,58 @@ function hasEsmExport(exportPaths, tsCompilerOptions) {
753
755
  }
754
756
  return Boolean(hasEsm || (tsCompilerOptions == null ? void 0 : tsCompilerOptions.esModuleInterop));
755
757
  }
756
- const splitChunks = (id, ctx)=>{
757
- const moduleInfo = ctx.getModuleInfo(id);
758
- const moduleMeta = moduleInfo == null ? void 0 : moduleInfo.meta;
759
- if (!moduleInfo || !moduleMeta) {
760
- return;
761
- }
758
+ function getModuleLater(moduleMeta) {
762
759
  const directives = (moduleMeta.preserveDirectives || {
763
760
  directives: []
764
761
  }).directives.map((d)=>d.replace(/^use /, '')).filter((d)=>d !== 'strict');
765
762
  const moduleLayer = directives[0];
766
- if (moduleLayer && !moduleMeta.isEntry) {
767
- const chunkName = path__default.default.basename(id, path__default.default.extname(id));
768
- return `${chunkName}-${moduleLayer}`;
769
- }
770
- return;
771
- };
772
- function buildOutputConfigs(pkg, exportPaths, options, exportCondition, cwd, { tsCompilerOptions }, dts) {
763
+ return moduleLayer;
764
+ }
765
+ // dependencyGraphMap: Map<subModuleId, Set<entryParentId>>
766
+ function createSplitChunks(dependencyGraphMap) {
767
+ return function splitChunks(id, ctx) {
768
+ const moduleInfo = ctx.getModuleInfo(id);
769
+ if (!moduleInfo) {
770
+ return;
771
+ }
772
+ const { isEntry } = moduleInfo;
773
+ const moduleMeta = moduleInfo.meta;
774
+ const moduleLayer = getModuleLater(moduleMeta);
775
+ // Collect the sub modules of the entry, if they're having layer, and the same layer with the entry, push them to the dependencyGraphMap.
776
+ if (isEntry) {
777
+ const subModuleIds = ctx.getModuleIds();
778
+ for (const subId of subModuleIds){
779
+ const subModuleInfo = ctx.getModuleInfo(subId);
780
+ if (!subModuleInfo) {
781
+ continue;
782
+ }
783
+ const subModuleLayer = getModuleLater(moduleMeta);
784
+ if (subModuleLayer === moduleLayer) {
785
+ if (!dependencyGraphMap.has(subId)) {
786
+ dependencyGraphMap.set(subId, new Set());
787
+ }
788
+ dependencyGraphMap.get(subId).add(id);
789
+ }
790
+ }
791
+ }
792
+ // If current module has a layer, and it's not an entry
793
+ if (moduleLayer && !isEntry) {
794
+ // If the module is imported by the entry:
795
+ // when the module layer is same as entry layer, keep it as part of entry and don't split it;
796
+ // when the module layer is different from entry layer, split the module into a separate chunk as a separate boundary.
797
+ if (dependencyGraphMap.has(id)) {
798
+ const parentModuleLayers = Array.from(dependencyGraphMap.get(id));
799
+ if (parentModuleLayers.every((layer)=>layer === moduleLayer)) {
800
+ return;
801
+ }
802
+ const chunkName = path__default.default.basename(id, path__default.default.extname(id));
803
+ return `${chunkName}-${moduleLayer}`;
804
+ }
805
+ }
806
+ return;
807
+ };
808
+ }
809
+ function buildOutputConfigs(pkg, exportPaths, options, exportCondition, cwd, { tsCompilerOptions }, pluginContext, dts) {
773
810
  const { format } = options;
774
811
  // Add esm mark and interop helper if esm export is detected
775
812
  const useEsModuleMark = hasEsmExport(exportPaths, tsCompilerOptions);
@@ -795,7 +832,7 @@ function buildOutputConfigs(pkg, exportPaths, options, exportCondition, cwd, { t
795
832
  freeze: false,
796
833
  strict: false,
797
834
  sourcemap: options.sourcemap,
798
- manualChunks: splitChunks,
835
+ manualChunks: createSplitChunks(pluginContext.moduleDirectiveLayerMap),
799
836
  chunkFileNames: '[name]-[hash].js',
800
837
  // By default in rollup, when creating multiple chunks, transitive imports of entry chunks
801
838
  // will be added as empty imports to the entry chunks. Disable to avoid imports hoist outside of boundaries
@@ -803,56 +840,54 @@ function buildOutputConfigs(pkg, exportPaths, options, exportCondition, cwd, { t
803
840
  entryFileNames: path.basename(outputFile)
804
841
  };
805
842
  }
806
- // build configs for each entry from package exports
807
- async function buildEntryConfig(pkg, entryPath, exportPaths, bundleConfig, cwd, tsOptions, dts) {
843
+ async function buildEntryConfig(entries, pkg, exportPaths, bundleConfig, cwd, tsOptions, pluginContext, dts) {
808
844
  const configs = [];
809
- Object.keys(exportPaths).forEach(async (entryExport)=>{
810
- const exportCond = exportPaths[entryExport];
811
- const buildConfigs = [
812
- createBuildConfig('', exportCond)
813
- ];
814
- // For dts job, only build the default config.
815
- // For assets job, build all configs.
816
- if (!dts) {
845
+ for (const exportCondition of Object.values(entries)){
846
+ const rollupConfig = buildConfig(entries, pkg, exportPaths, bundleConfig, exportCondition, cwd, tsOptions, pluginContext, dts);
847
+ configs.push(rollupConfig);
848
+ }
849
+ return await Promise.all(configs);
850
+ }
851
+ /*
852
+ * build configs for each entry from package exports
853
+ *
854
+ * return { <pkg>/<export>: { input: InputOptions, output: OutputOptions[] }
855
+ */ async function collectEntries(pkg, entryPath, exportPaths, cwd) {
856
+ const entries = {};
857
+ async function collectEntry(// export type, e.g. react-server, edge-light those special cases required suffix
858
+ exportType, exportCondRef, // export name, e.g. ./<export-path> in exports field of package.json
859
+ entryExport) {
860
+ let exportCondForType = {
861
+ ...exportCondRef
862
+ };
863
+ // Special cases of export type, only pass down the exportPaths for the type
864
+ if (availableExportConventions.includes(exportType)) {
865
+ exportCondForType = {
866
+ [entryExport]: exportCondRef[exportType]
867
+ };
868
+ // Basic export type, pass down the exportPaths with erasing the special ones
869
+ } else {
817
870
  for (const exportType of availableExportConventions){
818
- if (exportCond[exportType]) {
819
- buildConfigs.push(createBuildConfig(exportType, exportCond));
820
- }
871
+ delete exportCondForType[exportType];
821
872
  }
822
873
  }
823
- async function createBuildConfig(exportType, exportCondRef) {
824
- let exportCondForType = {
825
- ...exportCondRef
826
- };
827
- // Special cases of export type, only pass down the exportPaths for the type
828
- if (availableExportConventions.includes(exportType)) {
829
- exportCondForType = {
830
- [entryExport]: exportCondRef[exportType]
831
- };
832
- // Basic export type, pass down the exportPaths with erasing the special ones
833
- } else {
834
- for (const exportType of availableExportConventions){
835
- delete exportCondForType[exportType];
836
- }
837
- }
838
- let source = entryPath;
839
- if (!source) {
840
- source = await getSourcePathFromExportPath(cwd, entryExport, exportType);
841
- }
842
- if (!source) {
843
- return undefined;
844
- }
845
- const exportCondition = {
846
- source,
847
- name: entryExport,
848
- export: exportCondForType
849
- };
850
- const entry = resolveSourceFile(cwd, source);
851
- const rollupConfig = buildConfig(entry, pkg, exportPaths, bundleConfig, exportCondition, cwd, tsOptions, dts);
852
- return rollupConfig;
874
+ let source = entryPath;
875
+ if (source) {
876
+ source = resolveSourceFile(cwd, source);
877
+ } else {
878
+ source = await getSourcePathFromExportPath(cwd, entryExport, exportType);
853
879
  }
854
- configs.push(...buildConfigs);
855
- });
880
+ if (!source) {
881
+ return undefined;
882
+ }
883
+ const exportCondition = {
884
+ source,
885
+ name: entryExport,
886
+ export: exportCondForType
887
+ };
888
+ const entryImportPath = path__default.default.join(pkg.name || '', exportCondition.name) + (exportType ? `.${exportType}` : '');
889
+ entries[entryImportPath] = exportCondition;
890
+ }
856
891
  const binaryExports = pkg.bin;
857
892
  if (binaryExports) {
858
893
  // binDistPaths: [ [ 'bin1', './dist/bin1.js'], [ 'bin2', './dist/bin2.js'] ]
@@ -883,35 +918,45 @@ async function buildEntryConfig(pkg, entryPath, exportPaths, bundleConfig, cwd,
883
918
  continue;
884
919
  }
885
920
  const binEntryPath = await resolveSourceFile(cwd, source);
886
- const binEntryConfig = buildConfig(binEntryPath, pkg, binExportPaths, bundleConfig, {
921
+ entries[binName] = {
887
922
  source: binEntryPath,
888
923
  name: binName,
889
924
  export: binExportPaths[binName]
890
- }, cwd, tsOptions, dts);
891
- configs.push(binEntryConfig);
925
+ };
892
926
  }
893
927
  }
894
- return (await Promise.all(configs)).filter(nonNullable);
928
+ const collectEntriesPromises = Object.keys(exportPaths).map(async (entryExport)=>{
929
+ const exportCond = exportPaths[entryExport];
930
+ await collectEntry('', exportCond, entryExport);
931
+ for (const exportType of availableExportConventions){
932
+ if (exportCond[exportType]) {
933
+ await collectEntry(exportType, exportCond, entryExport);
934
+ }
935
+ }
936
+ });
937
+ await Promise.all(collectEntriesPromises);
938
+ return entries;
895
939
  }
896
- async function buildConfig(entry, pkg, exportPaths, bundleConfig, exportCondition, cwd, tsOptions, dts) {
940
+ async function buildConfig(entries, pkg, exportPaths, bundleConfig, exportCondition, cwd, tsOptions, pluginContext, dts) {
897
941
  const { file } = bundleConfig;
898
942
  const useTypescript = Boolean(tsOptions.tsConfigPath);
899
943
  const options = {
900
944
  ...bundleConfig,
901
945
  useTypescript
902
946
  };
903
- const inputOptions = await buildInputConfig(entry, pkg, options, cwd, tsOptions, dts);
947
+ const entry = exportCondition.source;
948
+ const inputOptions = await buildInputConfig(entry, entries, pkg, options, cwd, tsOptions, pluginContext, dts);
904
949
  const outputExports = getExportConditionDist(pkg, exportCondition, cwd);
905
950
  let outputConfigs = [];
906
951
  // Generate dts job - single config
907
952
  if (dts) {
908
953
  const typeOutputExports = getExportTypeDist(exportCondition, cwd);
909
- outputConfigs = typeOutputExports.map((v)=>buildOutputConfigs(pkg, exportPaths, {
954
+ outputConfigs = typeOutputExports.map((typeFile)=>buildOutputConfigs(pkg, exportPaths, {
910
955
  ...bundleConfig,
911
956
  format: 'es',
912
957
  useTypescript,
913
- file: v
914
- }, exportCondition, cwd, tsOptions, dts));
958
+ file: typeFile
959
+ }, exportCondition, cwd, tsOptions, pluginContext, dts));
915
960
  } else {
916
961
  // multi outputs with specified format
917
962
  outputConfigs = outputExports.map((exportDist)=>{
@@ -920,7 +965,7 @@ async function buildConfig(entry, pkg, exportPaths, bundleConfig, exportConditio
920
965
  file: exportDist.file,
921
966
  format: exportDist.format,
922
967
  useTypescript
923
- }, exportCondition, cwd, tsOptions, dts);
968
+ }, exportCondition, cwd, tsOptions, pluginContext, dts);
924
969
  });
925
970
  // CLI output option is always prioritized
926
971
  if (file) {
@@ -932,7 +977,7 @@ async function buildConfig(entry, pkg, exportPaths, bundleConfig, exportConditio
932
977
  file,
933
978
  format: bundleConfig.format || fallbackFormat,
934
979
  useTypescript
935
- }, exportCondition, cwd, tsOptions, dts)
980
+ }, exportCondition, cwd, tsOptions, pluginContext, dts)
936
981
  ];
937
982
  }
938
983
  }
@@ -943,6 +988,72 @@ async function buildConfig(entry, pkg, exportPaths, bundleConfig, exportConditio
943
988
  };
944
989
  }
945
990
 
991
+ function createChunkSizeCollector({ entries }) {
992
+ const sizeStats = new Map();
993
+ function addSize({ fileName, size, sourceFileName, exportPath }) {
994
+ if (!sizeStats.has(exportPath)) {
995
+ sizeStats.set(exportPath, []);
996
+ }
997
+ const distFilesStats = sizeStats.get(exportPath);
998
+ if (distFilesStats) {
999
+ distFilesStats.push([
1000
+ fileName,
1001
+ sourceFileName,
1002
+ size
1003
+ ]);
1004
+ }
1005
+ }
1006
+ const reversedMapping = new Map();
1007
+ Object.entries(entries).forEach(([, entry])=>{
1008
+ reversedMapping.set(entry.source, entry.name || '.');
1009
+ });
1010
+ return {
1011
+ plugin: (cwd)=>{
1012
+ return {
1013
+ name: 'collect-sizes',
1014
+ augmentChunkHash () {
1015
+ // Do nothing, but use the hook to keep the plugin instance alive
1016
+ },
1017
+ renderChunk (code, chunk, options) {
1018
+ const sourceId = chunk.facadeModuleId || '';
1019
+ const dir = options.dir || options.file && path__default.default.dirname(options.file);
1020
+ let fileName = chunk.fileName;
1021
+ if (dir) {
1022
+ const filePath = path__default.default.join(dir, fileName);
1023
+ fileName = filePath.startsWith(cwd) ? path__default.default.relative(cwd, filePath) : filePath;
1024
+ }
1025
+ addSize({
1026
+ fileName,
1027
+ size: code.length,
1028
+ sourceFileName: sourceId,
1029
+ exportPath: reversedMapping.get(sourceId) || '.'
1030
+ });
1031
+ return null;
1032
+ }
1033
+ };
1034
+ },
1035
+ getSizeStats () {
1036
+ return sizeStats;
1037
+ }
1038
+ };
1039
+ }
1040
+ function logSizeStats(sizeCollector) {
1041
+ const stats = sizeCollector.getSizeStats();
1042
+ const allFileNameLengths = Array.from(stats.values()).flat(1).map(([filename])=>filename.length);
1043
+ const maxLength = Math.max(...allFileNameLengths);
1044
+ [
1045
+ ...stats.entries()
1046
+ ].sort(([a], [b])=>a.length - b.length).forEach(([, filesList])=>{
1047
+ filesList.forEach((item)=>{
1048
+ const [filename, , size] = item;
1049
+ const padding = ' '.repeat(maxLength - filename.length);
1050
+ const action = dtsExtensionRegex.test(filename) ? 'Typed' : 'Built';
1051
+ const prettiedSize = prettyBytes__default.default(size);
1052
+ logger.info(`${action} ${filename}${padding} - ${prettiedSize}`);
1053
+ });
1054
+ });
1055
+ }
1056
+
946
1057
  let hasLoggedTsWarning = false;
947
1058
  function resolveTypescript(cwd) {
948
1059
  let ts;
@@ -1107,15 +1218,22 @@ async function bundle(entryPath, { cwd: _cwd, ...options } = {}) {
1107
1218
  err.name = 'NOT_EXISTED';
1108
1219
  return Promise.reject(err);
1109
1220
  }
1110
- let result;
1111
- const buildConfigs = await buildEntryConfig(pkg, entryPath, exportPaths, options, cwd, defaultTsOptions, false);
1221
+ const entries = await collectEntries(pkg, entryPath, exportPaths, cwd);
1222
+ const sizeCollector = createChunkSizeCollector({
1223
+ entries
1224
+ });
1225
+ const pluginContext = {
1226
+ sizeCollector,
1227
+ moduleDirectiveLayerMap: new Map()
1228
+ };
1229
+ const buildConfigs = await buildEntryConfig(entries, pkg, exportPaths, options, cwd, defaultTsOptions, pluginContext, false);
1112
1230
  const assetsJobs = buildConfigs.map((rollupConfig)=>bundleOrWatch(rollupConfig));
1113
- const typesJobs = hasTsConfig ? (await buildEntryConfig(pkg, entryPath, exportPaths, options, cwd, defaultTsOptions, true)).map((rollupConfig)=>bundleOrWatch(rollupConfig)) : [];
1114
- result = await Promise.all(assetsJobs.concat(typesJobs));
1231
+ const typesJobs = hasTsConfig ? (await buildEntryConfig(entries, pkg, exportPaths, options, cwd, defaultTsOptions, pluginContext, true)).map((rollupConfig)=>bundleOrWatch(rollupConfig)) : [];
1232
+ const result = await Promise.all(assetsJobs.concat(typesJobs));
1115
1233
  if (result.length === 0) {
1116
1234
  logger.warn('The "src" directory does not contain any entry files. ' + 'For proper usage, please refer to the following link: ' + 'https://github.com/huozhi/bunchee#usage');
1117
1235
  }
1118
- logSizeStats();
1236
+ logSizeStats(sizeCollector);
1119
1237
  return result;
1120
1238
  }
1121
1239
  function runWatch({ input, output }, metadata) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bunchee",
3
- "version": "4.1.1",
3
+ "version": "4.2.1",
4
4
  "description": "zero config bundler for js/ts/jsx libraries",
5
5
  "bin": "./dist/bin/cli.js",
6
6
  "main": "./dist/index.js",
@@ -47,7 +47,7 @@
47
47
  "dependencies": {
48
48
  "@rollup/plugin-commonjs": "^25.0.7",
49
49
  "@rollup/plugin-esm-shim": "^0.1.5",
50
- "@rollup/plugin-json": "^6.0.1",
50
+ "@rollup/plugin-json": "^6.1.0",
51
51
  "@rollup/plugin-node-resolve": "^15.2.3",
52
52
  "@rollup/plugin-replace": "^5.0.5",
53
53
  "@rollup/plugin-wasm": "^6.2.2",
@@ -56,8 +56,8 @@
56
56
  "@swc/helpers": "^0.5.3",
57
57
  "arg": "^5.0.2",
58
58
  "pretty-bytes": "^5.6.0",
59
- "publint": "~0.2.6",
60
- "rollup": "^4.6.1",
59
+ "publint": "~0.2.7",
60
+ "rollup": "^4.9.1",
61
61
  "rollup-plugin-dts": "^6.1.0",
62
62
  "rollup-plugin-swc3": "^0.11.0",
63
63
  "rollup-preserve-directives": "^1.1.0",
@@ -86,7 +86,8 @@
86
86
  "prettier": "^3.0.0",
87
87
  "react": "^18.2.0",
88
88
  "tsx": "^4.6.2",
89
- "typescript": "^5.3.2"
89
+ "typescript": "^5.3.2",
90
+ "bunchee": "link:./"
90
91
  },
91
92
  "lint-staged": {
92
93
  "*.{ts,tsx,js,jsx,md,json,yml}": "prettier --write"