-
Notifications
You must be signed in to change notification settings - Fork 5
/
no-escape-hatch.ts
101 lines (90 loc) · 3.32 KB
/
no-escape-hatch.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
import { isPandaAttribute, isPandaProp, isRecipeVariant } from '../utils/helpers'
import { type Rule, createRule } from '../utils'
import { getArbitraryValue } from '@pandacss/shared'
import { isIdentifier, isJSXExpressionContainer, isLiteral, isTemplateLiteral } from '../utils/nodes'
import { TSESTree } from '@typescript-eslint/utils'
export const RULE_NAME = 'no-escape-hatch'
const rule: Rule = createRule({
name: RULE_NAME,
meta: {
docs: {
description: 'Prohibit the use of escape hatch syntax in the code.',
},
messages: {
escapeHatch:
'Avoid using the escape hatch [value] for undefined tokens. Define a corresponding token in your design system for better consistency and maintainability.',
remove: 'Remove the square brackets (`[]`).',
},
type: 'problem',
hasSuggestions: true,
schema: [],
},
defaultOptions: [],
create(context) {
// Helper function to adjust the range for fixing (removing brackets)
const removeBrackets = (range: readonly [number, number]) => {
const [start, end] = range
return [start + 1, end - 1] as const
}
// Function to check if a value contains escape hatch syntax
const hasEscapeHatch = (value: string | undefined): boolean => {
if (!value) return false
// Early return if the value doesn't contain brackets
if (!value.includes('[')) return false
return getArbitraryValue(value) !== value.trim()
}
// Unified function to handle reporting
const handleNodeValue = (node: TSESTree.Node, value: string) => {
if (!hasEscapeHatch(value)) return
context.report({
node,
messageId: 'escapeHatch',
suggest: [
{
messageId: 'remove',
fix: (fixer) => {
return fixer.replaceTextRange(removeBrackets(node.range as [number, number]), getArbitraryValue(value))
},
},
],
})
}
return {
JSXAttribute(node: TSESTree.JSXAttribute) {
if (!node.value) return
// Ensure the attribute is a Panda prop
if (!isPandaProp(node, context)) return
const { value } = node
if (isLiteral(value)) {
const val = value.value?.toString() ?? ''
handleNodeValue(value, val)
} else if (isJSXExpressionContainer(value)) {
const expr = value.expression
if (isLiteral(expr)) {
const val = expr.value?.toString() ?? ''
handleNodeValue(expr, val)
} else if (isTemplateLiteral(expr) && expr.expressions.length === 0) {
const val = expr.quasis[0].value.raw
handleNodeValue(expr.quasis[0], val)
}
}
},
Property(node: TSESTree.Property) {
if (!isIdentifier(node.key)) return
// Ensure the property is a Panda attribute
if (!isPandaAttribute(node, context)) return
// Exclude recipe variants
if (isRecipeVariant(node, context)) return
const value = node.value
if (isLiteral(value)) {
const val = value.value?.toString() ?? ''
handleNodeValue(value, val)
} else if (isTemplateLiteral(value) && value.expressions.length === 0) {
const val = value.quasis[0].value.raw
handleNodeValue(value.quasis[0], val)
}
},
}
},
})
export default rule