-
Notifications
You must be signed in to change notification settings - Fork 2
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
Dual-build package as ES modules and CommonJS #200
Conversation
This will smooth the migration of downstream applications to a new bundler that speaks ES modules natively [1]. The existing `lib/` dir and `.babelrc` module now relate to the ES module build, and the `lib-cjs/` dir and `.babelrc-cjs` module are for the CommonJS build. Once all downstream dependencies have been switched over to CommonJS we can remove these. In the process I changed the file extension of the Babel config files from .cjs to .js which results in them being formatted by Prettier like other .js files. The browser targets of the `@babel/preset-env` package have also been updated to reflect the current reality [2] of what we support. [1] In particular it is important that Preact is only included once in the generated app bundle. For that to happen both the app and @hypothesis/frontend-shared need to reference the same build of Preact (ES modules or CommonJS). [2] hypothesis/client#3744
Codecov Report
@@ Coverage Diff @@
## main #200 +/- ##
=========================================
Coverage 100.00% 100.00%
=========================================
Files 14 14
Lines 227 227
Branches 78 78
=========================================
Hits 227 227 Continue to review full report at Codecov.
|
@@ -0,0 +1,42 @@ | |||
'use strict'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This file is the same as .babelrc.cjs
except for the changes to the @babel/preset-env
plugin configuration, and reformatting with Prettier.
.babelrc.js
Outdated
'@babel/preset-env', | ||
{ | ||
bugfixes: true, | ||
modules: false, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
modules: false
is the magic that results in ES module output in lib/index.js
. See https://babeljs.io/docs/en/babel-preset-env#modules.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Welp, we're not in JSON here, so you could add a helpful comment :)
.babelrc.js
Outdated
chrome: '70', | ||
firefox: '70', | ||
safari: '11.1', | ||
edge: '79', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These targets have been changed to reflect hypothesis/client#3744.
"lib", | ||
"styles", | ||
"images" | ||
], | ||
"main": "./lib/index.js", | ||
"main": "./lib-cjs/index.js", | ||
"module": "./lib/index.js", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Modern module bundlers understand the convention that main
in package.json
specifies the CommonJS build of a package and the module
field specifies the ES build. Browserify just ignores the new module
field.
I encountered some typechecking errors in the LMS app with this. I'll look into those next week. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Whoof! This is rather brief, but dense. I left some questions.
Build is working as (I believe is) expected for me locally
Would these changes have any effect on package publishing or consumption?
Thanks for your patience with my bundler ignorances.
const presetEnvConfig = config.presets.find( | ||
([name]) => name === '@babel/preset-env' | ||
)[1]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This lookup is a little gross, but seems necessitated by babel config structure 🤷🏻 . It seems more fragile than it is :). I'd be curious as to the archaeology of the babel-config data structure format...
@@ -0,0 +1,9 @@ | |||
const baseConfig = require('./.babelrc'); | |||
|
|||
const config = { ...baseConfig }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perhaps a helpful comment on the module level here to explain what it's doing (overriding the modules
setting...etc.).
"build": "yarn build-js && tsc --build src/tsconfig.json", | ||
"build-esm": "babel src --out-dir lib/ --source-maps --ignore '**/test' --ignore '**/karma.config.js'", | ||
"build-cjs": "babel src --out-dir lib-cjs/ --source-maps --ignore '**/test' --ignore '**/karma.config.js' --config-file ./.babelrc-cjs.js", | ||
"build": "yarn build-cjs && yarn build-esm && tsc --build src/tsconfig.json", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it desired that the CJS and ESM builds will build even if tsc
fails? Or might it make more sense to have the tsc
command first? Thoughts?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The most important thing is that CI fails if tsc
fails. Otherwise I don't think it matters too much either way.
.babelrc.js
Outdated
'@babel/preset-env', | ||
{ | ||
bugfixes: true, | ||
modules: false, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Welp, we're not in JSON here, so you could add a helpful comment :)
src/karma.config.js
Outdated
@@ -69,14 +69,14 @@ module.exports = function (config) { | |||
// The existence of this preset option is due to a config issue with where jsx modules | |||
// are not transpiled to js. | |||
// See https://github.com/hypothesis/client/issues/2929 | |||
presets: require('../.babelrc.cjs').presets, | |||
presets: require('../.babelrc-cjs.js').presets, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you walk me through this a bit? The linked client issue hypothesis/client#2929 is confusing me as it references a preset-react
configuration property that doesn't appear to be set explicitly by our configuration. I am having trouble following.
Also, this elects to use the CJS configuration. Would it work with the ESM variant? Can you help me understand?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
On second review the comment here is out of date. The tests do need to use the CJS configuration, but that's because the current bundler (Browserify) doesn't understand ES modules. When we replace the bundler we can remove this.
We do need to configure Babel to target CommonJS in tests, but the comment explaining the import was out-dated. The current reason is because the bundler used in tests doesn't speak ES modules natively.
It's not that obvious from the name whether it refers to input or output.
I fixed a typechecking issue in downstream projects when using a build from this branch. There is one minor breaking change for downstream projects which use the pattern library, due to the location of the pattern library entry point having changed from |
One minor change will be required in the client and lms apps (see #200 (comment)) but there shouldn't be any other immediate consequences. |
.babelrc.js
Outdated
chrome: '70', | ||
firefox: '70', | ||
safari: '11.1', | ||
edge: '79', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it is better to take this out and rely on browserslist
in package.json, to give consistency to all our projects.
Maybe the browsers should be similar to the list in lms
not the client
?
"browserslist": "chrome 57, edge 17, firefox 53, safari 10.1",
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it is better to take this out and rely on browserslist in package.json, to give consistency to all our projects.
I agree. I'll do that in this PR.
Maybe the browsers should be similar to the list in lms not the client?
The effective browser requirements for the lms app is the maximum of the lms and client's code, since the lms app isn't useful without the client. I intend to update the client and lms app to have the same baseline as this.
It also occurred to me that since Edge is now based on Chrome, there shouldn't be a need to specify the Edge version any more. We can just treat it as being covered by Chrome.
This brings up a few questions for me, several of which are educational opportunities that I'll ping you directly about, but the salient one is: would this be a sort of temporary change in those applications until those applications' bundlers speak ESM? Or would those applications always use the CJS pattern library? |
Yes. The intent is that we'll switch over all bundling in both this package and downstream to use ESM and then we can drop the CommonJS build entirely. |
Move browser requirements to `browserslist` entry in package.json for consistency with other projects and to make them available to all build tools. The current Edge requirement is Edge >= 79. Since this version of Edge is a derivative of Chrome it is covered by the "chrome" entry.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK, I'm ready to try this out! Do you think you can take on merging this and publishing a new package version? I'll leave it up to you what you think a proper version bump is.
It doesn't look like much else of note has changed since the last release: v3.10.0...main
Let me know if you would like a hand!
Oh! Also, the |
This required adapting pattern library import paths due to changes in hypothesis/frontend-shared#200.
This required adapting pattern library import paths due to changes in hypothesis/frontend-shared#200.
This required adapting pattern library import paths due to changes in hypothesis/frontend-shared#200.
This required adapting pattern library import paths due to changes in hypothesis/frontend-shared#200.
This will smooth the migration of downstream applications to a new bundler that speaks ES modules natively [1]. On this branch the existing
lib/
dir and.babelrc
module now relate to the ES module build, and thelib-cjs/
dir and
.babelrc-cjs
module are for the CommonJS build. Once all downstream dependencies have been switched over to CommonJS we can remove these.In the process I changed the file extension of the Babel config files from .cjs to .js which results in them being formatted by Prettier like other .js files.
The browser targets of the
@babel/preset-env
package have also been updated to reflect the current reality [2] of what we support.The pattern library and tests are still using the CommonJS build. We'll need to change the bundler before we can make them use the ES module build.
[1] In particular it is important that Preact is only included once in the generated app bundle. For that to happen both the app and @hypothesis/frontend-shared need to reference the same build of Preact (ES modules or CommonJS).
[2] hypothesis/client#3744