Skip to content

Commit 9abf469

Browse files
authored
Add support for imported types in SFC macros (#2134)
1 parent 4c5fe34 commit 9abf469

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+1882
-466
lines changed

Diff for: docs/.vitepress/config.ts

+10-8
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ const sidebarCategories = [
7373
}
7474
]
7575

76-
const categorizedRules: DefaultTheme.SidebarGroup[] = []
76+
const categorizedRules: DefaultTheme.SidebarItem[] = []
7777
for (const { title, categoryIds } of sidebarCategories) {
7878
const categoryRules = rules
7979
.filter((rule) => rule.meta.docs.categories && !rule.meta.deprecated)
@@ -84,8 +84,10 @@ for (const { title, categoryIds } of sidebarCategories) {
8484
)
8585
const children: DefaultTheme.SidebarItem[] = categoryRules
8686
.filter(({ ruleId }) => {
87-
const exists = categorizedRules.some(({ items }) =>
88-
items.some(({ text: alreadyRuleId }) => alreadyRuleId === ruleId)
87+
const exists = categorizedRules.some(
88+
({ items }) =>
89+
items &&
90+
items.some(({ text: alreadyRuleId }) => alreadyRuleId === ruleId)
8991
)
9092
return !exists
9193
})
@@ -101,16 +103,16 @@ for (const { title, categoryIds } of sidebarCategories) {
101103
}
102104
categorizedRules.push({
103105
text: title,
104-
collapsible: false,
106+
collapsed: false,
105107
items: children
106108
})
107109
}
108110

109-
const extraCategories: DefaultTheme.SidebarGroup[] = []
111+
const extraCategories: DefaultTheme.SidebarItem[] = []
110112
if (uncategorizedRules.length > 0) {
111113
extraCategories.push({
112114
text: 'Uncategorized',
113-
collapsible: false,
115+
collapsed: false,
114116
items: uncategorizedRules.map(({ ruleId, name }) => ({
115117
text: ruleId,
116118
link: `/rules/${name}`
@@ -120,7 +122,7 @@ if (uncategorizedRules.length > 0) {
120122
if (uncategorizedExtensionRule.length > 0) {
121123
extraCategories.push({
122124
text: 'Extension Rules',
123-
collapsible: false,
125+
collapsed: false,
124126
items: uncategorizedExtensionRule.map(({ ruleId, name }) => ({
125127
text: ruleId,
126128
link: `/rules/${name}`
@@ -130,7 +132,7 @@ if (uncategorizedExtensionRule.length > 0) {
130132
if (deprecatedRules.length > 0) {
131133
extraCategories.push({
132134
text: 'Deprecated',
133-
collapsible: false,
135+
collapsed: false,
134136
items: deprecatedRules.map(({ ruleId, name }) => ({
135137
text: ruleId,
136138
link: `/rules/${name}`

Diff for: lib/rules/no-restricted-props.js

+9-6
Original file line numberDiff line numberDiff line change
@@ -109,14 +109,17 @@ module.exports = {
109109
option.message ||
110110
`Using \`${prop.propName}\` props is not allowed.`
111111
context.report({
112-
node: prop.key,
112+
node: prop.type !== 'infer-type' ? prop.key : prop.node,
113113
messageId: 'restrictedProp',
114114
data: { message },
115-
suggest: createSuggest(
116-
prop.key,
117-
option,
118-
withDefaultsProps && withDefaultsProps[prop.propName]
119-
)
115+
suggest:
116+
prop.type !== 'infer-type'
117+
? createSuggest(
118+
prop.key,
119+
option,
120+
withDefaultsProps && withDefaultsProps[prop.propName]
121+
)
122+
: null
120123
})
121124
break
122125
}

Diff for: lib/rules/no-unused-properties.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ const {
3030
* @typedef {object} ComponentNonObjectPropertyData
3131
* @property {string} name
3232
* @property {GroupName} groupName
33-
* @property {'array' | 'type'} type
33+
* @property {'array' | 'type' | 'infer-type'} type
3434
* @property {ASTNode} node
3535
*
3636
* @typedef { ComponentNonObjectPropertyData | ComponentObjectPropertyData } ComponentPropertyData
@@ -423,7 +423,7 @@ module.exports = {
423423
type: prop.type,
424424
name: prop.propName,
425425
groupName: 'props',
426-
node: prop.key
426+
node: prop.type !== 'infer-type' ? prop.key : prop.node
427427
})
428428
}
429429
}

Diff for: lib/rules/padding-lines-in-component-definition.js

+19-13
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
/**
88
* @typedef {import('../utils').ComponentProp} ComponentProp
9+
* @typedef {import('../utils').ComponentEmit} ComponentEmit
910
* @typedef {import('../utils').GroupName} GroupName
1011
*/
1112

@@ -33,10 +34,19 @@ function isComma(node) {
3334
}
3435

3536
/**
36-
* @param {string} nodeType
37+
* @typedef {Exclude<ComponentProp | ComponentEmit, {type:'infer-type'}> & { node: {type: 'Property' | 'SpreadElement'} }} ValidComponentPropOrEmit
3738
*/
38-
function isValidProperties(nodeType) {
39-
return ['Property', 'SpreadElement'].includes(nodeType)
39+
/**
40+
* @template {ComponentProp | ComponentEmit} T
41+
* @param {T} propOrEmit
42+
* @returns {propOrEmit is ValidComponentPropOrEmit & T}
43+
*/
44+
function isValidProperties(propOrEmit) {
45+
return Boolean(
46+
propOrEmit.type !== 'infer-type' &&
47+
propOrEmit.node &&
48+
['Property', 'SpreadElement'].includes(propOrEmit.node.type)
49+
)
4050
}
4151

4252
/**
@@ -320,11 +330,9 @@ module.exports = {
320330
}),
321331
utils.defineScriptSetupVisitor(context, {
322332
onDefinePropsEnter(_, props) {
323-
const propNodes = /** @type {(Property | SpreadElement)[]} */ (
324-
props
325-
.filter((prop) => prop.node && isValidProperties(prop.node.type))
326-
.map((prop) => prop.node)
327-
)
333+
const propNodes = props
334+
.filter(isValidProperties)
335+
.map((prop) => prop.node)
328336

329337
const withinOption = parseOption(options, OptionKeys.WithinOption)
330338
const propsOption = withinOption && parseOption(withinOption, 'props')
@@ -337,11 +345,9 @@ module.exports = {
337345
)
338346
},
339347
onDefineEmitsEnter(_, emits) {
340-
const emitNodes = /** @type {(Property | SpreadElement)[]} */ (
341-
emits
342-
.filter((emit) => emit.node && isValidProperties(emit.node.type))
343-
.map((emit) => emit.node)
344-
)
348+
const emitNodes = emits
349+
.filter(isValidProperties)
350+
.map((emit) => emit.node)
345351

346352
const withinOption = parseOption(options, OptionKeys.WithinOption)
347353
const emitsOption = withinOption && parseOption(withinOption, 'emits')

Diff for: lib/rules/prefer-prop-type-boolean-first.js

+3
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,9 @@ module.exports = {
6767
* @param {import('../utils').ComponentProp} prop
6868
*/
6969
function checkProperty(prop) {
70+
if (prop.type !== 'object') {
71+
return
72+
}
7073
const { value } = prop
7174
if (!value) {
7275
return

Diff for: lib/rules/require-emit-validator.js

+14-9
Original file line numberDiff line numberDiff line change
@@ -34,32 +34,37 @@ module.exports = {
3434
* @param {ComponentEmit} emit
3535
*/
3636
function checker(emit) {
37-
if (emit.type !== 'object' && emit.type !== 'array') {
38-
return
39-
}
40-
const { value, node, emitName } = emit
41-
const hasType =
42-
!!value &&
43-
(value.type === 'ArrowFunctionExpression' ||
37+
/** @type {Expression|null} */
38+
let value = null
39+
let hasType = false
40+
if (emit.type === 'object') {
41+
value = emit.value
42+
hasType =
43+
value.type === 'ArrowFunctionExpression' ||
4444
value.type === 'FunctionExpression' ||
4545
// validator may from outer scope
46-
value.type === 'Identifier')
46+
value.type === 'Identifier'
47+
} else if (emit.type !== 'array') {
48+
return
49+
}
4750

4851
if (!hasType) {
52+
const { node, emitName } = emit
4953
const name =
5054
emitName ||
5155
(node.type === 'Identifier' && node.name) ||
5256
'Unknown emit'
5357

5458
if (value && value.type === 'Literal' && value.value === null) {
59+
const valueNode = value
5560
context.report({
5661
node,
5762
messageId: 'skipped',
5863
data: { name },
5964
suggest: [
6065
{
6166
messageId: 'emptyValidation',
62-
fix: (fixer) => fixer.replaceText(value, '() => true')
67+
fix: (fixer) => fixer.replaceText(valueNode, '() => true')
6368
}
6469
]
6570
})

Diff for: lib/rules/require-explicit-emits.js

+4-1
Original file line numberDiff line numberDiff line change
@@ -429,7 +429,10 @@ module.exports = {
429429
function buildSuggest(define, emits, nameWithLoc, context) {
430430
const emitsKind =
431431
define.type === 'ObjectExpression' ? '`emits` option' : '`defineEmits`'
432-
const certainEmits = emits.filter((e) => e.key)
432+
const certainEmits = emits.filter(
433+
/** @returns {e is ComponentEmit & {type:'array'|'object'}} */
434+
(e) => e.type === 'array' || e.type === 'object'
435+
)
433436
if (certainEmits.length > 0) {
434437
const last = certainEmits[certainEmits.length - 1]
435438
return [

Diff for: lib/rules/require-prop-comment.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ module.exports = {
6262
*/
6363
function verifyProps(props) {
6464
for (const prop of props) {
65-
if (!prop.propName) {
65+
if (!prop.propName || prop.type === 'infer-type') {
6666
continue
6767
}
6868

Diff for: lib/rules/require-prop-type-constructor.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ module.exports = {
7979
/** @param {ComponentProp[]} props */
8080
function verifyProps(props) {
8181
for (const prop of props) {
82-
if (!prop.value || prop.propName == null) {
82+
if (prop.type !== 'object' || prop.propName == null) {
8383
continue
8484
}
8585
if (

Diff for: lib/rules/require-prop-types.js

+3-2
Original file line numberDiff line numberDiff line change
@@ -51,12 +51,12 @@ module.exports = {
5151
if (prop.type !== 'object' && prop.type !== 'array') {
5252
return
5353
}
54-
const { value, node, propName } = prop
5554
let hasType = true
5655

57-
if (!value) {
56+
if (prop.type === 'array') {
5857
hasType = false
5958
} else {
59+
const { value } = prop
6060
switch (value.type) {
6161
case 'ObjectExpression': {
6262
// foo: {
@@ -77,6 +77,7 @@ module.exports = {
7777
}
7878

7979
if (!hasType) {
80+
const { node, propName } = prop
8081
const name =
8182
propName ||
8283
(node.type === 'Identifier' && node.name) ||

Diff for: lib/rules/require-valid-default-prop.js

+8-6
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,11 @@ const utils = require('../utils')
77
const { capitalize } = require('../utils/casing')
88

99
/**
10+
* @typedef {import('../utils').ComponentProp} ComponentProp
1011
* @typedef {import('../utils').ComponentObjectProp} ComponentObjectProp
1112
* @typedef {import('../utils').ComponentArrayProp} ComponentArrayProp
1213
* @typedef {import('../utils').ComponentTypeProp} ComponentTypeProp
14+
* @typedef {import('../utils').ComponentInferTypeProp} ComponentInferTypeProp
1315
* @typedef {import('../utils').ComponentUnknownProp} ComponentUnknownProp
1416
* @typedef {import('../utils').VueObjectData} VueObjectData
1517
*/
@@ -108,7 +110,7 @@ module.exports = {
108110
*/
109111
/**
110112
* @typedef {object} PropDefaultFunctionContext
111-
* @property {ComponentObjectProp | ComponentTypeProp} prop
113+
* @property {ComponentObjectProp | ComponentTypeProp | ComponentInferTypeProp} prop
112114
* @property {Set<string>} types
113115
* @property {FunctionValueType} default
114116
*/
@@ -225,7 +227,7 @@ module.exports = {
225227

226228
/**
227229
* @param {*} node
228-
* @param {ComponentObjectProp | ComponentTypeProp} prop
230+
* @param {ComponentObjectProp | ComponentTypeProp | ComponentInferTypeProp} prop
229231
* @param {Iterable<string>} expectedTypeNames
230232
*/
231233
function report(node, prop, expectedTypeNames) {
@@ -245,7 +247,7 @@ module.exports = {
245247
}
246248

247249
/**
248-
* @param {(ComponentObjectDefineProp | ComponentTypeProp)[]} props
250+
* @param {(ComponentObjectDefineProp | ComponentTypeProp | ComponentInferTypeProp)[]} props
249251
* @param { { [key: string]: Expression | undefined } } withDefaults
250252
*/
251253
function processPropDefs(props, withDefaults) {
@@ -394,15 +396,15 @@ module.exports = {
394396
}),
395397
utils.defineScriptSetupVisitor(context, {
396398
onDefinePropsEnter(node, baseProps) {
397-
/** @type {(ComponentObjectDefineProp | ComponentTypeProp)[]} */
398399
const props = baseProps.filter(
399400
/**
400-
* @param {ComponentObjectProp | ComponentArrayProp | ComponentTypeProp | ComponentUnknownProp} prop
401-
* @returns {prop is ComponentObjectDefineProp | ComponentTypeProp}
401+
* @param {ComponentProp} prop
402+
* @returns {prop is ComponentObjectDefineProp | ComponentInferTypeProp | ComponentTypeProp}
402403
*/
403404
(prop) =>
404405
Boolean(
405406
prop.type === 'type' ||
407+
prop.type === 'infer-type' ||
406408
(prop.type === 'object' &&
407409
prop.value.type === 'ObjectExpression')
408410
)

Diff for: lib/rules/return-in-emits-validator.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ module.exports = {
6565
*/
6666
function processEmits(emits) {
6767
for (const emit of emits) {
68-
if (!emit.value) {
68+
if (emit.type !== 'object' || !emit.value) {
6969
continue
7070
}
7171
if (

Diff for: lib/utils/indent-ts.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ const {
1212
isClosingBracketToken,
1313
isOpeningBracketToken
1414
} = require('@eslint-community/eslint-utils')
15-
const { isTypeNode } = require('./ts-ast-utils')
15+
const { isTypeNode } = require('./ts-utils')
1616

1717
/**
1818
* @typedef {import('../../typings/eslint-plugin-vue/util-types/indent-helper').TSNodeListener} TSNodeListener
@@ -227,7 +227,7 @@ function defineVisitor({
227227
processSemicolons(node)
228228
},
229229
/**
230-
* @param {TSESTreeNode} node
230+
* @param {ASTNode} node
231231
*/
232232
// eslint-disable-next-line complexity -- ignore
233233
'*[type=/^TS/]'(node) {

0 commit comments

Comments
 (0)