bunchee 6.0.0-rc.0 → 6.0.0-rc.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
@@ -40,6 +40,20 @@ cd ./my-lib
40
40
  mkdir src && touch ./src/index.ts
41
41
  ```
42
42
 
43
+ #### Build
44
+
45
+ Then files in `src` folders will be treated as entry files and match the export names in package.json. For example:
46
+ `src/index.ts` will match the exports name `"."` or the only main export.
47
+
48
+ Now just run `npm run build` (or `pnpm build` / `yarn build`) if you're using these package managers, `bunchee` will find the entry files and build them.
49
+ The output format will based on the exports condition and also the file extension. Given an example:
50
+
51
+ - It's CommonJS for `require` and ESM for `import` based on the exports condition.
52
+ - It's CommonJS for `.js` and ESM for `.mjs` based on the extension regardless the exports condition. Then for export condition like "node" you could choose the format with your extension.
53
+
54
+ > [!NOTE]
55
+ > All the `dependencies` and `peerDependencies` will be marked as external automatically and not included in the bundle. If you want to include them in the bundle, you can use the `--no-external` option.
56
+
43
57
  #### Prepare
44
58
 
45
59
  ```sh
@@ -128,19 +142,17 @@ If you're using TypeScript with Node 10 and Node 16 module resolution, you can u
128
142
 
129
143
  </details>
130
144
 
131
- #### Build
145
+ #### Lint
132
146
 
133
- Then files in `src` folders will be treated as entry files and match the export names in package.json. For example:
134
- `src/index.ts` will match the exports name `"."` or the only main export.
147
+ `lint` command will check the package.json configuration is valid or not, it can valid few things like:
135
148
 
136
- Now just run `npm run build` (or `pnpm build` / `yarn build`) if you're using these package managers, `bunchee` will find the entry files and build them.
137
- The output format will based on the exports condition and also the file extension. Given an example:
149
+ - if the entry files are matched with the exports conditions.
150
+ - if the entry files are matched with the exports paths.
138
151
 
139
- - It's CommonJS for `require` and ESM for `import` based on the exports condition.
140
- - It's CommonJS for `.js` and ESM for `.mjs` based on the extension regardless the exports condition. Then for export condition like "node" you could choose the format with your extension.
141
-
142
- > [!NOTE]
143
- > All the `dependencies` and `peerDependencies` will be marked as external automatically and not included in the bundle. If you want to include them in the bundle, you can use the `--no-external` option.
152
+ ```sh
153
+ # Use bunchee to lint if the package.json configuration is valid
154
+ npm exec bunchee lint
155
+ ```
144
156
 
145
157
  ## Usage
146
158
 
package/dist/bin/cli.js CHANGED
@@ -6,6 +6,7 @@ var perf_hooks = require('perf_hooks');
6
6
  var fs = require('fs');
7
7
  var fsp = require('fs/promises');
8
8
  var require$$0 = require('tty');
9
+ var picomatch = require('picomatch');
9
10
  var index_js = require('../index.js');
10
11
  require('module');
11
12
  var glob = require('glob');
@@ -18,6 +19,7 @@ var yargs__default = /*#__PURE__*/_interopDefault(yargs);
18
19
  var fs__default = /*#__PURE__*/_interopDefault(fs);
19
20
  var fsp__default = /*#__PURE__*/_interopDefault(fsp);
20
21
  var require$$0__default = /*#__PURE__*/_interopDefault(require$$0);
22
+ var picomatch__default = /*#__PURE__*/_interopDefault(picomatch);
21
23
  var prettyBytes__default = /*#__PURE__*/_interopDefault(prettyBytes);
22
24
 
