Open jhildenbiddle opened 3 years ago
Thanks for all this input, I'll get to it next week 👏 🙇
@juliangruber --
All of my client side projects are transpiled, none of my server side projects are
Yep. Same for me. Transpiling to ES5 for node isn't (usually) the concern. The browser is where I'm focused.
...so I've been using ESM in the browser for a while.
Which means you're targeting modern browsers and won't run into the issue I'm describing. I think that might be where we're getting our signals crossed. A mixture of ES5 and ES6 code won't break modern browsers that can handle both, but it will break legacy browsers that can't handle ES6.
... for this library I anticipate it will be consumed in conjunction with others, where then using a bundler makes a lot more sense.
That's exactly how I consume this library in css-vars-ponyfill, and the change from ES5 to ES6-only caused me to release a broken ES5 bundle. The same will happen to any other project that depends on balanced-match that fails to transpile their dependencies (which they shouldn't have to, IMHO) if they support legacy browsers.
Consider these two versions of balanced-match and how they impact css-vars-ponyfill:
const
and let
)My transpilation configuration (almost) always ignores files in node_modules
because the expectation is that distributable libraries have already been transpiled to ES5. This expectation exists for two reasons: 1) babel shouldn't have to process dependencies in /node_modules
because this will impact build times, and 2) devs shouldn't have to manually track and configure babel to transpile individual dependencies in node_modules
because doing so incorrectly will (probably) break things.
Using v1.0.2 with babel configured to transpile only my /src/
directory, my legacy-compatible (ES5) bundle contains all ES5 code as expected. This is because all dependencies in /node_modules
(including balanced-match) are written in ES5. Great!
Using v2.0.0 with the same babel configuration, my legacy-compatible bundle contains ES5 code (transpiled from /src/
) and ES6 code (untranspiled from /node_modules/balanced_match
). This is because balanced-match contains ES6 code that babel did not transpile. My legacy bundle now breaks in legacy browsers. Not great. Now I'm forced to update my babel configuration so balanced match is transpiled properly with one of the two bad options listed above.
If you were to consume and not have a bundler set up, how would you do it? Is that a realistic use case for this library?
Via a CDN, as an ES module for modern browsers or an IIFE/UMD for legacy browsers. Is this less likely that consuming via bundler? Probably, but why not let people consume the library however they see fit?
But just to be clear, this PR isn't about generating different versions of balanced-match so it can be consumed via a CDN. That was just a bonus that came from implementing a bundler which made it easy to do. The real purpose of this PR is to ensure that balanced-match offers an ES5 distributable. Notice that the /dist
folder created after running npm run build
with this PR contains only ES5 code, so even if a project bundles the ES modules (as mine does via rollup), the bundle will contain ES5 code.
Hope this helps explain bit better. Apologies for the lengthy response.
Which means you're targeting modern browsers and won't run into the issue I'm describing. I think that might be where we're getting our signals crossed. A mixture of ES5 and ES6 code won't break modern browsers that can handle both, but it will break legacy browsers that can't handle ES6.
You can transpile ES6 code to ES5 code for browsers, so import isn't a deal breaker 🤔
You can transpile ES6 code to ES5 code for browsers, so import isn't a deal breaker 🤔
Yep. I just propose that you (the library author) should do the transpilation to ES5 so your library consumers don't have to. Why? For all of the reasons stated above, but summarized because it is much easier for you to do so and the cost to you is near zero.
The goal of this PR is to support all platforms (legacy and modern, node and browser), allow consumers to use the library however they see fit (import
, require
, or <script>
), and not break dependent projects (like mine).
With v1, consumers could do this:
// ES5 CJS ("main" in package.json)
const balanced = require('balanced-match');
With v2, consumers can do this:
// ES6 CJS ("main" in package.json)
// 👎 Legacy bundles now require transpilation of node_modules
const balanced = require('balanced-match');
With this PR, consumers can do this:
<!-- ES5 ESM ("module" in package.json) -->
<script type="module" src="http://my.cdn.com/balanced-match/dist/balanced-match.esm.min.js"></script>
<!-- ES5 UMD ("browser" in package.json) -->
<script src="http://my.cdn.com/balanced-match/dist/balanced-match.min.js"></script>
// ES5 ESM ("module" in package.json)
import balanced from 'balanced-match';
// ES5 UMD ("main" value in package.json)
const balanced = require('balanced-match');
The main criticism of the approach above is that modern platforms are forced to use the (potentially) larger transpiled ES5 source. That is not an issue for the particular library though as the ES5 and ES6 versions are roughly the same size. So, why not just ship ES5 everywhere and make life easier for consumers?
The other logical option is to deliver ES6 code as an ES module and ES5 code as a UMD. The advantage to this is that ES6-capable platforms receive ES6 code. The disadvantage is that dependents that bundle code for legacy browsers will either have to 1) require
balanced-match to ensure they are getting ES5 code, or 2) import
and transpile the balanced-match source in /node_modules
. Since balanced-match is only offered as a CommonJS module today, this isn't really a big deal.
// ES6 ESM ("module" in package.json)
// 👍 Modern platforms get ES6 code
// ⚠️ Legacy bundles require transpilation of node_modules when using `import`
import balanced from 'balanced-match';
// ES5 UMD ("main" value in package.json)
// 👍 No transpilation necessary for legacy bundles
// 👎 Modern platforms get ES5 code
const balanced = require('balanced-match');
Either options seems like a better alternative to the current "ES6 as CJS" approach used in balanced-match v2.x
Anyway, this is a lot of back-and-forth for what I thought would be a pretty straight-forward PR. Happy to continue the discussion if you like or move on if you feel strongly about leaving things as-is.
Thanks!
fix #32
package.json
to reference distributable files (main
,module
,browser
, andunpkg
)files
field inpackage.json
to remove source and include/dist
files in package published to npm.gitignore
to accommodate changesThe end results is a new
/dist
directory with the following files:These eight files provide support for legacy and modern browser and node environments.