Skip to content

Commit 9161964

Browse files
rewrite force-types-on-object-props
1 parent 75fc825 commit 9161964

File tree

2 files changed

+379
-158
lines changed

2 files changed

+379
-158
lines changed

lib/rules/force-types-on-object-props.js

+109-65
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,11 @@
44
*/
55
'use strict'
66

7+
const utils = require('../utils')
8+
/**
9+
* @typedef {import('../utils').ComponentProp} ComponentProp
10+
*/
11+
712
// ------------------------------------------------------------------------------
813
// Helpers
914
// ------------------------------------------------------------------------------
@@ -29,6 +34,97 @@ const isLooksLike = (a, b) =>
2934
: isLooksLike(aVal, bVal)
3035
})
3136

37+
/**
38+
* @param {ComponentProp} property
39+
* @param {RuleContext} context
40+
*/
41+
const checkProperty = (property, context) => {
42+
if (!property.value) {
43+
return
44+
}
45+
46+
if (
47+
isLooksLike(property.value, { type: 'Identifier', name: 'Object' }) &&
48+
property.node.value.type !== 'TSAsExpression'
49+
) {
50+
context.report({
51+
node: property.node,
52+
message: 'Expected type annotation on object prop.'
53+
})
54+
}
55+
56+
if (
57+
property.type === 'object' &&
58+
property.value.type === 'ObjectExpression' &&
59+
property.node.value.type === 'ObjectExpression'
60+
) {
61+
const typePropert = property.node.value.properties.find(
62+
(prop) =>
63+
prop.type === 'Property' &&
64+
prop.key.type === 'Identifier' &&
65+
prop.key.name === 'type'
66+
)
67+
if (
68+
typePropert &&
69+
typePropert.type === 'Property' &&
70+
isLooksLike(typePropert.value, { type: 'Identifier', name: 'Object' })
71+
) {
72+
context.report({
73+
node: property.node,
74+
message: 'Expected type annotation on object prop.'
75+
})
76+
}
77+
}
78+
79+
if (property.node.value.type === 'ObjectExpression') {
80+
for (const prop of property.node.value.properties) {
81+
if (prop.type !== 'Property') {
82+
continue
83+
}
84+
if (prop.key.type !== 'Identifier' || prop.key.name !== 'type') {
85+
continue
86+
}
87+
if (prop.value.type !== 'TSAsExpression') {
88+
continue
89+
}
90+
91+
const { typeAnnotation } = prop.value
92+
if (
93+
['TSAnyKeyword', 'TSUnknownKeyword'].includes(typeAnnotation.type) ||
94+
!typeAnnotation.typeName ||
95+
!['Prop', 'PropType'].includes(typeAnnotation.typeName.name)
96+
) {
97+
context.report({
98+
node: property.node,
99+
message: 'Expected type annotation on object prop.'
100+
})
101+
}
102+
}
103+
}
104+
105+
if (property.node.value.type === 'TSAsExpression') {
106+
const { typeAnnotation } = property.node.value
107+
if (typeAnnotation.type === 'TSFunctionType') {
108+
return
109+
}
110+
if (
111+
[
112+
'TSAnyKeyword',
113+
'TSTypeLiteral',
114+
'TSUnknownKeyword',
115+
'TSObjectKeyword'
116+
].includes(typeAnnotation.type) ||
117+
!typeAnnotation.typeName ||
118+
!['Prop', 'PropType'].includes(typeAnnotation.typeName.name)
119+
) {
120+
context.report({
121+
node: property.node,
122+
message: 'Expected type annotation on object prop.'
123+
})
124+
}
125+
}
126+
}
127+
32128
//------------------------------------------------------------------------------
33129
// Rule Definition
34130
//------------------------------------------------------------------------------
@@ -47,73 +143,21 @@ module.exports = {
47143
},
48144
/** @param {RuleContext} context */
49145
create(context) {
50-
return {
51-
/** @param {ExportDefaultDeclaration} node */
52-
ExportDefaultDeclaration(node) {
53-
if (node.declaration.type !== 'ObjectExpression') {
54-
return
55-
}
56-
if (!Array.isArray(node.declaration.properties)) {
57-
return
146+
return utils.compositingVisitors(
147+
utils.defineScriptSetupVisitor(context, {
148+
onDefinePropsEnter(_node, props) {
149+
for (const prop of props) {
150+
checkProperty(prop, context)
151+
}
58152
}
153+
}),
154+
utils.executeOnVue(context, (obj) => {
155+
const props = utils.getComponentPropsFromOptions(obj)
59156

60-
const property = node.declaration.properties.find(
61-
(property) =>
62-
property.type === 'Property' &&
63-
isLooksLike(property.key, { type: 'Identifier', name: 'props' }) &&
64-
property.value.type === 'ObjectExpression'
65-
)
66-
67-
if (
68-
!property ||
69-
property.type === 'SpreadElement' ||
70-
!('properties' in property.value)
71-
) {
72-
return
73-
}
74-
const properties = property.value.properties
75-
.filter(
76-
(prop) =>
77-
prop.type === 'Property' && prop.value.type === 'ObjectExpression'
78-
)
79-
.map((prop) =>
80-
prop.value.properties.find((propValueProperty) =>
81-
isLooksLike(propValueProperty.key, {
82-
type: 'Identifier',
83-
name: 'type'
84-
})
85-
)
86-
)
87-
for (const prop of properties) {
88-
if (!prop) {
89-
continue
90-
}
91-
if (isLooksLike(prop.value, { type: 'Identifier', name: 'Object' })) {
92-
context.report({
93-
node: prop,
94-
message: 'Expected type annotation on object prop.'
95-
})
96-
}
97-
if (prop.value.type === 'TSAsExpression') {
98-
const { typeAnnotation } = prop.value
99-
if (
100-
[
101-
'TSAnyKeyword',
102-
'TSTypeLiteral',
103-
'TSUnknownKeyword',
104-
'TSObjectKeyword'
105-
].includes(typeAnnotation.type) ||
106-
!typeAnnotation.typeName ||
107-
!['Prop', 'PropType'].includes(typeAnnotation.typeName.name)
108-
) {
109-
context.report({
110-
node: prop,
111-
message: 'Expected type annotation on object prop.'
112-
})
113-
}
114-
}
157+
for (const prop of props) {
158+
checkProperty(prop, context)
115159
}
116-
}
117-
}
160+
})
161+
)
118162
}
119163
}

0 commit comments

Comments
 (0)