Skip to content

Commit

Permalink
feat: Auto Fixing for ternary operator (#60)
Browse files Browse the repository at this point in the history
  • Loading branch information
AhmedBaset authored Aug 5, 2024
2 parents ee27392 + 7757d46 commit c675a21
Show file tree
Hide file tree
Showing 9 changed files with 192 additions and 213 deletions.
5 changes: 5 additions & 0 deletions .changeset/great-ducks-smile.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"eslint-plugin-rtl-friendly": patch
---

Add auto-fixing for ternary operator
1 change: 1 addition & 0 deletions .github/workflows/release-canary.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ name: Release Canary
on:
pull_request:
types: [labeled]
branches: ["*"]

concurrency: ${{ github.workflow }}-${{ github.ref }}

Expand Down
8 changes: 6 additions & 2 deletions e2e/no-physical-properties.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -478,7 +478,11 @@ declare const React;
</>

<>
{/* eslint-disable-next-line rtl-friendly/no-physical-properties */}
<div className={Math.random() > 0.5 ? "pl-1 text-right mr-2" : "pl-1 text-right mr-2"} />
<div
className={
// eslint-disable-next-line rtl-friendly/no-physical-properties
Math.random() > 0.5 ? "pl-1 text-right mr-2" : "pr-1 text-left ml-2"
}
/>
</>
</>;
5 changes: 2 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -57,11 +57,10 @@
"@changesets/changelog-github": "^0.5.0",
"@changesets/cli": "^2.27.7",
"@eslint/js": "9.8.0",
"@types/eslint": "^9.6.0",
"@types/estree": "^1.0.5",
"@types/estree-jsx": "^1.0.5",
"@types/jest": "^29.5.12",
"@types/node": "20",
"@typescript-eslint/rule-tester": "^8.0.0",
"@typescript-eslint/utils": "^8.0.0",
"eslint": "^9.8.0",
"eslint-plugin-eslint-plugin": "^6.2.0",
"globals": "^15.9.0",
Expand Down
56 changes: 27 additions & 29 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

15 changes: 7 additions & 8 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,16 @@
import type { ESLint, Linter } from "eslint";
import pkg from "../package.json";
import type { FlatConfig } from "@typescript-eslint/utils/ts-eslint";
import { noPhysicalProperties } from "./rules/no-phyisical-properties/rule";

const rtlFriendly = {
meta: {
name: pkg.name,
version: pkg.version,
name: "eslint-plugin-rtl-friendly",
// version: pkg.version,
},
configs: {} as { recommended: Linter.FlatConfig },
configs: {} as { recommended: FlatConfig.Config },
rules: {
"no-physical-properties": noPhysicalProperties,
},
} satisfies ESLint.Plugin;
};

const configs = {
recommended: {
Expand All @@ -29,8 +28,8 @@ const configs = {
},
},
},
},
} satisfies ESLint.Plugin["configs"];
} satisfies FlatConfig.Config,
};

Object.assign(rtlFriendly.configs, configs);

Expand Down
81 changes: 45 additions & 36 deletions src/rules/no-phyisical-properties/rule.ts
Original file line number Diff line number Diff line change
@@ -1,35 +1,43 @@
import { Rule, type AST } from "eslint";

import * as ESTree from "estree";
import type { JSXAttribute } from "estree-jsx";
import { extractTokenFromNode } from "../../utils/ast.js";
import type { TSESTree } from "@typescript-eslint/utils";
import type {
RuleContext,
RuleModule,
} from "@typescript-eslint/utils/ts-eslint";
import { type Token, extractTokenFromNode } from "../../utils/ast.js";
import { parseForPhysicalClasses } from "../../utils/tailwind.js";

// const cache = new Map<
// /** invalid */ string,
// /** valid */ string
// >();

// const createRule = ESLintUtils.RuleCreator((ruleName) => {
// return `https://github.com/AhmedBaset/eslint-plugin-rtl-friendly/blob/main/src/rules/${ruleName}/README.md`;
// });

// const RULE_NAME = "no-physical-properties";
export const NO_PHYSICAL_CLASSESS = "NO_PHYSICAL_CLASSESS";
type NO_PHYSICAL_CLASSESS = typeof NO_PHYSICAL_CLASSESS;

