Skip to content

Commit 6f6e080

Browse files
refactor: Output ESM files w/ .mjs when appropriate (#950)
* fix: Output ESM w/ .mjs ext when pkg type is cjs * docs: Adding changeset * Update README.md * Update README.md Co-authored-by: Jason Miller <developit@users.noreply.github.com> * chore: Add warning for new ESM output extension behavior * Update src/index.js Co-authored-by: Jason Miller <developit@users.noreply.github.com> Co-authored-by: Jason Miller <developit@users.noreply.github.com>
1 parent 242754f commit 6f6e080

File tree

4 files changed

+317
-297
lines changed

4 files changed

+317
-297
lines changed

.changeset/clean-ducks-push.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'microbundle': minor
3+
---
4+
5+
Microbundle will now output ESM using `.mjs` as the file extension when the package type is CJS

README.md

+12-12
Original file line numberDiff line numberDiff line change
@@ -120,8 +120,8 @@ export default function (e, t, r) {
120120
```jsonc
121121
{
122122
"main": "./dist/foo.umd.js", // legacy UMD output (for Node & CDN use)
123-
"module": "./dist/foo.module.js", // legacy ES Modules output (for bundlers)
124-
"exports": "./dist/foo.modern.js", // modern ES2017 output
123+
"module": "./dist/foo.module.mjs", // legacy ES Modules output (for bundlers)
124+
"exports": "./dist/foo.modern.mjs", // modern ES2017 output
125125
"scripts": {
126126
"build": "microbundle src/foo.js"
127127
}
@@ -134,9 +134,9 @@ The `"exports"` field can also be an object for packages with multiple entry mod
134134
{
135135
"name": "foo",
136136
"exports": {
137-
".": "./dist/foo.modern.js", // import "foo" (the default)
138-
"./lite": "./dist/lite.modern.js", // import "foo/lite"
139-
"./full": "./dist/full.modern.js" // import "foo/full"
137+
".": "./dist/foo.modern.mjs", // import "foo" (the default)
138+
"./lite": "./dist/lite.modern.mjs", // import "foo/lite"
139+
"./full": "./dist/full.modern.mjs" // import "foo/full"
140140
},
141141
"scripts": {
142142
"build": "microbundle src/*.js" // build foo.js, lite.js and full.js
@@ -163,15 +163,15 @@ The filenames and paths for generated bundles in each format are defined by the
163163

164164
```jsonc
165165
{
166-
"source": "src/index.js", // input
167-
"main": "dist/foo.js", // CommonJS output bundle
168-
"umd:main": "dist/foo.umd.js", // UMD output bundle
169-
"module": "dist/foo.m.js", // ES Modules output bundle
166+
"source": "src/index.js", // input
167+
"main": "dist/foo.js", // CommonJS output bundle
168+
"umd:main": "dist/foo.umd.js", // UMD output bundle
169+
"module": "dist/foo.mjs", // ES Modules output bundle
170170
"exports": {
171-
"require": "./dist/foo.js", // CommonJS output bundle
172-
"default": "./dist/foo.modern.js", // Modern ES Modules output bundle
171+
"require": "./dist/foo.js", // CommonJS output bundle
172+
"default": "./dist/foo.modern.mjs", // Modern ES Modules output bundle
173173
},
174-
"types": "dist/foo.d.ts" // TypeScript typings directory
174+
"types": "dist/foo.d.ts" // TypeScript typings directory
175175
}
176176
```
177177

src/index.js

+20-5
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import fs from 'fs';
22
import { resolve, relative, dirname, basename, extname } from 'path';
33
import camelCase from 'camelcase';
44
import escapeStringRegexp from 'escape-string-regexp';
5-
import { blue, red } from 'kleur';
5+
import { blue, yellow, red } from 'kleur';
66
import { map, series } from 'asyncro';
77
import glob from 'tiny-glob/sync';
88
import autoprefixer from 'autoprefixer';
@@ -282,6 +282,7 @@ function walk(exports, includeDefault) {
282282
function getMain({ options, entry, format }) {
283283
const { pkg } = options;
284284
const pkgMain = options['pkg-main'];
285+
const pkgTypeModule = pkg.type === 'module';
285286

286287
if (!pkgMain) {
287288
return options.output;
@@ -301,18 +302,23 @@ function getMain({ options, entry, format }) {
301302
mainsByFormat.es = replaceName(
302303
pkg.module && !pkg.module.match(/src\//)
303304
? pkg.module
304-
: pkg['jsnext:main'] || 'x.esm.js',
305+
: pkg['jsnext:main'] || pkgTypeModule
306+
? 'x.esm.js'
307+
: 'x.esm.mjs',
305308
mainNoExtension,
306309
);
310+
307311
mainsByFormat.modern = replaceName(
308-
(pkg.exports && walk(pkg.exports, pkg.type === 'module')) ||
312+
(pkg.exports && walk(pkg.exports, pkgTypeModule)) ||
309313
(pkg.syntax && pkg.syntax.esmodules) ||
310314
pkg.esmodule ||
311-
'x.modern.js',
315+
pkgTypeModule
316+
? 'x.modern.js'
317+
: 'x.modern.mjs',
312318
mainNoExtension,
313319
);
314320
mainsByFormat.cjs = replaceName(
315-
pkg['cjs:main'] || (pkg.type && pkg.type === 'module' ? 'x.cjs' : 'x.js'),
321+
pkg['cjs:main'] || (pkgTypeModule ? 'x.cjs' : 'x.js'),
316322
mainNoExtension,
317323
);
318324
mainsByFormat.umd = replaceName(
@@ -440,6 +446,15 @@ function createConfig(options, entry, format, writeMeta) {
440446
const outputDir = dirname(absMain);
441447
const outputEntryFileName = basename(absMain);
442448

449+
// Warn about the (somewhat) breaking change in #950
450+
if (format === 'es' && !pkg.module && outputEntryFileName.endsWith('.mjs')) {
451+
stdout(
452+
yellow(
453+
'Warning: your package.json does not specify {"type":"module"}. Microbundle assumes this is a CommonJS package and is generating ES Modules with the ".mjs" file extension.',
454+
),
455+
);
456+
}
457+
443458
let config = {
444459
/** @type {import('rollup').InputOptions} */
445460
inputOptions: {

0 commit comments

Comments
 (0)