23
25
  const availableExtensions = new Set([
@@ -208,14 +210,23 @@ function isTypeFile(filename) {
208
210
  return filename.endsWith('.d.ts') || filename.endsWith('.d.mts') || filename.endsWith('.d.cts');
209
211
  }
210
212
  // shared.ts -> ./shared
211
- // shared.<export condition>.ts -> ./shared
213
+ // shared.<export condition>.ts -> ./shared.<export condition>
212
214
  // index.ts -> ./index
213
215
  // index.development.ts -> ./index.development
216
+ // foo/index.ts -> ./foo
214
217
  function sourceFilenameToExportFullPath(filename) {
215
- const baseName = baseNameWithoutExtension(filename);
216
- let exportPath = baseName;
218
+ const ext = path__default.default.extname(filename);
219
+ const exportPath = filename.slice(0, -ext.length);
217
220
  return relativify(exportPath);
218
221
  }
222
+ // If the file is matching the private module convention file export path.
223
+ // './lib/_foo' -> true
224
+ // './_util/index' -> true
225
+ // './lib/_foo/bar' -> true
226
+ // './foo' -> false
227
+ function isPrivateExportPath(exportPath) {
228
+ return /\/_/.test(exportPath);
229
+ }
219
230
 
220
231
  function collectExportPath(exportValue, exportKey, currentPath, exportTypes, exportToDist) {
221
232
  // End of searching, export value is file path.
@@ -370,6 +381,49 @@ function validateTypesFieldCondition(pair) {
370
381
  }
371
382
  return false;
372
383
  }
384
+ const isPathIncluded = (filesField, filePath)=>{
385
+ return filesField.some((pattern)=>{
386
+ const normalizedPattern = path__default.default.normalize(pattern);
387
+ const matcher = picomatch__default.default(normalizedPattern);
388
+ return matcher(filePath);
389
+ });
390
+ };
391
+ function validateFilesField(packageJson) {
392
+ const state = {
393
+ definedField: false,
394
+ missingFiles: []
395
+ };
396
+ const filesField = packageJson.files || [];
397
+ const exportsField = packageJson.exports || {};
398
+ state.definedField = !!packageJson.files;
399
+ const resolveExportsPaths = (exports)=>{
400
+ const paths = [];
401
+ if (typeof exports === 'string') {
402
+ paths.push(exports);
403
+ } else if (typeof exports === 'object') {
404
+ for(const key in exports){
405
+ paths.push(...resolveExportsPaths(exports[key]));
406
+ }
407
+ }
408
+ return paths;
409
+ };
410
+ const exportedPaths = resolveExportsPaths(exportsField).map((p)=>path__default.default.normalize(p));
411
+ const commonFields = [
412
+ 'main',
413
+ 'module',
414
+ 'types',
415
+ 'module-sync'
416
+ ];
417
+ for (const field of commonFields){
418
+ if (field in packageJson) {
419
+ exportedPaths.push(packageJson[field]);
420
+ }
421
+ }
422
+ state.missingFiles = exportedPaths.filter((exportPath)=>{
423
+ return !isPathIncluded(filesField, exportPath);
424
+ });
425
+ return state;
426
+ }
373
427
  function lint$1(pkg) {
374
428
  const { name, main, exports } = pkg;
375
429
  const isESM = isESModulePackage(pkg.type);
@@ -493,6 +547,15 @@ function lint$1(pkg) {
493
547
  }
494
548
  }
495
549
  }
550
+ const fieldState = validateFilesField(pkg);
551
+ if (!fieldState.definedField) {
552
+ logger.warn('Missing files field in package.json');
553
+ } else if (fieldState.missingFiles.length) {
554
+ logger.warn('Missing files in package.json');
555
+ fieldState.missingFiles.forEach((p)=>{
556
+ logger.warn(` ${p}`);
557
+ });
558
+ }
496
559
  if (state.badMainExtension) {
497
560
  logger.warn('Cannot export `main` field with .mjs extension in CJS package, only .js extension is allowed');
498
561
  }
@@ -538,7 +601,7 @@ function lint$1(pkg) {
538
601
  }
539
602
  }
540
603
 
541
- var version = "6.0.0-rc.0";
604
+ var version = "6.0.0-rc.1";
542
605
 
