From 3cc5bbc979da1de53845fa2ae395f9cc02bdfec9 Mon Sep 17 00:00:00 2001 From: melissa Date: Tue, 10 Dec 2024 14:48:34 -0800 Subject: [PATCH] feat: add autofix for logical to inline properties in valid-styles rule (#805) --- .../__tests__/stylex-valid-styles-test.js | 81 +++++++++++++++++++ .../eslint-plugin/src/stylex-valid-styles.js | 45 ++++++++++- 2 files changed, 125 insertions(+), 1 deletion(-) diff --git a/packages/eslint-plugin/__tests__/stylex-valid-styles-test.js b/packages/eslint-plugin/__tests__/stylex-valid-styles-test.js index e9a9f4c1..78ef1011 100644 --- a/packages/eslint-plugin/__tests__/stylex-valid-styles-test.js +++ b/packages/eslint-plugin/__tests__/stylex-valid-styles-test.js @@ -39,6 +39,21 @@ eslintTester.run('stylex-valid-styles', rule.default, { } }); `, + ` + import stylex from "stylex"; + const styles = stylex.create({ + validStyle: { + marginInlineStart: "10px", + marginInlineEnd: "5px", + marginInline: "15px", + marginBlock: "20px", + paddingInlineStart: "8px", + paddingInlineEnd: "12px", + paddingInline: "10px", + paddingBlock: "16px", + }, + }); + `, ` import stylex from 'stylex'; const start = 'start'; @@ -583,6 +598,72 @@ eslintTester.run('stylex-valid-styles', rule.default, { }, ], }, + { + code: ` + import stylex from 'stylex'; + const styles = stylex.create({ + invalidStyle: { + marginStart: '10px', + marginEnd: '5px', + marginHorizontal: '15px', + marginVertical: '15px', + paddingStart: '10px', + paddingEnd: '5px', + paddingHorizontal: '5px', + paddingVertical: '15px', + }, + }); + `, + errors: [ + { + message: + 'The key "marginStart" is not a standard CSS property. Did you mean "marginInlineStart"?', + }, + { + message: + 'The key "marginEnd" is not a standard CSS property. Did you mean "marginInlineEnd"?', + }, + { + message: + 'The key "marginHorizontal" is not a standard CSS property. Did you mean "marginInline"?', + }, + { + message: + 'The key "marginVertical" is not a standard CSS property. Did you mean "marginBlock"?', + }, + { + message: + 'The key "paddingStart" is not a standard CSS property. Did you mean "paddingInlineStart"?', + }, + { + message: + 'The key "paddingEnd" is not a standard CSS property. Did you mean "paddingInlineEnd"?', + }, + { + message: + 'The key "paddingHorizontal" is not a standard CSS property. Did you mean "paddingInline"?', + }, + { + message: + 'The key "paddingVertical" is not a standard CSS property. Did you mean "paddingBlock"?', + }, + ], + output: ` + import stylex from 'stylex'; + const styles = stylex.create({ + invalidStyle: { + marginInlineStart: '10px', + marginInlineEnd: '5px', + marginInline: '15px', + marginBlock: '15px', + paddingInlineStart: '10px', + paddingInlineEnd: '5px', + paddingInline: '5px', + paddingBlock: '15px', + }, + }); + `, + }, { code: "import stylex from 'stylex'; stylex.create({default: {textAlign: 'lfet'}});", errors: [ diff --git a/packages/eslint-plugin/src/stylex-valid-styles.js b/packages/eslint-plugin/src/stylex-valid-styles.js index 2f36eddf..584a5ddb 100644 --- a/packages/eslint-plugin/src/stylex-valid-styles.js +++ b/packages/eslint-plugin/src/stylex-valid-styles.js @@ -1595,6 +1595,17 @@ const SupportedVendorSpecificCSSProperties = { ), }; +const validStyleMapping: $ReadOnly<{ [key: string]: ?string }> = { + marginStart: 'marginInlineStart', + marginEnd: 'marginInlineEnd', + marginHorizontal: 'marginInline', + marginVertical: 'marginBlock', + paddingStart: 'paddingInlineStart', + paddingEnd: 'paddingInlineEnd', + paddingHorizontal: 'paddingInline', + paddingVertical: 'paddingBlock', +}; + const SVGProperties = { colorInterpolation: makeUnionRule('auto', 'sRGB', 'linearRGB'), // colorRendering: color, @@ -2292,6 +2303,7 @@ const stylexValidStyles = { meta: { type: 'problem', hasSuggestions: true, + fixable: 'code', docs: { descriptions: 'Enforce that you create valid stylex styles', category: 'Possible Errors', @@ -2635,10 +2647,41 @@ const stylexValidStyles = { const distance = getDistance(key, cssProp, 2); return distance <= 2; }); + + const replacementKey = + style.key.type === 'Identifier' && validStyleMapping[style.key.name] + ? validStyleMapping[style.key.name] + : style.key.type === 'Literal' && + typeof style.key.value === 'string' && + validStyleMapping[style.key.value] + ? validStyleMapping[style.key.value] + : null; + + let originalKey = ''; + + if (style.key.type === 'Identifier') { + originalKey = style.key.name; + } else if ( + style.key.type === 'Literal' && + typeof style.key.value === 'string' + ) { + originalKey = style.key.value; + } + return context.report({ node: style.key, loc: style.key.loc, - message: 'This is not a key that is allowed by stylex', + message: + replacementKey && + (style.key.type === 'Identifier' || style.key.type === 'Literal') + ? `The key "${originalKey}" is not a standard CSS property. Did you mean "${replacementKey}"?` + : 'This is not a key that is allowed by stylex', + fix: (fixer) => { + if (replacementKey) { + return fixer.replaceText(style.key, replacementKey); + } + return null; + }, suggest: closestKey != null ? [