eslint-plugin-vue icon indicating copy to clipboard operation
eslint-plugin-vue copied to clipboard

[vue/no-unused-properties] bug detecting properties defined within setup() and used with template option

Open jacksongross opened this issue 3 years ago • 0 comments

Checklist

  • [x] I have tried restarting my IDE and the issue persists.
  • [x] I have read the FAQ and my problem is not listed.

Tell us about your environment

  • ESLint version: 8.8.0
  • eslint-plugin-vue version: 8.6.0
  • Node version: v14.17.0
  • Operating System: macOS Monterey 12.2.1 (M1 processor)

Please show your full configuration:

module.exports = {
  root: true,
  parserOptions: {
    parser: '@babel/eslint-parser',
    requireConfigFile: false,
    sourceType: 'module',
  },
  env: {
    browser: true,
    jest: true
  },
  extends: [
    'airbnb-base',
    'plugin:vue/recommended'
  ],
  plugins: [
    'vue',
    'simple-import-sort'
  ],
  globals: {
    ga: true,
    cordova: true,
    __statics: true,
    context: true,
    fixture: true
  },
  settings: {
    'import/resolver': {
      alias: {
        map: [
          ['src', './src'],
          ['shared', './shared']
        ],
        extensions: ['.vue', '.js', '.json']
      }
    }
  },
  rules: {
    // allow async-await
    'generator-star-spacing': 'off',

    'class-methods-use-this': 'off',

    'no-implicit-coercion': ['error', {
      'disallowTemplateShorthand': true,
      'boolean': true,
      'number': true,
      'string': true
    }],

    // variable names must be min 2 characters
    'id-length': ['error', {
      'min': 2,
      'properties': 'never',
      'exceptions': ['_', 'h']
    }],

    'max-len': ['error', {
      code: 100,
      ignorePattern: '(`|\\$\\{)',
      ignoreUrls: true,
      ignoreComments: false,
      ignoreRegExpLiterals: true,
      ignoreStrings: true,
      ignoreTemplateLiterals: true,
    }],

    // force import order of external > shared > src > local modules
    'simple-import-sort/imports': ['error', {
      groups: [
        // Node.js builtins. You could also generate this regex if you use a `.js` config.
        // For example: `^(${require("module").builtinModules.join("|")})(/|$)`
        [
          "^(assert|buffer|child_process|cluster|console|constants|crypto|dgram|dns|domain|events|fs|http|https|module|net|os|path|punycode|querystring|readline|repl|stream|string_decoder|sys|timers|tls|tty|url|util|vm|zlib|freelist|v8|process|async_hooks|http2|perf_hooks)(/.*|$)",
        ],
        // Packages
        ["^@?\\w"],
        // shared imports
        ["^(shared)(/.*|$)"],
        // src imports
        ["^(src)(/.*|$)"],
        // Internal imports.
        // ["^(@|@company|@ui|components|utils|config|vendored-lib)(/.*|$)"],
        // Side effect imports.
        ["^\\u0000"],
        // Parent imports. Put `..` last.
        ["^\\.\\.(?!/?$)", "^\\.\\./?$"],
        // Other relative imports. Put same-folder imports and `.` last.
        ["^\\./(?=.*/)(?!/?$)", "^\\.(?!/?$)", "^\\./?$"],
        // Style imports.
        ["^.+\\.s?css$"],
      ],
    }],

    // allow mutating only certain variables
    'no-param-reassign': ['error', {
      props: true,
      ignorePropertyModificationsFor: [
        'acc', // for reduce accumulators
        'e', // for e.returnvalue
        'ctx', // for Koa routing
        'req', // for Express requests
        'request', // for Express requests
        'res', // for Express responses
        'response', // for Express responses
        'state', // for vuex mutations
      ]
    }],

    // disable using quasar utils -- should be using date-fns and lodash
    'no-restricted-imports': ['error', {
      'paths': [
        {
        'name': 'quasar',
        'importNames': ['date'],
        'message': 'Please use individual date-fns imports instead of quasar\'s date utilities.'
        },
        {
          'name': 'quasar',
          'importNames': ['debounce'],
          'message': 'Please use lodash/debounce instead of quasar\'s debounce utility.'
        },
      ],
    }],

    'padding-line-between-statements': [
      'error',
      { blankLine: 'always', prev: '*', next: 'block' },
      { blankLine: 'always', prev: 'block', next: '*' },
      { blankLine: 'always', prev: '*', next: 'block-like' },
      { blankLine: 'always', prev: 'block-like', next: '*' },
      { blankLine: 'never', prev: 'case', next: 'case' },
      { blankLine: 'always', prev: '*', next: 'return' },
    ],

    // allow paren-less arrow functions
    'arrow-parens': 0,
    'one-var': 0,

    'import/first': 0,
    'import/named': 2,
    'import/namespace': 2,
    'import/default': 2,
    'import/export': 2,
    'import/extensions': 0,
    'import/no-unresolved': 0,
    'import/no-extraneous-dependencies': 0,

    'semi': [2, 'always'],
    'no-debugger': 2,

    // force PascalCase for components
    'vue/component-name-in-template-casing': ['error', 'PascalCase', {
      'registeredComponentsOnly': false,
    }],

    // single prop, single line
    'vue/first-attribute-linebreak': ['error', {
      'singleline': 'beside',
    }],

    // force ordering of SFC top level tags
    'vue/component-tags-order': ['error', {
      'order': ['template', 'script', 'style']
    }],

    // component name must be defined and match filename
    'vue/match-component-file-name': ['error', {
      'extensions': ['vue'],
      'shouldMatchCase': true
    }],

    // prevent unused props, data, computed, methods in components
    'vue/no-unused-properties': ['error', {
      'groups': [
        // 'props', // TODO: check back if they fix it for props bound with v-bind or used within setup method or passed to composable
        // 'data',
        'computed',
        'methods',
        'setup',
      ],
      // 'deepData': true,
      'ignorePublicMembers': true,
    }],

    // no empty script/template/style blocks in SFC
    'vue/no-empty-component-block': 2,

    // TODO: fix all instances where props are mutated
    'vue/no-mutating-props': 0,

    // TODO: rename all single word components
    'vue/multi-word-component-names': 0,
  }
};

What did you do?

This is a very dumbed down version that illustrates the issue. I am basically trying to test a composable with Jest and vue-test-utils (the composable setup code is omitted here as it isn't relevant to the issue itself) but the idea is we create a component to use within the tests that includes the composable and some other setup code like the snippet below.

  const mockComponent = defineComponent({
    setup() {
      const someRef = ref(null);

      return {
        someRef,
      };
    },
    template: `
      <div ref="someRef" />
    `,
  });

What did you expect to happen? Eslint reports no error for vue/no-unused-properties when using properties within the template option of a component.

What actually happened? When defining the template option and using a ref defined within setup, I get the following error:

error  'someRef' of property returned from `setup()` found, but never used  vue/no-unused-properties

even though that someRef is definitely used within the template string of the component options.

Repository to reproduce this issue Unfortunately closed source, but seems simple to reproduce.

jacksongross avatar Apr 07 '22 00:04 jacksongross