31

Ok. I am bootstraping a simple app. I am using flow.js. Presets that I use are babel-preset-2015, babel-preset-react and babel-preset-stage-0. I have to put same presets inside my .babelrc and inside my webpack.config for it all to work. If I for example romove them from webpack.config I get an error 'React is not defined'. If I remove .babelrc and babel-register I get an error because I use import and Flow.js annotation. Why is this happening? If I put presets inside webpack.config I should be able to delete .babelrc or vice verse. This is how my code looks now when it all works (minus some files that are not important for the question).

start-dev.js

require('babel-register')
require('./src/server/index.js')

index.js

/* @flow */

import Express from 'express'
import path from 'path'
import conf from '../conf/'

const APP_PORT: number = conf.APP_PORT
const PORT = process.env.PORT || APP_PORT

const app: Express = new Express()

// Middleware
app.set('views', path.join(__dirname, 'views'))
app.set('view engine', 'ejs')
app.use(Express.static(path.join(__dirname, '../', 'client', 'dist')))

// Routes
app.get('*', function (req: Object, res: Object) {
  res.render('index')
})

app.listen(PORT, function () {
  console.log(`Express server is up on port ${PORT}`)
})

app.js

import React from 'react'
import ReactDOM from 'react-dom'

ReactDOM.render(
  <h1>First</h1>,
  document.getElementById('app')
)

package.json

{
  "scripts": {
    "start-dev": "set \"NODE_ENV=development\" && babel-node ./start-dev.js",
    "start": "set \"NODE_ENV=development\" && node ./start-dev.js",
    "flow": "./node_modules/.bin/flow check",
    "standard": "node_modules/.bin/standard --verbose | node_modules/.bin/snazzy"
  },
  "dependencies": {
    "ejs": "^2.5.6",
    "express": "^4.15.2",
    "react": "^15.4.2",
    "react-dom": "^15.4.2"
  },
  "devDependencies": {
    "babel-cli": "^6.24.0",
    "babel-core": "^6.24.0",
    "babel-eslint": "^7.2.1",
    "babel-loader": "^6.4.1",
    "babel-preset-es2015": "^6.24.0",
    "babel-preset-react": "^6.23.0",
    "babel-preset-stage-0": "^6.22.0",
    "babel-register": "^6.24.0",
    "eslint": "^3.18.0",
    "eslint-config-standard": "^7.1.0",
    "eslint-plugin-flowtype": "^2.30.4",
    "eslint-plugin-react": "^6.10.3",
    "flow-bin": "^0.42.0",
    "snazzy": "^6.0.0",
    "standard": "^9.0.2",
    "webpack": "^2.3.2"
  }
}

.babelrc

{
  "passPerPreset": true,
  "presets": [
    "es2015",
    "react",
    "stage-0"
  ]
}

webpack.config.babel.js

'use strict'

import path from 'path'
const publicPath = path.resolve(__dirname, './src/client')

module.exports = {
  devtool: '#source-maps',
  performance: {
    hints: false
  },
  context: publicPath,
  entry: {
    bundle: './app.js'
  },
  output: {
    path: path.join(publicPath, 'dist'),
    filename: '[name].js',
    publicPath: '/dist/'
  },
  resolve: {
    extensions: ['.js', '.jsx']
  },
  module: {
    rules: [
      {
        test: /\.jsx?$/,
        exclude: /node_modules/,
        loader: 'babel-loader',
        options: {
          presets: [
            'react',
            'es2015',
            'stage-0'
          ]
        }
      }
    ]
  }
}
Michael Jungo
  • 31,583
  • 3
  • 91
  • 84
Igor-Vuk
  • 3,551
  • 7
  • 30
  • 65
  • https://babeljs.io/docs/en/config-files >>> https://babeljs.io/docs/en/next/configuration >>>https://sebastiandedeyne.com/whats-in-our-babelrc – zloctb Sep 13 '18 at 07:24

2 Answers2

50

If I put presets inside webpack.config I should be able to delete .babelrc or vice verse.

No, this is not the case. Specifying the presets in the webpack config will only affect webpack, everything else that uses babel (e.g. babel-node, babel-register, etc.) will not care about your webpack config and therefore doesn't see them.

