bunchee 6.0.4 → 6.1.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/README.md CHANGED
@@ -290,7 +290,7 @@ If you're using `"use client"` or `"use server"` in a file that used as a depend
290
290
 
291
291
  ### Shared Modules
292
292
 
293
- In some cases, you may need to share code across multiple bundles without promoting them to separate entries or exports. These modules should be bundled into shared chunks that can be reused by various bundles. By convention, files or directories prefixed with an underscore (`_<name>.<ext>` or `_<name>/index.<ext>`) are treated as **shared modules**. They're private and not exposed publicly as entry points or exports.
293
+ In some cases, you may need to share code across multiple bundles without promoting them to separate entries or exports. These modules should be bundled into shared chunks that can be reused by various bundles. By convention, files or directories **prefixed with an underscore** (`_<name>.<ext>` or `_<name>/**`) are treated as **shared modules**. They're private and not exposed publicly as entry points or exports. Testing, mocking related files are ignored. e.g. `_foo/a.test.ts` will not be treated as shared module.
294
294
 
295
295
  <details>
296
296
  <summary>Shared Utils Example</summary>
package/dist/bin/cli.js CHANGED
@@ -74,6 +74,8 @@ const DEFAULT_TS_CONFIG = {
74
74
  }
75
75
  };
76
76
  const BINARY_TAG = '$binary';
77
+ const PRIVATE_GLOB_PATTERN = '**/_*/**';
78
+ const TESTS_GLOB_PATTERN = '**/{__tests__/**,__mocks__/**,*.{test,spec}.*}';
77
79
 
