You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
120 lines
3.1 KiB
120 lines
3.1 KiB
/** |
|
* @author Toru Nagashima |
|
* @copyright 2017 Toru Nagashima. All rights reserved. |
|
* See LICENSE file in root directory for full license. |
|
*/ |
|
'use strict' |
|
|
|
// ------------------------------------------------------------------------------ |
|
// Requirements |
|
// ------------------------------------------------------------------------------ |
|
|
|
const utils = require('../utils') |
|
|
|
// ------------------------------------------------------------------------------ |
|
// Helpers |
|
// ------------------------------------------------------------------------------ |
|
|
|
/** |
|
* Get the name of the given attribute node. |
|
* @param {VAttribute | VDirective} attribute The attribute node to get. |
|
* @returns {string | null} The name of the attribute. |
|
*/ |
|
function getName(attribute) { |
|
if (!attribute.directive) { |
|
return attribute.key.name |
|
} |
|
if (attribute.key.name.name === 'bind') { |
|
return ( |
|
(attribute.key.argument && |
|
attribute.key.argument.type === 'VIdentifier' && |
|
attribute.key.argument.name) || |
|
null |
|
) |
|
} |
|
return null |
|
} |
|
|
|
// ------------------------------------------------------------------------------ |
|
// Rule Definition |
|
// ------------------------------------------------------------------------------ |
|
|
|
module.exports = { |
|
meta: { |
|
type: 'problem', |
|
docs: { |
|
description: 'disallow duplication of attributes', |
|
categories: ['vue3-essential', 'essential'], |
|
url: 'https://eslint.vuejs.org/rules/no-duplicate-attributes.html' |
|
}, |
|
fixable: null, |
|
|
|
schema: [ |
|
{ |
|
type: 'object', |
|
properties: { |
|
allowCoexistClass: { |
|
type: 'boolean' |
|
}, |
|
allowCoexistStyle: { |
|
type: 'boolean' |
|
} |
|
}, |
|
additionalProperties: false |
|
} |
|
] |
|
}, |
|
/** @param {RuleContext} context */ |
|
create(context) { |
|
const options = context.options[0] || {} |
|
const allowCoexistStyle = options.allowCoexistStyle !== false |
|
const allowCoexistClass = options.allowCoexistClass !== false |
|
|
|
/** @type {Set<string>} */ |
|
const directiveNames = new Set() |
|
/** @type {Set<string>} */ |
|
const attributeNames = new Set() |
|
|
|
/** |
|
* @param {string} name |
|
* @param {boolean} isDirective |
|
*/ |
|
function isDuplicate(name, isDirective) { |
|
if ( |
|
(allowCoexistStyle && name === 'style') || |
|
(allowCoexistClass && name === 'class') |
|
) { |
|
return isDirective ? directiveNames.has(name) : attributeNames.has(name) |
|
} |
|
return directiveNames.has(name) || attributeNames.has(name) |
|
} |
|
|
|
return utils.defineTemplateBodyVisitor(context, { |
|
VStartTag() { |
|
directiveNames.clear() |
|
attributeNames.clear() |
|
}, |
|
VAttribute(node) { |
|
const name = getName(node) |
|
if (name == null) { |
|
return |
|
} |
|
|
|
if (isDuplicate(name, node.directive)) { |
|
context.report({ |
|
node, |
|
loc: node.loc, |
|
message: "Duplicate attribute '{{name}}'.", |
|
data: { name } |
|
}) |
|
} |
|
|
|
if (node.directive) { |
|
directiveNames.add(name) |
|
} else { |
|
attributeNames.add(name) |
|
} |
|
} |
|
}) |
|
} |
|
}
|
|
|