543
606
  async function writeDefaultTsconfig(tsConfigPath) {
544
607
  await fs.promises.writeFile(tsConfigPath, JSON.stringify(DEFAULT_TS_CONFIG, null, 2), 'utf-8');
@@ -682,7 +745,8 @@ async function collectSourceEntries(sourceFolderPath) {
682
745
  for (const file of binMatches){
683
746
  // convert relative path to export path
684
747
  const exportPath = sourceFilenameToExportFullPath(file);
685
- await collectSourceEntriesByExportPath(sourceFolderPath, exportPath, bins, exportsEntries);
748
+ const binExportPath = exportPath.replace(/^\.[\//]bin/, BINARY_TAG);
749
+ await collectSourceEntriesByExportPath(sourceFolderPath, binExportPath, bins, exportsEntries);
686
750
  }
687
751
  for (const file of srcMatches){
688
752
  const binExportPath = file.replace(/^bin/, BINARY_TAG)// Remove index.<ext> to [^index].<ext> to build the export path
@@ -914,12 +978,21 @@ function logOutputState(stats) {
914
978
  }).forEach((item, index)=>{
915
979
  const [filename, , size] = item;
916
980
  const normalizedExportName = normalizeExportName(exportName);
917
- const prefix = index === 0 ? normalizedExportName : ' '.repeat(normalizedExportName.length);
981
+ const exportNameWithPadding = index === 0 ? // For other formats, just show the padding spaces.
982
+ normalizedExportName : ' '.repeat(normalizedExportName.length);
918
983
  const filenamePadding = ' '.repeat(Math.max(maxLengthOfExportName, 'Exports'.length) - normalizedExportName.length);
919
984
  const isType = isTypeFile(filename);
920
- const sizePadding = ' '.repeat(Math.max(maxFilenameLength, 'File'.length) - filename.length);
921
985
  const prettiedSize = prettyBytes__default.default(size);
922
- console.log(prefix, filenamePadding, `${pc[isType ? 'dim' : 'bold'](filename)}`, sizePadding, prettiedSize);
986
+ const sizePadding = ' '.repeat(Math.max(maxFilenameLength, 'File'.length) - filename.length);
987
+ // Logging shared in debug mode
988
+ if (isPrivateExportPath(exportName)) {
989
+ if (index === 0 && process.env.DEBUG) {
990
+ const sizePadding = ' '.repeat(Math.max(maxFilenameLength, 'File'.length) - 'private shared'.length);
991
+ console.log(pc.dim(normalizeExportName(exportName)), filenamePadding, pc.dim('private shared'), sizePadding, pc.dim(prettiedSize));
992
+ }
993
+ return;
994
+ }
995
+ console.log(exportNameWithPadding, filenamePadding, `${pc[isType ? 'dim' : 'bold'](filename)}`, sizePadding, prettiedSize);
923
996
  });
924
997
  });
925
998
  }
@@ -928,7 +1001,8 @@ const helpMessage = `
928
1001
  Usage: bunchee [options]
929
1002
 
930
1003
  Commands:
931
- prepare auto configure package.json exports for building
1004
+ prepare auto setup package.json for building
1005
+ lint lint configuration in package.json
932
1006
 
933
1007
  Options:
934
1008
  -v, --version output the version number
@@ -1025,6 +1099,12 @@ async function parseCliArgs(argv) {
1025
1099
  }).command('lint', 'lint package.json', ()=>{}, (argv)=>{
1026
1100
  return lint(argv.cwd || process.cwd());
1027
1101
  }).version(version).help('help', 'output usage information').showHelpOnFail(true).parse();
1102
+ const cmd = args._[0];
1103
+ if (cmd === 'prepare' || cmd === 'lint') {
1104
+ return {
1105
+ cmd
1106
+ };
1107
+ }
1028
1108
  const source = args._[0];
1029
1109
  const parsedArgs = {
1030
1110
  source,
@@ -1170,6 +1250,9 @@ async function main() {
1170
1250
  // if (!error) help()
1171
1251
  return exit(error);
1172
1252
  }
1253
+ if ('cmd' in params) {
1254
+ return;
1255
+ }
1173
1256
  await run(params);
1174
1257
  }
1175
1258
  function logWatcherBuildTime(result, spinner) {
package/dist/index.d.ts CHANGED
@@ -31,6 +31,7 @@ type PackageMetadata = {
31
31
  main?: string;
32
32
  bin?: string | Record<string, string>;
33
33
  module?: string;
34
+ files?: string[];
34
35
  type?: 'commonjs' | 'module';
35
36
  dependencies?: Record<string, string>;
36
37
  peerDependencies?: Record<string, string>;
package/dist/index.js CHANGED
@@ -369,12 +369,13 @@ function isBinExportPath(exportPath) {
369
369
  return exportPath === BINARY_TAG || exportPath.startsWith(BINARY_TAG + '/');
370
370
  }
371
371
  // shared.ts -> ./shared
372
- // shared.<export condition>.ts -> ./shared
372
+ // shared.<export condition>.ts -> ./shared.<export condition>
373
373
  // index.ts -> ./index
374
374
  // index.development.ts -> ./index.development
375
+ // foo/index.ts -> ./foo
375
376
  function sourceFilenameToExportFullPath(filename) {
376
- const baseName = baseNameWithoutExtension(filename);
377
- let exportPath = baseName;
377
+ const ext = path__default.default.extname(filename);
378
+ const exportPath = filename.slice(0, -ext.length);
378
379
  return relativify(exportPath);
379
380
  }
380
381
 
@@ -690,6 +691,10 @@ function getSpecialExportTypeFromComposedExportPath(composedExportType) {
690
691
  }
691
692
  return 'default';
692
693
  }
694
+ function getSpecialExportTypeFromSourcePath(sourcePath) {
695
+ const fileBaseName = baseNameWithoutExtension(sourcePath);
696
+ return getSpecialExportTypeFromComposedExportPath(fileBaseName);
697
+ }
693
698
  function getExportTypeFromExportTypesArray(types) {
694
699
  let exportType = 'default';
695
700
  new Set(types).forEach((value)=>{
@@ -820,36 +825,61 @@ async function collectSourceEntriesByExportPath(sourceFolderPath, originalSubpat
820
825
  }
821
826
  // Search private shared module files which are not in the parsedExportsInfo, but start with _.
822
827
  // e.g. _utils.ts, _utils/index.ts
823
- const privatePattern = `**/_*{,/index}.{${[
824
- ...availableExtensions
825
- ].join(',')}}`;
828
+ // e.g. _utils.development.ts, _utils/index.development.js
829
+ const privatePattern = [
830
+ `**/_*{,/index}.{${[
831
+ ...availableExtensions
832
+ ].join(',')}}`,
833
+ `**/_*{,/index}.{${[
834
+ ...runtimeExportConventions
835
+ ].join(',')}}.{${[
836
+ ...availableExtensions
837
+ ].join(',')}}`
838
+ ];
826
839
  const privateFiles = await glob.glob(privatePattern, {
827
840
  cwd: sourceFolderPath,
828
841
  nodir: true
829
842
  });
830
843
  for (const file of privateFiles){
831
844
  const sourceFileAbsolutePath = path.join(sourceFolderPath, file);
832
- const relativeFilePath = relativify(file);
833
- const exportPath = filePathWithoutExtension(relativeFilePath);
834
- // exportsEntries.set(specialExportPath, sourceFilesMap)
845
+ const exportPath = sourceFilenameToExportFullPath(file);
835
846
  const isEsmPkg = isESModulePackage(pkg.type);
836
- // Add private shared files to parsedExportsInfo
837
- parsedExportsInfo.set(exportPath, [
838
- // Map private shared files to the dist directory
839
- // e.g. ./_utils.ts -> ./dist/_utils.js
847
+ // const specialItems: [string, string][] = []
848
+ const specialExportType = getSpecialExportTypeFromSourcePath(file);
849
+ const normalizedExportPath = stripSpecialCondition(exportPath);
850
+ const isSpecialExport = specialExportType !== 'default';
851
+ // export type: default => ''
852
+ // export type: development => '.development'
853
+ const condPart = isSpecialExport ? specialExportType + '.' : '';
854
+ // Map private shared files to the dist directory
855
+ // e.g. ./_utils.ts -> ./dist/_utils.js
856
+ const privateExportInfo = [
840
857
  [
841
858
  relativify(path.join('./dist', exportPath + (isEsmPkg ? '.js' : '.mjs'))),
842
- 'import'
859
+ condPart + 'import'
843
860
  ],
844
861
  [
845
862
  relativify(path.join('./dist', exportPath + (isEsmPkg ? '.cjs' : '.js'))),
846
- 'require'
863
+ condPart + 'require'
847
864
  ]
848
- ]);
865
+ ];
866
+ const exportsInfo = parsedExportsInfo.get(normalizedExportPath);
867
+ if (!exportsInfo) {
868
+ // Add private shared files to parsedExportsInfo
869
+ parsedExportsInfo.set(normalizedExportPath, privateExportInfo);
870
+ } else {
871
+ // Merge private shared files to the existing exportsInfo
872
+ exportsInfo.push(...privateExportInfo);
873
+ }
849
874
  // Insert private shared modules into the entries
850
- exportsEntries.set(exportPath, {
851
- default: sourceFileAbsolutePath
852
- });
875
+ const entry = exportsEntries.get(exportPath);
876
+ if (!entry) {
877
+ exportsEntries.set(exportPath, {
878
+ [specialExportType]: sourceFileAbsolutePath
879
+ });
880
+ } else {
881
+ entry[specialExportType] = sourceFileAbsolutePath;
882
+ }
853
883
  }
854
884
  return {
855
885
  bins,
@@ -1026,7 +1056,7 @@ async function writeDefaultTsconfig(tsConfigPath) {
1026
1056
  const FILENAME_REGEX = /__filename/;
1027
1057
  const DIRNAME_REGEX = /__dirname/;
1028
1058
  // not char, or space before require(.resolve)?(
1029
- const GLOBAL_REQUIRE_REGEX = /(?:^|[^.\w'"`])\brequire(\.resolve)?\(\s*[\r\n]*(['"`])/;
1059
+ const GLOBAL_REQUIRE_REGEX = /(?:^|[^.\w'"`])\brequire(\.resolve)?\(\s*[\r\n]*(\w|['"`])/;
1030
1060
  const PolyfillComment = '/** rollup-private-do-not-use-esm-shim-polyfill */';
1031
1061
  const createESMShim = ({ filename, dirname, globalRequire })=>{
1032
1062
  const useNodeUrl = filename || dirname;
@@ -1303,28 +1333,6 @@ function aliasEntries({ entry: sourceFilePath, conditionNames, entries, format,
1303
1333
  };
1304
1334
  }
1305
1335
 
1306
- function prependDirectives() {
1307
- return {
1308
- name: 'prependDirective',
1309
- transform: {
1310
- order: 'post',
1311
- handler (code, id) {
1312
- var _moduleInfo_meta;
1313
- const moduleInfo = this.getModuleInfo(id);
1314
- if (moduleInfo == null ? void 0 : (_moduleInfo_meta = moduleInfo.meta) == null ? void 0 : _moduleInfo_meta.preserveDirectives) {
1315
- const firstDirective = moduleInfo.meta.preserveDirectives.directives[0];
1316
- if (firstDirective) {
1317
- const directive = firstDirective.value;
1318
- const directiveCode = `'${directive}';`;
1319
- return directiveCode + '\n' + code;
1320
- }
1321
- }
1322
- return null;
1323
- }
1324
- }
1325
- };
1326
- }
1327
-
1328
1336
  const prependShebang = (entry)=>({
1329
1337
  name: 'prependShebang',
1330
1338
  transform: (code, id)=>{
@@ -1508,8 +1516,7 @@ async function buildInputConfig(entry, bundleConfig, exportCondition, buildConte
1508
1516
  }),
1509
1517
  commonjs__default.default({
1510
1518
  exclude: bundleConfig.external || null
1511
- }),
1512
- prependDirectives()
1519
+ })
1513
1520
  ]).filter(isNotNull);
1514
1521
  return {
1515
1522
  input: entry,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bunchee",
3
- "version": "6.0.0-rc.0",
3
+ "version": "6.0.0-rc.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",
@@ -48,11 +48,12 @@
48
48
  "glob": "^11.0.0",
49
49
  "magic-string": "^0.30.11",
50
50
  "ora": "^8.0.1",
51
+ "picomatch": "^4.0.2",
51
52
  "pretty-bytes": "^5.6.0",
52
53
  "rollup": "^4.27.4",
53
54
  "rollup-plugin-dts": "^6.1.1",
54
55
  "rollup-plugin-swc3": "^0.11.1",
55
- "rollup-preserve-directives": "^1.1.2",
56
+ "rollup-preserve-directives": "^1.1.3",
56
57
  "tslib": "^2.7.0",
57
58
  "yargs": "^17.7.2"
58
59
  },
@@ -75,6 +76,7 @@
75
76
  "@types/clean-css": "^4.2.11",
76
77
  "@types/jest": "29.0.0",
77
78
  "@types/node": "^22.9.3",
79
+ "@types/picomatch": "^3.0.1",
78
80
  "@types/yargs": "^17.0.33",
79
81
  "bunchee": "link:./",
80
82
  "cross-env": "^7.0.3",
@@ -114,10 +116,10 @@
114
116
  "test:ci": "[ -z $CI ] || pnpm test",
115
117
  "clean": "rm -rf ./dist",
116
118
  "typecheck": "tsc --noEmit && tsc -p test/tsconfig.json --noEmit",
117
- "cli": "cross-env SWC_NODE_IGNORE_DYNAMIC=1 node -r @swc-node/register",
118
- "ts-bunchee": "pnpm cli ./src/bin/index.ts",
119
+ "run-ts": "cross-env SWC_NODE_IGNORE_DYNAMIC=1 node -r @swc-node/register",
120
+ "ts-bunchee": "pnpm run-ts ./src/bin/index.ts",
119
121
  "build-dir": "pnpm ts-bunchee --cwd",
120
- "build": "pnpm cli ./src/bin/index.ts --runtime node",
122
+ "build": "pnpm run-ts ./src/bin/index.ts --runtime node",
121
123
  "format": "prettier --write ."
122
124
  }
123
125
  }