-
-
Notifications
You must be signed in to change notification settings - Fork 681
/
Copy pathrequire-name-property.js
151 lines (139 loc) · 4.26 KB
/
require-name-property.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
138
139
140
141
142
143
144
145
146
147
148
149
150
151
/**
* @fileoverview Require a name property in Vue components
* @author LukeeeeBennett
*/
'use strict'
const path = require('path')
const utils = require('../utils')
const { getVueComponentDefinitionType } = require('../utils')
/**
* Get the text of the empty indentation part of the line which the given token is on.
* @param {SourceCode} sourceCode the source code object
* @param {Token} token the token to get the indentation text of the line which the token is on
* @returns {string} The text of indentation part.
*/
function getLineEmptyIndent(sourceCode, token) {
const LT_CHAR = /[\n\r\u2028\u2029]/
const EMPTY_CHAR = /\s/
const text = sourceCode.text
let i = token.range[0] - 1
while (i >= 0 && !LT_CHAR.test(text[i])) {
i -= 1
}
let j = i
while (EMPTY_CHAR.test(text[j])) {
j += 1
}
return text.slice(i + 1, j)
}
/**
* @param {Property | SpreadElement} node
* @returns {node is ObjectExpressionProperty}
*/
function isNameProperty(node) {
return (
node.type === 'Property' &&
utils.getStaticPropertyName(node) === 'name' &&
!node.computed
)
}
/**
* Report error if there has no name property
* @param {RuleContext} context
* @param {CallExpression | ObjectExpression} node
* @param {ObjectExpression} expression
*/
function report(context, node, expression) {
context.report({
node,
messageId: 'missingName',
suggest: [
{
messageId: 'addName',
fix(fixer) {
const extension = path.extname(context.getFilename())
const filename = path.basename(context.getFilename(), extension)
const sourceCode = context.getSourceCode()
if (expression.properties.length > 0) {
const firstToken = sourceCode.getFirstToken(
expression.properties[0]
)
const indentText = getLineEmptyIndent(sourceCode, firstToken)
// insert name property before the first property
return fixer.insertTextBefore(
expression.properties[0],
`name: '${filename}',\n${indentText}`
)
}
const firstToken = sourceCode.getFirstToken(expression)
const lastToken = sourceCode.getLastToken(expression)
// if the component is empty, insert name property and indent
if (firstToken.value === '{' && lastToken.value === '}') {
const indentText = getLineEmptyIndent(sourceCode, firstToken)
return fixer.replaceTextRange(
[firstToken.range[1], lastToken.range[0]],
`\n${indentText} name: '${filename}'\n${indentText}`
)
}
return null
}
}
]
})
}
module.exports = {
meta: {
type: 'suggestion',
docs: {
description: 'require a name property in Vue components',
categories: undefined,
url: 'https://eslint.vuejs.org/rules/require-name-property.html'
},
fixable: null,
hasSuggestions: true,
schema: [
{
type: 'object',
properties: {
checkScriptSetup: {
type: 'boolean'
}
},
additionalProperties: false
}
],
messages: {
missingName: 'Required name property is not set.',
addName: 'Add name property to component.'
}
},
/** @param {RuleContext} context */
create(context) {
const option = context.options[1] || {}
const checkScriptSetup = !!option.checkScriptSetup
return utils.compositingVisitors(
checkScriptSetup
? utils.defineScriptSetupVisitor(context, {
onDefineOptionsEnter(node) {
if (node.arguments.length === 0) return
const define = node.arguments[0]
if (define.type !== 'ObjectExpression') return
const nameNode = utils.findProperty(define, 'name')
if (nameNode) return
report(context, node, define)
}
})
: {},
utils.executeOnVue(context, (component, type) => {
if (type === 'definition') {
const defType = getVueComponentDefinitionType(component)
if (defType === 'mixin') {
return
}
}
if (component.properties.some(isNameProperty)) return
report(context, component, component)
})
)
}
}