Skip to content

Commit

Permalink
Support TaggedTemplate ("tw\...") (#67)
Browse files Browse the repository at this point in the history
- tw\`...\` is supported but tw\`... ${...}\` is not yet
  • Loading branch information
AhmedBaset authored Aug 7, 2024
2 parents 869f772 + 9a8721f commit c763974
Show file tree
Hide file tree
Showing 4 changed files with 67 additions and 31 deletions.
6 changes: 6 additions & 0 deletions .changeset/smart-lies-admire.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"eslint-plugin-rtl-friendly": minor
---

Support TaggedTemplate ("tw\`...`")
- tw\`...\` is supported but tw\`... ${...}\` is not yet
4 changes: 2 additions & 2 deletions src/rules/no-phyisical-properties/rule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import type {
RuleContext,
RuleModule,
} from "@typescript-eslint/utils/ts-eslint";
import { type Token, extractTokenFromNode } from "../../utils/ast.js";
import { type Token, extractTokensFromNode } from "../../utils/ast.js";
import { parseForPhysicalClasses } from "../../utils/tailwind.js";

// const cache = new Map<
Expand Down Expand Up @@ -58,7 +58,7 @@ export const noPhysicalProperties: RuleModule<NO_PHYSICAL_CLASSESS> = {
// return;
// }

const tokens = extractTokenFromNode(node, "checker");
const tokens = extractTokensFromNode(node, "checker");
tokens?.forEach((token) => {
const classValue = token?.getValue();
if (!classValue) return;
Expand Down
28 changes: 27 additions & 1 deletion src/rules/no-phyisical-properties/test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,11 @@ tester.run("no-physical-properties", noPhysicalProperties, {
name: `{"..." + "..."}`,
code: `<div className={"pl-2 " + "mr-1" + "text-left"} />`,
output: `<div className={"ps-2 " + "me-1" + "text-start"} />`,
errors: [{messageId: NO_PHYSICAL_CLASSESS}, {messageId: NO_PHYSICAL_CLASSESS}, {messageId: NO_PHYSICAL_CLASSESS}]
errors: [
{ messageId: NO_PHYSICAL_CLASSESS },
{ messageId: NO_PHYSICAL_CLASSESS },
{ messageId: NO_PHYSICAL_CLASSESS },
],
},
{
name: '{isCondition && "..."}',
Expand Down Expand Up @@ -218,6 +222,28 @@ tester.run("no-physical-properties", noPhysicalProperties, {
{ messageId: NO_PHYSICAL_CLASSESS },
],
},
{
name: "Identifier",
code: `
const variable = "text-left";
<div className={variable} />
`,
errors: [{ messageId: NO_PHYSICAL_CLASSESS }],
skip: true,
},
{
name: "TaggedTemplate tw`...`",
code: "<div className={tw`pl-1`} />",
output: "<div className={tw`ps-1`} />",
errors: [{ messageId: NO_PHYSICAL_CLASSESS }],
},
{
name: "TaggedTemplate tw`...` with ${...} inside (Not Supported)",
code: "<div className={tw`pl-1 ${'text-left'}`} />", // ${...} within TaggedTemplate
output: "<div className={tw`ps-1 ${'text-left'}`} />",
errors: [{ messageId: NO_PHYSICAL_CLASSESS }],
skip: true,
},
{
name: "should report if physical margin properties are used and fix them",
code: `<div className="ml-1 mr-2">text</div>`,
Expand Down
60 changes: 32 additions & 28 deletions src/utils/ast.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export type Token = (
getRaw: () => string;
};

export function extractTokenFromNode(
export function extractTokensFromNode(
node: TSESTree.JSXAttribute,
runner: "checker" | "fixer"
): (Token | undefined | null)[] {
Expand All @@ -29,7 +29,7 @@ export function extractTokenFromNode(

if (!expression || expression?.type === "JSXEmptyExpression") return [];

return extractTokenFromExpression(expression, runner);
return extractTokensFromExpression(expression, runner);
}

if (value.type === "JSXElement" || value.type === "JSXSpreadChild") {
Expand All @@ -40,20 +40,14 @@ export function extractTokenFromNode(
return [];
}

function extractTokenFromExpression(
exp: TSESTree.Expression,
type Exp = TSESTree.Expression | TSESTree.TemplateElement;

function extractTokensFromExpression(
exp: Exp,
runner: "checker" | "fixer"
): (Token | undefined)[] {
// We care about:
// -> Literal;
// -> TemplateLiteral;
// -> BinaryExpression
// -> CallExpression;
// -> ConditionalExpression;
// -> LogicalExpression;

const rerun = (expression: TSESTree.Expression) => {
return extractTokenFromExpression(expression, runner);
const rerun = (expression: Exp) => {
return extractTokensFromExpression(expression, runner);
};

// const isFixer = runner === "fixer";
Expand All @@ -76,6 +70,14 @@ function extractTokenFromExpression(
);
}

if (is(exp, "TemplateElement")) {
return format(
exp,
exp.value.cooked,
`\`${exp.value.raw}\``
);
}

if (is(exp, "LogicalExpression")) {
// isCondition && "..."
return rerun(exp.right);
Expand Down Expand Up @@ -103,8 +105,9 @@ function extractTokenFromExpression(
if (
el.type === "AssignmentPattern" ||
el.type === "TSEmptyBodyFunctionExpression"
)
) {
return [];
}

return rerun(el);
});
Expand All @@ -121,20 +124,21 @@ function extractTokenFromExpression(
});
}

if (is(exp, "BinaryExpression")) {
if (is(exp, "BinaryExpression")) {
const right = rerun(exp.right);
if (exp.left.type === "PrivateIdentifier") return right;
return [...right, ...rerun(exp.left)];
return [...right, ...rerun(exp.left)];
}

// if (
// is(exp, "Identifier") ||
// is(exp, "MemberExpression") ||
// is(exp, "TaggedTemplateExpression")
// ) {
// // Will be implemented
// return [];
// }
if (is(exp, "TaggedTemplateExpression")) {
// tw`...`
return exp.quasi.quasis.flatMap((q) => rerun(q));
}

if (is(exp, "Identifier") || is(exp, "MemberExpression")) {
// We should follow the identifier and get the value
return [];
}

// if ((unsupported as typeof exp.type[]).includes(exp.type)) {
// if (
Expand Down Expand Up @@ -237,9 +241,9 @@ function callOrValue<T extends string, P>(
return typeof func === "function" ? func(param!) : func;
}

function is<T extends TSESTree.AST_NODE_TYPES>(
exp: TSESTree.Expression,
function is<E extends Exp, T extends E["type"]>(
exp: Exp,
type: `${T}`
): exp is Extract<TSESTree.Expression, { type: T }> {
): exp is Extract<Exp, { type: T }> {
return exp.type === type;
}

0 comments on commit c763974

Please sign in to comment.