Skip to content

Commit d359d30

Browse files
authored
Revert "feat(no-physical-properties): detect textAlign physical values like "left"/"right"" (#244)
2 parents 8d608f9 + b58f13f commit d359d30

File tree

4 files changed

+83
-261
lines changed

4 files changed

+83
-261
lines changed

docs/rules/no-physical-properties.md

Lines changed: 0 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -30,20 +30,6 @@ function App(){
3030
return <Circle _hover={{ borderBottom: 'solid 1px' }} />;
3131
}
3232
```
33-
```js
34-
35-
import { css } from './panda/css';
36-
37-
const styles = css({ textAlign: 'left' });
38-
```
39-
```js
40-
41-
import { Box } from './panda/jsx';
42-
43-
function App(){
44-
return <Box textAlign={"right"} />;
45-
}
46-
```
4733

4834
✔️ Examples of **correct** code:
4935
```js
@@ -67,20 +53,6 @@ function App(){
6753
return <Circle _hover={{ borderBlockEnd: 'solid 1px' }} />;
6854
}
6955
```
70-
```js
71-
72-
import { css } from './panda/css';
73-
74-
const styles = css({ textAlign: 'start' });
75-
```
76-
```js
77-
78-
import { Box } from './panda/jsx';
79-
80-
function App(){
81-
return <Box textAlign={"end"} />;
82-
}
83-
```
8456

8557
## Resources
8658

plugin/src/rules/no-physical-properties.ts

Lines changed: 83 additions & 152 deletions
Original file line numberDiff line numberDiff line change
@@ -1,139 +1,22 @@
11
import { isRecipeVariant, isPandaAttribute, isPandaProp, resolveLonghand } from '../utils/helpers'
22
import { type Rule, createRule } from '../utils'
3-
import { isIdentifier, isJSXIdentifier, isLiteral, isJSXExpressionContainer } from '../utils/nodes'
4-
import { physicalProperties, physicalPropertyValues } from '../utils/physical-properties'
5-
import type { TSESTree, TSESLint } from '@typescript-eslint/utils'
6-
7-
type CacheMap<K extends object, V> = WeakMap<K, V | undefined>
8-
type ValueNode = TSESTree.Property['value'] | TSESTree.JSXAttribute['value']
9-
type IdentifierNode = TSESTree.Identifier | TSESTree.JSXIdentifier
10-
type RuleContextType = TSESLint.RuleContext<keyof typeof MESSAGES, [{ whitelist: string[] }]>
3+
import { isIdentifier, isJSXIdentifier } from '../utils/nodes'
4+
import { physicalProperties } from '../utils/physical-properties'
5+
import type { TSESTree } from '@typescript-eslint/utils'
116

127
export const RULE_NAME = 'no-physical-properties'
138

14-
const MESSAGES = {
15-
physical: 'Use logical property instead of {{physical}}. Prefer `{{logical}}`.',
16-
physicalValue: 'Use logical value instead of {{physical}}. Prefer `{{logical}}`.',
17-
replace: 'Replace `{{physical}}` with `{{logical}}`.',
18-
} as const
19-
20-
class PropertyCache {
21-
private longhandCache = new Map<string, string>()
22-
private pandaPropCache: CacheMap<TSESTree.JSXAttribute, boolean> = new WeakMap()
23-
private pandaAttributeCache: CacheMap<TSESTree.Property, boolean> = new WeakMap()
24-
private recipeVariantCache: CacheMap<TSESTree.Property, boolean> = new WeakMap()
25-
26-
getLonghand(name: string, context: RuleContextType): string {
27-
if (this.longhandCache.has(name)) {
28-
return this.longhandCache.get(name)!
29-
}
30-
const longhand = resolveLonghand(name, context) ?? name
31-
this.longhandCache.set(name, longhand)
32-
return longhand
33-
}
34-
35-
isPandaProp(node: TSESTree.JSXAttribute, context: RuleContextType): boolean {
36-
if (this.pandaPropCache.has(node)) {
37-
return this.pandaPropCache.get(node)!
38-
}
39-
const result = isPandaProp(node, context)
40-
this.pandaPropCache.set(node, result)
41-
return !!result
42-
}
43-
44-
isPandaAttribute(node: TSESTree.Property, context: RuleContextType): boolean {
45-
if (this.pandaAttributeCache.has(node)) {
46-
return this.pandaAttributeCache.get(node)!
47-
}
48-
const result = isPandaAttribute(node, context)
49-
this.pandaAttributeCache.set(node, result)
50-
return !!result
51-
}
52-
53-
isRecipeVariant(node: TSESTree.Property, context: RuleContextType): boolean {
54-
if (this.recipeVariantCache.has(node)) {
55-
return this.recipeVariantCache.get(node)!
56-
}
57-
const result = isRecipeVariant(node, context)
58-
this.recipeVariantCache.set(node, result)
59-
return !!result
60-
}
61-
}
62-
63-
const extractStringLiteralValue = (valueNode: ValueNode): string | null => {
64-
if (isLiteral(valueNode) && typeof valueNode.value === 'string') {
65-
return valueNode.value
66-
}
67-
68-
if (
69-
isJSXExpressionContainer(valueNode) &&
70-
isLiteral(valueNode.expression) &&
71-
typeof valueNode.expression.value === 'string'
72-
) {
73-
return valueNode.expression.value
74-
}
75-
76-
return null
77-
}
78-
79-
const createPropertyReport = (
80-
node: IdentifierNode,
81-
longhandName: string,
82-
logical: string,
83-
context: RuleContextType,
84-
) => {
85-
const physicalName = `\`${node.name}\`${longhandName !== node.name ? ` (resolved to \`${longhandName}\`)` : ''}`
86-
87-
context.report({
88-
node,
89-
messageId: 'physical',
90-
data: { physical: physicalName, logical },
91-
suggest: [
92-
{
93-
messageId: 'replace',
94-
data: { physical: node.name, logical },
95-
fix: (fixer: TSESLint.RuleFixer) => fixer.replaceText(node, logical),
96-
},
97-
],
98-
})
99-
}
100-
101-
const createValueReport = (
102-
valueNode: NonNullable<ValueNode>,
103-
valueText: string,
104-
logical: string,
105-
context: RuleContextType,
106-
) => {
107-
context.report({
108-
node: valueNode,
109-
messageId: 'physicalValue',
110-
data: { physical: `"${valueText}"`, logical: `"${logical}"` },
111-
suggest: [
112-
{
113-
messageId: 'replace',
114-
data: { physical: `"${valueText}"`, logical: `"${logical}"` },
115-
fix: (fixer: TSESLint.RuleFixer) => {
116-
if (isLiteral(valueNode)) {
117-
return fixer.replaceText(valueNode, `"${logical}"`)
118-
}
119-
if (isJSXExpressionContainer(valueNode) && isLiteral(valueNode.expression)) {
120-
return fixer.replaceText(valueNode.expression, `"${logical}"`)
121-
}
122-
return null
123-
},
124-
},
125-
],
126-
})
127-
}
128-
1299
const rule: Rule = createRule({
13010
name: RULE_NAME,
13111
meta: {
13212
docs: {
13313
description:
13414
'Encourage the use of logical properties over physical properties to foster a responsive and adaptable user interface.',
13515
},
136-
messages: MESSAGES,
16+
messages: {
17+
physical: 'Use logical property instead of {{physical}}. Prefer `{{logical}}`.',
18+
replace: 'Replace `{{physical}}` with `{{logical}}`.',
19+
},
13720
type: 'suggestion',
13821
hasSuggestions: true,
13922
schema: [
@@ -153,54 +36,102 @@ const rule: Rule = createRule({
15336
},
15437
],
15538
},
156-
defaultOptions: [{ whitelist: [] }],
39+
defaultOptions: [
40+
{
41+
whitelist: [],
42+
},
43+
],
15744
create(context) {
15845
const whitelist: string[] = context.options[0]?.whitelist ?? []
159-
const cache = new PropertyCache()
16046

161-
const checkPropertyName = (node: IdentifierNode) => {
162-
if (whitelist.includes(node.name)) return
163-
const longhandName = cache.getLonghand(node.name, context)
164-
if (!(longhandName in physicalProperties)) return
47+
// Cache for resolved longhand properties
48+
const longhandCache = new Map<string, string>()
49+
50+
// Cache for helper functions
51+
const pandaPropCache = new WeakMap<TSESTree.JSXAttribute, boolean | undefined>()
52+
const pandaAttributeCache = new WeakMap<TSESTree.Property, boolean | undefined>()
53+
const recipeVariantCache = new WeakMap<TSESTree.Property, boolean | undefined>()
54+
55+
const getLonghand = (name: string): string => {
56+
if (longhandCache.has(name)) {
57+
return longhandCache.get(name)!
58+
}
59+
const longhand = resolveLonghand(name, context) ?? name
60+
longhandCache.set(name, longhand)
61+
return longhand
62+
}
16563

166-
const logical = physicalProperties[longhandName]
167-
createPropertyReport(node, longhandName, logical, context)
64+
const isCachedPandaProp = (node: TSESTree.JSXAttribute): boolean => {
65+
if (pandaPropCache.has(node)) {
66+
return pandaPropCache.get(node)!
67+
}
68+
const result = isPandaProp(node, context)
69+
pandaPropCache.set(node, result)
70+
return !!result
16871
}
16972

170-
const checkPropertyValue = (keyNode: IdentifierNode, valueNode: NonNullable<ValueNode>): boolean => {
171-
const propName = keyNode.name
172-
if (!(propName in physicalPropertyValues)) return false
73+
const isCachedPandaAttribute = (node: TSESTree.Property): boolean => {
74+
if (pandaAttributeCache.has(node)) {
75+
return pandaAttributeCache.get(node)!
76+
}
77+
const result = isPandaAttribute(node, context)
78+
pandaAttributeCache.set(node, result)
79+
return !!result
80+
}
17381

174-
const valueText = extractStringLiteralValue(valueNode)
175-
if (valueText === null) return false
82+
const isCachedRecipeVariant = (node: TSESTree.Property): boolean => {
83+
if (recipeVariantCache.has(node)) {
84+
return recipeVariantCache.get(node)!
85+
}
86+
const result = isRecipeVariant(node, context)
87+
recipeVariantCache.set(node, result)
88+
return !!result
89+
}
17690

177-
const valueMap = physicalPropertyValues[propName]
178-
if (!valueMap[valueText]) return false
91+
const sendReport = (node: TSESTree.Identifier | TSESTree.JSXIdentifier) => {
92+
if (whitelist.includes(node.name)) return
93+
const longhandName = getLonghand(node.name)
94+
if (!(longhandName in physicalProperties)) return
17995

180-
createValueReport(valueNode, valueText, valueMap[valueText], context)
181-
return true
96+
const logical = physicalProperties[longhandName]
97+
const physicalName = `\`${node.name}\`${longhandName !== node.name ? ` (resolved to \`${longhandName}\`)` : ''}`
98+
99+
context.report({
100+
node,
101+
messageId: 'physical',
102+
data: {
103+
physical: physicalName,
104+
logical,
105+
},
106+
suggest: [
107+
{
108+
messageId: 'replace',
109+
data: {
110+
physical: node.name,
111+
logical,
112+
},
113+
fix: (fixer) => {
114+
return fixer.replaceText(node, logical)
115+
},
116+
},
117+
],
118+
})
182119
}
183120

184121
return {
185122
JSXAttribute(node: TSESTree.JSXAttribute) {
186123
if (!isJSXIdentifier(node.name)) return
187-
if (!cache.isPandaProp(node, context)) return
124+
if (!isCachedPandaProp(node)) return
188125

189-
checkPropertyName(node.name)
190-
if (node.value) {
191-
checkPropertyValue(node.name, node.value)
192-
}
126+
sendReport(node.name)
193127
},
194128

195129
Property(node: TSESTree.Property) {
196130
if (!isIdentifier(node.key)) return
197-
if (!cache.isPandaAttribute(node, context)) return
198-
if (cache.isRecipeVariant(node, context)) return
131+
if (!isCachedPandaAttribute(node)) return
132+
if (isCachedRecipeVariant(node)) return
199133

200-
checkPropertyName(node.key)
201-
if (node.value) {
202-
checkPropertyValue(node.key, node.value)
203-
}
134+
sendReport(node.key)
204135
},
205136
}
206137
},

plugin/src/utils/physical-properties.ts

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -32,12 +32,3 @@ export const physicalProperties: Record<string, string> = {
3232
top: 'insetBlockStart',
3333
bottom: 'insetBlockEnd',
3434
}
35-
36-
// Map of property names to their physical values and corresponding logical values
37-
export const physicalPropertyValues: Record<string, Record<string, string>> = {
38-
// text-align physical values mapped to logical values
39-
textAlign: {
40-
left: 'start',
41-
right: 'end',
42-
},
43-
}

0 commit comments

Comments
 (0)