Hello,
More and more npm packages contain ES2015+ code (for now usually features supported by Node 4 but i guess it will move to Node 6 features once 4 will be unmaintained). Unfortunately, this breaks compatibility with Uglify and browsers.
The only answer to this is, IMHO, to also transpile external dependencies. Is this supported by create-react-app?
I'm asking because my own build set up is getting confronted to this and I'm thinking about migrating to create-react-app :)
No, this is not supported because builds will get much slower. We don't recommend using libraries that don't precompile their code to ES5 but are supposed to be usable in ES5.
We will fix the Uglify problem in the future by using Babili. However it still means that even uglified code will still be ES6 if that's what your dependencies are shipping.
With time it won't matter as much because mainstream browsers will support ES6. Until then I recommend to use libraries that take care of transpilation step.
I understand but it creates a dichotomy between Node's and browsers' worlds, which will lead to less isomorphic JavaScript components/libraries :/
@sindresorhus, you have migrated many of your packages to ES2015, what's your opinion on this?
And, once Uglify as been replaced by Babili, I'm not sure it'll have a big impact to add other Babel presets to the compilation pass (@hzoo, any inputs on this?)
Concerning the perf impact, I guess it depends of how many times the code is parsed/generated, it would be interesting to have everything done in a single one but it probably depends on the bundler (Webpack).
OK, letās keep it open and wait for more feedback.
The cold start time is pretty bad. I saw an increase from 6 to 20 seconds on a relatively small app.
However, since we enable caching for Babel loader, warm start stays 6 second.
So maybe itās not such a big deal.
I would be comfortable doing this if:
npm start
and npm run build
, and that whatever caches we are using work well for next start, for edits while it is running, and for builds.I understand very well your pain about long compile time, but I think the answer is somewhere because with bundling, tree-shaking and minification, this code is already parsed.
Maybe the work is more on the ecosystem side (Babel, Webpack, etc.) to avoid doing unnecessary operations (parsing and generation).
Running babel on all of node_modules does sound really slow - right it might be faster (would need to measure) passing an AST to webpack or vice versa.
@hzoo but if Babili is used, the code won't get much slower by adding other Babel plugins/presets right?
At least much faster that Babel + Uglify.
I understand very well your pain about long compile time, but I think the answer is somewhere because with bundling, tree-shaking and minification, this code is already parsed.
To be fair there is no minification in development.
To be fair there is no minification in development.
Seems logic, but watch mode may be enough to mitigate the issueā¦
At this point Iām more concerned about correctness than speed.
My gut tells me some projects will break if you Babel them.
Yes, that's the main issue.
Maybe we can just enable a limited number of plugins on external deps, like those in babel-preset-latest
.
I don't know if running Babel on external deps is a good idea, but I fear the problem of incompatible deps will continue to grow, and fast. :/
I think this should be on the library authors because they know which JS version they wrote code in, and they can spend their CPU once every release so that other people donāt have to waste their CPU compiling their libraries. It is not difficult to compile JS code to ES5 as a build step, with Babel it's literally a single command. And they can always provide multiple versions (e.g. my-library/es5
or something).
Yes, but they might do this because it's easier, or because the code is faster (generators, async functions) if not transpiled, or simply because they did not thought about the use of their code in something else than Node.
They also might say that it's up to the consumers to compile because only them know which JS version they target ;)
@sindresorhus, when you have time, I would love to have your opinion on this :)
I think it's really weird to be compiling node_modules. Libraries/packages should be compiling themselves or at least providing an optional es5 version?
This is also what caused https://github.com/babel/babylon/pull/154 - where you would have an issue in 3rd party code
The problem is the shifting baseline. Why are we using ES5 as a baseline? Because it was the lowest common denominator. But itās shifting, and it wonāt make sense to still use ES5 as lowest common denominator in 10 years. So the question is: when should the lowest common denominator shift to ES6?
I donāt think the answer is now, and I also donāt think itās a good idea to compile ES6 code in dependencies. Itās just asking for random breakage. I think itās up to dependencies to declare which environments they support, and if they choose to ship ES6 as main
then itās their decision to be incompatible with older browsers.
Maybe the user could configure a list of packages to compile?
But then there is the question of their own dependenciesā¦ š¢
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've expanded on my answer here: https://github.com/sindresorhus/ama/issues/446
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.
Wouldn't that be more of a webpack
thing? The browser version might need to be transpiled, but I don't think anything react-scripts
depends upon needs anything higher than node
4.
I use include
to decide what folders go through babel-loader
. So far only one of my third-party dependencies is published using ES2015 code. My solution was to add it to that list:
{
test: /\.js$/,
include: [
paths.app,
path.resolve(paths.node_modules, 'cron-parser')
],
loader: 'babel-loader',
// ... other options
}
I think if you only care about node, setting up the tools and tranpsiling is actually a solid barrier, especially if you're unfamiliar with the overall ecosystem. First you need to get babel-cli and babel-preset-env. If you look at the docs you might ask yourself: should I enable that"loose" mode thing? Then, depending on what features you're using, you might need to grab babel-plugin-transform-runtime and babel-runtime, figure out what their options do, and which ones you might need to turn on / off. I don't think it's complicated once you're familiarized with the tooling, but there's still room for stumbling. Oh, and the end result usually means replacing the regular main field version with the harder to debug equivalent. :(
That's not so bad, maybe it should be exposed a nicer way in create-react-app.
This Webpack plugin could be an a solution to this problem.
Thanks for linking that plugin @julien-f. Exactly, with that plugin I want to tackle the problem of transpiling your entire node_modules
directory. I'm always open to suggestions on how I can improve the plugin and make it more robust.
There are experiments in the community with shipping several entry points, or using babel-preset-env
(which presumably includes only āsafeā transforms) together with engine
field. But I still donāt see how this could work if Babel breaks non-strict code by inserting 'use strict'
in it.
For App authors that just want to break an app up into a few imported git repos, whats the common approach? we are using yarn install +git//. would prefer not having to transpile separately, but open to a good tool and workflow that works for others. Any thoughts?
some of the repos contain react components. others don't. they are all written in es6 with spread operators. thanks in advance for any suggestions.
What's the recommended solution to this problem currently? I used a couple of libraries which contain ES6 syntax
I tried transpiling each library inside the node_modules
directory with babel, but then the libraries failed to function, I am very confused on how library imports work in create-react-app. What should I do to use the above libraries, please? It does not seem trivial to re-implement them.
Update: I was suggested to replaced uglify with babili, which looks promising, but I didn't try it myself. I ended up implementing the key features I needed myself.
I'm also wondering what would be a simple and portable way of using ES6 modules. A lot of good modules nowadays are written in ES6, including sindresorhus' and a lot more. A definite solution would be something like babel-engine-plugin
, but it requires ejecting. I was thinking as an interim solution making kind of a proxy npm repo/server from which you can install an alternative version of any ES6 module, and it will automatically pull the official npm version of that package, transpile it and serve it.
I would also like to know the easiest way to get around this. Everything works fine when I'm developing but now that I need to deploy some code I can't because of a few packages (that are key to the project) are using ES6 features. Is there any way to get around this without ejecting?
@jefflau well, if it's compatible with babel-preset-react-app, you can just drop the source in the src folder. CRA will transpile it to es5 for you. You need to install the dependencies of that module manually though.
I'm working on a modular app, written in ES6, where components under development are added to the node_modules
folder using npm link
.
The app and its components need to be compiled with Babel, so currently each component has to be compiled to ES5 after any change is made.
It would be helpful if specific paths/patterns in the node_modules
folder could be added to the includes
array for babel-loader
without having to eject/fork react-scripts.
@sindresorhus @mifi @gaearon @julien-f See also my webpack-babel-env-deps as an alternative to babel-engine-plugin that does feature detection based on babel-preset-env
rather than hard-coding 0.10.0
as a minimum target, so should continue to work into future for new features that even Node 4 doesn't support, and also libraries published with the module
key preferred over main
by webpack 3.
@sindresorhus @mifi @gaearon @julien-f @SamVerschueren FYI there was an npm publish issue and other issues with webpack-babel-env-deps
<1.2.4
- if you were trying to use, please upgrade.
So the main and only argument against transpiling node_modules
contents is _speed_.
@gaearon mentioned the argument that "this is not supported because builds will get much slower".
Idea:
What if we had two Build scripts, one as it is currently, and another with full ES6 transpiling on node_modules?
Let's just get some solution shipped because it is blocking many developers from building stuff. It's not realistic to expect every dependency to be transpiled because it's two years that Chrome can handle ES6 and it's becoming a common practice to publish non-transpiled packages onto npm. Equally it's not realistic for regular create-react-app
user to follow our the docs advice to "fork the package and publish a corrected version yourself". Imagine forking Sindre's libraries, transpiling them and re-publishing them. It's unethical, inefficient and pollutes the npm library namespace (to start with). Equally, the term "correct" in "corrected version" above is very dubious and opinionated, since an ES6 package is not "incorrect" per se. Just create-react-app
can't cope with transpiling them (yet), that's it.
All we need is a way to consume raw, un-transpiled ES6 dependencies in create-react-app
.
Wanted to make it clear I don't mind reviewing a PR for this as long as it's opt in for modules via engines
field. It should probably coordinate with existing work on supporting browserslist
somehow.
If someone is just looking for a temporary solution to these Unexpected token
errors, in an app where I keep React and React Native code in the same repo I simply add the raw ES6 libraries (that are typically used only in the React Native parts) to the ignore list in the webpack config (using Webpack's IgnorePlugin):
plugins: [
// ...
// ignore raw ES6 packages
new webpack.IgnorePlugin(/react-native-cached-image/),
new webpack.IgnorePlugin(/react-native-material-ripple/),
new webpack.IgnorePlugin(/react-native-material-dropdown/),
new webpack.IgnorePlugin(/react-native-modal-datetime-picker/),
// Moment.js is an extremely popular library that bundles large locale files
// by default due to how Webpack interprets its code. This is a practical
// solution that requires the user to opt into importing specific locales.
// https://github.com/jmblog/how-to-optimize-momentjs-with-webpack
// You can remove this if you don't use Moment.js:
new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/),
]
Was struggling with this issue for a while before I finally figured out that it's not actually a problem in any of my config files, but that it's just a conscious decision in create-react-app to not transpile node_modules/.
Same case here. We use monorepos and that symlinks the local packages written in ES6. An official support for such cases would be awesome.
Same case here. We use monorepos and that symlinks the local packages written in ES6. An official support for such cases would be awesome.
Same here. I tried to get it to work by eject
ing it and adjust the scripts but without success.
If you run eject
you need to adjust the config/webpack.config.dev.js
& config/webpack.config.prod.js
file:
// Process JS with Babel.
{
test: /\.(js|jsx)$/,
include: [
paths.appSrc,
path.resolve(paths.appNodeModules, '@my-org/my-uncompiled-module') // <-- Add this
],
loader: require.resolve('babel-loader'),
options: {
compact: true,
},
},
Wonder if there is a clean way to just add to the include
property array using a config file or package.json
. Really hate to have to run eject for this small of a change.. even specifying an npm org to enable babel on.
I'm using this workaround for now: https://github.com/facebookincubator/create-react-app/issues/2108#issuecomment-347623672
I'm pretty new to CRA and Babel/Webpack, so no idea if this is a "good" way to do it, but the workaround I'm using for now is:
babel-cli
as a devDependencyscripts
object of package.json:"prebuild": "./node_modules/.bin/babel ./node_modules/[ES6 MODULE] --out-dir ./node_modules/[ES6 MODULE]"
It avoids ejecting and maintaining a separate webpack config.
Super hacky - would be happy to hear a better solution.
While this gets dealt with (and if you don't want to eject), you can probably adapt this rewire-react-app plugin I made to work with yarn workspaces for this https://learn.viewsdx.com/how-to-use-yarn-workspaces-with-create-react-app-and-create-react-native-app-expo-to-share-common-ea27bc4bad62
One year later and still no consensus on a way forward. And it's becoming increasinly inconvenient.
So, why can't create-react-app run node_modules by babel, again?
There is a way forward:
No config, just write:
And freely use ES6/imports in your code and in your node_modules.
@remon-georgy I remember reading maintainer's comments a while ago, it was implied it's for perf reasons. Anyone correct me if there are other reasons.
@metakermit if we _are_ willing to change to a different bundler, but that's fixing apple tree's branch by ripping off whole apple tree and planting a pear instead. The real root of the problem is that the current minification engine is not ES6-friendly. But maybe it can be solved? For example, when using Rollup, I'm minifying my ES6 libs using uglify-es, passing it as a custom minifier function to rollup-plugin-uglify. There should be a way to patch up the existing setup... But, I might be wrong, I never did ES6 minification outside Rollup...
I created a PR to suggest a fix for this. There may be a better way to do this without breaking the rules of the core ideas.
https://github.com/facebookincubator/create-react-app/pull/3566
I ended up using react-app-rewired. It allows altering any webpack config while keeping CRA non ejected. Besides transpiling ES6 packages, it helps with adding aliases, plugins, modules...etc.
Not sure if this is helpful or not, but I thought it was worth mentioning that Browserify uses an entry in package.json
to determine if a dependency should have a transform applied or not. I've got an ES6 package that I'm maintaining that uses this in package.json
to signal to dependents that it should be transformed;
"browserify": {
"transform": [
"babelify"
]
},
"babel": {
"presets": [
"env"
]
}
This works seamlessly for dependents using Browserify, but I'm trying to pull the same dependency into something built with create-react-app
now and realizing that I need to find a new solution for this.
@remon-georgy Can you share the webpack config you're using with react-app-rewired
in order to transpile ES6 dependencies?
@localjo here's my config-overrides.js
file:
const path = require('path')
const fs = require('fs')
const appDirectory = fs.realpathSync(process.cwd())
const resolveApp = relativePath => path.resolve(appDirectory, relativePath)
module.exports = function override(config, env) {
config.module.rules[1].oneOf[1].include = [
config.module.rules[1].oneOf[1].include,
resolveApp('node_modules/react-native-vector-icons')
]
return config
}
@remon-georgy Is my understanding correct that the config-overrides.js
you shared should cause react-native-vector-icons
to be transpiled into ES6? I'm not getting the results I expect, and I'm not super familiar with the Webpack API, so it's likely that I'm misunderstanding what's going on here.
So, why can't create-react-app run node_modules by babel, again?
Why not read the thread above? :-)
I'm tagging this for one of the future milestones.
We will not be compiling all node_modules
by default. This is both slow and combined with looking for .babelrc
causes tons of issues (ask RN users).
However, we'll want to determine packages to precompile with babel-preset-env
based on their engines
field. This is related to https://github.com/facebookincubator/create-react-app/issues/892. That's what we'll track in this issue.
I don't think we can do this before we switch to Babel 7 though.
@gaearon what if we switched to a minifier which supports ES6? The current minification library is the cause of errors. This doesn't solve the compatibility issues but at least builds would pass (provided nothing throws down the line after minification).
We'll probably do that although as you rightly noted it just makes the actual problem harder to discover for most users. Since now they will learn their website is broken in older browsers in production.
There's a lot of overlap with this issue and monorepos/source sharing -- both have the same problem: how does a dependency package specify its source/code spec so that CRA/builder can handle it correctly?
If I understand correctly, the proposal here is to use the dependency package's engines field to indicate the package's source/code spec, but I didn't see anything about what "engines" should contain. If that detail exists, can someone point it out? (If the proposal is just to specify a "node" version, is that really adequate? -- for one thing, it doesn't indicate whether the module contains JSX which I think would be necessary for supporting react components, and for another it just seems like a stretch to use "node" for this.)
it doesn't indicate whether the module contains JSX which I think would be necessary for supporting react components
We definitely won't be supporting importing modules from npm that use non-standard features like JSX. The goal is to only support modern standard JS features (for which node
should be an adequate marker).
We definitely won't be supporting importing modules from npm that use non-standard features like JSX.
I really hate to question this definite decision, but I would argue that supporting a JSX marker is related and would help fix several issues that have been opened against CRA related to sharing and reusing components, including, but not limited to, monorepo support. I don't see how sharing components is going to be friendly without supporting JSX transpilation. Furthermore, I would argue that node
version really is intended to mean something else (like certain node apis are required), and it will actually be quite confusing for developers to decide which node version to specify....so, why not just specify ES version and (optionally) JSX version? PS, thanks for all the CRAwesomeness!!
Edit: another reason that node
might not be the right marker is that there isn't any node version that officially supports ES6 modules, is there?, so it would be inaccurate for any package that has import statements to claim it can run on node at all.
Iād argue monorepo support is a separate issue. It seems related but is not exactly because you control the source code and know the intended build setup for it.
Publishing things like JSX to npm registry is a problem for the ecosystem because now every library can depend on specific build tools. You don't want a component library to stop working three years from now just because it relied on some deprecated transform that's not supported by Babel 10.
I'd like to point out one additional time for those who expressed frustration in this and other threads that it is unsafe to run Babel 6 on node_modules
because of this: https://babeljs.io/blog/2017/03/01/upgrade-to-babel-7#-use-strict-and-this-in-common-js
So we can only revisit this after Babel 7 is out. The good news is we're working on a beta branch that uses Babel 7 so hopefully we can get our own beta out soon too.
Maybe helps to someone. As a workaround you can do:
npm i -D shelljs babel-cli babel-preset-es2015
prebuild.js
const { echo, exec } = require('shelljs');
const packages = [
'./node_modules/hoek',
'./node_modules/hawk',
// ... other dependencies
];
echo('\nPre build starts.\n');
packages.forEach(pack => exec(`babel --presets=es2015 ${pack} --out-dir ${pack}`));
echo('\nPre build finished.\n');
package.json
{
"scripts": {
"prebuild": "node ./prebuild.js",
},
}
First proof of concept incoming https://github.com/facebookincubator/create-react-app/pull/3776
If it works okay and isn't terribly slow, we'll publish a canary version to try soon. š§š§š§
Support for this has been released in the first v2 alpha version. See https://github.com/facebookincubator/create-react-app/issues/3815 for more details!
I'll close because this is implemented, and you can try 2.x alphas already.
We're tracking follow-up work in https://github.com/facebookincubator/create-react-app/issues/3777.
@gaearon how can i backport this to a crna
app that has already been ejected
. please let there be an easy button!
š
The problem IMHO is that different people/companies have very different expectations for what is the baseline.
If you are supporting 1000s of devs for a "modern" npm library (as my own) then your baseline won't be ES5, it'll be ES6 already. If you are a company with millions (or billions) of users, or worse you are developing for Japan (as I do) then you definitely need to support IE and ES5. Same as if you use Electron, etc.
I normally do the export with UMD because I like being able to import
, require
and <script>
for the different environments. But beyond that for my personal new projects I normally don't bother transpiling it. PR always welcome, but I expect this to start happening more and more.
When you have 10s or 100s of repositories/npm libraries, every extra, optional step counts towards maintainability and bandwidth.
Edit: so, thanks for adding support!
Is there a solution for those who haven't ejected as of now?
I'm running into Failed to minify the code from this file
today and discovering the whole thing, 2.0 version hasn't been released, I'm gonna try to update react-scripts but I doubt it'll fix it.
My issue was with ajv-error-messages
, I forked and fixed it: https://github.com/StudyLink-fr/ajv-error-messages/
Update react scripts, Latest version supports your scenario but there might be some problem as they are still in alpha version
This shipped in 2.0.
https://reactjs.org/blog/2018/10/01/create-react-app-v2.html
Hello, I'm experimenting with this new feature by substituting all of my named exports from lodash
with named exports from lodash-es
in a rather minimal application (although migrated from create-react-app 1.x).
The app starts just fine with create-react-app v2, but I am unable to run jest tests, as it's breaking on the es6 imports and exports:
ā Test suite failed to run
Jest encountered an unexpected token
This usually means that you are trying to import a file which Jest cannot parse, e.g. it's not plain JavaScript.
[...]
Details:
/Users/nerfologist/proj/newapp/node_modules/lodash-es/lodash.js:10
export { default as add } from './add.js';
^^^^^^
SyntaxError: Unexpected token export
2 | import PropTypes from 'prop-types';
3 | import qs from 'qs';
> 4 | import { isEmpty } from 'lodash-es';
| ^
5 | import { Link } from 'react-router-dom';
6 | import styled from 'styled-components';
7 | import { translate } from 'react-i18next';
at ScriptTransformer._transformAndBuildScript (node_modules/jest-runtime/build/script_transformer.js:403:17)
at Object.<anonymous> (src/auth/ResetPasswordPage/ResetPasswordPage.js:4:1)
What is the correct way to address this? Should I add a custom Jest configuration file jest.config.js
with a transformIgnorePatterns
?
@nerfologist please file a new issue
Most helpful comment
So the main and only argument against transpiling
node_modules
contents is _speed_.@gaearon mentioned the argument that "this is not supported because builds will get much slower".
Idea:
What if we had two Build scripts, one as it is currently, and another with full ES6 transpiling on node_modules?
Let's just get some solution shipped because it is blocking many developers from building stuff. It's not realistic to expect every dependency to be transpiled because it's two years that Chrome can handle ES6 and it's becoming a common practice to publish non-transpiled packages onto npm. Equally it's not realistic for regular
create-react-app
user to follow our the docs advice to "fork the package and publish a corrected version yourself". Imagine forking Sindre's libraries, transpiling them and re-publishing them. It's unethical, inefficient and pollutes the npm library namespace (to start with). Equally, the term "correct" in "corrected version" above is very dubious and opinionated, since an ES6 package is not "incorrect" per se. Justcreate-react-app
can't cope with transpiling them (yet), that's it.All we need is a way to consume raw, un-transpiled ES6 dependencies in
create-react-app
.