Skip to content

Commit a4656dc

Browse files
committed
feat: replace select selection slot with chip
1 parent 5ceb7c6 commit a4656dc

File tree

1 file changed

+169
-133
lines changed

1 file changed

+169
-133
lines changed

src/rules/no-deprecated-slots.js

Lines changed: 169 additions & 133 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,152 @@
11
'use strict'
22

3+
const { classify, getAttributes } = require('../util/helpers')
4+
5+
const groups = [
6+
{
7+
components: ['VDialog', 'VMenu', 'VTooltip'],
8+
slots: ['activator'],
9+
handler (context, node, directive, param) {
10+
if (param.type === 'Identifier') {
11+
// #activator="data"
12+
const boundVariables = {}
13+
node.variables.find(variable => variable.id.name === param.name)?.references.forEach(ref => {
14+
if (ref.id.parent.type !== 'MemberExpression') return
15+
if (
16+
// v-bind="data.props"
17+
ref.id.parent.property.name === 'props' &&
18+
ref.id.parent.parent.parent.directive &&
19+
ref.id.parent.parent.parent.key.name.name === 'bind' &&
20+
!ref.id.parent.parent.parent.key.argument
21+
) return
22+
if (ref.id.parent.property.name === 'on') {
23+
boundVariables.on = ref.id
24+
} else if (ref.id.parent.property.name === 'attrs') {
25+
boundVariables.attrs = ref.id
26+
}
27+
})
28+
if (boundVariables.on) {
29+
const ref = boundVariables.on
30+
context.report({
31+
node: ref,
32+
messageId: 'changedProps',
33+
data: {
34+
component: node.parent.name,
35+
slot: directive.key.argument.name,
36+
},
37+
fix (fixer) {
38+
return fixer.replaceText(ref.parent.parent.parent, `v-bind="${param.name}.props"`)
39+
},
40+
})
41+
}
42+
if (boundVariables.attrs) {
43+
const ref = boundVariables.attrs
44+
if (!boundVariables.on) {
45+
context.report({
46+
node: boundVariables.attrs,
47+
messageId: 'invalidProps',
48+
})
49+
} else {
50+
context.report({
51+
node: ref,
52+
messageId: 'changedProps',
53+
data: {
54+
component: node.parent.name,
55+
slot: directive.key.argument.name,
56+
},
57+
fix (fixer) {
58+
return fixer.removeRange([ref.parent.parent.parent.range[0] - 1, ref.parent.parent.parent.range[1]])
59+
},
60+
})
61+
}
62+
}
63+
} else if (param.type === 'ObjectPattern') {
64+
// #activator="{ on, attrs }"
65+
const boundVariables = {}
66+
param.properties.forEach(prop => {
67+
node.variables.find(variable => variable.id.name === prop.value.name)?.references.forEach(ref => {
68+
if (prop.key.name === 'on') {
69+
boundVariables.on = { prop, id: ref.id }
70+
} else if (prop.key.name === 'attrs') {
71+
boundVariables.attrs = { prop, id: ref.id }
72+
}
73+
})
74+
})
75+
if (boundVariables.on || boundVariables.attrs) {
76+
if (boundVariables.attrs && !boundVariables.on) {
77+
context.report({
78+
node: boundVariables.attrs.prop.key,
79+
messageId: 'invalidProps',
80+
})
81+
} else {
82+
context.report({
83+
node: param,
84+
messageId: 'changedProps',
85+
data: {
86+
component: node.parent.name,
87+
slot: directive.key.argument.name,
88+
},
89+
* fix (fixer) {
90+
if (boundVariables.on) {
91+
const ref = boundVariables.on
92+
yield fixer.replaceText(ref.prop, 'props')
93+
yield fixer.replaceText(ref.id.parent.parent, `v-bind="props"`)
94+
}
95+
if (boundVariables.attrs) {
96+
const template = context.parserServices.getTemplateBodyTokenStore()
97+
const ref = boundVariables.attrs
98+
const isLast = ref.prop === param.properties.at(-1)
99+
if (isLast) {
100+
const comma = template.getTokenBefore(ref.prop, { filter: token => token.value === ',' })
101+
if (comma) {
102+
yield fixer.removeRange([comma.start, ref.prop.end])
103+
} else {
104+
yield fixer.removeRange([ref.prop.start - 1, ref.prop.end])
105+
}
106+
} else {
107+
const comma = template.getTokenAfter(ref.prop, { filter: token => token.value === ',' })
108+
if (comma) {
109+
yield fixer.removeRange([ref.prop.start - 1, comma.end])
110+
} else {
111+
yield fixer.removeRange([ref.prop.start - 1, ref.prop.end])
112+
}
113+
}
114+
yield fixer.removeRange([ref.id.parent.parent.range[0] - 1, ref.id.parent.parent.range[1]])
115+
}
116+
},
117+
})
118+
}
119+
}
120+
} else {
121+
context.report({
122+
node: directive,
123+
messageId: 'invalidProps',
124+
})
125+
}
126+
},
127+
},
128+
{
129+
components: ['VSelect', 'VAutocomplete', 'VCombobox'],
130+
slots: ['selection'],
131+
handler (context, node, directive, param) {
132+
if (!getAttributes(node.parent).some(attr => ['chips', 'closable-chips'].includes(attr.name))) return
133+
134+
context.report({
135+
node: directive,
136+
messageId: 'renamed',
137+
data: {
138+
component: node.parent.name,
139+
slot: directive.key.argument.name,
140+
newSlot: 'chip',
141+
},
142+
fix (fixer) {
143+
return fixer.replaceText(directive.key.argument, 'chip')
144+
},
145+
})
146+
},
147+
},
148+
]
149+
3150
// ------------------------------------------------------------------------------
4151
// Rule Definition
5152
// ------------------------------------------------------------------------------
@@ -13,13 +160,13 @@ module.exports = {
13160
fixable: 'code',
14161
schema: [],
15162
messages: {
163+
renamed: `{{ component }}'s '{{ slot }}' slot has been renamed to '{{ newSlot }}'`,
16164
changedProps: `{{ component }}'s '{{ slot }}' slot has changed props`,
17165
invalidProps: `Slot has invalid props`,
18166
},
19167
},
20168