export const noPhysicalProperties: Rule.RuleModule = {
export const noPhysicalProperties: RuleModule<NO_PHYSICAL_CLASSESS> = {
// name: RULE_NAME,
defaultOptions: [],
meta: {
type: "suggestion",
docs: {
description: "Encourage the use of RTL-friendly styles",
recommended: true,
url: "https://github.com/AhmedBaset/eslint-plugin-rtl-friendly/blob/main/src/rules/no-physical-properties/README.md",
},
fixable: "code",
messages: {
// eslint-disable-next-line eslint-plugin/no-unused-message-ids
[NO_PHYSICAL_CLASSESS]: `Avoid using physical properties such as "{{ invalid }}". Instead, use logical properties like "{{ valid }}" for better RTL support.`,
},
schema: [],
},
create(ctx) {
create: (ctx) => {
return {
JSXAttribute: (estreeNode: ESTree.Node) => {
const node = estreeNode as JSXAttribute;

JSXAttribute: (node) => {
if (node.name.type !== "JSXIdentifier") return;
const attr = node.name.name;

Expand All @@ -50,58 +58,59 @@ export const noPhysicalProperties: Rule.RuleModule = {
// return;
// }

const classesAsString = extractTokenFromNode(node, "checker")?.value;
if (!classesAsString) return;

const classes = classesAsString.split(" ");
const tokens = extractTokenFromNode(node, "checker");
tokens?.forEach((token) => {
const classValue = token?.getValue();
if (!classValue) return;

const parsed = parseForPhysicalClasses(classes);
const classes = classValue.split(" ");

const isInvalid = parsed.some((p) => p.isInvalid);
if (!isInvalid) return;
const parsed = parseForPhysicalClasses(classes);

const invalid = parsed.map((p) => p.original).join(" ");
const valid = parsed.map((p) => p.valid).join(" ");
const isInvalid = parsed.some((p) => p.isInvalid);
if (!isInvalid) return;

// cache.set(classesAsString, valid);
report({ ctx, node, invalid, valid });
const invalid = parsed.map((p) => p.original).join(" ");
const valid = parsed.map((p) => p.valid).join(" ");

// cache.set(classesAsString, valid);
report({ ctx, node, invalid, valid, token: token ?? null });
});
},
};
},
};

type Context = Readonly<RuleContext<"NO_PHYSICAL_CLASSESS", []>>;

function report({
ctx,
invalid,
valid,
node,
token,
}: {
ctx: Rule.RuleContext;
node: JSXAttribute;
ctx: Context;
node: TSESTree.JSXAttribute;
invalid: string;
valid: string;
token: Token | null;
}) {
return ctx.report({
node,
messageId: "NO_PHYSICAL_CLASSESS",
messageId: NO_PHYSICAL_CLASSESS,
data: {
invalid,
valid,
},
loc: {
start: node.loc!.start,
end: node.loc!.end,
start: token?.loc?.start ?? node.loc!.start,
end: token?.loc?.end ?? node.loc!.end,
},
fix: (fixer) => {
const token = extractTokenFromNode(node, "fixer");
if (token?.raw) {
return fixer.replaceText(
token as AST.Token,
token.raw?.replace(invalid, valid)
);
}

return null;
if (!token) return null;

return fixer.replaceText(token, token?.getRaw()?.replace(invalid, valid));
},
});
}
24 changes: 19 additions & 5 deletions src/rules/no-phyisical-properties/test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { RuleTester } from "eslint";
import { noPhysicalProperties, NO_PHYSICAL_CLASSESS } from "./rule";
import { RuleTester } from "@typescript-eslint/rule-tester";
import { NO_PHYSICAL_CLASSESS, noPhysicalProperties } from "./rule";

const tester = new RuleTester({
languageOptions: {
Expand Down Expand Up @@ -106,9 +106,23 @@ tester.run("no-physical-properties", noPhysicalProperties, {
},
{
name: '{isCondition ? "..." : "..."}',
code: `<div className={isCondition ? "pl-1 text-right mr-2" : "pl-1 text-right mr-2"} />`,
// output: `<div className={isCondition ? "ps-1 text-end me-2" : "pl-1 text-right mr-2"} />`, // TODO: Fix this
errors: [{ messageId: NO_PHYSICAL_CLASSESS }],
code: `<div className={isCondition ? "pl-1 text-right" : "pr-1 text-left"} />`,
output:
'<div className={isCondition ? "ps-1 text-end" : "pe-1 text-start"} />',
errors: [
{ messageId: NO_PHYSICAL_CLASSESS },
{ messageId: NO_PHYSICAL_CLASSESS },
],
},
{
name: "{isCondition ? `...` : `...`}",
code: "<div className={isCondition ? `pl-1 text-right` : `pr-1 text-left`} />",
output:
"<div className={isCondition ? `ps-1 text-end` : `pe-1 text-start`} />",
errors: [
{ messageId: NO_PHYSICAL_CLASSESS },
{ messageId: NO_PHYSICAL_CLASSESS },
],
},
{
name: "should report if physical margin properties are used and fix them",
Expand Down
Loading

0 comments on commit c675a21

Please sign in to comment.