bunchee 4.2.11 → 4.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -35,39 +35,96 @@ npm install --save-dev bunchee typescript
35
35
 
36
36
  Create your library entry file and package.json.
37
37
  ```sh
38
- cd ./my-lib && mkdir src
39
- touch ./src/index.ts
40
- touch package.json
38
+ cd ./my-lib
39
+ mkdir src && touch ./src/index.ts
41
40
  ```
42
41
 
42
+ #### Prepare
43
+
44
+ ```sh
45
+ # Use bunchee to prepare package.json configuration
46
+ npm bunchee --prepare
47
+ # "If you're using other package manager such as pnpm"
48
+ # pnpm bunchee --prepare
49
+
50
+ # "Or use with npx"
51
+ # npx bunchee@latest --prepare
52
+ ```
53
+
54
+ Or you can checkout the following cases to configure your package.json.
55
+
56
+ <details>
57
+ <summary> JavaScript</summary>
58
+
43
59
  Then use use the [exports field in package.json](https://nodejs.org/api/packages.html#exports-sugar) to configure different conditions and leverage the same functionality as other bundlers, such as webpack. The exports field allows you to define multiple conditions.
44
60
  ```json
45
61
  {
46
62
  "files": ["dist"],
47
63
  "exports": {
48
- "import": "./dist/index.mjs",
49
- "require": "./dist/index.cjs"
64
+ "import": "./dist/es/index.mjs",
65
+ "require": "./dist/cjs/index.cjs"
66
+ },
67
+ "scripts": {
68
+ "build": "bunchee"
69
+ }
70
+ }
71
+ ```
72
+ </details>
73
+
74
+ <details>
75
+ <summary>TypeScript</summary>
76
+
77
+ If you're build a TypeScript library, separate the types from the main entry file and specify the types path in package.json. When you're using `.mjs` or `.cjs` extensions with TypeScript and modern module resolution (above node16), TypeScript will require specific type declaration files like `.d.mts` or `.d.cts` to match the extension. `bunchee` can automatically generate them to match the types to match the condition and extensions. One example is to configure your exports like this in package.json:
78
+
79
+ ```json
80
+ {
81
+ "files": ["dist"],
82
+ "exports": {
83
+ "import": {
84
+ "types": "./dist/es/index.d.mts",
85
+ "default": "./dist/es/index.mjs"
86
+ },
87
+ "require": {
88
+ "types": "./dist/cjs/index.d.cts",
89
+ "default": "./dist/cjs/index.cjs"
90
+ }
50
91
  },
51
92
  "scripts": {
52
93
  "build": "bunchee"
53
94
  }
54
95
  }
55
96
  ```
97
+ </details>
98
+
99
+
100
+ <details>
101
+ <summary>Hybrid (CJS & ESM) Module Resolution with TypeScript</summary>
102
+ If you're using TypeScript with Node 10 and Node 16 module resolution, you can use the `types` field in package.json to specify the types path. Then `bunchee` will generate the types file with the same extension as the main entry file.
56
103
 
57
- If you want to use ESM package, change the `type` field in package.json to `module`, `bunchee` will change the output format to ESM.
58
104
  ```json
59
105
  {
60
- "type": "module",
61
106
  "files": ["dist"],
107
+ "main": "./dist/cjs/index.cjs",
108
+ "module": "./dist/es/index.mjs",
109
+ "types": "./dist/cjs/index.d.ts",
62
110
  "exports": {
63
- "import": "./dist/index.mjs",
64
- "require": "./dist/index.cjs"
111
+ "import": {
112
+ "types": "./dist/es/index.d.mts",
113
+ "default": "./dist/es/index.mjs"
114
+ },
115
+ "require": {
116
+ "types": "./dist/cjs/index.d.cts",
117
+ "default": "./dist/cjs/index.cjs"
118
+ }
65
119
  },
66
120
  "scripts": {
67
121
  "build": "bunchee"
68
122
  }
69
123
  }
70
124
  ```
125
+ </details>
126
+
127
+ #### Build
71
128
 
72
129
  Then files in `src` folders will be treated as entry files and match the export names in package.json. For example:
73
130
  `src/index.ts` will match the exports name `"."` or the only main export.
@@ -122,28 +179,6 @@ Then you need to add two entry files `index.ts` and `lite.ts` in project root di
122
179
 
123
180
  It will also look up for `index.<ext>` file under the directory having the name of the export path. For example, if you have `"./lite": "./dist/lite.js"` in exports field, then it will look up for `./lite/index.js` as the entry file as well.
124
181
 
125
- ### TypeScript Declaration
126
-
127
- When you're using `.mjs` or `.cjs` extensions with TypeScript and modern module resolution (above node16), TypeScript will require specific type declaration files like `.d.mts` or `.d.cts` to match the extension.
128
- `bunchee` can automatically generate them to match the types to match the condition and extensions. One example is to configure your exports like this in package.json:
129
-
130
- ```json
131
- {
132
- "exports": {
133
- ".": {
134
- "import": {
135
- "types": "./dist/index.d.ts",
136
- "default": "./dist/index.mjs"
137
- },
138
- "require": {
139
- "types": "./dist/index.d.ts",
140
- "default": "./dist/index.js"
141
- }
142
- }
143
- }
144
- }
145
- ```
146
-
147
182
  ### Multiple Runtime
148
183
 
149
184
  For exports condition like `react-native`, `react-server` and `edge-light` as they're special platforms, they could have different exports or different code conditions. In this case bunchee provides an override input source file convention if you want to build them as different code bundle.
@@ -322,7 +357,6 @@ This will match the export names `"foo"` and `"bar"` and will be treated as the
322
357
  {
323
358
  "exports": {
324
359
  ".": {
325
- "types": "./dist/index.d.ts",
326
360
  "import": "./dist/index.js"
327
361
  },
328
362
  "./foo": {
@@ -380,26 +414,6 @@ output
380
414
  export default "hello world"
381
415
  ```
382
416
 
383
- ### TypeScript
384
-
385
- By default bunchee includes Typescript v3.9.x inside as a dependency. If you want to use your own version, just install typescript as another dev dependency then bunchee will automatically pick it.
386
-
387
- ```sh
388
- npm i -D bunchee typescript
389
- ```
390
-
391
- Create `tsconfig.json` to specify any compiler options for TypeScript.
392
-
393
- This library requires at least TypeScript 4.1.x.
394
-
395
- Adding `"types"` or `"typing"` field in your package.json, types will be generated with that path.
396
-
397
- ```json
398
- {
399
- "types": "dist/types/index.d.ts"
400
- }
401
- ```
402
-
403
417
  ### Node.js API
404
418
 
405
419
  ```ts
package/dist/bin/cli.js CHANGED
@@ -1,16 +1,41 @@
1
1
  #!/usr/bin/env node
2
2
  var path = require('path');
3
3
  var arg = require('arg');
4
- var fs = require('fs/promises');
4
+ var fsp = require('fs/promises');
5
5
  var require$$0 = require('tty');
6
6
  var bunchee = require('bunchee');
7
+ var fs = require('fs');
7
8
 
8
9
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
9
10
 
10
11
  var path__default = /*#__PURE__*/_interopDefault(path);
11
12
  var arg__default = /*#__PURE__*/_interopDefault(arg);
12
- var fs__default = /*#__PURE__*/_interopDefault(fs);
13
+ var fsp__default = /*#__PURE__*/_interopDefault(fsp);
13
14
  var require$$0__default = /*#__PURE__*/_interopDefault(require$$0);
15
+ var fs__default = /*#__PURE__*/_interopDefault(fs);
16
+
17
+ const availableExtensions = [
18
+ 'js',
19
+ 'cjs',
20
+ 'mjs',
21
+ 'jsx',
22
+ 'ts',
23
+ 'tsx',
24
+ 'cts',
25
+ 'mts'
26
+ ];
27
+ const SRC = 'src';
28
+ const dtsExtensionsMap = {
29
+ js: 'd.ts',
30
+ cjs: 'd.cts',
31
+ mjs: 'd.mts'
32
+ };
33
+ const tsExtensions = [
34
+ 'ts',
35
+ 'tsx',
36
+ 'cts',
37
+ 'mts'
38
+ ];
14
39
 
15
40
  function getDefaultExportFromCjs (x) {
16
41
  return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x;
@@ -64,25 +89,29 @@ picocolors.exports.createColors = createColors;
64
89
  var picocolorsExports = picocolors.exports;
65
90
  var pc = /*@__PURE__*/ getDefaultExportFromCjs(picocolorsExports);
66
91
 
92
+ const defaultColorFn = (text)=>text;
93
+ function color(prefixColor) {
94
+ return pc.isColorSupported ? pc[prefixColor] : defaultColorFn;
95
+ }
67
96
  const logger = {
68
97
  log (...arg) {
69
- console.log(' ', ...arg);
98
+ console.log(...arg);
70
99
  },
71
100
  warn (...arg) {
72
- console.warn(' ⚠️', ...arg);
101
+ console.warn(color('yellow')('⚠️'), ...arg);
73
102
  },
74
103
  error (...arg) {
75
- console.error(' ⨯', ...arg);
104
+ console.error(color('red')('⨯'), ...arg);
76
105
  },
77
106
  info (...arg) {
78
- console.log(' ✓', ...arg);
107
+ console.log(color('green')('✓'), ...arg);
79
108
  }
80
109
  };
81
110
  function paint(prefix, prefixColor, ...arg) {
82
111
  if (pc.isColorSupported) {
83
- console.log(' ' + pc[prefixColor](prefix), ...arg);
112
+ console.log(pc[prefixColor](prefix), ...arg);
84
113
  } else {
85
- console.log(' ' + prefix, ...arg);
114
+ console.log(prefix, ...arg);
86
115
  }
87
116
  }
88
117
 
@@ -97,15 +126,19 @@ async function getPackageMeta(cwd) {
97
126
  const pkgFilePath = path__default.default.resolve(cwd, 'package.json');
98
127
  let targetPackageJson = {};
99
128
  try {
100
- targetPackageJson = JSON.parse(await fs__default.default.readFile(pkgFilePath, {
129
+ targetPackageJson = JSON.parse(await fsp__default.default.readFile(pkgFilePath, {
101
130
  encoding: 'utf-8'
102
131
  }));
103
132
  } catch (_) {}
104
133
  return targetPackageJson;
105
134
  }
135
+ function isTypescriptFile(filename) {
136
+ const ext = path__default.default.extname(filename).slice(1);
137
+ return tsExtensions.includes(ext);
138
+ }
106
139
  async function fileExists(filePath) {
107
140
  try {
108
- await fs__default.default.access(filePath);
141
+ await fsp__default.default.access(filePath);
109
142
  return true;
110
143
  } catch (err) {
111
144
  if (err.code === 'ENOENT') {
@@ -114,8 +147,209 @@ async function fileExists(filePath) {
114
147
  throw err;
115
148
  }
116
149
  }
150
+ const hasAvailableExtension = (filename)=>availableExtensions.includes(path__default.default.extname(filename).slice(1));
151
+ const baseNameWithoutExtension = (filename)=>path__default.default.basename(filename, path__default.default.extname(filename));
152
+
153
+ var version = "4.3.0";
117
154
 
118
- var version = "4.2.11";
155
+ function relativify(path) {
156
+ return path.startsWith('.') ? path : `./${path}`;
157
+ }
158
+
159
+ const DIST = 'dist';
160
+ // Output with posix style in package.json
161
+ function getDistPath(...subPaths) {
162
+ return `./${DIST}/${subPaths.join('/')}`;
163
+ }
164
+ const normalizeBaseNameToExportName = (baseName)=>{
165
+ return /^index(\.|$)/.test(baseName) ? '.' : relativify(baseName);
166
+ };
167
+ function createExportCondition(exportName, sourceFile, moduleType) {
168
+ const isTsSourceFile = isTypescriptFile(sourceFile);
169
+ let cjsExtension = 'js';
170
+ if (moduleType === 'module') {
171
+ cjsExtension = 'cjs';
172
+ }
173
+ if (isTsSourceFile) {
174
+ return {
175
+ import: {
176
+ types: getDistPath('es', `${exportName}.d.mts`),
177
+ default: getDistPath('es', `${exportName}.mjs`)
178
+ },
179
+ require: {
180
+ types: getDistPath('cjs', `${exportName}.${dtsExtensionsMap[cjsExtension]}`),
181
+ default: getDistPath('cjs', `${exportName}.${cjsExtension}`)
182
+ }
183
+ };
184
+ }
185
+ return {
186
+ import: getDistPath(`${exportName}.mjs`),
187
+ require: getDistPath(`${exportName}.${cjsExtension}`)
188
+ };
189
+ }
190
+ async function collectSourceEntries(sourceFolderPath) {
191
+ const bins = new Map();
192
+ const exportsEntries = new Map();
193
+ const entryFileDirentList = await fsp__default.default.readdir(sourceFolderPath, {
194
+ withFileTypes: true
195
+ });
196
+ for (const dirent of entryFileDirentList){
197
+ if (dirent.isDirectory()) {
198
+ if (dirent.name === 'bin') {
199
+ const binDirentList = await fsp__default.default.readdir(path__default.default.join(sourceFolderPath, dirent.name), {
200
+ withFileTypes: true
201
+ });
202
+ for (const binDirent of binDirentList){
203
+ if (binDirent.isFile()) {
204
+ const binFile = path__default.default.join(sourceFolderPath, dirent.name, binDirent.name);
205
+ const binName = baseNameWithoutExtension(binDirent.name);
206
+ if (fs__default.default.existsSync(binFile)) {
207
+ bins.set(binName, binDirent.name);
208
+ }
209
+ }
210
+ }
211
+ } else {
212
+ // Search folder/<index>.<ext> convention entries
213
+ const extensions = availableExtensions;
214
+ for (const extension of extensions){
215
+ const indexFile = path__default.default.join(sourceFolderPath, dirent.name, `index.${extension}`);
216
+ if (fs__default.default.existsSync(indexFile)) {
217
+ exportsEntries.set(dirent.name, indexFile);
218
+ break;
219
+ }
220
+ }
221
+ }
222
+ } else if (dirent.isFile()) {
223
+ const isAvailableExtension = availableExtensions.includes(path__default.default.extname(dirent.name).slice(1));
224
+ if (isAvailableExtension) {
225
+ const baseName = baseNameWithoutExtension(dirent.name);
226
+ const isBinFile = baseName === 'bin';
227
+ if (isBinFile) {
228
+ bins.set('.', dirent.name);
229
+ } else {
230
+ if (hasAvailableExtension(dirent.name)) {
231
+ exportsEntries.set(baseName, dirent.name);
232
+ }
233
+ }
234
+ }
235
+ }
236
+ }
237
+ return {
238
+ bins,
239
+ exportsEntries
240
+ };
241
+ }
242
+ function createExportConditionPair(exportName, sourceFile, moduleType) {
243
+ // <exportName>.<specialCondition>
244
+ let specialCondition;
245
+ let exportCondName;
246
+ if (exportName.indexOf('.') > 0) {
247
+ const [originExportName, specialConditionName] = exportName.split('.');
248
+ specialCondition = {
249
+ [specialConditionName]: getDistPath('es', `${originExportName}-${specialConditionName}.mjs`)
250
+ };
251
+ exportCondName = normalizeBaseNameToExportName(originExportName);
252
+ return [
253
+ exportCondName,
254
+ specialCondition
255
+ ];
256
+ }
257
+ exportCondName = normalizeBaseNameToExportName(exportName);
258
+ const exportCond = createExportCondition(exportName, sourceFile, moduleType);
259
+ return [
260
+ exportCondName,
261
+ exportCond
262
+ ];
263
+ }
264
+ async function prepare(cwd) {
265
+ const sourceFolder = path__default.default.resolve(cwd, SRC);
266
+ if (!fs__default.default.existsSync(sourceFolder)) {
267
+ logger.error(`Source folder ${sourceFolder} does not exist. Cannot proceed to configure \`exports\` field.`);
268
+ process.exit(1);
269
+ }
270
+ const pkgJsonPath = path__default.default.join(cwd, 'package.json');
271
+ let pkgJson = {};
272
+ if (fs__default.default.existsSync(pkgJsonPath)) {
273
+ const pkgJsonString = await fsp__default.default.readFile(pkgJsonPath, 'utf-8');
274
+ pkgJson = JSON.parse(pkgJsonString);
275
+ }
276
+ // configure `files` field with `dist`
277
+ const files = pkgJson.files || [];
278
+ if (!files.includes(DIST)) {
279
+ files.push(DIST);
280
+ }
281
+ pkgJson.files = files;
282
+ let isUsingTs = false;
283
+ // Collect bins and exports entries
284
+ const { bins, exportsEntries } = await collectSourceEntries(sourceFolder);
285
+ const tsconfigPath = path__default.default.join(cwd, 'tsconfig.json');
286
+ if (!fs__default.default.existsSync(tsconfigPath)) {
287
+ const sourceFiles = [
288
+ ...exportsEntries.values()
289
+ ].concat([
290
+ ...bins.values()
291
+ ]);
292
+ const hasTypeScriptFiles = sourceFiles.some((filename)=>isTypescriptFile(filename));
293
+ if (hasTypeScriptFiles) {
294
+ isUsingTs = true;
295
+ await fsp__default.default.writeFile(tsconfigPath, '{}', 'utf-8');
296
+ logger.log(`Detected using TypeScript but tsconfig.json is missing, created a ${pc.blue('tsconfig.json')} for you.`);
297
+ }
298
+ }
299
+ // Configure as ESM package by default if there's no `type` field
300
+ if (!pkgJson.type) {
301
+ pkgJson.type = 'module';
302
+ }
303
+ if (bins.size > 0) {
304
+ logger.log('Discovered binaries entries:');
305
+ const maxLengthOfBinName = Math.max(...Array.from(bins.keys()).map((binName)=>normalizeBaseNameToExportName(binName).length));
306
+ for (const [binName, binFile] of bins.entries()){
307
+ const spaces = ' '.repeat(Math.max(maxLengthOfBinName - normalizeBaseNameToExportName(binName).length, 0));
308
+ logger.log(` ${normalizeBaseNameToExportName(binName)}${spaces}: ${binFile}`);
309
+ }
310
+ if (bins.size === 1 && bins.has('.')) {
311
+ pkgJson.bin = getDistPath('bin', 'index.js');
312
+ } else {
313
+ pkgJson.bin = {};
314
+ for (const [binName] of bins.entries()){
315
+ pkgJson.bin[binName === '.' ? pkgJson.name : binName] = getDistPath('bin', binName + '.js');
316
+ }
317
+ }
318
+ }
319
+ if (exportsEntries.size > 0) {
320
+ logger.log('Discovered exports entries:');
321
+ const maxLengthOfExportName = Math.max(...Array.from(exportsEntries.keys()).map((exportName)=>normalizeBaseNameToExportName(exportName).length));
322
+ for (const [exportName, exportFile] of exportsEntries.entries()){
323
+ const spaces = ' '.repeat(Math.max(maxLengthOfExportName - normalizeBaseNameToExportName(exportName).length, 0));
324
+ logger.log(` ${normalizeBaseNameToExportName(exportName)}${spaces}: ${exportFile}`);
325
+ }
326
+ const pkgExports = {};
327
+ for (const [exportName, sourceFile] of exportsEntries.entries()){
328
+ const [key, value] = createExportConditionPair(exportName, sourceFile, pkgJson.type);
329
+ pkgExports[key] = {
330
+ ...value,
331
+ ...pkgExports[key]
332
+ };
333
+ }
334
+ // Configure node10 module resolution
335
+ if (exportsEntries.has('index')) {
336
+ const isESM = pkgJson.type === 'module';
337
+ const mainExport = pkgExports['.'];
338
+ const mainCondition = isESM ? 'import' : 'require';
339
+ pkgJson.main = isUsingTs ? mainExport[mainCondition].default : mainExport[mainCondition];
340
+ pkgJson.module = isUsingTs ? mainExport.import.default : mainExport.import;
341
+ if (isUsingTs) {
342
+ pkgJson.types = isESM ? mainExport.import.types : mainExport.require.types;
343
+ }
344
+ }
345
+ // Assign the properties by order: files, main, module, types, exports
346
+ if (Object.keys(pkgExports).length > 0) {
347
+ pkgJson.exports = pkgExports;
348
+ }
349
+ }
350
+ await fsp__default.default.writeFile(pkgJsonPath, JSON.stringify(pkgJson, null, 2));
351
+ logger.info('Configured `exports` in package.json');
352
+ }
119
353
 
120
354
  const helpMessage = `
121
355
  Usage: bunchee [options]
@@ -127,6 +361,7 @@ Options:
127
361
  -o, --output <file> specify output filename
128
362
  -f, --format <format> type of output (esm, amd, cjs, iife, umd, system), default: esm
129
363
  -h, --help output usage information
364
+ --prepare auto configure package.json exports for building
130
365
  --external <mod> specify an external dependency, separate by comma
131
366
  --no-external do not bundle external dependencies
132
367
  --target <target> js features target: swc target es versions. default: es2015
@@ -172,6 +407,7 @@ function parseCliArgs(argv) {
172
407
  '--env': String,
173
408
  '--external': String,
174
409
  '--no-external': Boolean,
410
+ '--prepare': Boolean,
175
411
  '-h': '--help',
176
412
  '-v': '--version',
177
413
  '-w': '--watch',
@@ -197,7 +433,8 @@ function parseCliArgs(argv) {
197
433
  runtime: args['--runtime'],
198
434
  target: args['--target'],
199
435
  external: !!args['--no-external'] ? null : args['--external'],
200
- env: args['--env']
436
+ env: args['--env'],
437
+ prepare: !!args['--prepare']
201
438
  };
202
439
  return parsedArgs;
203
440
  }
@@ -225,6 +462,9 @@ async function run(args) {
225
462
  if (args.help) {
226
463
  return help();
227
464
  }
465
+ if (args.prepare) {
466
+ return await prepare(cwd);
467
+ }
228
468
  const entry = source ? path__default.default.resolve(cwd, source) : '';
229
469
  try {
230
470
  await bunchee.bundle(entry, bundleConfig);
@@ -241,7 +481,7 @@ async function run(args) {
241
481
  return;
242
482
  }
243
483
  // build mode
244
- console.log();
484
+ logger.log();
245
485
  paint('✓', 'green', `bunchee ${version} build completed`);
246
486
  await lintPackage(cwd);
247
487
  }
package/dist/index.d.ts CHANGED
@@ -1,6 +1,9 @@
1
1
  import { JscTarget } from '@swc/types';
2
2
  import { OutputOptions } from 'rollup';
3
3
 
4
+ type ExportCondition = string | {
5
+ [key: string]: ExportCondition | string;
6
+ };
4
7
  type BundleConfig = {
5
8
  file?: string;
6
9
  cwd?: string;
@@ -13,6 +16,20 @@ type BundleConfig = {
13
16
  env?: string[];
14
17
  dts?: boolean;
15
18
  runtime?: string;
19
+ pkg?: PackageMetadata;
20
+ };
21
+ type PackageMetadata = {
22
+ name?: string;
23
+ main?: string;
24
+ bin?: string | Record<string, string>;
25
+ module?: string;
26
+ type?: 'commonjs' | 'module';
27
+ dependencies?: Record<string, string>;
28
+ peerDependencies?: Record<string, string>;
29
+ peerDependenciesMeta?: Record<string, Record<string, string>>;
30
+ exports?: string | Record<string, ExportCondition>;
31
+ types?: string;
32
+ typings?: string;
16
33
  };
17
34
 
18
35
  declare function bundle(entryPath: string, { cwd: _cwd, ...options }?: BundleConfig): Promise<any>;
package/dist/index.js CHANGED
@@ -52,12 +52,11 @@ const availableExportConventions = [
52
52
  'edge-light'
53
53
  ];
54
54
  const availableESExtensionsRegex = /\.(m|c)?[jt]sx?$/;
55
- const dtsExtensionRegex = /\.d\.(m|c)?ts$/;
56
55
  const SRC = 'src';
57
- const dtsExtensions = {
58
- js: '.d.ts',
59
- cjs: '.d.cts',
60
- mjs: '.d.mts'
56
+ const dtsExtensionsMap = {
57
+ js: 'd.ts',
58
+ cjs: 'd.cts',
59
+ mjs: 'd.mts'
61
60
  };
62
61
  const disabledWarnings = new Set([
63
62
  'MIXED_EXPORTS',
@@ -120,27 +119,24 @@ picocolors.exports.createColors = createColors;
120
119
  var picocolorsExports = picocolors.exports;
121
120
  var pc = /*@__PURE__*/ getDefaultExportFromCjs(picocolorsExports);
122
121
 
122
+ const defaultColorFn = (text)=>text;
123
+ function color(prefixColor) {
124
+ return pc.isColorSupported ? pc[prefixColor] : defaultColorFn;
125
+ }
123
126
  const logger = {
124
127
  log (...arg) {
125
- console.log(' ', ...arg);
128
+ console.log(...arg);
126
129
  },
127
130
  warn (...arg) {
128
- console.warn(' ⚠️', ...arg);
131
+ console.warn(color('yellow')('⚠️'), ...arg);
129
132
  },
130
133
  error (...arg) {
131
- console.error(' ⨯', ...arg);
134
+ console.error(color('red')('⨯'), ...arg);
132
135
  },
133
136
  info (...arg) {
134
- console.log(' ✓', ...arg);
137
+ console.log(color('green')('✓'), ...arg);
135
138
  }
136
139
  };
137
- function paint(prefix, prefixColor, ...arg) {
138
- if (pc.isColorSupported) {
139
- console.log(' ' + pc[prefixColor](prefix), ...arg);
140
- } else {
141
- console.log(' ' + prefix, ...arg);
142
- }
143
- }
144
140
 
145
141
  function exit(err) {
146
142
  logger.error(err);
@@ -209,7 +205,7 @@ async function getSourcePathFromExportPath(cwd, exportPath, exportType) {
209
205
  return;
210
206
  }
211
207
  // Unlike path.basename, forcedly removing extension
212
- function filenameWithoutExtension(file) {
208
+ function filePathWithoutExtension(file) {
213
209
  return file ? file.replace(new RegExp(`${path__default.default.extname(file)}$`), '') : undefined;
214
210
  }
215
211
  const nonNullable = (n)=>Boolean(n);
@@ -610,7 +606,7 @@ const getExportTypeDist = (parsedExportCondition, cwd)=>{
610
606
  continue;
611
607
  }
612
608
  const ext = path.extname(filePath).slice(1);
613
- const typeFile = getDistPath(`${filenameWithoutExtension(filePath) || ''}${dtsExtensions[ext]}`, cwd);
609
+ const typeFile = getDistPath(`${filePathWithoutExtension(filePath) || ''}.${dtsExtensionsMap[ext]}`, cwd);
614
610
  if (existed.has(typeFile)) {
615
611
  continue;
616
612
  }
@@ -691,7 +687,7 @@ function getExportConditionDist(pkg, parsedExportCondition, cwd) {
691
687
  return dist;
692
688
  }
693
689
  function getTypeFilePath(entryFilePath, exportCondition, cwd) {
694
- const name = filenameWithoutExtension(entryFilePath);
690
+ const name = filePathWithoutExtension(entryFilePath);
695
691
  const firstDistPath = exportCondition ? Object.values(exportCondition.export)[0] : undefined;
696
692
  const exportName = (exportCondition == null ? void 0 : exportCondition.name) || 'index';
697
693
  return entryFilePath ? name + '.d.ts' : path.resolve(firstDistPath ? path.dirname(firstDistPath) : path.join(cwd, 'dist'), (exportName === '.' ? 'index' : exportName) + '.d.ts');
@@ -739,7 +735,8 @@ function getBuildEnv(envs) {
739
735
  }
740
736
  return alias;
741
737
  }
742
- async function buildInputConfig(entry, entries, pkg, options, cwd, { tsConfigPath, tsCompilerOptions }, pluginContext, dts) {
738
+ async function buildInputConfig(entry, options, buildContext, dts) {
739
+ const { entries, pkg, cwd, tsOptions: { tsConfigPath, tsCompilerOptions }, pluginContext } = buildContext;
743
740
  const hasNoExternal = options.external === null;
744
741
  var _options_external;
745
742
  const externals = hasNoExternal ? [] : [
@@ -780,7 +777,7 @@ async function buildInputConfig(entry, entries, pkg, options, cwd, { tsConfigPat
780
777
  inlineSourcesContent: false,
781
778
  isModule: true
782
779
  };
783
- const sizePlugin = pluginContext.sizeCollector.plugin(cwd);
780
+ const sizePlugin = pluginContext.outputState.plugin(cwd);
784
781
  // common plugins for both dts and ts assets that need to be processed
785
782
  const commonPlugins = [
786
783
  sizePlugin,
@@ -962,14 +959,15 @@ function createSplitChunks(dependencyGraphMap, entryFiles) {
962
959
  return;
963
960
  };
964
961
  }
965
- function buildOutputConfigs(entries, pkg, exportPaths, options, exportCondition, cwd, { tsCompilerOptions }, pluginContext, dts) {
962
+ function buildOutputConfigs(options, exportCondition, buildContext, dts) {
966
963
  const { format } = options;
964
+ const { entries, pkg, exportPaths, cwd, tsOptions: { tsCompilerOptions }, pluginContext } = buildContext;
967
965
  // Add esm mark and interop helper if esm export is detected
968
966
  const useEsModuleMark = hasEsmExport(exportPaths, tsCompilerOptions);
969
967
  const typings = getTypings(pkg);
970
968
  const file = options.file && path.resolve(cwd, options.file);
971
969
  const dtsDir = typings ? path.dirname(path.resolve(cwd, typings)) : path.resolve(cwd, 'dist');
972
- const name = filenameWithoutExtension(file);
970
+ const name = filePathWithoutExtension(file);
973
971
  // TODO: simplify dts file name detection
974
972
  const dtsFile = file ? file : exportCondition.export['types'] ? path.resolve(cwd, exportCondition.export['types']) : path.resolve(dtsDir, (exportCondition.name === '.' ? 'index' : exportCondition.name) + '.d.ts');
975
973
  const dtsPathConfig = {
@@ -997,10 +995,11 @@ function buildOutputConfigs(entries, pkg, exportPaths, options, exportCondition,
997
995
  entryFileNames: path.basename(outputFile)
998
996
  };
999
997
  }
1000
- async function buildEntryConfig(entries, pkg, exportPaths, bundleConfig, cwd, tsOptions, pluginContext, dts) {
998
+ async function buildEntryConfig(bundleConfig, pluginContext, dts) {
1001
999
  const configs = [];
1000
+ const { entries } = pluginContext;
1002
1001
  for (const exportCondition of Object.values(entries)){
1003
- const rollupConfig = buildConfig(entries, pkg, exportPaths, bundleConfig, exportCondition, cwd, tsOptions, pluginContext, dts);
1002
+ const rollupConfig = buildConfig(bundleConfig, exportCondition, pluginContext, dts);
1004
1003
  configs.push(rollupConfig);
1005
1004
  }
1006
1005
  return await Promise.all(configs);
@@ -1042,7 +1041,9 @@ async function buildEntryConfig(entries, pkg, exportPaths, bundleConfig, cwd, ts
1042
1041
  name: entryExport,
1043
1042
  export: exportCondForType
1044
1043
  };
1045
- const entryImportPath = path__default.default.join(pkg.name || '', exportCondition.name) + (exportType ? `.${exportType}` : '');
1044
+ const nameWithExportPath = pkg.name ? path__default.default.join(pkg.name, exportCondition.name) : exportCondition.name;
1045
+ const needsDelimiter = !nameWithExportPath.endsWith('.') && exportType;
1046
+ const entryImportPath = nameWithExportPath + (needsDelimiter ? '.' : '') + exportType;
1046
1047
  entries[entryImportPath] = exportCondition;
1047
1048
  }
1048
1049
  const binaryExports = pkg.bin;
@@ -1092,47 +1093,48 @@ async function buildEntryConfig(entries, pkg, exportPaths, bundleConfig, cwd, ts
1092
1093
  await Promise.all(collectEntriesPromises);
1093
1094
  return entries;
1094
1095
  }
1095
- async function buildConfig(entries, pkg, exportPaths, bundleConfig, exportCondition, cwd, tsOptions, pluginContext, dts) {
1096
+ async function buildConfig(bundleConfig, exportCondition, pluginContext, dts) {
1096
1097
  const { file } = bundleConfig;
1098
+ const { pkg, cwd, tsOptions } = pluginContext;
1097
1099
  const useTypescript = Boolean(tsOptions.tsConfigPath);
1098
1100
  const options = {
1099
1101
  ...bundleConfig,
1100
1102
  useTypescript
1101
1103
  };
1102
1104
  const entry = exportCondition.source;
1103
- const inputOptions = await buildInputConfig(entry, entries, pkg, options, cwd, tsOptions, pluginContext, dts);
1105
+ const inputOptions = await buildInputConfig(entry, options, pluginContext, dts);
1104
1106
  const outputExports = getExportConditionDist(pkg, exportCondition, cwd);
1105
1107
  let outputConfigs = [];
1106
1108
  // Generate dts job - single config
1107
1109
  if (dts) {
1108
1110
  const typeOutputExports = getExportTypeDist(exportCondition, cwd);
1109
- outputConfigs = typeOutputExports.map((typeFile)=>buildOutputConfigs(entries, pkg, exportPaths, {
1111
+ outputConfigs = typeOutputExports.map((typeFile)=>buildOutputConfigs({
1110
1112
  ...bundleConfig,
1111
1113
  format: 'es',
1112
1114
  useTypescript,
1113
1115
  file: typeFile
1114
- }, exportCondition, cwd, tsOptions, pluginContext, dts));
1116
+ }, exportCondition, pluginContext, dts));
1115
1117
  } else {
1116
1118
  // multi outputs with specified format
1117
1119
  outputConfigs = outputExports.map((exportDist)=>{
1118
- return buildOutputConfigs(entries, pkg, exportPaths, {
1120
+ return buildOutputConfigs({
1119
1121
  ...bundleConfig,
1120
1122
  file: exportDist.file,
1121
1123
  format: exportDist.format,
1122
1124
  useTypescript
1123
- }, exportCondition, cwd, tsOptions, pluginContext, dts);
1125
+ }, exportCondition, pluginContext, dts);
1124
1126
  });
1125
1127
  // CLI output option is always prioritized
1126
1128
  if (file) {
1127
1129
  var _outputExports_;
1128
1130
  const fallbackFormat = (_outputExports_ = outputExports[0]) == null ? void 0 : _outputExports_.format;
1129
1131
  outputConfigs = [
1130
- buildOutputConfigs(entries, pkg, exportPaths, {
1132
+ buildOutputConfigs({
1131
1133
  ...bundleConfig,
1132
1134
  file,
1133
1135
  format: bundleConfig.format || fallbackFormat,
1134
1136
  useTypescript
1135
- }, exportCondition, cwd, tsOptions, pluginContext, dts)
1137
+ }, exportCondition, pluginContext, dts)
1136
1138
  ];
1137
1139
  }
1138
1140
  }
@@ -1143,7 +1145,11 @@ async function buildConfig(entries, pkg, exportPaths, bundleConfig, exportCondit
1143
1145
  };
1144
1146
  }
1145
1147
 
1146
- function createChunkSizeCollector({ entries }) {
1148
+ function relativify(path) {
1149
+ return path.startsWith('.') ? path : `./${path}`;
1150
+ }
1151
+
1152
+ function createOutputState({ entries }) {
1147
1153
  const sizeStats = new Map();
1148
1154
  function addSize({ fileName, size, sourceFileName, exportPath }) {
1149
1155
  if (!sizeStats.has(exportPath)) {
@@ -1159,8 +1165,8 @@ function createChunkSizeCollector({ entries }) {
1159
1165
  }
1160
1166
  }
1161
1167
  const reversedMapping = new Map();
1162
- Object.entries(entries).forEach(([, entry])=>{
1163
- reversedMapping.set(entry.source, entry.name || '.');
1168
+ Object.entries(entries).forEach(([resolvedExportName, entry])=>{
1169
+ reversedMapping.set(entry.source, resolvedExportName);
1164
1170
  });
1165
1171
  return {
1166
1172
  plugin: (cwd)=>{
@@ -1191,20 +1197,70 @@ function createChunkSizeCollector({ entries }) {
1191
1197
  }
1192
1198
  };
1193
1199
  }
1194
- function logSizeStats(sizeCollector) {
1200
+ function isBin(filename) {
1201
+ return filename === 'bin' || filename.startsWith('bin/');
1202
+ }
1203
+ function isTypeFile(filename) {
1204
+ return filename.endsWith('.d.ts') || filename.endsWith('.d.mts') || filename.endsWith('.d.cts');
1205
+ }
1206
+ function normalizeExportName(exportName) {
1207
+ const isBinary = isBin(exportName);
1208
+ let result = exportName;
1209
+ const isSubpathExport = exportName.includes('/');
1210
+ const isSpecialExport = exportName.includes('.');
1211
+ if (isBinary) {
1212
+ result = (exportName.replace(/bin(\/|$)/, '') || '.') + ' (bin)';
1213
+ } else if (isSubpathExport || isSpecialExport) {
1214
+ const subExportName = exportName.split('/')[1] || exportName;
1215
+ if (subExportName.includes('.') && subExportName !== '.') {
1216
+ const [originExportName, specialCondition] = subExportName.split('.');
1217
+ result = (isSubpathExport ? relativify(originExportName) : '.') + ' (' + specialCondition + ')';
1218
+ } else {
1219
+ result = isSubpathExport ? relativify(subExportName) : '.';
1220
+ }
1221
+ } else {
1222
+ result = '.';
1223
+ }
1224
+ return result;
1225
+ }
1226
+ function getExportNameWithoutExportCondition(exportName) {
1227
+ return exportName.includes('.') ? exportName.split('.')[0] : exportName;
1228
+ }
1229
+ function logOutputState(sizeCollector) {
1195
1230
  const stats = sizeCollector.getSizeStats();
1196
1231
  const allFileNameLengths = Array.from(stats.values()).flat(1).map(([filename])=>filename.length);
1197
- const maxLength = Math.max(...allFileNameLengths);
1198
- [
1232
+ const maxFilenameLength = Math.max(...allFileNameLengths);
1233
+ const statsArray = [
1199
1234
  ...stats.entries()
1200
- ].sort(([a], [b])=>a.length - b.length).forEach(([, filesList])=>{
1201
- filesList.forEach((item)=>{
1235
+ ].sort(([a], [b])=>{
1236
+ const comp = getExportNameWithoutExportCondition(a).length - getExportNameWithoutExportCondition(b).length;
1237
+ return comp === 0 ? a.localeCompare(b) : comp;
1238
+ });
1239
+ const maxLengthOfExportName = Math.max(...statsArray.map(([exportName])=>normalizeExportName(exportName).length));
1240
+ console.log(pc.underline('Exports'), ' '.repeat(Math.max(maxLengthOfExportName - 'Exports'.length, 0)), pc.underline('File'), ' '.repeat(Math.max(maxFilenameLength - 'File'.length, 0)), pc.underline('Size'));
1241
+ statsArray.forEach(([exportName, filesList])=>{
1242
+ // sort by file type, first js files then types, js/mjs/cjs are prioritized than .d.ts/.d.mts/.d.cts
1243
+ filesList.sort(([a], [b])=>{
1244
+ const aIsType = isTypeFile(a);
1245
+ const bIsType = isTypeFile(b);
1246
+ if (aIsType && bIsType) {
1247
+ return 0;
1248
+ }
1249
+ if (aIsType) {
1250
+ return 1;
1251
+ }
1252
+ if (bIsType) {
1253
+ return -1;
1254
+ }
1255
+ return 0;
1256
+ }).forEach((item, index)=>{
1202
1257
  const [filename, , size] = item;
1203
- const padding = ' '.repeat(maxLength - filename.length);
1204
- const isTypeFile = dtsExtensionRegex.test(filename);
1205
- const action = isTypeFile ? '[types]' : '[chunk]';
1258
+ const normalizedExportName = normalizeExportName(exportName);
1259
+ const prefix = index === 0 ? normalizedExportName + ' '.repeat(maxLengthOfExportName - normalizedExportName.length) : ' '.repeat(maxLengthOfExportName);
1260
+ const sizePadding = ' '.repeat(maxFilenameLength - filename.length);
1206
1261
  const prettiedSize = prettyBytes__default.default(size);
1207
- paint(' ' + action, isTypeFile ? 'blue' : 'white', `${filename}${padding} - ${prettiedSize}`);
1262
+ const isType = isTypeFile(filename);
1263
+ console.log(` ${prefix} ${pc[isType ? 'dim' : 'bold'](filename)}${sizePadding} ${prettiedSize}`);
1208
1264
  });
1209
1265
  });
1210
1266
  }
@@ -1235,7 +1291,7 @@ async function getExportables(cwd, excludeKeys) {
1235
1291
  function mapWildcard(wildcardExports, exportables) {
1236
1292
  return exportables.map((exportable)=>{
1237
1293
  const isFile = exportable.includes('.');
1238
- const filename = isFile ? filenameWithoutExtension(exportable) : exportable;
1294
+ const filename = isFile ? filePathWithoutExtension(exportable) : exportable;
1239
1295
  return {
1240
1296
  [`./${filename}`]: Object.fromEntries(Object.entries(wildcardExports['./*']).map(([key, value])=>[
1241
1297
  key,
@@ -1322,12 +1378,9 @@ async function bundle(entryPath, { cwd: _cwd, ...options } = {}) {
1322
1378
  const { input, exportName } = rollupConfig;
1323
1379
  const exportPath = getExportPath(pkg, cwd, exportName);
1324
1380
  // Log original entry file relative path
1325
- const source = typeof input.input === 'string' ? path.relative(cwd, input.input) : exportPath;
1326
- const buildMetadata = {
1327
- source
1328
- };
1381
+ typeof input.input === 'string' ? path.relative(cwd, input.input) : exportPath;
1329
1382
  if (options.watch) {
1330
- return Promise.resolve(runWatch(rollupConfig, buildMetadata));
1383
+ return Promise.resolve(runWatch(rollupConfig));
1331
1384
  }
1332
1385
  return runBundle(rollupConfig);
1333
1386
  };
@@ -1339,23 +1392,30 @@ async function bundle(entryPath, { cwd: _cwd, ...options } = {}) {
1339
1392
  return Promise.reject(err);
1340
1393
  }
1341
1394
  const entries = await collectEntries(pkg, entryPath, exportPaths, cwd);
1342
- const sizeCollector = createChunkSizeCollector({
1395
+ const sizeCollector = createOutputState({
1343
1396
  entries
1344
1397
  });
1345
1398
  const entriesAlias = getReversedAlias(entries);
1346
- const pluginContext = {
1347
- sizeCollector,
1348
- moduleDirectiveLayerMap: new Map(),
1349
- entriesAlias
1399
+ const buildContext = {
1400
+ entries,
1401
+ pkg,
1402
+ exportPaths,
1403
+ cwd,
1404
+ tsOptions: defaultTsOptions,
1405
+ pluginContext: {
1406
+ outputState: sizeCollector,
1407
+ moduleDirectiveLayerMap: new Map(),
1408
+ entriesAlias
1409
+ }
1350
1410
  };
1351
- const buildConfigs = await buildEntryConfig(entries, pkg, exportPaths, options, cwd, defaultTsOptions, pluginContext, false);
1411
+ const buildConfigs = await buildEntryConfig(options, buildContext, false);
1352
1412
  const assetsJobs = buildConfigs.map((rollupConfig)=>bundleOrWatch(rollupConfig));
1353
- const typesJobs = hasTsConfig ? (await buildEntryConfig(entries, pkg, exportPaths, options, cwd, defaultTsOptions, pluginContext, true)).map((rollupConfig)=>bundleOrWatch(rollupConfig)) : [];
1413
+ const typesJobs = hasTsConfig ? (await buildEntryConfig(options, buildContext, true)).map((rollupConfig)=>bundleOrWatch(rollupConfig)) : [];
1354
1414
  const result = await Promise.all(assetsJobs.concat(typesJobs));
1355
1415
  if (result.length === 0) {
1356
1416
  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');
1357
1417
  }
1358
- logSizeStats(sizeCollector);
1418
+ logOutputState(sizeCollector);
1359
1419
  return result;
1360
1420
  }
1361
1421
  function runWatch({ input, output }, metadata) {
@@ -1379,7 +1439,6 @@ function runWatch({ input, output }, metadata) {
1379
1439
  }
1380
1440
  case 'START':
1381
1441
  {
1382
- logger.log(`Start building ${metadata.source} ...`);
1383
1442
  break;
1384
1443
  }
1385
1444
  case 'END':
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bunchee",
3
- "version": "4.2.11",
3
+ "version": "4.3.0",
4
4
  "description": "zero config bundler for js/ts/jsx libraries",
5
5
  "bin": "./dist/bin/cli.js",
6
6
  "main": "./dist/index.js",