21169
create (context) {
22-
const template = context.parserServices.getTemplateBodyTokenStore()
23170
let scopeStack
24171

25172
return context.parserServices.defineTemplateBodyVisitor({
@@ -32,140 +179,29 @@ module.exports = {
32179
scopeStack.nodes.push(variable.id)
33180
}
34181

35-
if (node.name !== 'template') return
36-
if (!['v-dialog', 'v-menu', 'v-tooltip'].includes(node.parent.name)) return
182+
if (node.name !== 'template' || node.parent.type !== 'VElement') return
37183

38-
const directive = node.startTag.attributes.find(attr => {
39-
return (
40-
attr.directive &&
41-
attr.key.name.name === 'slot' &&
42-
attr.key.argument?.name === 'activator'
43-
)
44-
})
45-
46-
if (
47-
!directive ||
48-
!directive.value ||
49-
directive.value.type !== 'VExpressionContainer' ||
50-
!directive.value.expression ||
51-
directive.value.expression.params.length !== 1
52-
) return
53-
54-
const param = directive.value.expression.params[0]
55-
if (param.type === 'Identifier') {
56-
// #activator="data"
57-
const boundVariables = {}
58-
node.variables.find(variable => variable.id.name === param.name)?.references.forEach(ref => {
59-
if (ref.id.parent.type !== 'MemberExpression') return
60-
if (
61-
// v-bind="data.props"
62-
ref.id.parent.property.name === 'props' &&
63-
ref.id.parent.parent.parent.directive &&
64-
ref.id.parent.parent.parent.key.name.name === 'bind' &&
65-
!ref.id.parent.parent.parent.key.argument
66-
) return
67-
if (ref.id.parent.property.name === 'on') {
68-
boundVariables.on = ref.id
69-
} else if (ref.id.parent.property.name === 'attrs') {
70-
boundVariables.attrs = ref.id
71-
}
72-
})
73-
if (boundVariables.on) {
74-
const ref = boundVariables.on
75-
context.report({
76-
node: ref,
77-
messageId: 'changedProps',
78-
data: {
79-
component: node.parent.name,
80-
slot: directive.key.argument.name,
81-
},
82-
fix (fixer) {
83-
return fixer.replaceText(ref.parent.parent.parent, `v-bind="${param.name}.props"`)
84-
},
85-
})
86-
}
87-
if (boundVariables.attrs) {
88-
const ref = boundVariables.attrs
89-
if (!boundVariables.on) {
90-
context.report({
91-
node: boundVariables.attrs,
92-
messageId: 'invalidProps',
93-
})
94-
} else {
95-
context.report({
96-
node: ref,
97-
messageId: 'changedProps',
98-
data: {
99-
component: node.parent.name,
100-
slot: directive.key.argument.name,
101-
},
102-
fix (fixer) {
103-
return fixer.removeRange([ref.parent.parent.parent.range[0] - 1, ref.parent.parent.parent.range[1]])
104-
},
105-
})
106-
}
107-
}
108-
} else if (param.type === 'ObjectPattern') {
109-
// #activator="{ on, attrs }"
110-
const boundVariables = {}
111-
param.properties.forEach(prop => {
112-
node.variables.find(variable => variable.id.name === prop.value.name)?.references.forEach(ref => {
113-
if (prop.key.name === 'on') {
114-
boundVariables.on = { prop, id: ref.id }
115-
} else if (prop.key.name === 'attrs') {
116-
boundVariables.attrs = { prop, id: ref.id }
117-
}
118-
})
119-
})
120-
if (boundVariables.on || boundVariables.attrs) {
121-
if (boundVariables.attrs && !boundVariables.on) {
122-
context.report({
123-
node: boundVariables.attrs.prop.key,
124-
messageId: 'invalidProps',
125-
})
126-
} else {
127-
context.report({
128-
node: param,
129-
messageId: 'changedProps',
130-
data: {
131-
component: node.parent.name,
132-
slot: directive.key.argument.name,
133-
},
134-
* fix (fixer) {
135-
if (boundVariables.on) {
136-
const ref = boundVariables.on
137-
yield fixer.replaceText(ref.prop, 'props')
138-
yield fixer.replaceText(ref.id.parent.parent, `v-bind="props"`)
139-
}
140-
if (boundVariables.attrs) {
141-
const ref = boundVariables.attrs
142-
const isLast = ref.prop === param.properties.at(-1)
143-
if (isLast) {
144-
const comma = template.getTokenBefore(ref.prop, { filter: token => token.value === ',' })
145-
if (comma) {
146-
yield fixer.removeRange([comma.start, ref.prop.end])
147-
} else {
148-
yield fixer.removeRange([ref.prop.start - 1, ref.prop.end])
149-
}
150-
} else {
151-
const comma = template.getTokenAfter(ref.prop, { filter: token => token.value === ',' })
152-
if (comma) {
153-
yield fixer.removeRange([ref.prop.start - 1, comma.end])
154-
} else {
155-
yield fixer.removeRange([ref.prop.start - 1, ref.prop.end])
156-
}
157-
}
158-
yield fixer.removeRange([ref.id.parent.parent.range[0] - 1, ref.id.parent.parent.range[1]])
159-
}
160-
},
161-
})
162-
}
163-
}
164-
} else {
165-
context.report({
166-
node: directive,
167-
messageId: 'invalidProps',
184+
for (const group of groups) {
185+
if (
186+
!group.components.includes(classify(node.parent.name)) &&
187+
!group.components.includes(node.parent.name)
188+
) continue
189+
const directive = node.startTag.attributes.find(attr => {
190+
return (
191+
attr.directive &&
192+
attr.key.name.name === 'slot' &&
193+
group.slots.includes(attr.key.argument?.name)
194+
)
168195
})
196+
if (
197+
!directive ||
198+
!directive.value ||
199+
directive.value.type !== 'VExpressionContainer' ||
200+
!directive.value.expression ||
201+
directive.value.expression.params.length !== 1
202+
) continue
203+
const param = directive.value.expression.params[0]
204+
group.handler(context, node, directive, param)
169205
}
170206
},
171207
'VElement:exit' () {

0 commit comments

Comments
 (0)