eslint: Getting `error Parsing error: Unexpected token .` when using optional chaining

I use optional chaining in one of my .js files and it worked fine until last few days and I cannot figure out what might have caused it to break. Even more strange is the fact the project compiles fine on Heroku.

Module Error (from ./node_modules/eslint-loader/dist/cjs.js):                                 friendly-errors 16:50:32

/Users/davidfilat/GitHub/moldovacrestina-ssr-app/store/resources.js
  36:50  error  Parsing error: Unexpected token .

✖ 1 problem (1 error, 0 warnings)

                                                                                              friendly-errors 16:50:32
 @ ./.nuxt/store.js 39:22-54 43:4-48:6 43:72-48:5
 @ ./.nuxt/index.js
 @ ./.nuxt/client.js
 @ multi eventsource-polyfill webpack-hot-middleware/client?reload=true&timeout=30000&ansiColors=&overlayStyles=&name=client&path=/__webpack_hmr/client ./.nuxt/client.js

I use Node 14 on my device and here is my package.json the eslint configuration is the default one that comes with nuxt.js.

"dependencies": {
    "@babel/runtime-corejs2": "^7.9.6",
    "@nuxtjs/axios": "^5.10.3",
    "@nuxtjs/device": "^1.2.7",
    "@nuxtjs/dotenv": "^1.4.1",
    "@nuxtjs/localtunnel": "^1.1.3",
    "@nuxtjs/proxy": "^1.3.3",
    "@nuxtjs/pwa": "^3.0.0-beta.20",
    "body-scroll-lock": "^3.0.2",
    "bootstrap": "^4.4.1",
    "bootstrap-vue": "^2.13.0",
    "cross-env": "^7.0.2",
    "dayjs": "^1.8.26",
    "express": "^4.17.1",
    "font-awesome": "^4.7.0",
    "he": "^1.2.0",
    "jquery": "^3.5.0",
    "nuxt": "^2.12.2",
    "plyr": "~3.5.10",
    "popper.js": "1.16.1",
    "vue-plyr": "^6.0.4"
  },
  "devDependencies": {
    "@nuxtjs/color-mode": "^1.0.0",
    "@nuxtjs/eslint-config": "^2.0.2",
    "@nuxtjs/eslint-module": "^1.2.0",
    "@nuxtjs/stylelint-module": "^3.2.2",
    "@vue/test-utils": "^1.0.0-beta.33",
    "babel-jest": "^25.5.1",
    "eslint": "^6.8.0",
    "eslint-config-prettier": "^6.11.0",
    "eslint-plugin-babel": "^5.3.0",
    "eslint-plugin-nuxt": ">=0.5.2",
    "eslint-plugin-prettier": "^3.1.3",
    "husky": "4.2.5",
    "jest": "^25.5.4",
    "lint-staged": "^10.2.2",
    "nodemon": "^2.0.3",
    "postcss-hexrgba": "^2.0.0",
    "postcss-nested": "^4.2.1",
    "postcss-preset-env": "^6.7.0",
    "postcss-responsive-type": "^1.0.0",
    "postcss-url": "^8.0.0",
    "prettier": "^2.0.5",
    "sass": "^1.26.5",
    "sass-loader": "^8.0.2",
    "stylelint": "^13.3.3",
    "stylelint-config-prettier": "^8.0.1",
    "stylelint-config-recommended": "^3.0.0",
    "stylelint-order": "^4.0.0",
    "stylelint-prettier": "^1.1.2",
    "stylelint-scss": "^3.17.1",
    "ts-jest": "^25.4.0",
    "vue-jest": "^4.0.0-0"
  }

About this issue

  • Original URL
  • State: closed
  • Created 4 years ago
  • Comments: 17 (4 by maintainers)

Most upvoted comments

@dschreij Though the team couldn’t provide any clarifying explanation, I’m still pretty sure it’s caused by the upgrade to Node 14. Here is the solution to solve the problem on my project:

  1. Add the following dependencies to your package.json:
	"devDependencies: {
		...
		"@babel/plugin-proposal-nullish-coalescing-operator": "^7.10.1",
		"@babel/plugin-proposal-optional-chaining": "^7.10.1",
		"babel-eslint": "^10.1.0",
		"eslint-plugin-babel": "^5.3.0",
	},

Modify your .babelrc file to look like this:

{
  "env": {
    "test": {
      "presets": [
        [
          "@babel/preset-env",
          {
            "targets": {
              "node": "current"
            }
          }
        ]
      ]
    }
  },
  "plugins": [
    "@babel/plugin-proposal-nullish-coalescing-operator",
    "@babel/plugin-proposal-optional-chaining"
  ]
}
  1. Modify your eslintrc.js:
module.exports = {
  root: true,
  parserOptions: {
    parser: 'babel-eslint',
    sourceType: 'module',
  },
  env: {
    browser: true,
    node: true,
  },
  extends: [
    '@nuxtjs',
  ],
  plugins: ['babel'],
  rules: {},
}

actually node supports optional chaining since version 14.0

 $ node --version
v14.2.0
 $ echo "let foo = {a: {c: 3}, b: {}}; console.log('a: ' + foo?.a?.c + ', c: ' + foo?.b?.c)" | node -
a: 3, c: undefined

The compatibility chart at https://node.green/#ES2020-features-optional-chaining-operator----- is broken and out of date.

my .eslintrc:

module.exports = {
  root: true,
  env: {
    browser: true,
    node: true,
  },
  extends: [
    'prettier',
    'prettier/vue',
    'plugin:prettier/recommended',
    'plugin:nuxt/recommended',
  ],
  plugins: ['babel', 'prettier'],
  // add your custom rules here
  rules: {
    'nuxt/no-cjs-in-config': 'off',
  },
}

example of file: state/resource.js

import ResourcesService from '@/services/ResourcesService'
import dayjs from 'dayjs'
import 'dayjs/locale/ro'
import { decode } from 'he'
import _ from 'lodash'

export const state = () => ({
  resources: [],
  resource: {},
})

export const mutations = {
  SET_RESOURCES(state, resources) {
    state.resources = resources
  },
  APPEND_PAGE_TO_RESOURCES(state, resources) {
    state.resources = state.resources.concat(resources)
  },
  SET_RESOURCE(state, resource) {
    state.resource.content = resource.content.rendered

    resource.video_meta
      ? (state.resource.resourceType = 'video')
      : (state.resource.resourceType = 'article')

    state.resource.author = resource._embedded.author[0].name

    state.resource.titleRaw = decode(resource.title.rendered)

    state.resource.title = _.split(state.resource.titleRaw, '|', 2)[0]

    state.resource.typeClass = 'resource-card--' + state.resourceType

    state.resource.date = dayjs(resource.date_gmt).format('D MMMM YYYY')

    state.resource.videoID = resource.video_meta?._ayvpp_video

    state.resource.slug = resource.slug
  },
}

export const actions = {
  async fetchResources({ commit }) {
    const response = await ResourcesService.getResources()
    commit('SET_RESOURCES', response.data)
  },
  async fetchNextResourcesPage({ commit }, payload) {
    const response = await ResourcesService.getResources(payload.pageNr, 12)
    commit('APPEND_PAGE_TO_RESOURCES', response.data)
  },
  async fetchResource({ commit }, slug) {
    const response = await ResourcesService.getResource(slug)
    commit('SET_RESOURCE', response.data[0])
  },
}