@shrpne/eslint-plugin-vue-extra 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json
CHANGED
|
@@ -27,6 +27,125 @@ function isDefinePropsCall(node) {
|
|
|
27
27
|
);
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
+
function isDefineEmitsCall(node) {
|
|
31
|
+
return (
|
|
32
|
+
node &&
|
|
33
|
+
node.type === 'CallExpression' &&
|
|
34
|
+
node.callee &&
|
|
35
|
+
node.callee.type === 'Identifier' &&
|
|
36
|
+
node.callee.name === 'defineEmits'
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function getPropNameFromUpdateEventName(eventName) {
|
|
41
|
+
if (typeof eventName !== 'string') return null;
|
|
42
|
+
if (!eventName.startsWith('update:')) return null;
|
|
43
|
+
|
|
44
|
+
const propName = eventName.slice('update:'.length);
|
|
45
|
+
return propName || null;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function addModelPropNameFromEventName(set, eventName) {
|
|
49
|
+
const propName = getPropNameFromUpdateEventName(eventName);
|
|
50
|
+
if (propName) {
|
|
51
|
+
set.add(propName);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function collectModelPropNamesFromTypeNode(typeNode, modelPropNames) {
|
|
56
|
+
if (!typeNode || typeNode.type !== 'TSTypeLiteral') return;
|
|
57
|
+
|
|
58
|
+
for (const member of typeNode.members) {
|
|
59
|
+
if (member.type === 'TSCallSignatureDeclaration') {
|
|
60
|
+
const firstParam = member.params && member.params[0];
|
|
61
|
+
if (
|
|
62
|
+
firstParam &&
|
|
63
|
+
firstParam.type === 'Identifier' &&
|
|
64
|
+
firstParam.typeAnnotation &&
|
|
65
|
+
firstParam.typeAnnotation.typeAnnotation &&
|
|
66
|
+
firstParam.typeAnnotation.typeAnnotation.type === 'TSLiteralType' &&
|
|
67
|
+
firstParam.typeAnnotation.typeAnnotation.literal &&
|
|
68
|
+
firstParam.typeAnnotation.typeAnnotation.literal.type === 'Literal' &&
|
|
69
|
+
typeof firstParam.typeAnnotation.typeAnnotation.literal.value === 'string'
|
|
70
|
+
) {
|
|
71
|
+
addModelPropNameFromEventName(
|
|
72
|
+
modelPropNames,
|
|
73
|
+
firstParam.typeAnnotation.typeAnnotation.literal.value
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if (member.type === 'TSPropertySignature' && member.key) {
|
|
80
|
+
if (member.key.type === 'Literal' && typeof member.key.value === 'string') {
|
|
81
|
+
addModelPropNameFromEventName(modelPropNames, member.key.value);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function collectModelPropNamesFromDefineEmitsCall(node, modelPropNames) {
|
|
88
|
+
if (!isDefineEmitsCall(node)) return;
|
|
89
|
+
|
|
90
|
+
const firstArg = node.arguments && node.arguments[0];
|
|
91
|
+
if (firstArg && firstArg.type === 'ArrayExpression') {
|
|
92
|
+
for (const element of firstArg.elements) {
|
|
93
|
+
if (element && element.type === 'Literal' && typeof element.value === 'string') {
|
|
94
|
+
addModelPropNameFromEventName(modelPropNames, element.value);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if (firstArg && firstArg.type === 'ObjectExpression') {
|
|
100
|
+
for (const prop of firstArg.properties) {
|
|
101
|
+
if (prop.type !== 'Property' || prop.computed || !prop.key) continue;
|
|
102
|
+
if (prop.key.type === 'Literal' && typeof prop.key.value === 'string') {
|
|
103
|
+
addModelPropNameFromEventName(modelPropNames, prop.key.value);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const typeArgs = node.typeArguments;
|
|
109
|
+
if (typeArgs && typeArgs.params && typeArgs.params.length > 0) {
|
|
110
|
+
collectModelPropNamesFromTypeNode(typeArgs.params[0], modelPropNames);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function collectModelPropNamesFromAst(rootNode) {
|
|
115
|
+
const modelPropNames = new Set(['modelValue']);
|
|
116
|
+
const visited = new WeakSet();
|
|
117
|
+
|
|
118
|
+
function visit(node) {
|
|
119
|
+
if (!node || typeof node !== 'object') return;
|
|
120
|
+
if (visited.has(node)) return;
|
|
121
|
+
visited.add(node);
|
|
122
|
+
|
|
123
|
+
if (node.type === 'CallExpression') {
|
|
124
|
+
collectModelPropNamesFromDefineEmitsCall(node, modelPropNames);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
for (const value of Object.values(node)) {
|
|
128
|
+
if (!value) continue;
|
|
129
|
+
|
|
130
|
+
if (Array.isArray(value)) {
|
|
131
|
+
for (const item of value) {
|
|
132
|
+
if (item && typeof item.type === 'string') {
|
|
133
|
+
visit(item);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
continue;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
if (value && typeof value.type === 'string') {
|
|
140
|
+
visit(value);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
visit(rootNode);
|
|
146
|
+
return modelPropNames;
|
|
147
|
+
}
|
|
148
|
+
|
|
30
149
|
function isBooleanLikeType(type) {
|
|
31
150
|
if (!type) return false;
|
|
32
151
|
|
|
@@ -199,6 +318,7 @@ const preferOptionalBooleanProp = {
|
|
|
199
318
|
const services = context.sourceCode.parserServices;
|
|
200
319
|
const hasTypeInfo = !!(services && services.program && services.esTreeNodeToTSNodeMap);
|
|
201
320
|
const fixReferencedTypes = !!(context.options[0] && context.options[0].fixReferencedTypes);
|
|
321
|
+
const modelPropNames = collectModelPropNamesFromAst(context.sourceCode.ast);
|
|
202
322
|
|
|
203
323
|
const programTsNode = hasTypeInfo
|
|
204
324
|
? services.esTreeNodeToTSNodeMap.get(context.sourceCode.ast)
|
|
@@ -226,6 +346,7 @@ const preferOptionalBooleanProp = {
|
|
|
226
346
|
const booleanDefaultsMap = getBooleanDefaultsMap(secondArg);
|
|
227
347
|
|
|
228
348
|
for (const [name, metadata] of propMetadataMap.entries()) {
|
|
349
|
+
if (modelPropNames.has(name)) continue;
|
|
229
350
|
if (!metadata.isBooleanLike) continue;
|
|
230
351
|
if (metadata.isOptional) continue;
|
|
231
352
|
|