123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129 |
- /**
- * @author Yosuke Ota <https://github.com/ota-meshi>
- * See LICENSE file in root directory for full license.
- */
- 'use strict'
- const { findVariable } = require('@eslint-community/eslint-utils')
- const utils = require('../utils')
- module.exports = {
- meta: {
- type: 'problem',
- docs: {
- description: 'enforce valid `defineOptions` compiler macro',
- // TODO Switch in the next major version
- // categories: ['vue3-essential', 'vue2-essential'],
- categories: undefined,
- url: 'https://eslint.vuejs.org/rules/valid-define-options.html'
- },
- fixable: null,
- schema: [],
- messages: {
- referencingLocally:
- '`defineOptions` is referencing locally declared variables.',
- multiple: '`defineOptions` has been called multiple times.',
- notDefined: 'Options are not defined.',
- disallowProp:
- '`defineOptions()` cannot be used to declare `{{propName}}`. Use `{{insteadMacro}}()` instead.',
- typeArgs: '`defineOptions()` cannot accept type arguments.'
- }
- },
- /** @param {RuleContext} context */
- create(context) {
- const scriptSetup = utils.getScriptSetupElement(context)
- if (!scriptSetup) {
- return {}
- }
- /** @type {Set<Expression | SpreadElement>} */
- const optionsDefExpressions = new Set()
- /** @type {CallExpression[]} */
- const defineOptionsNodes = []
- return utils.compositingVisitors(
- utils.defineScriptSetupVisitor(context, {
- onDefineOptionsEnter(node) {
- defineOptionsNodes.push(node)
- if (node.arguments.length > 0) {
- const define = node.arguments[0]
- if (define.type === 'ObjectExpression') {
- for (const [propName, insteadMacro] of [
- ['props', 'defineProps'],
- ['emits', 'defineEmits'],
- ['expose', 'defineExpose'],
- ['slots', 'defineSlots']
- ]) {
- const prop = utils.findProperty(define, propName)
- if (prop) {
- context.report({
- node,
- messageId: 'disallowProp',
- data: { propName, insteadMacro }
- })
- }
- }
- }
- optionsDefExpressions.add(node.arguments[0])
- } else {
- context.report({
- node,
- messageId: 'notDefined'
- })
- }
- const typeArguments =
- 'typeArguments' in node ? node.typeArguments : node.typeParameters
- if (typeArguments) {
- context.report({
- node: typeArguments,
- messageId: 'typeArgs'
- })
- }
- },
- Identifier(node) {
- for (const defineOptions of optionsDefExpressions) {
- if (utils.inRange(defineOptions.range, node)) {
- const variable = findVariable(utils.getScope(context, node), node)
- if (
- variable &&
- variable.references.some((ref) => ref.identifier === node) &&
- variable.defs.length > 0 &&
- variable.defs.every(
- (def) =>
- def.type !== 'ImportBinding' &&
- utils.inRange(scriptSetup.range, def.name) &&
- !utils.inRange(defineOptions.range, def.name)
- )
- ) {
- if (utils.withinTypeNode(node)) {
- continue
- }
- //`defineOptions` is referencing locally declared variables.
- context.report({
- node,
- messageId: 'referencingLocally'
- })
- }
- }
- }
- }
- }),
- {
- 'Program:exit'() {
- if (defineOptionsNodes.length > 1) {
- // `defineOptions` has been called multiple times.
- for (const node of defineOptionsNodes) {
- context.report({
- node,
- messageId: 'multiple'
- })
- }
- }
- }
- }
- )
- }
- }
|