Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for imported types in SFC macros #2134

Merged
merged 12 commits into from
May 15, 2023
18 changes: 10 additions & 8 deletions docs/.vitepress/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ const sidebarCategories = [
}
]

const categorizedRules: DefaultTheme.SidebarGroup[] = []
const categorizedRules: DefaultTheme.SidebarItem[] = []
for (const { title, categoryIds } of sidebarCategories) {
const categoryRules = rules
.filter((rule) => rule.meta.docs.categories && !rule.meta.deprecated)
Expand All @@ -84,8 +84,10 @@ for (const { title, categoryIds } of sidebarCategories) {
)
const children: DefaultTheme.SidebarItem[] = categoryRules
.filter(({ ruleId }) => {
const exists = categorizedRules.some(({ items }) =>
items.some(({ text: alreadyRuleId }) => alreadyRuleId === ruleId)
const exists = categorizedRules.some(
({ items }) =>
items &&
items.some(({ text: alreadyRuleId }) => alreadyRuleId === ruleId)
)
return !exists
})
Expand All @@ -101,16 +103,16 @@ for (const { title, categoryIds } of sidebarCategories) {
}
categorizedRules.push({
text: title,
collapsible: false,
collapsed: false,
items: children
})
}

const extraCategories: DefaultTheme.SidebarGroup[] = []
const extraCategories: DefaultTheme.SidebarItem[] = []
if (uncategorizedRules.length > 0) {
extraCategories.push({
text: 'Uncategorized',
collapsible: false,
collapsed: false,
items: uncategorizedRules.map(({ ruleId, name }) => ({
text: ruleId,
link: `/rules/${name}`
Expand All @@ -120,7 +122,7 @@ if (uncategorizedRules.length > 0) {
if (uncategorizedExtensionRule.length > 0) {
extraCategories.push({
text: 'Extension Rules',
collapsible: false,
collapsed: false,
items: uncategorizedExtensionRule.map(({ ruleId, name }) => ({
text: ruleId,
link: `/rules/${name}`
Expand All @@ -130,7 +132,7 @@ if (uncategorizedExtensionRule.length > 0) {
if (deprecatedRules.length > 0) {
extraCategories.push({
text: 'Deprecated',
collapsible: false,
collapsed: false,
items: deprecatedRules.map(({ ruleId, name }) => ({
text: ruleId,
link: `/rules/${name}`
Expand Down
15 changes: 9 additions & 6 deletions lib/rules/no-restricted-props.js
Original file line number Diff line number Diff line change
Expand Up @@ -109,14 +109,17 @@ module.exports = {
option.message ||
`Using \`${prop.propName}\` props is not allowed.`
context.report({
node: prop.key,
node: prop.type !== 'infer-type' ? prop.key : prop.node,
messageId: 'restrictedProp',
data: { message },
suggest: createSuggest(
prop.key,
option,
withDefaultsProps && withDefaultsProps[prop.propName]
)
suggest:
prop.type !== 'infer-type'
? createSuggest(
prop.key,
option,
withDefaultsProps && withDefaultsProps[prop.propName]
)
: null
})
break
}
Expand Down
4 changes: 2 additions & 2 deletions lib/rules/no-unused-properties.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ const {
* @typedef {object} ComponentNonObjectPropertyData
* @property {string} name
* @property {GroupName} groupName
* @property {'array' | 'type'} type
* @property {'array' | 'type' | 'infer-type'} type
* @property {ASTNode} node
*
* @typedef { ComponentNonObjectPropertyData | ComponentObjectPropertyData } ComponentPropertyData
Expand Down Expand Up @@ -423,7 +423,7 @@ module.exports = {
type: prop.type,
name: prop.propName,
groupName: 'props',
node: prop.key
node: prop.type !== 'infer-type' ? prop.key : prop.node
})
}
}
Expand Down
32 changes: 19 additions & 13 deletions lib/rules/padding-lines-in-component-definition.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

/**
* @typedef {import('../utils').ComponentProp} ComponentProp
* @typedef {import('../utils').ComponentEmit} ComponentEmit
* @typedef {import('../utils').GroupName} GroupName
*/

Expand Down Expand Up @@ -33,10 +34,19 @@ function isComma(node) {
}

/**
* @param {string} nodeType
* @typedef {Exclude<ComponentProp | ComponentEmit, {type:'infer-type'}> & { node: {type: 'Property' | 'SpreadElement'} }} ValidComponentPropOrEmit
*/
function isValidProperties(nodeType) {
return ['Property', 'SpreadElement'].includes(nodeType)
/**
* @template {ComponentProp | ComponentEmit} T
* @param {T} propOrEmit
* @returns {propOrEmit is ValidComponentPropOrEmit & T}
*/
function isValidProperties(propOrEmit) {
return Boolean(
propOrEmit.type !== 'infer-type' &&
propOrEmit.node &&
['Property', 'SpreadElement'].includes(propOrEmit.node.type)
)
}

/**
Expand Down Expand Up @@ -320,11 +330,9 @@ module.exports = {
}),
utils.defineScriptSetupVisitor(context, {
onDefinePropsEnter(_, props) {
const propNodes = /** @type {(Property | SpreadElement)[]} */ (
props
.filter((prop) => prop.node && isValidProperties(prop.node.type))
.map((prop) => prop.node)
)
const propNodes = props
.filter(isValidProperties)
.map((prop) => prop.node)

const withinOption = parseOption(options, OptionKeys.WithinOption)
const propsOption = withinOption && parseOption(withinOption, 'props')
Expand All @@ -337,11 +345,9 @@ module.exports = {
)
},
onDefineEmitsEnter(_, emits) {
const emitNodes = /** @type {(Property | SpreadElement)[]} */ (
emits
.filter((emit) => emit.node && isValidProperties(emit.node.type))
.map((emit) => emit.node)
)
const emitNodes = emits
.filter(isValidProperties)
.map((emit) => emit.node)

const withinOption = parseOption(options, OptionKeys.WithinOption)
const emitsOption = withinOption && parseOption(withinOption, 'emits')
Expand Down
3 changes: 3 additions & 0 deletions lib/rules/prefer-prop-type-boolean-first.js
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,9 @@ module.exports = {
* @param {import('../utils').ComponentProp} prop
*/
function checkProperty(prop) {
if (prop.type !== 'object') {
return
}
const { value } = prop
if (!value) {
return
Expand Down
23 changes: 14 additions & 9 deletions lib/rules/require-emit-validator.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,32 +34,37 @@ module.exports = {
* @param {ComponentEmit} emit
*/
function checker(emit) {
if (emit.type !== 'object' && emit.type !== 'array') {
return
}
const { value, node, emitName } = emit
const hasType =
!!value &&
(value.type === 'ArrowFunctionExpression' ||
/** @type {Expression|null} */
let value = null
let hasType = false
if (emit.type === 'object') {
value = emit.value
hasType =
value.type === 'ArrowFunctionExpression' ||
value.type === 'FunctionExpression' ||
// validator may from outer scope
value.type === 'Identifier')
value.type === 'Identifier'
} else if (emit.type !== 'array') {
return
}

if (!hasType) {
const { node, emitName } = emit
const name =
emitName ||
(node.type === 'Identifier' && node.name) ||
'Unknown emit'

if (value && value.type === 'Literal' && value.value === null) {
const valueNode = value
context.report({
node,
messageId: 'skipped',
data: { name },
suggest: [
{
messageId: 'emptyValidation',
fix: (fixer) => fixer.replaceText(value, '() => true')
fix: (fixer) => fixer.replaceText(valueNode, '() => true')
}
]
})
Expand Down
5 changes: 4 additions & 1 deletion lib/rules/require-explicit-emits.js
Original file line number Diff line number Diff line change
Expand Up @@ -429,7 +429,10 @@ module.exports = {
function buildSuggest(define, emits, nameWithLoc, context) {
const emitsKind =
define.type === 'ObjectExpression' ? '`emits` option' : '`defineEmits`'
const certainEmits = emits.filter((e) => e.key)
const certainEmits = emits.filter(
/** @returns {e is ComponentEmit & {type:'array'|'object'}} */
(e) => e.type === 'array' || e.type === 'object'
)
if (certainEmits.length > 0) {
const last = certainEmits[certainEmits.length - 1]
return [
Expand Down
2 changes: 1 addition & 1 deletion lib/rules/require-prop-comment.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ module.exports = {
*/
function verifyProps(props) {
for (const prop of props) {
if (!prop.propName) {
if (!prop.propName || prop.type === 'infer-type') {
continue
}

Expand Down
2 changes: 1 addition & 1 deletion lib/rules/require-prop-type-constructor.js
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ module.exports = {
/** @param {ComponentProp[]} props */
function verifyProps(props) {
for (const prop of props) {
if (!prop.value || prop.propName == null) {
if (prop.type !== 'object' || prop.propName == null) {
continue
}
if (
Expand Down
5 changes: 3 additions & 2 deletions lib/rules/require-prop-types.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,12 +51,12 @@ module.exports = {
if (prop.type !== 'object' && prop.type !== 'array') {
return
}
const { value, node, propName } = prop
let hasType = true

if (!value) {
if (prop.type === 'array') {
hasType = false
} else {
const { value } = prop
switch (value.type) {
case 'ObjectExpression': {
// foo: {
Expand All @@ -77,6 +77,7 @@ module.exports = {
}

if (!hasType) {
const { node, propName } = prop
const name =
propName ||
(node.type === 'Identifier' && node.name) ||
Expand Down
14 changes: 8 additions & 6 deletions lib/rules/require-valid-default-prop.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,11 @@ const utils = require('../utils')
const { capitalize } = require('../utils/casing')

/**
* @typedef {import('../utils').ComponentProp} ComponentProp
* @typedef {import('../utils').ComponentObjectProp} ComponentObjectProp
* @typedef {import('../utils').ComponentArrayProp} ComponentArrayProp
* @typedef {import('../utils').ComponentTypeProp} ComponentTypeProp
* @typedef {import('../utils').ComponentInferTypeProp} ComponentInferTypeProp
* @typedef {import('../utils').ComponentUnknownProp} ComponentUnknownProp
* @typedef {import('../utils').VueObjectData} VueObjectData
*/
Expand Down Expand Up @@ -108,7 +110,7 @@ module.exports = {
*/
/**
* @typedef {object} PropDefaultFunctionContext
* @property {ComponentObjectProp | ComponentTypeProp} prop
* @property {ComponentObjectProp | ComponentTypeProp | ComponentInferTypeProp} prop
* @property {Set<string>} types
* @property {FunctionValueType} default
*/
Expand Down Expand Up @@ -225,7 +227,7 @@ module.exports = {

/**
* @param {*} node
* @param {ComponentObjectProp | ComponentTypeProp} prop
* @param {ComponentObjectProp | ComponentTypeProp | ComponentInferTypeProp} prop
* @param {Iterable<string>} expectedTypeNames
*/
function report(node, prop, expectedTypeNames) {
Expand All @@ -245,7 +247,7 @@ module.exports = {
}

/**
* @param {(ComponentObjectDefineProp | ComponentTypeProp)[]} props
* @param {(ComponentObjectDefineProp | ComponentTypeProp | ComponentInferTypeProp)[]} props
* @param { { [key: string]: Expression | undefined } } withDefaults
*/
function processPropDefs(props, withDefaults) {
Expand Down Expand Up @@ -394,15 +396,15 @@ module.exports = {
}),
utils.defineScriptSetupVisitor(context, {
onDefinePropsEnter(node, baseProps) {
/** @type {(ComponentObjectDefineProp | ComponentTypeProp)[]} */
const props = baseProps.filter(
/**
* @param {ComponentObjectProp | ComponentArrayProp | ComponentTypeProp | ComponentUnknownProp} prop
* @returns {prop is ComponentObjectDefineProp | ComponentTypeProp}
* @param {ComponentProp} prop
* @returns {prop is ComponentObjectDefineProp | ComponentInferTypeProp | ComponentTypeProp}
*/
(prop) =>
Boolean(
prop.type === 'type' ||
prop.type === 'infer-type' ||
(prop.type === 'object' &&
prop.value.type === 'ObjectExpression')
)
Expand Down
2 changes: 1 addition & 1 deletion lib/rules/return-in-emits-validator.js
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ module.exports = {
*/
function processEmits(emits) {
for (const emit of emits) {
if (!emit.value) {
if (emit.type !== 'object' || !emit.value) {
continue
}
if (
Expand Down
4 changes: 2 additions & 2 deletions lib/utils/indent-ts.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ const {
isClosingBracketToken,
isOpeningBracketToken
} = require('@eslint-community/eslint-utils')
const { isTypeNode } = require('./ts-ast-utils')
const { isTypeNode } = require('./ts-utils')

/**
* @typedef {import('../../typings/eslint-plugin-vue/util-types/indent-helper').TSNodeListener} TSNodeListener
Expand Down Expand Up @@ -227,7 +227,7 @@ function defineVisitor({
processSemicolons(node)
},
/**
* @param {TSESTreeNode} node
* @param {ASTNode} node
*/
// eslint-disable-next-line complexity -- ignore
'*[type=/^TS/]'(node) {
Expand Down