Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

major: output ESM for .mjs or "type": "module" builds #720

Merged
merged 24 commits into from Jul 16, 2021
Merged

Conversation

guybedford
Copy link
Contributor

@guybedford guybedford commented Jun 26, 2021

This adds support to ncc for a new esm option to output ES modules from the build. It defaults to true when the input file is ".mjs" or the input file is contained within a "type": "module" boundary.

The support is based on Webpack's experimental support which seems to have a few edge cases that I have posted in webpack/webpack#13639.

If releasing this feature it would be best to treat it as having an experimental status just like the Webpack feature it exposes.

Resolves #679.

To be merged after #723 and vercel/webpack-asset-relocator-loader#140, #724.

@codecov-commenter
Copy link

Codecov Report

Merging #720 (6e0e2f1) into main (a529543) will increase coverage by 0.16%.
The diff coverage is 100.00%.

❗ Current head 6e0e2f1 differs from pull request most recent head 00abd7b. Consider uploading reports for the commit 00abd7b to get more accurate results
Impacted file tree graph

@@            Coverage Diff             @@
##             main     #720      +/-   ##
==========================================
+ Coverage   73.52%   73.69%   +0.16%     
==========================================
  Files          13       13              
  Lines         476      479       +3     
==========================================
+ Hits          350      353       +3     
  Misses        126      126              
Impacted Files Coverage Δ
src/cli.js 63.27% <100.00%> (+0.41%) ⬆️
src/index.js 79.58% <100.00%> (+0.08%) ⬆️

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update a529543...00abd7b. Read the comment docs.

src/cli.js Outdated Show resolved Hide resolved
@guybedford
Copy link
Contributor Author

I've rebased this PR to the webpack update at #723.

@guybedford guybedford force-pushed the esm-output branch 3 times, most recently from f0db790 to bade59e Compare July 5, 2021 20:59
@guybedford guybedford changed the title --esm flag for emitting es modules Output ESM for .mjs or "type": "module" builds Jul 5, 2021
src/index.js Outdated Show resolved Hide resolved
src/index.js Show resolved Hide resolved
src/index.js Show resolved Hide resolved
@guybedford
Copy link
Contributor Author

guybedford commented Jul 7, 2021

There is one last remaining failure here I'm pretty sure is a Webpack bug. (fixed by #724)

To replicate, clone the repo and run:

./src/cli.js build test/fixtures/interop-test.mjs -o bug && node bug/index.mjs

This gives the error:

const external_assert_namespaceObject = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("assert");
                                        ^

ReferenceError: __WEBPACK_EXTERNAL_createRequire is not defined

where the contents of that file are:

/******/ var __webpack_modules__ = ({

/***/ 181:
/***/ ((__unused_webpack_module, exports) => {

Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.s = 's';
exports['com' + 'puted'] = 'y';
exports.default = 'z';



/***/ })

/******/ });
/************************************************************************/
/******/ // The module cache
/******/ var __webpack_module_cache__ = {};
/******/ 
/******/ // The require function
/******/ function __nccwpck_require__(moduleId) {
/******/ 	// Check if module is in cache
/******/ 	var cachedModule = __webpack_module_cache__[moduleId];
/******/ 	if (cachedModule !== undefined) {
/******/ 		return cachedModule.exports;
/******/ 	}
/******/ 	// Create a new module (and put it into the cache)
/******/ 	var module = __webpack_module_cache__[moduleId] = {
/******/ 		// no module.id needed
/******/ 		// no module.loaded needed
/******/ 		exports: {}
/******/ 	};
/******/ 
/******/ 	// Execute the module function
/******/ 	var threw = true;
/******/ 	try {
/******/ 		__webpack_modules__[moduleId](module, module.exports, __nccwpck_require__);
/******/ 		threw = false;
/******/ 	} finally {
/******/ 		if(threw) delete __webpack_module_cache__[moduleId];
/******/ 	}
/******/ 
/******/ 	// Return the exports of the module
/******/ 	return module.exports;
/******/ }
/******/ 
/************************************************************************/
/******/ /* webpack/runtime/compat */
/******/ 
/******/ if (typeof __nccwpck_require__ !== 'undefined') __nccwpck_require__.ab = new URL('.', import.meta.url).pathname.slice(import.meta.url.match(/^file:\/\/\/\w:/) ? 1 : 0, -1) + "/";/************************************************************************/
var __webpack_exports__ = {};
// This entry need to be wrapped in an IIFE because it need to be in strict mode.
(() => {
"use strict";

;// CONCATENATED MODULE: external "assert"
const external_assert_namespaceObject = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("assert");
// EXTERNAL MODULE: ./test/fixtures/interop.cjs
var interop = __nccwpck_require__(181);
;// CONCATENATED MODULE: ./test/fixtures/interop-test.mjs



(0,external_assert_namespaceObject.strictEqual)(interop.s, 's');
(0,external_assert_namespaceObject.strictEqual)(/* __esModule */true, true);
(0,external_assert_namespaceObject.strictEqual)(interop.default, 'z');
(0,external_assert_namespaceObject.strictEqual)(interop.s, 's');

})();

Copy link
Member

@sokra sokra left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you try that?

src/index.js Outdated Show resolved Hide resolved
@guybedford guybedford requested a review from sokra July 7, 2021 17:40
@guybedford
Copy link
Contributor Author

Tests are failing here pending webpack bug webpack/webpack#13744.

src/cli.js Outdated Show resolved Hide resolved
src/cli.js Outdated Show resolved Hide resolved
let startTime = Date.now();
let ps;
const buildFile = eval("require.resolve")(resolve(args._[1] || "."));
const ext = buildFile.endsWith('.cjs') ? '.cjs' : '.js';
const esm = buildFile.endsWith('.mjs') || buildFile.endsWith('.js') && hasTypeModule(buildFile);
const ext = buildFile.endsWith('.cjs') ? '.cjs' : esm && (buildFile.endsWith('.mjs') || !hasTypeModule(buildFile)) ? '.mjs' : '.js';
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm curious why you didn't decide to preserve the extension of the input file?

I would expect this:

.cjs => .cjs
.mjs => .mjs
.js => .js

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Style we do keep the extension so that .cjs -> .cjs and .mjs -> .mjs. The issue with .js is that it will be read by webpack as supporting both CJS and ESM, so isn't enough information on its own, hence the boundary check.

src/cli.js Outdated Show resolved Hide resolved
Copy link
Member

@styfle styfle left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me, thanks! 🎉

@styfle styfle enabled auto-merge (squash) July 12, 2021 22:45
@styfle styfle changed the title Output ESM for .mjs or "type": "module" builds major: output ESM for .mjs or "type": "module" builds Jul 12, 2021
@styfle styfle added the automerge Automatically merge PR once checks pass label Jul 12, 2021
sokra
sokra previously requested changes Jul 13, 2021
src/cli.js Outdated Show resolved Hide resolved
src/index.js Outdated Show resolved Hide resolved
@guybedford
Copy link
Contributor Author

Thanks @sokra for the review, updated.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
automerge Automatically merge PR once checks pass
Projects
None yet
Development

Successfully merging this pull request may close these issues.

ES module output mode
4 participants