3

I have the following file:

src/index.js

let foo = null ?? "waffles"

And the following webpack config:

import path from 'path'

const defaultConf = {
    name: 'default',
    entry: './src/index.js',
    output: {
        filename: 'main.js',
        path: path.resolve(__dirname, 'dist')
    }
}

export default defaultConf

Build fails with:

ERROR in ./src/index.js 2:18
Module parse failed: Unexpected token (2:18)

Environment is:

node v14.8.0
webpack@4.44.1

Why does my build fail?
How can I debug what makes it fail?
How can I fix it?

Edit: Said syntax is supported by node (+all major browsers). The expectation is the build will succeed.

Edit: Based on Anthony's answer I can make the questions more specific:

  1. Based on the answer evidently, wepback is attempting to transpile that code. Why does webpack attempt to transpile that code? Is it operating on some default I'm unaware of? What is that default?
  2. If I'm tripping over some default, how can I find out what it is? Webpack docs say the following:

Out of the box, webpack won't require you to use a configuration file. However, it will assume the entry point of your project is src/index.js and will output the result in dist/main.js minified and optimized for production.

What other assumptions does it make? What does "optimized" mean? How does it minify the code? How can I inspect the default config? Potentially debug the build process (i.e. step-by-step debugging)?

  1. How can I prevent webpack from attempting to transpile this code? How can I set the language level? E.g. to es2020, of which nullish coalescing is part of.

Note: The point of this question is not to end up with a working piece of code. If I wanted that I'd just do foo = null ? null : "waffles" instead of null coalescing. The point is to demystify webpack.

martixy
  • 784
  • 1
  • 10
  • 26
  • `webpack` is saying it can pick module syntax styles of `es6`, but it doesn't say the `esnext` which means it doesn't support `es2020` syntax :) – tmhao2005 Sep 06 '20 at 07:41
  • I have updated my answer. Unless its a strict requirement, I think using es5 syntax in webpack AND instructing webpack to use babel-loader to transpile your code is the way to go. Also make sure your webpack config is being used. – Anthony Sep 07 '20 at 08:31
  • After some research I have an educated guess. It remains conjecture. Webpack may not be transpiling code by default, but it transforms code, therefore needs to understand it, and its syntax. v4 does not understand new ES features, however v5 does this out of the box. One example of why it needs to understand it is the code optimization for prod. It transforms this: `let foo = null ?? "waffles"; console.log(foo)` into `console.log("waffles")` - it needed to understand the operator's function to reduce it to its shorter version. Optimizer is actually fairly smart in that regard. – martixy Sep 19 '20 at 20:47

2 Answers2

0

------------Edit: 18 September 2020.

TL;DR. Typically, a webpack configuration file is written for Node, therefore it uses CommonJS "syntax" - recommended. If you want your webpack config to be written as an es6 module, you would require additional steps - either transpile the configuration file using Babel, or start Node with the flag --experimental-modules. If your code is also written as es6+ modules you need configure webpack to transpile your code (not configuration file) using babel.

Node.js uses Common.js by default. In order to run es6 modules (there are other module types - AMD, Require.js etc...) you need to convert those modules so that your version of Node can run those modules when you do node myscript.js or npm run xyz. So first Node needs to understand your webpack config.

In your version - v14.x - node experimental-modules is enabled by default so you can run your es6 config/scripts but you have to save your script file with the .mjs extension OR add { "type": "module" } in your package.json. See here and here.

For older node versions, you will need to run node with the flag --experimental-modules (it is not enabled by default)

To run webpack with a configuration using es6 modules, you either transpile the configuration in es6 to CommonJS OR write a script that calls webpack from Javascript (in es6). You could do something like:

//in webpack.config.mjs
import path from 'path'

const defaultConf = {
    name: 'default',
    entry: './src/index.js',
    output: {
        filename: 'main.js',
        path: path.resolve() + '/dist'
    },
    module: {
      rules: [
        {
          use: 'babel-loader', //if your code is in es6+ you still need this transpiled
          test: /\.js$/,
          exclude: /node_modules/
        }
      ]
    }
}

