-
-
Notifications
You must be signed in to change notification settings - Fork 681
/
Copy pathvalid-slot-scope.js
137 lines (125 loc) · 4.62 KB
/
valid-slot-scope.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
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
/**
* @fileoverview enforce valid `slot-scope` attributes
* @author Yosuke Ota
*/
'use strict'
// ------------------------------------------------------------------------------
// Requirements
// ------------------------------------------------------------------------------
const utils = require('../utils')
// ------------------------------------------------------------------------------
// Helpers
// ------------------------------------------------------------------------------
/**
* Check whether the given token is a comma.
* @param {Token} token The token to check.
* @returns {boolean} `true` if the token is a comma.
*/
function isComma (token) {
return token != null && token.type === 'Punctuator' && token.value === ','
}
/**
* Check whether the given token is a quote.
* @param {Token} token The token to check.
* @returns {boolean} `true` if the token is a quote.
*/
function isQuote (token) {
return token != null && token.type === 'Punctuator' && (token.value === '"' || token.value === "'")
}
/**
* Gets the extra access parameter tokens of the given slot-scope node.
* @param {ASTNode} node The slot-scope node to check.
* @param {TokenStore} tokenStore The TokenStore.
* @returns {Array} the extra tokens.
*/
function getExtraAccessParameterTokens (node, tokenStore) {
const valueNode = node.value
const result = []
const valueFirstToken = tokenStore.getFirstToken(valueNode)
const valueLastToken = tokenStore.getLastToken(valueNode)
const exprLastToken = isQuote(valueFirstToken) && isQuote(valueLastToken) && valueFirstToken.value === valueLastToken.value
? tokenStore.getTokenBefore(valueLastToken)
: valueLastToken
const idLastToken = tokenStore.getLastToken(valueNode.expression.id)
if (idLastToken !== exprLastToken) {
// e.g. `<div slot-scope="a, b">`
// ^^^ Invalid
result.push(...tokenStore.getTokensBetween(idLastToken, exprLastToken))
result.push(exprLastToken)
}
return result
}
// ------------------------------------------------------------------------------
// Rule Definition
// ------------------------------------------------------------------------------
module.exports = {
meta: {
docs: {
description: 'enforce valid `slot-scope` attributes',
category: undefined,
url: 'https://github.com/vuejs/eslint-plugin-vue/blob/v5.0.0-beta.4/docs/rules/valid-slot-scope.md'
},
fixable: null,
schema: [],
messages: {
expectedValue: "'{{attrName}}' attributes require a value.",
unexpectedRestParameter: 'The top level rest parameter is useless.',
unexpectedExtraAccessParams: 'Unexpected extra access parameters `{{value}}`.',
unexpectedTrailingComma: 'Unexpected trailing comma.'
}
},
create (context) {
const tokenStore =
context.parserServices.getTemplateBodyTokenStore &&
context.parserServices.getTemplateBodyTokenStore()
return utils.defineTemplateBodyVisitor(context, {
'VAttribute[directive=true][key.name=/^(slot-)?scope$/]' (node) {
if (!utils.hasAttributeValue(node)) {
context.report({
node,
loc: node.loc,
messageId: 'expectedValue',
data: { attrName: node.key.name }
})
return
}
const idNode = node.value.expression.id
if (idNode.type === 'RestElement') {
// e.g. `<div slot-scope="...a">`
context.report({
node: idNode,
loc: idNode.loc,
messageId: 'unexpectedRestParameter'
})
}
const extraAccessParameterTokens = getExtraAccessParameterTokens(node, tokenStore)
if (extraAccessParameterTokens.length) {
const startToken = extraAccessParameterTokens[0]
if (extraAccessParameterTokens.length === 1 && isComma(startToken)) {
context.report({
node: startToken,
loc: startToken.loc,
messageId: 'unexpectedTrailingComma'
})
} else {
const endToken = extraAccessParameterTokens[extraAccessParameterTokens.length - 1]
const value = context.getSourceCode().text.slice(
extraAccessParameterTokens.length > 1 && isComma(startToken)
? extraAccessParameterTokens[1].range[0]
: startToken.range[0],
endToken.range[1]
)
context.report({
loc: {
start: startToken.loc.start,
end: endToken.loc.end
},
messageId: 'unexpectedExtraAccessParams',
data: { value }
})
}
}
}
})
}
}