Eslint-plugin-vue: Parsing error on optional chaining (?.) token

Created on 23 Mar 2020  路  3Comments  路  Source: vuejs/eslint-plugin-vue

Tell us about your environment

  • ESLint version: 6.6.0
  • eslint-plugin-vue version: 6.2.2
  • Node version: 12

Please show your full configuration:

const fs = require('fs');

// Import AirBNB base rules to wrap them onto framework specific configs.
const airbnbRulesStyle = require('eslint-config-airbnb-base/rules/style').rules;
const airbnbRulesES6 = require('eslint-config-airbnb-base/rules/es6').rules;
const airbnbRulesBestPractices = require('eslint-config-airbnb-base/rules/best-practices').rules;

const maxLenBaseRule = {
  code: 120,
  ignoreUrls: true,
};

const rules = {
  'object-property-newline': ['error', {
    allowAllPropertiesOnSameLine: false,
  }],
  'array-element-newline': ['error', 'consistent'],
  'array-bracket-newline': ['error', 'consistent'],
  'max-len': ['error', maxLenBaseRule],
  'no-param-reassign': ['error', {
    props: false,
  }],
  'no-plusplus': ['error', {
    allowForLoopAfterthoughts: true,
  }],
  'no-underscore-dangle': ['error', {
    allow: ['_id'],
  }],
  'import/extensions': ['error', 'always', {
    js: 'never',
    ts: 'never',
  }],
  'import/no-extraneous-dependencies': 'off',
};

const tsRules = {
  '@typescript-eslint/explicit-function-return-type': ['error', {
    allowExpressions: true,
  }],
  '@typescript-eslint/no-unused-vars': ['error', {
    args: 'none',
  }],
  '@typescript-eslint/no-explicit-any': 'off',
};

if (!fs.existsSync('node_modules')) {
  rules['import/no-unresolved'] = 'off';
}

const vueRules = {
  indent: 'off', // @see https://eslint.vuejs.org/rules/script-indent.html#options
  'vue/array-bracket-spacing': airbnbRulesStyle['array-bracket-spacing'],
  'vue/arrow-spacing': airbnbRulesES6['arrow-spacing'],
  'vue/block-spacing': airbnbRulesStyle['block-spacing'],
  'vue/brace-style': airbnbRulesStyle['brace-style'],
  'vue/camelcase': airbnbRulesStyle.camelcase,
  'vue/comma-dangle': airbnbRulesStyle['comma-dangle'],
  'vue/component-definition-name-casing': 'error',
  'vue/component-name-in-template-casing': 'error',
  'vue/component-tags-order': ['error', {
    order: ['script', 'style', 'template'],
  }],
  'vue/dot-location': airbnbRulesBestPractices['dot-location'],
  'vue/eqeqeq': airbnbRulesBestPractices.eqeqeq,
  'vue/key-spacing': airbnbRulesStyle['key-spacing'],
  'vue/keyword-spacing': airbnbRulesStyle['keyword-spacing'],
  'vue/match-component-file-name': ['error', {
    extensions: ['vue'],
    shouldMatchCase: true,
  }],
  'vue/max-len': ['error', maxLenBaseRule],
  'vue/no-empty-pattern': airbnbRulesBestPractices['no-empty-pattern'],
  'vue/no-irregular-whitespace': 'error',
  'vue/no-reserved-component-names': 'error',
  'vue/no-restricted-syntax': airbnbRulesStyle['no-restricted-syntax'],
  'vue/object-curly-spacing': airbnbRulesStyle['object-curly-spacing'],
  'vue/padding-line-between-blocks': 'error',
  'vue/script-indent': ['error', 2, {
    baseIndent: 1,
  }],
  'vue/space-infix-ops': airbnbRulesStyle['space-infix-ops'],
  'vue/space-unary-ops': airbnbRulesStyle['space-unary-ops'],
  'vue/v-on-function-call': 'error',
  'vue/v-slot-style': 'error',
  'vue/valid-v-slot': 'error',
};

