29
votes

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'
          ]
        }
      }
    ]
  }
}
2

2 Answers

42
votes

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"
  ]
}
2
votes

Try and modify your Loaders like

module: {
    rules: [
      {
        test: /\.jsx?$/,
        exclude: [/node_modules/],
        use: [{
          loader: 'babel-loader',
          options: { presets: ['react','es2015', 'stage-0'] }
        }],
      }
    ]
  }