sindresorhus / ama

[[I'm slow at replying these days, but I hope to get back to answering questions eventually]] Ask me anything!
https://blog.sindresorhus.com/answering-anything-678ce5623798
141 stars 32 forks source link

Why don't you add ES5 transpiled code to your modules for browsers? #446

Closed SamVerschueren closed 7 years ago

SamVerschueren commented 7 years ago

Noticed more and more questions about adding ES5 files to your projects. The reason is off course that we started dropping support for older Node versions. So I expect more questions in the future. Same situation as https://github.com/sindresorhus/ama/issues/439. I want to be able to link your answer so that people don't have to take me on my word when I answer and close.

ewnd9 commented 7 years ago

I looked through the is:issue user:sindresorhus transpile GitHub search results and I think this is look like a statement you can share https://github.com/facebookincubator/create-react-app/issues/1125#issuecomment-264217076 (linked from https://github.com/sindresorhus/file-type/issues/70#issuecomment-268853935)

I think you're asking too much of module maintainers. I target Node.js 4 for modules I make. If users wants them to run in older browsers, it's up to them to transpile. I'm not interested in having a compile step in all my modules.

Maybe there could be a Babel feature (or another tool) that checks the "engine" package.json field in dependencies and decides which dependencies to transpile. Caching should also help, so a dependency is only transpiled once and cached forever for that version.


I wonder about tooling:

oligot commented 7 years ago

I also faced this problem (using a library in the browser that is written in ES2015) when using jspm: https://github.com/jspm/registry/pull/985. In the meantime, I've switched to Rollup (for various reasons) and I found another way to solve it. The basic idea is to decouple the bundling task from the transpilation task:

  1. bundle the application code as well as all the dependencies (example)
  2. transpile the bundle from ES2015 to ES5 (example)

This way, we don't have to specify which dependency is written in ES2015. Moreover, this opens the door to further explorations like serving non-transpiled ES2015 code in browsers that support it, as currently tested by Netflix.

I don't exactly know how this could be applied to Webpack as I'm not familiar with it.

sindresorhus commented 7 years ago

While most of my modules also work in the browser, I make them mainly for Node.js. I love how dependencies in Node.js just work without having to resort to a bundler, compile-step, and a huge config file. Just npm i -S foo, require('foo'), and npm start to run ❤️. The web on the other hand is a mess. I don't think it's fair to expect every maintainer to give up all this niceness just so that you can slightly simplify an already complicated build-step. It would also be hard for individual packages to know what Babel config to use. Should it include polyfills for newer JavaScript APIs? That might have adverse affect on other dependencies expecting native APIs. Should it compile to ES5 or ES3? Some users might still need ES3 support.

I think this problem should be solved by Babel or bundlers like webpack. One solution would be to automatically check the "engine" field in the package.json of dependencies and decide which dependencies to transpile. Babel has pretty good caching, so it would only transpile the ES2015-written dependencies once per version.

sindresorhus commented 7 years ago

Is there a chrome extension which shows engine from package.json on repository pages? Is there a webpack plugin which throws error if bundled code contains es6?

Not that I'm aware of. People more familiar with webpack might be able to answer the second question.

sindresorhus commented 7 years ago

@TheLarkInn @hzoo Could we maybe take this discussion and https://github.com/facebookincubator/create-react-app/issues/1125 to a new Babel or webpack issue?

nolanlawson commented 7 years ago

@sindresorhus FWIW this was also discussed a bit in https://github.com/w3ctag/polyfills/issues/6 and I think we came to similar conclusions as you. There maybe ought to be some kind of specification in package.json for different engines/browsers/ESversions ala "browser" or "engines".

TheLarkInn commented 7 years ago

So yeah, as webpack'y answer would specify that bundling sources are good and that leveraging tools like babel-preset-env to transpile based on target only specific features is a good start.

However I know that's not a all encompassing solution for everyone.

elektronik2k5 commented 7 years ago

@sindresorhus,

While most of my modules also work in the browser, I make them mainly for Node.js. I love how dependencies in Node.js just work without having to resort to a bundler, compile-step, and a huge config file. Just npm i -S foo, require('foo'), and npm start to run :heart:. The web on the other hand is a mess. I don't think it's fair to expect every maintainer to give up all this niceness just so that you can slightly simplify an already complicated build-step. It would also be hard for individual packages to know what Babel config to use. Should it include polyfills for newer JavaScript APIs? That might have adverse affect on other dependencies expecting native APIs. Should it compile to ES5 or ES3? Some users might still need ES3 support. I think this problem should be solved by Babel or bundlers like webpack. One solution would be to automatically check the "engine" field in the package.json of dependencies and decide which dependencies to transpile. Babel has pretty good caching, so it would only transpile the ES2015-written dependencies once per version.

I understand your rationale and that's legit and fine! Please consider documenting this fact clearly in your modules, so people don't waste your and their time opening issues about it.

@TheLarkInn is there any particular recommended way to detect and transpile ESM in webpack without setting up ugly include lists for babel-loader? Perhaps using package.json's engines field?

sindresorhus commented 7 years ago

I understand your rationale and that's legit and fine! Please consider documenting this fact clearly in your modules, so people don't waste your and their time opening issues about it.

The Node.js version I target is documented in package.json in every module I maintain: https://github.com/sindresorhus/camelcase-keys/blob/fa7d9883e8ffb6d98b8d04533f2e0b342c7d35ee/package.json#L12-L14 I'm not going to document anything more than that as I don't target the browser. You're free to use my modules in the browser, but you're on your own.

Ideally babel-loader would detect the Node.js version in the engine field, yes.

elektronik2k5 commented 7 years ago

Yeah, I just saw you came up with the same idea a while ago. :)

I'll pay attention to the engines field next time.

SamVerschueren commented 7 years ago

I'm seriously considering forking babel-loader to babel-engine-loader so we can all move on...

sindresorhus commented 7 years ago

Or babel-loader-platypus, because they're awesome.

hzoo commented 7 years ago

@SamVerschueren would appreciate if we could work together on something instead of just making a fork. I asked about this earlier https://twitter.com/left_pad/status/838028823572725761 and seems like although a lot of people are in favor some really didn't want it.

I would be cool with a pkg that just enables env by default with a polyfill

SamVerschueren commented 7 years ago

@hzoo Totally agree, I'm all in for co-operating on this one. Forgive me my lack of experience with Babel and Webpack though, so if I say something that isn't correct, please say so :). The thing is that a lot of people exclude node_modules from the babel-loader because it becomes very slow. So I'm just looking into creating a small plugin that detects the dependencies being loaded from node_modules, reads the engines.node property from package.json and if it doesn't support 0.12, transpile it with Babel. If it does support 0.12, do nothing. So instead of transpiling everything, this would only transpile the dependencies that needs a transpile-step.

