'Error while importing a module with optional chaining

Project setup:

  • Vuejs 3
    • Webpack 4
    • Babel
    • TS

We created the project using vue-cli and add the dependency to the library.

We then imported a project (Vue Currency Input v2.0.0) that uses optional chaining. But we get the following error while executing the serve script:

error  in ./node_modules/vue-currency-input/dist/index.esm.js

Module parse failed: Unexpected token (265:36)
You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file. See https://webpack.js.org/concepts#loaders
|     getMinValue() {
|         let min = this.toFloat(-Number.MAX_SAFE_INTEGER);
>         if (this.options.valueRange?.min !== undefined) {
|             min = Math.max(this.options.valueRange?.min, this.toFloat(-Number.MAX_SAFE_INTEGER));
|         }

I read that Webpack 4 doesn't support optional chaining by default. So, we added the Babel plugin for optional chaining. This is our babel.config.js file:

module.exports = {
  presets: ["@vue/cli-plugin-babel/preset"],
  plugins: ["@babel/plugin-proposal-optional-chaining"],
};

(But, if I am correct, this plugin is now enable by default in the babel-preset. So this modification might be useless ^^)

One thing that I don't understand is that we can use optional chaining in the .vue files.

I created a SandBox with all the files: SandBox

How could I solve this error?



Solution 1:[1]

I was able to overcome this issue using @babel/plugin-proposal-optional-chaining, but for me the only way I could get Webpack to use the Babel plugin was to shove the babel-loader configuration through the Webpack options in vue.config.js. Here is a minimal vue.config.js:

const path = require('path');
module.exports = {
  chainWebpack: config => {
    config.module
      .rule('supportChaining')
      .test(/\.js$/)
        .include
          .add(path.resolve('node_modules/PROBLEM_MODULE'))
          .end()
      .use('babel-loader')
        .loader('babel-loader')
        .tap(options => ({ ...options, 
          plugins : ['@babel/plugin-proposal-optional-chaining']
        }))
        .end()
    }
};

Surprisingly I did not need to install @babel/plugin-proposal-optional-chaining with NPM. I did a go/no-go test with an app scaffolded with @vue/cli 4.5.13, in my case without typescript. I imported the NPM module that has been causing my grief (@vime/vue-next 5.0.31 BTW), ran the serve script and got the Unexpected token error on a line containing optional chaining. I then plunked the above vue.config.js into the project root and ran the serve script again, this time with no errors.

My point is it appears this problem can be addressed without polluting one's development environment very much.

The Vue forums are in denial about this problem, claiming Vue 3 supports optional chaining. Apparently not, however, in node modules. A post in this thread by atflick on 2/26/2021 was a big help.

Solution 2:[2]

I had a similar problem. I'm using nuxt but my .babelrc file looks like the below, and got it working for me.

{
  "presets": [
    ["@babel/preset-env"]
  ],
  "plugins":[
    ["@babel/plugin-transform-runtime",
        {
          "regenerator": true
        }
    ]
  ],
  "env": {
    "test": {
      "plugins": [
        ["transform-regenerator", {
            "regenerator": true
        }],
        "@babel/plugin-transform-runtime"
        ],
      "presets": [
        ["@babel/preset-env", {
            "useBuiltIns": false
        }]
      ]
    }
  }
}

Solution 3:[3]

I managed to fix the solution by adding these lines to package.json:

...
"scripts": {
   "preinstall": "npx npm-force-resolutions",
   ...
},
"resolutions": {
  "acorn": "8.0.1"
},
...

Solution 4:[4]

Had same issue with Vue 2 without typescript.

To fix this you need to force babel preset to include optional chaining rule:

presets: [
    [
      '@vue/cli-plugin-babel/preset',
      {
        include: ['@babel/plugin-proposal-optional-chaining'],
      },
    ],
  ],

Can also be achieved by setting old browser target in browserslist config.

Most importantly, you need to add your failing module to transpileDependencies in vue.config.js:

module.exports = {
  ...
  transpileDependencies: ['vue-currency-input],
}

This is required, because babel by default will exclude all node_modules from transpilation (mentioned in vue cli docs), thus no configured plugins will be applied.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 Dharman
Solution 3 Ludovic Mouline
Solution 4 Pavel Gurecki