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

Dual-build package as ES modules and CommonJS #200

Merged
merged 7 commits into from
Sep 29, 2021

Conversation

robertknight
Copy link
Member

@robertknight robertknight commented Sep 23, 2021

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 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.

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

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
Copy link

codecov bot commented Sep 23, 2021

Codecov Report

Merging #200 (a2223b4) into main (5cd5890) will not change coverage.
The diff coverage is n/a.

Impacted file tree graph

@@            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.

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

@@ -0,0 +1,42 @@
'use strict';
Copy link
Member Author

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,
Copy link
Member Author

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.

Copy link
Contributor

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',
Copy link
Member Author

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",
Copy link
Member Author

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.

@robertknight robertknight marked this pull request as draft September 23, 2021 09:01
@robertknight
Copy link
Member Author

I encountered some typechecking errors in the LMS app with this. I'll look into those next week.

Base automatically changed from convert-require-to-import to main September 23, 2021 13:17
Copy link
Contributor

@lyzadanger lyzadanger left a 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.

Comment on lines +4 to +6
const presetEnvConfig = config.presets.find(
([name]) => name === '@babel/preset-env'
)[1];
Copy link
Contributor

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 };
Copy link
Contributor

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",
Copy link
Contributor

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?

Copy link
Member Author

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,
Copy link
Contributor

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 :)

@@ -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,
Copy link
Contributor

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?

Copy link
Member Author

@robertknight robertknight Sep 23, 2021

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.
@robertknight robertknight marked this pull request as ready for review September 23, 2021 14:07
@robertknight
Copy link
Member Author

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 lib/pattern-library/index.js to lib-cjs/pattern-library/index.js

@robertknight
Copy link
Member Author

Would these changes have any effect on package publishing or consumption?

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
Comment on lines 18 to 21
chrome: '70',
firefox: '70',
safari: '11.1',
edge: '79',
Copy link
Contributor

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",

Copy link
Member Author

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.

@lyzadanger
Copy link
Contributor

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 lib/pattern-library/index.js to lib-cjs/pattern-library/index.js

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?

@robertknight
Copy link
Member Author

would this be a sort of temporary change in those applications until those applications' bundlers speak ESM?

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.
Copy link
Contributor

@lyzadanger lyzadanger left a 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!

@lyzadanger
Copy link
Contributor

Oh! Also, the client and lms apps will need to be updated with the CJS pattern-library import. Is that something you could take (bumping those and updating the import)?

@robertknight robertknight merged commit df754f0 into main Sep 29, 2021
@robertknight robertknight deleted the dual-build-esm-and-cjs branch September 29, 2021 15:57
robertknight added a commit to hypothesis/client that referenced this pull request Sep 30, 2021
This required adapting pattern library import paths due to changes in
hypothesis/frontend-shared#200.
robertknight added a commit to hypothesis/client that referenced this pull request Sep 30, 2021
This required adapting pattern library import paths due to changes in
hypothesis/frontend-shared#200.
robertknight added a commit to hypothesis/lms that referenced this pull request Sep 30, 2021
This required adapting pattern library import paths due to changes in
hypothesis/frontend-shared#200.
robertknight added a commit to hypothesis/lms that referenced this pull request Sep 30, 2021
This required adapting pattern library import paths due to changes in
hypothesis/frontend-shared#200.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants