Skip to content

Commit c8d284b

Browse files
authored
Update vue/no-restricted-props rule to support <script setup> (#1552)
* Update `vue/no-restricted-props` rule to support `<script setup>` * fix * fix
1 parent b132191 commit c8d284b

File tree

3 files changed

+359
-39
lines changed

3 files changed

+359
-39
lines changed

lib/rules/no-restricted-props.js

+57-22
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,12 @@
77
const utils = require('../utils')
88
const regexp = require('../utils/regexp')
99

10+
/**
11+
* @typedef {import('../utils').ComponentArrayProp} ComponentArrayProp
12+
* @typedef {import('../utils').ComponentObjectProp} ComponentObjectProp
13+
* @typedef {import('../utils').ComponentTypeProp} ComponentTypeProp
14+
*/
15+
1016
/**
1117
* @typedef {object} ParsedOption
1218
* @property { (name: string) => boolean } test
@@ -88,39 +94,58 @@ module.exports = {
8894
/** @type {ParsedOption[]} */
8995
const options = context.options.map(parseOption)
9096

91-
return utils.defineVueVisitor(context, {
92-
onVueObjectEnter(node) {
93-
for (const prop of utils.getComponentProps(node)) {
94-
if (!prop.propName) {
95-
continue
96-
}
97+
/**
98+
* @param {(ComponentArrayProp | ComponentObjectProp | ComponentTypeProp)[]} props
99+
* @param { { [key: string]: Property | undefined } } [withDefaultsProps]
100+
*/
101+
function processProps(props, withDefaultsProps) {
102+
for (const prop of props) {
103+
if (!prop.propName) {
104+
continue
105+
}
97106

98-
for (const option of options) {
99-
if (option.test(prop.propName)) {
100-
const message =
101-
option.message ||
102-
`Using \`${prop.propName}\` props is not allowed.`
103-
context.report({
104-
node: prop.key,
105-
messageId: 'restrictedProp',
106-
data: { message },
107-
suggest: createSuggest(prop.key, option)
108-
})
109-
break
110-
}
107+
for (const option of options) {
108+
if (option.test(prop.propName)) {
109+
const message =
110+
option.message ||
111+
`Using \`${prop.propName}\` props is not allowed.`
112+
context.report({
113+
node: prop.key,
114+
messageId: 'restrictedProp',
115+
data: { message },
116+
suggest: createSuggest(
117+
prop.key,
118+
option,
119+
withDefaultsProps && withDefaultsProps[prop.propName]
120+
)
121+
})
122+
break
111123
}
112124
}
113125
}
114-
})
126+
}
127+
return utils.compositingVisitors(
128+
utils.defineScriptSetupVisitor(context, {
129+
onDefinePropsEnter(node, props) {
130+
processProps(props, utils.getWithDefaultsProps(node))
131+
}
132+
}),
133+
utils.defineVueVisitor(context, {
134+
onVueObjectEnter(node) {
135+
processProps(utils.getComponentProps(node))
136+
}
137+
})
138+
)
115139
}
116140
}
117141

118142
/**
119143
* @param {Expression} node
120144
* @param {ParsedOption} option
145+
* @param {Property} [withDefault]
121146
* @returns {Rule.SuggestionReportDescriptor[]}
122147
*/
123-
function createSuggest(node, option) {
148+
function createSuggest(node, option, withDefault) {
124149
if (!option.suggest) {
125150
return []
126151
}
@@ -140,7 +165,17 @@ function createSuggest(node, option) {
140165
return [
141166
{
142167
fix(fixer) {
143-
return fixer.replaceText(node, replaceText)
168+
const fixes = [fixer.replaceText(node, replaceText)]
169+
if (withDefault) {
170+
if (withDefault.shorthand) {
171+
fixes.push(
172+
fixer.insertTextBefore(withDefault.value, `${replaceText}:`)
173+
)
174+
} else {
175+
fixes.push(fixer.replaceText(withDefault.key, replaceText))
176+
}
177+
}
178+
return fixes.sort((a, b) => a.range[0] - b.range[0])
144179
},
145180
messageId: 'instead',
146181
data: { suggest: option.suggest }

lib/utils/index.js

+41-16
Original file line numberDiff line numberDiff line change
@@ -1152,29 +1152,24 @@ module.exports = {
11521152
* @returns { { [key: string]: Expression | undefined } }
11531153
*/
11541154
getWithDefaultsPropExpressions(node) {
1155-
if (!hasWithDefaults(node)) {
1156-
return {}
1157-
}
1158-
const param = node.parent.arguments[1]
1159-
if (!param || param.type !== 'ObjectExpression') {
1160-
return {}
1161-
}
1155+
const map = getWithDefaultsProps(node)
11621156

1163-
/** @type {Record<string, Expression>} */
1157+
/** @type {Record<string, Expression | undefined>} */
11641158
const result = {}
11651159

1166-
for (const prop of param.properties) {
1167-
if (prop.type !== 'Property') {
1168-
return {}
1169-
}
1170-
const name = getStaticPropertyName(prop)
1171-
if (name != null) {
1172-
result[name] = prop.value
1173-
}
1160+
for (const key of Object.keys(map)) {
1161+
const prop = map[key]
1162+
result[key] = prop && prop.value
11741163
}
11751164

11761165
return result
11771166
},
1167+
/**
1168+
* Gets a map of the property nodes defined in withDefaults.
1169+
* @param {CallExpression} node The node of defineProps
1170+
* @returns { { [key: string]: Property | undefined } }
1171+
*/
1172+
getWithDefaultsProps,
11781173

11791174
getVueObjectType,
11801175
/**
@@ -2400,6 +2395,36 @@ function hasWithDefaults(node) {
24002395
)
24012396
}
24022397

2398+
/**
2399+
* Gets a map of the property nodes defined in withDefaults.
2400+
* @param {CallExpression} node The node of defineProps
2401+
* @returns { { [key: string]: Property | undefined } }
2402+
*/
2403+
function getWithDefaultsProps(node) {
2404+
if (!hasWithDefaults(node)) {
2405+
return {}
2406+
}
2407+
const param = node.parent.arguments[1]
2408+
if (!param || param.type !== 'ObjectExpression') {
2409+
return {}
2410+
}
2411+
2412+
/** @type {Record<string, Property>} */
2413+
const result = {}
2414+
2415+
for (const prop of param.properties) {
2416+
if (prop.type !== 'Property') {
2417+
return {}
2418+
}
2419+
const name = getStaticPropertyName(prop)
2420+
if (name != null) {
2421+
result[name] = prop
2422+
}
2423+
}
2424+
2425+
return result
2426+
}
2427+
24032428
/**
24042429
* Get all props by looking at all component's properties
24052430
* @param {ObjectExpression|ArrayExpression} propsNode Object with props definition

0 commit comments

Comments
 (0)