azangru
azangru

Reputation: 2748

Webpack error when importing a json file

Here is a simple test case that breaks:

First, save a json file from Node:

const fs = require('fs')

let test = {
  foo: [
    { 1: 'a'},
    { 2: 'b'}
  ]
};

fs.writeFileSync('./test.json', JSON.stringify(test), 'utf-8');

Then, try importing this json into a js file that is processed by Webpack (I am using the latest version of Webpack, 3.4.1):

import test from './test.json';

console.log(test);

This fails with the following error:

ERROR in ./test.json                                       
Module build failed: SyntaxError: Unexpected token, expected ; (1:6)

> 1 | {"foo":[{"1":"a"},{"2":"b"}]}

The offending character that the console output is pointing at is the colon after "foo".

My Webpack config was very simple, with only these module options:

  module: {
    rules: [
      {
        test: /\.js/,
        exclude: [ path.resolve(__dirname, 'node_modules') ],
        loader: 'babel-loader'
      },
      {
        test: /\.css$/,
        use: ExtractTextPlugin.extract({
          fallback: "style-loader",
          use: "css-loader"
        })
      }
    ]
  },

Confused, I opened the JSON loader page, which informed me that:

Since webpack >= v2.0.0, importing of JSON files will work by default.

Since I was using Webpack v. 3.4.1, I assumed json-loader was unnecessary. Still, out of desperation, I added the following rule to the modules field of Webpack config:

  {
    test: /\.json/,
    loader: 'json-loader'
  }

This actually worked! The json file got loaded, and the error disappeared.

So my question is: was I doing something wrong with Webpack trying to import the json file, or is the newest Webpack somehow broken as regards to the importing of json files?

Upvotes: 4

Views: 4741

Answers (1)

Michael Jungo
Michael Jungo

Reputation: 33002

The regular expression you used for the .js rule also matches .json, because all you're looking for is a .js anywhere in the path. All of the following would match successfully (most of them aren't likely to be imported or even exist):

file.js
file.json
file.js.backup
.js/file.css
.jshintrc

test.json matches the regular expression, which means that you are applying babel-loader to it. Babel only accepts JavaScript and JSON will fail to parse. The reason that it complains about the colon (:) is because with ES6 you can create a new scope with curly brackets. For example:

const msg = "Outer";

// Entering new scope
{
  // msg is free in this scope, shadows the outer one.
  const msg = "Inner";
  console.log(msg); // Inner
}
// Exiting scope

console.log(msg); // Outer

// SyntaxError: Identifier 'msg' has already been declared
const msg = "Re-definition";

I don't think there are many uses of this in JavaScript, but it is used a lot more in other languages (e.g. Rust). The opening curly bracket of the JSON started a new scope, not an object, and a colon is not valid after a string in JavaScript.

To not apply babel-loader to .json files you need to only match .js at the end of the path, by using the $ anchor (end of string), like you did with the .css rule.

{
  test: /\.js$/,
  exclude: [ path.resolve(__dirname, 'node_modules') ],
  loader: 'babel-loader'
},

Upvotes: 9

Related Questions