The other way around does work. So you can remove the webpack presets options if you have a .babelrc, because babel-loader uses babel under the hood, which obviously respects the .babelrc.

If I for example remove them from webpack.config I get an error React is not defined.

The problem is that your .babelrc configuration is different from the configuration in the webpack config. The culprit is "passPerPreset": true. With this option every preset is applied individually without considering the others. And for this the order matters. From the babel docs - Plugin/Preset Ordering:

Preset ordering is reversed (last to first).

This means they will be applied in the following order: stage-0, react, es2015. As they are applied individually, react will transform the JSX to React.createElement, because React is in scope, and es2015 will transform just the import to _react2.default, therefore React is no longer defined. The entire diff between the two bundles is this:

@@ -9470,7 +9470,7 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { de
// var React = require('react')
// var ReactDOM = require('react-dom')

-_reactDom2.default.render(React.createElement(
+_reactDom2.default.render(_react2.default.createElement(
  'h1',
  null,
  'Juhuuuu'

There isn't much information about passPerPreset, but it was labelled experimental in the Release notes and you should probably avoid it altogether.

Although it would work if you put the react preset first in the list, I'd recommend to remove passPerPreset option, unless you have a very specific reason to use it.

{
  "presets": [
    "es2015",
    "react",
    "stage-0"
  ]
}
Michael Jungo
  • 31,583
  • 3
  • 91
  • 84
  • Thanks Michael, that worked. Putting react in the first place in .babelrc i was able to remove presets from config.js and it still works. I just have few more questions for better understanding. If I remove babel-register from the top of my start-dev.js file it doesnt work. What is the relationship between **babel-register** and **.babelrc** and what is the diference between **babel-register** and **babel-node**? It is written that using **babel-register** and **babel-node** is bad for production and that is better to compile ahead of time, before deploying. What is the best way to do that? – Igor-Vuk Apr 04 '17 at 14:04
  • `babel-register` will transpile any file you import whereas `babel-node` will just transpile the file you're running, not all the files that are imported by it. Everything that is officially part of babel will respect the configuration in your `.babelrc` unless you specifically tell it not to (e.g. with the [`--no-babelrc` flag](https://babeljs.io/docs/usage/cli/#babel-ignoring-babelrc)). To build for production you can use the `babel-cli`, see [Compile Directories](https://babeljs.io/docs/usage/cli/#babel-compile-directories). Webpack may also be used to bundle the backend. – Michael Jungo Apr 04 '17 at 14:14
  • Ok. I think I am getting it. So in development it practically doesnt matter if I test it with babel-register or babel-node. In production it Is better to use just babel. I tried it just now and I have one last question. After using babel-cli I got one lib folder with all the folders I had in my project, including the new bundle.js. I was always using webpack to make a bundle.js and then inside index.html I had a – Igor-Vuk Apr 04 '17 at 14:35
  • You should not transpile the files used with webpack, neither the sources nor the bundle, they already are and all you need is the bundle (it's self-contained). Webpack puts everything that is needed for your app into the bundle to make it work in the browser. You only need to transpile the server files (which are obviously not included in the webpack bundle) and then simply serve the bundle with it. Although webpack could be used for the backend, it's primarily used to create a bundle to be run in the browser, which allows you to use npm modules. – Michael Jungo Apr 04 '17 at 14:45
  • Thanks Michael. Unfortunately I am still not clear with some questions about this but I will close this question and open a new one for a larger discussion about webpack, bundle and production. Thanks for your help. – Igor-Vuk Apr 04 '17 at 14:58
2

Try and modify your Loaders like

module: {
    rules: [
      {
        test: /\.jsx?$/,
        exclude: [/node_modules/],
        use: [{
          loader: 'babel-loader',
          options: { presets: ['react','es2015', 'stage-0'] }
        }],
      }
    ]
  }
Shubham Khatri
  • 270,417
  • 55
  • 406
  • 400
  • This is not needed anymore with webpack 2. resolve.extensions This option no longer requires passing an empty string. This behavior was moved to resolve.enforceExtension. See resolving for more usage. – Igor-Vuk Apr 04 '17 at 11:49
  • Thanks, but no luck. If i put it like that and remove .babelrc I get an error unexpected token import – Igor-Vuk Apr 04 '17 at 12:02