78
80
  function getDefaultExportFromCjs (x) {
79
81
  return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x;
@@ -158,6 +160,36 @@ function posixRelativify(path) {
158
160
  return path.startsWith('.') ? path : `./${path}`;
159
161
  }
160
162
 
163
+ // Example: ./src/util/foo.development.ts -> foo.development
164
+ // Example: ./src/util/foo.react-server.ts -> foo.react-server
165
+ const baseNameWithoutExtension = (filePath)=>{
166
+ return path__default.default.basename(filePath, path__default.default.extname(filePath));
167
+ };
168
+ function validateEntryFiles(entryFiles) {
169
+ const fileBasePaths = new Set();
170
+ const duplicatePaths = new Set();
171
+ for (const filePath of entryFiles){
172
+ // Check if there are multiple files with the same base name
173
+ const filePathWithoutExt = filePath.slice(0, -path__default.default.extname(filePath).length).replace(/\\/g, '/');
174
+ const segments = filePathWithoutExt.split('/');
175
+ const lastSegment = segments.pop() || '';
176
+ if (lastSegment !== 'index' && lastSegment !== '') {
177
+ segments.push(lastSegment);
178
+ }
179
+ const fileBasePath = segments.join('/');
180
+ if (fileBasePaths.has(fileBasePath)) {
181
+ duplicatePaths.add(// Add a dot if the base name is empty, 'foo' -> './foo', '' -> '.'
182
+ './' + filePath.replace(/\\/g, '/'));
183
+ }
184
+ fileBasePaths.add(fileBasePath);
185
+ }
186
+ if (duplicatePaths.size > 0) {
187
+ throw new Error(`Conflicted entry files found for entries: ${[
188
+ ...duplicatePaths
189
+ ].join(', ')}`);
190
+ }
191
+ }
192
+
161
193
  function exit(err) {
162
194
  logger.error(err);
163
195
  process.exit(1);
@@ -188,8 +220,6 @@ const getMainFieldExportType = (pkg)=>{
188
220
  const mainExportType = isEsmPkg && pkg.main ? hasCjsExtension(pkg.main) ? 'require' : 'import' : 'require';
189
221
  return mainExportType;
190
222
  };
191
- // TODO: add unit test
192
- const baseNameWithoutExtension = (filename)=>path__default.default.basename(filename, path__default.default.extname(filename));
193
223
  const isTestFile = (filename)=>/\.(test|spec)$/.test(baseNameWithoutExtension(filename));
194
224
  function joinRelativePath(...segments) {
195
225
  let result = path__default.default.join(...segments);
@@ -612,7 +642,7 @@ function lint$1(pkg) {
612
642
  }
613
643
  }
614
644
 
615
- var version = "6.0.4";
645
+ var version = "6.1.1";
616
646
 
617
647
  async function writeDefaultTsconfig(tsConfigPath) {
618
648
  await fs.promises.writeFile(tsConfigPath, JSON.stringify(DEFAULT_TS_CONFIG, null, 2), 'utf-8');
@@ -678,7 +708,7 @@ async function collectSourceEntriesByExportPath(sourceFolderPath, originalSubpat
678
708
  ;
679
709
  const dirPath = path__default.default.join(sourceFolderPath, dirName);
680
710
  // Match <name>{,/index}.{<ext>,<runtime>.<ext>}
681
- const globalPatterns = [
711
+ const entryFilesPatterns = [
682
712
  `${baseName}.{${[
683
713
  ...availableExtensions
684
714
  ].join(',')}}`,
@@ -696,12 +726,13 @@ async function collectSourceEntriesByExportPath(sourceFolderPath, originalSubpat
696
726
  ...availableExtensions
697
727
  ].join(',')}}`
698
728
  ];
699
- const files = await glob.glob(globalPatterns, {
729
+ const entryFiles = await glob.glob(entryFilesPatterns, {
700
730
  cwd: dirPath,
701
731
  nodir: true,
702
- ignore: '**/_*'
732
+ ignore: PRIVATE_GLOB_PATTERN
703
733
  });
704
- for (const file of files){
734
+ validateEntryFiles(entryFiles);
735
+ for (const file of entryFiles){
705
736
  const ext = path__default.default.extname(file).slice(1);
706
737
  if (!availableExtensions.has(ext) || isTestFile(file)) continue;
707
738
  const sourceFileAbsolutePath = path__default.default.join(dirPath, file);
@@ -746,12 +777,18 @@ async function collectSourceEntries(sourceFolderPath) {
746
777
  const binMatches = await glob.glob(binPattern, {
747
778
  cwd: sourceFolderPath,
748
779
  nodir: true,
749
- ignore: '**/_*'
780
+ ignore: [
781
+ PRIVATE_GLOB_PATTERN,
782
+ TESTS_GLOB_PATTERN
783
+ ]
750
784
  });
751
785
  const srcMatches = await glob.glob(srcPattern, {
752
786
  cwd: sourceFolderPath,
753
787
  nodir: true,
754
- ignore: '**/_*'
788
+ ignore: [
789
+ PRIVATE_GLOB_PATTERN,
790
+ TESTS_GLOB_PATTERN
791
+ ]
755
792
  });
756
793
  for (const file of binMatches){
757
794
  // convert relative path to export path
package/dist/index.js CHANGED
@@ -241,11 +241,43 @@ const DEFAULT_TS_CONFIG = {
241
241
  }
242
242
  };
243
243
  const BINARY_TAG = '$binary';
244
+ const PRIVATE_GLOB_PATTERN = '**/_*/**';
245
+ const TESTS_GLOB_PATTERN = '**/{__tests__/**,__mocks__/**,*.{test,spec}.*}';
244
246
 
245
247
  function posixRelativify(path) {
246
248
  return path.startsWith('.') ? path : `./${path}`;
247
249
  }
248
250
 
251
+ // Example: ./src/util/foo.development.ts -> foo.development
252
+ // Example: ./src/util/foo.react-server.ts -> foo.react-server
253
+ const baseNameWithoutExtension = (filePath)=>{
254
+ return path__default.default.basename(filePath, path__default.default.extname(filePath));
255
+ };
256
+ function validateEntryFiles(entryFiles) {
257
+ const fileBasePaths = new Set();
258
+ const duplicatePaths = new Set();
259
+ for (const filePath of entryFiles){
260
+ // Check if there are multiple files with the same base name
261
+ const filePathWithoutExt = filePath.slice(0, -path__default.default.extname(filePath).length).replace(/\\/g, '/');
262
+ const segments = filePathWithoutExt.split('/');
263
+ const lastSegment = segments.pop() || '';
264
+ if (lastSegment !== 'index' && lastSegment !== '') {
265
+ segments.push(lastSegment);
266
+ }
267
+ const fileBasePath = segments.join('/');
268
+ if (fileBasePaths.has(fileBasePath)) {
269
+ duplicatePaths.add(// Add a dot if the base name is empty, 'foo' -> './foo', '' -> '.'
270
+ './' + filePath.replace(/\\/g, '/'));
271
+ }
272
+ fileBasePaths.add(fileBasePath);
273
+ }
274
+ if (duplicatePaths.size > 0) {
275
+ throw new Error(`Conflicted entry files found for entries: ${[
276
+ ...duplicatePaths
277
+ ].join(', ')}`);
278
+ }
279
+ }
280
+
249
281
  function exit(err) {
250
282
  logger.error(err);
251
283
  process.exit(1);
@@ -325,8 +357,6 @@ const getMainFieldExportType = (pkg)=>{
325
357
  const mainExportType = isEsmPkg && pkg.main ? hasCjsExtension(pkg.main) ? 'require' : 'import' : 'require';
326
358
  return mainExportType;
327
359
  };
328
- // TODO: add unit test
329
- const baseNameWithoutExtension = (filename)=>path__default.default.basename(filename, path__default.default.extname(filename));
330
360
  const isTestFile = (filename)=>/\.(test|spec)$/.test(baseNameWithoutExtension(filename));
331
361
  function joinRelativePath(...segments) {
332
362
  let result = path__default.default.join(...segments);
@@ -752,7 +782,7 @@ async function collectSourceEntriesByExportPath(sourceFolderPath, originalSubpat
752
782
  ;
753
783
  const dirPath = path__default.default.join(sourceFolderPath, dirName);
754
784
  // Match <name>{,/index}.{<ext>,<runtime>.<ext>}
755
- const globalPatterns = [
785
+ const entryFilesPatterns = [
756
786
  `${baseName}.{${[
757
787
  ...availableExtensions
758
788
  ].join(',')}}`,
@@ -770,12 +800,13 @@ async function collectSourceEntriesByExportPath(sourceFolderPath, originalSubpat
770
800
  ...availableExtensions
771
801
  ].join(',')}}`
772
802
  ];
773
- const files = await glob.glob(globalPatterns, {
803
+ const entryFiles = await glob.glob(entryFilesPatterns, {
774
804
  cwd: dirPath,
775
805
  nodir: true,
776
- ignore: '**/_*'
806
+ ignore: PRIVATE_GLOB_PATTERN
777
807
  });
778
- for (const file of files){
808
+ validateEntryFiles(entryFiles);
809
+ for (const file of entryFiles){
779
810
  const ext = path__default.default.extname(file).slice(1);
780
811
  if (!availableExtensions.has(ext) || isTestFile(file)) continue;
781
812
  const sourceFileAbsolutePath = path__default.default.join(dirPath, file);
@@ -826,21 +857,20 @@ async function collectSourceEntriesByExportPath(sourceFolderPath, originalSubpat
826
857
  }
827
858
  }
828
859
  // Search private shared module files which are not in the parsedExportsInfo, but start with _.
829
- // e.g. _utils.ts, _utils/index.ts
830
- // e.g. _utils.development.ts, _utils/index.development.js
831
- const privatePattern = [
832
- `**/_*{,/index}.{${[
833
- ...availableExtensions
834
- ].join(',')}}`,
835
- `**/_*{,/index}.{${[
836
- ...runtimeExportConventions
837
- ].join(',')}}.{${[
838
- ...availableExtensions
839
- ].join(',')}}`
840
- ];
860
+ // Leading underscore: e.g. _utils.ts, _utils/index.ts
861
+ // Segment contains leading underscore: e.g. a/_b/_c.ts, a/b/_c/index.ts
862
+ // Contains special suffix: e.g. _utils.development.ts, _utils/index.development.js
863
+ const suffixPattern = [
864
+ ...runtimeExportConventions
865
+ ].join(',');
866
+ const extPattern = [
867
+ ...availableExtensions
868
+ ].join(',');
869
+ const privatePattern = `**/_*{,/*}{,{.${suffixPattern}}}.{${extPattern}}`;
841
870
  const privateFiles = await glob.glob(privatePattern, {
842
871
  cwd: sourceFolderPath,
843
- nodir: true
872
+ nodir: true,
873
+ ignore: TESTS_GLOB_PATTERN
844
874
  });
845
875
  for (const file of privateFiles){
846
876
  const sourceFileAbsolutePath = path__default.default.join(sourceFolderPath, file);
@@ -855,6 +885,7 @@ async function collectSourceEntriesByExportPath(sourceFolderPath, originalSubpat
855
885
  const condPart = isSpecialExport ? specialExportType + '.' : '';
856
886
  // Map private shared files to the dist directory
857
887
  // e.g. ./_utils.ts -> ./dist/_utils.js
888
+ // TODO: improve the logic to only generate the required files, not all possible files
858
889
  const privateExportInfo = [
859
890
  [
860
891
  posixRelativify(path.posix.join('./dist', exportPath + (isEsmPkg ? '.js' : '.mjs'))),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bunchee",
3
- "version": "6.0.4",
3
+ "version": "6.1.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",