-
-
Notifications
You must be signed in to change notification settings - Fork 681
/
Copy pathscript-setup-uses-vars.js
121 lines (115 loc) · 3.92 KB
/
script-setup-uses-vars.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
/**
* @author Yosuke Ota
* See LICENSE file in root directory for full license.
*/
'use strict'
// ------------------------------------------------------------------------------
// Requirements
// ------------------------------------------------------------------------------
const utils = require('../utils')
const casing = require('../utils/casing')
// ------------------------------------------------------------------------------
// Rule Definition
// ------------------------------------------------------------------------------
module.exports = {
meta: {
type: 'problem',
docs: {
description:
'prevent `<script setup>` variables used in `<template>` to be marked as unused', // eslint-disable-line consistent-docs-description
categories: ['base'],
url: 'https://eslint.vuejs.org/rules/script-setup-uses-vars.html'
},
schema: []
},
/**
* @param {RuleContext} context - The rule context.
* @returns {RuleListener} AST event handlers.
*/
create(context) {
if (!utils.isScriptSetup(context)) {
return {}
}
/** @type {Set<string>} */
const scriptVariableNames = new Set()
const globalScope = context.getSourceCode().scopeManager.globalScope
if (globalScope) {
for (const variable of globalScope.variables) {
scriptVariableNames.add(variable.name)
}
const moduleScope = globalScope.childScopes.find(
(scope) => scope.type === 'module'
)
for (const variable of (moduleScope && moduleScope.variables) || []) {
scriptVariableNames.add(variable.name)
}
}
/**
* `casing.camelCase()` converts the beginning to lowercase,
* but does not convert the case of the beginning character when converting with Vue3.
* @see https://github.com/vuejs/vue-next/blob/1ffd48a2f5fd3eead3ea29dae668b7ed1c6f6130/packages/shared/src/index.ts#L116
* @param {string} str
*/
function camelize(str) {
return str.replace(/-(\w)/g, (_, c) => (c ? c.toUpperCase() : ''))
}
/**
* @see https://github.com/vuejs/vue-next/blob/1ffd48a2f5fd3eead3ea29dae668b7ed1c6f6130/packages/compiler-core/src/transforms/transformElement.ts#L321
* @param {string} name
*/
function markElementVariableAsUsed(name) {
if (scriptVariableNames.has(name)) {
context.markVariableAsUsed(name)
}
const camelName = camelize(name)
if (scriptVariableNames.has(camelName)) {
context.markVariableAsUsed(camelName)
}
const pascalName = casing.capitalize(camelName)
if (scriptVariableNames.has(pascalName)) {
context.markVariableAsUsed(pascalName)
}
}
return utils.defineTemplateBodyVisitor(
context,
{
VExpressionContainer(node) {
for (const ref of node.references.filter(
(ref) => ref.variable == null
)) {
context.markVariableAsUsed(ref.id.name)
}
},
VElement(node) {
if (
(!utils.isHtmlElementNode(node) && !utils.isSvgElementNode(node)) ||
(node.rawName === node.name &&
(utils.isHtmlWellKnownElementName(node.rawName) ||
utils.isSvgWellKnownElementName(node.rawName))) ||
utils.isBuiltInComponentName(node.rawName)
) {
return
}
markElementVariableAsUsed(node.rawName)
},
/** @param {VDirective} node */
'VAttribute[directive=true]'(node) {
if (utils.isBuiltInDirectiveName(node.key.name.name)) {
return
}
markElementVariableAsUsed(`v-${node.key.name.rawName}`)
},
/** @param {VAttribute} node */
'VAttribute[directive=false]'(node) {
if (node.key.name === 'ref' && node.value) {
context.markVariableAsUsed(node.value.value)
}
}
},
undefined,
{
templateBodyTriggerSelector: 'Program'
}
)
}
}