Skip to content

Commit

Permalink
Update
Browse files Browse the repository at this point in the history
  • Loading branch information
ota-meshi committed Jul 2, 2021
1 parent 7b816ab commit e5ca816
Show file tree
Hide file tree
Showing 4 changed files with 170 additions and 89 deletions.
4 changes: 4 additions & 0 deletions docs/rules/script-setup-uses-vars.md
Expand Up @@ -54,6 +54,10 @@ If you are not using `<script setup>` or if you do not use the `no-unused-vars`
- [vue/jsx-uses-vars](./jsx-uses-vars.md)
- [no-unused-vars](https://eslint.org/docs/rules/no-unused-vars)

## :books: Further Reading

- [Vue RFCs - 0040-script-setup](https://github.com/vuejs/rfcs/blob/master/active-rfcs/0040-script-setup.md)

## :mag: Implementation

- [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/script-setup-uses-vars.js)
Expand Down
11 changes: 1 addition & 10 deletions lib/rules/script-setup-uses-vars.js
Expand Up @@ -31,16 +31,7 @@ module.exports = {
* @returns {RuleListener} AST event handlers.
*/
create(context) {
const df =
context.parserServices.getDocumentFragment &&
context.parserServices.getDocumentFragment()
if (!df) {
return {}
}
const scriptSetupNode = df.children
.filter(utils.isVElement)
.find((e) => e.name === 'script' && utils.hasAttribute(e, 'setup'))
if (!scriptSetupNode) {
if (!utils.isScriptSetup(context)) {
return {}
}
/** @type {Set<string>} */
Expand Down
223 changes: 144 additions & 79 deletions lib/utils/index.js
Expand Up @@ -406,28 +406,6 @@ module.exports = {
return null
},

/**
* Check whether the given start tag has specific directive.
* @param {VElement} node The start tag node to check.
* @param {string} name The attribute name to check.
* @param {string} [value] The attribute value to check.
* @returns {boolean} `true` if the start tag has the attribute.
*/
hasAttribute(node, name, value) {
return Boolean(this.getAttribute(node, name, value))
},

/**
* Check whether the given start tag has specific directive.
* @param {VElement} node The start tag node to check.
* @param {string} name The directive name to check.
* @param {string} [argument] The directive argument to check.
* @returns {boolean} `true` if the start tag has the directive.
*/
hasDirective(node, name, argument) {
return Boolean(this.getDirective(node, name, argument))
},

/**
* Check whether the given directive attribute has their empty value (`=""`).
* @param {VDirective} node The directive attribute node to check.
Expand Down Expand Up @@ -515,71 +493,41 @@ module.exports = {
* @param {string} [value] The attribute value to check.
* @returns {VAttribute | null} The found attribute.
*/
getAttribute(node, name, value) {
return (
node.startTag.attributes.find(
/**
* @param {VAttribute | VDirective} node
* @returns {node is VAttribute}
*/
(node) => {
return (
!node.directive &&
node.key.name === name &&
(value === undefined ||
(node.value != null && node.value.value === value))
)
}
) || null
)
},
getAttribute,

/**
* Check whether the given start tag has specific directive.
* @param {VElement} node The start tag node to check.
* @param {string} name The attribute name to check.
* @param {string} [value] The attribute value to check.
* @returns {boolean} `true` if the start tag has the attribute.
*/
hasAttribute,

/**
* Get the directive list which has the given name.
* @param {VElement | VStartTag} node The start tag node to check.
* @param {string} name The directive name to check.
* @returns {VDirective[]} The array of `v-slot` directives.
*/
getDirectives(node, name) {
const attributes =
node.type === 'VElement' ? node.startTag.attributes : node.attributes
return attributes.filter(
/**
* @param {VAttribute | VDirective} node
* @returns {node is VDirective}
*/
(node) => {
return node.directive && node.key.name.name === name
}
)
},
getDirectives,
/**
* Get the directive which has the given name.
* @param {VElement} node The start tag node to check.
* @param {string} name The directive name to check.
* @param {string} [argument] The directive argument to check.
* @returns {VDirective | null} The found directive.
*/
getDirective(node, name, argument) {
return (
node.startTag.attributes.find(
/**
* @param {VAttribute | VDirective} node
* @returns {node is VDirective}
*/
(node) => {
return (
node.directive &&
node.key.name.name === name &&
(argument === undefined ||
(node.key.argument &&
node.key.argument.type === 'VIdentifier' &&
node.key.argument.name) === argument)
)
}
) || null
)
},
getDirective,

/**
* Check whether the given start tag has specific directive.
* @param {VElement} node The start tag node to check.
* @param {string} name The directive name to check.
* @param {string} [argument] The directive argument to check.
* @returns {boolean} `true` if the start tag has the directive.
*/
hasDirective,

/**
* Returns the list of all registered components
Expand Down Expand Up @@ -643,13 +591,13 @@ module.exports = {
for (const childNode of node.children) {
if (childNode.type === 'VElement') {
let connected
if (this.hasDirective(childNode, 'if')) {
if (hasDirective(childNode, 'if')) {
connected = false
vIf = true
} else if (this.hasDirective(childNode, 'else-if')) {
} else if (hasDirective(childNode, 'else-if')) {
connected = vIf
vIf = true
} else if (this.hasDirective(childNode, 'else')) {
} else if (hasDirective(childNode, 'else')) {
connected = vIf
vIf = false
} else {
Expand Down Expand Up @@ -685,9 +633,9 @@ module.exports = {
!this.isHtmlWellKnownElementName(node.rawName)) ||
(this.isSvgElementNode(node) &&
!this.isSvgWellKnownElementName(node.rawName)) ||
this.hasAttribute(node, 'is') ||
this.hasDirective(node, 'bind', 'is') ||
this.hasDirective(node, 'is')
hasAttribute(node, 'is') ||
hasDirective(node, 'bind', 'is') ||
hasDirective(node, 'is')
)
},

Expand Down Expand Up @@ -1023,6 +971,28 @@ module.exports = {

isVueFile,

/**
* Checks whether the current file is uses `<script setup>`
* @param {RuleContext} context The ESLint rule context object.
*/
isScriptSetup(context) {
const df =
context.parserServices.getDocumentFragment &&
context.parserServices.getDocumentFragment()
if (!df) {
return false
}
const scripts = df.children
.filter(isVElement)
.filter((e) => e.name === 'script')
if (scripts.length === 2) {
return scripts.some((e) => hasAttribute(e, 'setup'))
} else {
const script = scripts[0]
return script && hasAttribute(script, 'setup')
}
},

/**
* Check if current file is a Vue instance or component and call callback
* @param {RuleContext} context The ESLint rule context object.
Expand Down Expand Up @@ -2199,3 +2169,98 @@ function* iterateWatchHandlerValues(property) {
yield value
}
}

/**
* Get the attribute which has the given name.
* @param {VElement} node The start tag node to check.
* @param {string} name The attribute name to check.
* @param {string} [value] The attribute value to check.
* @returns {VAttribute | null} The found attribute.
*/
function getAttribute(node, name, value) {
return (
node.startTag.attributes.find(
/**
* @param {VAttribute | VDirective} node
* @returns {node is VAttribute}
*/
(node) => {
return (
!node.directive &&
node.key.name === name &&
(value === undefined ||
(node.value != null && node.value.value === value))
)
}
) || null
)
}

/**
* Get the directive list which has the given name.
* @param {VElement | VStartTag} node The start tag node to check.
* @param {string} name The directive name to check.
* @returns {VDirective[]} The array of `v-slot` directives.
*/
function getDirectives(node, name) {
const attributes =
node.type === 'VElement' ? node.startTag.attributes : node.attributes
return attributes.filter(
/**
* @param {VAttribute | VDirective} node
* @returns {node is VDirective}
*/
(node) => {
return node.directive && node.key.name.name === name
}
)
}
/**
* Get the directive which has the given name.
* @param {VElement} node The start tag node to check.
* @param {string} name The directive name to check.
* @param {string} [argument] The directive argument to check.
* @returns {VDirective | null} The found directive.
*/
function getDirective(node, name, argument) {
return (
node.startTag.attributes.find(
/**
* @param {VAttribute | VDirective} node
* @returns {node is VDirective}
*/
(node) => {
return (
node.directive &&
node.key.name.name === name &&
(argument === undefined ||
(node.key.argument &&
node.key.argument.type === 'VIdentifier' &&
node.key.argument.name) === argument)
)
}
) || null
)
}

/**
* Check whether the given start tag has specific directive.
* @param {VElement} node The start tag node to check.
* @param {string} name The attribute name to check.
* @param {string} [value] The attribute value to check.
* @returns {boolean} `true` if the start tag has the attribute.
*/
function hasAttribute(node, name, value) {
return Boolean(getAttribute(node, name, value))
}

/**
* Check whether the given start tag has specific directive.
* @param {VElement} node The start tag node to check.
* @param {string} name The directive name to check.
* @param {string} [argument] The directive argument to check.
* @returns {boolean} `true` if the start tag has the directive.
*/
function hasDirective(node, name, argument) {
return Boolean(getDirective(node, name, argument))
}
21 changes: 21 additions & 0 deletions tests/lib/rules/script-setup-uses-vars.js
Expand Up @@ -252,6 +252,27 @@ describe('script-setup-uses-vars', () => {
line: 4
}
]
},

// Not `<script setup>`
{
filename: 'test.vue',
code: `
<script>
/* eslint script-setup-uses-vars: 1 */
const msg = 'Hello!'
</script>
<template>
<div>{{ msg }}</div>
</template>
`,
errors: [
{
message: "'msg' is assigned a value but never used.",
line: 4
}
]
}
]
})
Expand Down

0 comments on commit e5ca816

Please sign in to comment.