export default defaultConf

Then write a script that calls webpack using the above configuration file like so:

//webpack-runner.mjs
import webpack from 'webpack';
import defaultConf from './webpack.config.mjs';

const webpackRunner = webpack(defaultConf);

webpackRunner.run();

Then in your scripts section of your package.json "build": "node --experimental-modules webpack-runner.mjs. Note that node --experimental-modules is enabled in Node v14. For further reading.

End of Edit


In any case, your webpack configuration should be in CommonJS format js var x = require('some-module) or you need babel to transpile es6 code into CommonJs modules. If you do not do this, your webpack config won't be transpiled and node will complain when you try to build.

If you have done this in your webpack config - perhaps in a webpack.config.babel.js, it may simply be that webpack is not using your config file and is using standard defaults which will also not transpile your code.

You get this error "Unexpected token..." in either scenario.

The webpack config below worked for me and webpack, using babel-loader, transpiled the code and I was able to run your index.js.

const path = require('path');

module.exports = [{
    name: 'default',
    entry: './src/index.js',
    output: {
        filename: 'main.js',
        path: path.resolve(__dirname, 'dist')
    },
    module: { //added module section so that webpack uses babel-loader
          rules: [
            {
              use: 'babel-loader',
              test: /\.js$/,
              exclude: /node_modules/
            }
          ]
        }

},] //add other named configurations before closing the array.

If for some reason you definitely want webpack config in es6 syntax, then that is possible, but you need it (webpack config) to be transpiled first - take a look at this and this

Anthony
  • 192
  • 2
  • 8
  • How can I prevent webpack from attempting to transpile my code? I just want to include it as is. I actually want to use all those new browser features we get. Ignore the config being in ES6 syntax. – martixy Sep 15 '20 at 10:22
  • It depends on what you need webpack for. You may not even need webpack if you're only using Javascript features up to es6 and the browser you're building for has adopted es6, and you're not doing any other kind of bundling/transformation. – Anthony Sep 17 '20 at 12:04
  • Generally the rest of what webpack is used for - bundling, asset management and other transformations. I tried debugging a webpack build and it's not practically possible. I wish webpack wasn't so opaque with the things it did under the hood. – martixy Sep 17 '20 at 12:13
  • OK. You'd have to configure webpack so that it works with es6 syntax then. – Anthony Sep 17 '20 at 12:21
  • Show me how and I'll do it. – martixy Sep 17 '20 at 12:25
  • To address your answer - nobody told webpack to transpile anything, so why does it need babel? – martixy Sep 17 '20 at 12:51
  • See my last edit. Tried to clarify between what webpack needs to build and what node needs to run webpack. – Anthony Sep 19 '20 at 04:55
  • I am sorry, but you have managed to completely misinterpret the question. I do not care about the format of webpack's configuration. The primary topic of this question is this single line: `let foo = null ?? "waffles"`. I have done some of my own research recently and I don't have a definite answer, but I have some educated guesses. That is not to say your answer isn't useful or interesting. I did wonder now with node 14 and modules no longer being experimental if I can rid myself of the preliminary step of transpiling the config. But this information is off-topic here. – martixy Sep 19 '20 at 20:11
  • Just reread your questions. I see no confusion. Your questions were "Why does my build fail?", "How can I debug what makes it fail?", "How can I fix it?". The answers to these are clear in my answer. ```let foo = null ?? "waffles"``` is not a question either. There is no suggestion that the syntax is wrong or that it cannot be correctly built and run. You might be looking for a complicated answer to a pretty simple query. I can't help you there. Good luck. – Anthony Sep 19 '20 at 20:34
0

Upgrade to Webpack 5 to enable the ?? syntax without fussy transpile issues.

Side issues I ran into:

  1. Webpack 5 includes the terser plugin, so no need to include it in your devDependencies. Terser's sourceMap option has been removed, so remove it from terser options if you used it.
  2. I upgraded webpack-node-externals plugin to the latest (mine went to 3.0.0). Its whitelist option has been renamed allowlist.
Karl Hill
  • 12,937
  • 5
  • 58
  • 95