module.exports = {
  env: {
    browser: true,
    commonjs: true,
    es6: true,
    node: true,
    jquery: true,
    'cypress/globals': true,
  },
  plugins: [
    'cypress',
    '@typescript-eslint',
  ],
  extends: [
    'eslint:recommended',
    'airbnb',
  ],
  parser: '@typescript-eslint/parser',
  parserOptions: {
    ecmaVersion: 2018,
    sourceType: 'module',
  },
  rules,
  overrides: [
    {
      files: ['*.ts'],
      extends: [
        'plugin:@typescript-eslint/eslint-recommended',
        'plugin:@typescript-eslint/recommended',
      ],
      rules: tsRules,
    },
    {
      files: ['*.vue'],
      extends: [
        'plugin:vue/recommended',
      ],
      rules: vueRules,
    },
  ],
  settings: {
    'import/resolver': {
      node: {
        extensions: [
          '.js',
          '.jsx',
          '.ts',
          '.d.ts',
          '.tsx',
        ],
      },
    },
  },
};

What did you do?

<script>
  const testObject = {};
  const test = testObject?.foo;
  console.log(test);

  export default {
    name: 'MyComponent',
    props: {
      title: {
        default: null,
        type: String,
      },
    },
  };
</script>

<template>
  <div class="panel panel-default">
    <div
      v-if="this.$slots.header || this.$slots.title || title"
      class="panel-heading"
    >
      <slot name="header">
        <h4 class="panel-title">
          <slot name="title">
            {{ title }}
          </slot>
        </h4>
      </slot>
    </div>
    <div class="panel-body">
      <slot />
    </div>
    <div
      v-if="this.$slots.footer"
      class="panel-footer"
    >
      <slot name="footer" />
    </div>

    <ul id="example-1">
      <li
        v-for="item in [1,2,3]"
        :key="item"
      >
        {{ item }}
      </li>
    </ul>
  </div>
</template>

What did you expect to happen?

Successful lint

What actually happened?

/code/fixtures/MyComponent.vue
  3:26  error  Parsing error: Unexpected token .

See also: https://gitlab.com/nexylan/pretty/-/merge_requests/89

Note: Optional chaining parsing works on eslint core, only the plugin parser crashes.

Most helpful comment

Hi.

You may need to use babel-eslint or @typescript-eslint/parser for .vue to solve the problem.

    {
      files: ['*.vue'],
      extends: [
        'plugin:vue/recommended',
      ],
+      parserOptions: {
+        parser: '@typescript-eslint/parser',
+      },
      rules: vueRules,
    },

https://eslint.vuejs.org/user-guide/#how-to-use-a-custom-parser

The parser that eslint uses by default does not yet support Optional Chaining.

https://github.com/eslint/eslint/issues/12642

All 3 comments

Hi.

You may need to use babel-eslint or @typescript-eslint/parser for .vue to solve the problem.

    {
      files: ['*.vue'],
      extends: [
        'plugin:vue/recommended',
      ],
+      parserOptions: {
+        parser: '@typescript-eslint/parser',
+      },
      rules: vueRules,
    },

https://eslint.vuejs.org/user-guide/#how-to-use-a-custom-parser

The parser that eslint uses by default does not yet support Optional Chaining.

https://github.com/eslint/eslint/issues/12642

If the problem persists, please reopen this issue.

@ota-meshi Thanks for the answer, but that didn't help.

Using the parser now give me this error (with the same vue file):

/code/fixtures/MyComponent.vue
  7:8  error  Parsing error: '}' expected

But the file seems correct.

Also, this parser is already set globally and works with my *.ts override. Why not with the *.vue override? :thinking:

Was this page helpful?
0 / 5 - 0 ratings

Related issues

filipalacerda picture filipalacerda  路  4Comments

lichnow picture lichnow  路  3Comments

Mouvedia picture Mouvedia  路  3Comments

maple-leaf picture maple-leaf  路  3Comments

apertureless picture apertureless  路  4Comments