elektronik2k5 commented 7 years ago

@SamVerschueren, we can - and I think should take a more granular approach, similar to how babel-preset-env works for node. This approach would work for all features, except those which can't be transpiled. These include:

Both cases can be mitigated by knowing which browsers we target (like autoprefixer does) and simply throwing an error such as this: Looks like you're targeting browser <FOO> but module <BAR> requires feature <BAZ>, which can't be transpiled.

@hzoo what do you think?

SamVerschueren commented 7 years ago

I was experimenting with this issue so I just created this Webpack babel-engine-plugin as I want to offer a (intermediate) solution. It checks for the engines.node field in package.json and only transpiles if it targets Node.js > 0.10 and doesn't have a browser field.

There might be a better solution in the long run though, so feel free to discuss this further.

gaearon commented 6 years ago

FYI, we're starting the work to compile deps with babel-preset-env in Create React App: https://github.com/facebookincubator/create-react-app/pull/3776

We couldn't do this earlier because compiling arbitrary packages with Babel 6 was not safe, but AFAIK it's supposed to be safe in Babel 7.

dvirben123 commented 6 years ago

Gaearon, many thanks for the update of create-react-app, it solved my big issue of transpiling node_modules to ES5, and now I can just do npm link to my libraries and not worrying for transpiling them to ES5, although something like Symbol object doesn't transpiled to ES5 (so I convert him manually)

bokub commented 5 years ago

The web on the other hand is a mess. I don't think it's fair to expect every maintainer to give up all this niceness just so that you can slightly simplify an already complicated build-step.

I totally agree with the fact that compiling a module for browsers is often a huge mess and a lot of headaches, there are so many different tools with complex configurations that you easily get lost.

I was tired of that, so I created Lyo, an opinionated compiler that doesn't need any configuration to work. In fact, Lyo is nothing more than a wrapper of other tools, the same way XO is an ESLint wrapper with pre-defined rules.

With Lyo installed globally, transpiling your module for browsers shouldn't be harder than running a single command: $ lyo.

And because you cannot expect every maintainer to provide transpiled code, Lyo also works with other people's modules.

If you need a transpiled version of query-string for example, just run $ lyo get query-string, and it's done.


So, I don't think I'll make Sindre change his mind about the idea of providing transpiled code with his modules, but I hope it will help:

Have a nice day 👍

sindresorhus commented 5 years ago

Create React App 2.0 is out with transpilation of dependencies 🎉

zavr-1 commented 5 years ago

Epic I don't want to transpile anything as I also write for Node :P @sindresorhus you can use import and export in your packages without Babel by installing my regex-based transpiler alamode: https://github.com/a-la/alamode! It's super-fast, has a require hook and works without building any AST! You can even debug with no problems. For example, I love the import syntax because it's a language feature therefore should be used, but I don't care about all this ECMA Modules stuff like async loading, I just want to write beautiful code for Node, and ALAMODE is the way to do it! Since Node 10 still doesn't support modules, but works with everything else, it is the most relevant and light-weight solution today. There is literally no alternative to Babel to do that -- please support my project? There currently is no caching, but it will be there shortly (also because it's fast there isn't a need really). I will also add an ability to write

require('alamode')()
import example from 'example'

in the same file which is not possible with babel/register! Also for everyone's information, up until I don't know what beta-7, Babel used to destroy any JSDoc in files, e.g., it would do this:

/**
 * Make a rule for pasting markers back.
 * @param {Marker} marker A marker used to cut and paste portions of text to exclude them from processing by other rules.
 * @returns {Rule} A rule to paste previously replaced chunks.
 */
exports.makeCutRule = makeCutRule;

const makePasteRule = marker => {
  const {
    regExp: re,
    map
  } = marker;
  const rule = {
    re,

    replacement(match, index) {
      const m = map[index];
      delete map[index];
      return m;
    }

  };
  return rule;
};

And nobody had noticed apart from me! And this is a project used by hundreds of thousands of people and considered essential! Screw the corporate Babel, support indie ÀLaMode!!!

sindresorhus commented 5 years ago

@zavr-1 This is not the place to advertise your project. Try submitting to JavaScript Weekly newsletter and Reddit instead.

For ES2015 module syntax, there's also esm.