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.
146 lines
4.6 KiB
146 lines
4.6 KiB
/** |
|
* @fileoverview Report used components |
|
* @author Michał Sajnóg |
|
*/ |
|
'use strict' |
|
|
|
// ------------------------------------------------------------------------------ |
|
// Requirements |
|
// ------------------------------------------------------------------------------ |
|
|
|
const utils = require('../utils') |
|
const casing = require('../utils/casing') |
|
|
|
// ------------------------------------------------------------------------------ |
|
// Rule Definition |
|
// ------------------------------------------------------------------------------ |
|
|
|
module.exports = { |
|
meta: { |
|
type: 'suggestion', |
|
docs: { |
|
description: |
|
'disallow registering components that are not used inside templates', |
|
categories: ['vue3-essential', 'essential'], |
|
url: 'https://eslint.vuejs.org/rules/no-unused-components.html' |
|
}, |
|
fixable: null, |
|
schema: [ |
|
{ |
|
type: 'object', |
|
properties: { |
|
ignoreWhenBindingPresent: { |
|
type: 'boolean' |
|
} |
|
}, |
|
additionalProperties: false |
|
} |
|
] |
|
}, |
|
/** @param {RuleContext} context */ |
|
create(context) { |
|
const options = context.options[0] || {} |
|
const ignoreWhenBindingPresent = |
|
options.ignoreWhenBindingPresent !== undefined |
|
? options.ignoreWhenBindingPresent |
|
: true |
|
const usedComponents = new Set() |
|
/** @type { { node: Property, name: string }[] } */ |
|
let registeredComponents = [] |
|
let ignoreReporting = false |
|
/** @type {Position} */ |
|
let templateLocation |
|
|
|
return utils.defineTemplateBodyVisitor( |
|
context, |
|
{ |
|
/** @param {VElement} node */ |
|
VElement(node) { |
|
if ( |
|
(!utils.isHtmlElementNode(node) && !utils.isSvgElementNode(node)) || |
|
utils.isHtmlWellKnownElementName(node.rawName) || |
|
utils.isSvgWellKnownElementName(node.rawName) |
|
) { |
|
return |
|
} |
|
|
|
usedComponents.add(node.rawName) |
|
}, |
|
/** @param {VDirective} node */ |
|
"VAttribute[directive=true][key.name.name='bind'][key.argument.name='is'], VAttribute[directive=true][key.name.name='is']"( |
|
node |
|
) { |
|
if ( |
|
!node.value || // `<component :is>` |
|
node.value.type !== 'VExpressionContainer' || |
|
!node.value.expression // `<component :is="">` |
|
) |
|
return |
|
|
|
if (node.value.expression.type === 'Literal') { |
|
usedComponents.add(node.value.expression.value) |
|
} else if (ignoreWhenBindingPresent) { |
|
ignoreReporting = true |
|
} |
|
}, |
|
/** @param {VAttribute} node */ |
|
"VAttribute[directive=false][key.name='is']"(node) { |
|
if (!node.value) { |
|
return |
|
} |
|
const value = node.value.value.startsWith('vue:') // Usage on native elements 3.1+ |
|
? node.value.value.slice(4) |
|
: node.value.value |
|
usedComponents.add(value) |
|
}, |
|
/** @param {VElement} node */ |
|
"VElement[name='template']"(node) { |
|
templateLocation = templateLocation || node.loc.start |
|
}, |
|
/** @param {VElement} node */ |
|
"VElement[name='template']:exit"(node) { |
|
if ( |
|
node.loc.start !== templateLocation || |
|
ignoreReporting || |
|
utils.hasAttribute(node, 'src') |
|
) |
|
return |
|
|
|
registeredComponents |
|
.filter(({ name }) => { |
|
// If the component name is PascalCase or camelCase |
|
// it can be used in various of ways inside template, |
|
// like "theComponent", "The-component" etc. |
|
// but except snake_case |
|
if (casing.isPascalCase(name) || casing.isCamelCase(name)) { |
|
return ![...usedComponents].some((n) => { |
|
return ( |
|
n.indexOf('_') === -1 && |
|
(name === casing.pascalCase(n) || |
|
casing.camelCase(n) === name) |
|
) |
|
}) |
|
} else { |
|
// In any other case the used component name must exactly match |
|
// the registered name |
|
return !usedComponents.has(name) |
|
} |
|
}) |
|
.forEach(({ node, name }) => |
|
context.report({ |
|
node, |
|
message: |
|
'The "{{name}}" component has been registered but not used.', |
|
data: { |
|
name |
|
} |
|
}) |
|
) |
|
} |
|
}, |
|
utils.executeOnVue(context, (obj) => { |
|
registeredComponents = utils.getRegisteredComponents(obj) |
|
}) |
|
) |
|
} |
|
}
|
|
|