From 07bbde9c715792954c5af91f7426bda66bea5bf7 Mon Sep 17 00:00:00 2001 From: Ahmed Abdelbaset Date: Mon, 5 Aug 2024 14:38:39 +0300 Subject: [PATCH 01/13] feat: CallExpression, ArrayExpression, ObjectExpression --- .changeset/itchy-knives-teach.md | 5 ++ src/rules/no-phyisical-properties/test.ts | 76 +++++++++++++++++++++++ src/utils/ast.ts | 48 +++++++++++++- 3 files changed, 126 insertions(+), 3 deletions(-) create mode 100644 .changeset/itchy-knives-teach.md diff --git a/.changeset/itchy-knives-teach.md b/.changeset/itchy-knives-teach.md new file mode 100644 index 0000000..f44fd14 --- /dev/null +++ b/.changeset/itchy-knives-teach.md @@ -0,0 +1,5 @@ +--- +"eslint-plugin-rtl-friendly": patch +--- + +feat: CallExpression, ArrayExpression, ObjectExpression diff --git a/src/rules/no-phyisical-properties/test.ts b/src/rules/no-phyisical-properties/test.ts index 0fb924f..003cb45 100644 --- a/src/rules/no-phyisical-properties/test.ts +++ b/src/rules/no-phyisical-properties/test.ts @@ -130,6 +130,82 @@ tester.run("no-physical-properties", noPhysicalProperties, { { messageId: NO_PHYSICAL_CLASSESS }, ], }, + { + name: '{cn("...")}', + code: `
`, + output: `
`, + errors: [{ messageId: NO_PHYSICAL_CLASSESS }], + only: true, + }, + { + name: '{cn(isCondition && "...")}', + code: `
`, + output: `
`, + errors: [{ messageId: NO_PHYSICAL_CLASSESS }], + only: true, + }, + { + name: '{cn(isCondition ? "..." : "...")}', + code: '
', + output: + '
', + errors: [ + { messageId: NO_PHYSICAL_CLASSESS }, + { messageId: NO_PHYSICAL_CLASSESS }, + ], + only: true, + }, + { + name: '{cn("...", isCondition && "...")}', + code: `
`, + output: `
`, + errors: [ + { messageId: NO_PHYSICAL_CLASSESS }, + { messageId: NO_PHYSICAL_CLASSESS }, + ], + only: true, + }, + { + name: '{cn(["...", "..."])}', + code: `
`, + output: `
`, + errors: [ + { messageId: NO_PHYSICAL_CLASSESS }, + { messageId: NO_PHYSICAL_CLASSESS }, + ], + only: true, + }, + { + name: '{cn(["...", ...["..."]])}', + code: `
`, + output: `
`, + errors: [ + { messageId: NO_PHYSICAL_CLASSESS }, + { messageId: NO_PHYSICAL_CLASSESS }, + { messageId: NO_PHYSICAL_CLASSESS }, + { messageId: NO_PHYSICAL_CLASSESS }, + ], + only: true, + }, + { + name: '{cn({"...": true})}', + code: `
`, + output: `
`, + errors: [{ messageId: NO_PHYSICAL_CLASSESS }], + only: true, + }, + { + name: '{cn({"...": "..."}, isCondition && {"...": "..."})}', + code: `
`, + output: `
`, + errors: [ + { messageId: NO_PHYSICAL_CLASSESS }, + { messageId: NO_PHYSICAL_CLASSESS }, + { messageId: NO_PHYSICAL_CLASSESS }, + { messageId: NO_PHYSICAL_CLASSESS }, + ], + only: true, + }, { name: "should report if physical margin properties are used and fix them", code: `
text
`, diff --git a/src/utils/ast.ts b/src/utils/ast.ts index e3c0859..fa4eec0 100644 --- a/src/utils/ast.ts +++ b/src/utils/ast.ts @@ -1,5 +1,7 @@ import type { TSESTree } from "@typescript-eslint/utils"; +const unimplemented = new Set(); + export type Token = ( | TSESTree.JSXAttribute | TSESTree.Expression @@ -54,14 +56,16 @@ function extractTokenFromExpression( }; // const isFixer = runner === "fixer"; - const type = exp.type; - if (isStringLiteral(exp)) + if (exp.type === "Literal") { + if (typeof exp.value !== "string") return []; // boolean, number, null, undefined, etc... + return format( exp, () => exp.value, () => exp.raw ); + } if (exp?.type === "TemplateLiteral") { return format( @@ -78,10 +82,48 @@ function extractTokenFromExpression( if (exp.type === "ConditionalExpression") { return [...rerun(exp.consequent), ...rerun(exp.alternate)]; + } + + if (exp.type === "ArrayExpression") { + return exp.elements.flatMap((el) => { + if (!el) return []; + + if (el.type === "SpreadElement") return rerun(el.argument); + + return rerun(el); + }); + } + + if (exp.type === "ObjectExpression") { + return exp.properties.flatMap((prop) => { + if (prop.type === "SpreadElement") return rerun(prop.argument); + return [prop.key, prop.value].flatMap((el) => { + if ( + el.type === "AssignmentPattern" || + el.type === "TSEmptyBodyFunctionExpression" + ) + return []; + + return rerun(el); + }); + }); } - // console.log("UNIMPLEMENTED: ", type); + if (exp.type === "CallExpression") { + return exp.arguments.flatMap((arg) => { + if (arg.type === "SpreadElement") { + return rerun(arg.argument); + } + + return rerun(arg); + }); + } + + if (!unimplemented.has(exp.type)) { + console.log("unimplemented: ", exp.type, exp); + unimplemented.add(exp.type); + } // if (expression.type === "BinaryExpression") { // result.push(...extractFromExpression(expression.left)); From e0a169432b1ba3405fba067e72c17fa41d3678c0 Mon Sep 17 00:00:00 2001 From: Ahmed Abdelbaset Date: Mon, 5 Aug 2024 14:39:38 +0300 Subject: [PATCH 02/13] rm only --- src/rules/no-phyisical-properties/test.ts | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/rules/no-phyisical-properties/test.ts b/src/rules/no-phyisical-properties/test.ts index 003cb45..ffcb25a 100644 --- a/src/rules/no-phyisical-properties/test.ts +++ b/src/rules/no-phyisical-properties/test.ts @@ -135,14 +135,12 @@ tester.run("no-physical-properties", noPhysicalProperties, { code: `
`, output: `
`, errors: [{ messageId: NO_PHYSICAL_CLASSESS }], - only: true, }, { name: '{cn(isCondition && "...")}', code: `
`, output: `
`, errors: [{ messageId: NO_PHYSICAL_CLASSESS }], - only: true, }, { name: '{cn(isCondition ? "..." : "...")}', @@ -153,7 +151,6 @@ tester.run("no-physical-properties", noPhysicalProperties, { { messageId: NO_PHYSICAL_CLASSESS }, { messageId: NO_PHYSICAL_CLASSESS }, ], - only: true, }, { name: '{cn("...", isCondition && "...")}', @@ -163,7 +160,6 @@ tester.run("no-physical-properties", noPhysicalProperties, { { messageId: NO_PHYSICAL_CLASSESS }, { messageId: NO_PHYSICAL_CLASSESS }, ], - only: true, }, { name: '{cn(["...", "..."])}', @@ -173,7 +169,6 @@ tester.run("no-physical-properties", noPhysicalProperties, { { messageId: NO_PHYSICAL_CLASSESS }, { messageId: NO_PHYSICAL_CLASSESS }, ], - only: true, }, { name: '{cn(["...", ...["..."]])}', @@ -185,14 +180,12 @@ tester.run("no-physical-properties", noPhysicalProperties, { { messageId: NO_PHYSICAL_CLASSESS }, { messageId: NO_PHYSICAL_CLASSESS }, ], - only: true, }, { name: '{cn({"...": true})}', code: `
`, output: `
`, errors: [{ messageId: NO_PHYSICAL_CLASSESS }], - only: true, }, { name: '{cn({"...": "..."}, isCondition && {"...": "..."})}', @@ -204,7 +197,6 @@ tester.run("no-physical-properties", noPhysicalProperties, { { messageId: NO_PHYSICAL_CLASSESS }, { messageId: NO_PHYSICAL_CLASSESS }, ], - only: true, }, { name: "should report if physical margin properties are used and fix them", From 6646da21782dd63a5e8b3144f64ee9262a53180c Mon Sep 17 00:00:00 2001 From: Ahmed Abdelbaset Date: Mon, 5 Aug 2024 20:00:35 +0300 Subject: [PATCH 03/13] Merge branch 'main' into call --- .github/workflows/release-canary.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release-canary.yml b/.github/workflows/release-canary.yml index 2c0ced2..d84f5c5 100644 --- a/.github/workflows/release-canary.yml +++ b/.github/workflows/release-canary.yml @@ -40,7 +40,7 @@ jobs: - name: Comment on PR if: steps.publish.outputs.published == 'true' - uses: actions/github-script@v3 + uses: actions/github-script@v7 with: github-token: ${{ secrets.MY_GITHUB_TOKEN }} script: | From 1b250d9614c87ba13f577a13df420ee4bb1bcdbb Mon Sep 17 00:00:00 2001 From: Ahmed Abdelbaset Date: Mon, 5 Aug 2024 20:20:24 +0300 Subject: [PATCH 04/13] Merge branch 'main' into call --- .github/workflows/release-canary.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release-canary.yml b/.github/workflows/release-canary.yml index d84f5c5..2c0ced2 100644 --- a/.github/workflows/release-canary.yml +++ b/.github/workflows/release-canary.yml @@ -40,7 +40,7 @@ jobs: - name: Comment on PR if: steps.publish.outputs.published == 'true' - uses: actions/github-script@v7 + uses: actions/github-script@v3 with: github-token: ${{ secrets.MY_GITHUB_TOKEN }} script: | From dfa1d20201a8c5b1ecf4a1eade4ddbfb9cb3adc9 Mon Sep 17 00:00:00 2001 From: Ahmed Abdelbaset Date: Mon, 5 Aug 2024 20:21:01 +0300 Subject: [PATCH 05/13] Update release-canary.yml --- .github/workflows/release-canary.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release-canary.yml b/.github/workflows/release-canary.yml index 2c0ced2..d69a3ad 100644 --- a/.github/workflows/release-canary.yml +++ b/.github/workflows/release-canary.yml @@ -30,7 +30,7 @@ jobs: run: | npm config set //registry.npmjs.org/:_authToken ${{ secrets.NPM_TOKEN }} pnpm changeset version --snapshot ${{ github.event.number }} - npm publish --access public --tag canary --no-git-checks + # npm publish --access public --tag canary --no-git-checks echo "published=true" >> "$GITHUB_OUTPUT" echo "version=$(npm pkg get version | sed -e 's/^"//;s/"$//')" >> "$GITHUB_OUTPUT" env: From fdcde90fc8c2ba48bde30b4fe84ac4aa7f93eb83 Mon Sep 17 00:00:00 2001 From: Ahmed Abdelbaset Date: Mon, 5 Aug 2024 20:25:10 +0300 Subject: [PATCH 06/13] Update release-canary.yml --- .github/workflows/release-canary.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/release-canary.yml b/.github/workflows/release-canary.yml index d69a3ad..480ef32 100644 --- a/.github/workflows/release-canary.yml +++ b/.github/workflows/release-canary.yml @@ -54,8 +54,9 @@ jobs: repo, body: ` 🚀 A new Canary version has been released - + \n\n You can install it by running: + \n \`\`\`bash pnpm add eslint-plugin-rtl-friendly@${{ steps.publish.outputs.version }} -D \`\`\` From ccee6ee18bee708969a88ff8999b9798e2a4f720 Mon Sep 17 00:00:00 2001 From: Ahmed Abdelbaset Date: Mon, 5 Aug 2024 20:30:28 +0300 Subject: [PATCH 07/13] Update release-canary.yml --- .github/workflows/release-canary.yml | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/.github/workflows/release-canary.yml b/.github/workflows/release-canary.yml index 480ef32..93f04f3 100644 --- a/.github/workflows/release-canary.yml +++ b/.github/workflows/release-canary.yml @@ -54,14 +54,16 @@ jobs: repo, body: ` 🚀 A new Canary version has been released - \n\n + You can install it by running: - \n + \`\`\`bash pnpm add eslint-plugin-rtl-friendly@${{ steps.publish.outputs.version }} -D \`\`\` - `.replace(/\s+/g, ' ') - }) + `.split("\n") + .map(l => l.trim()) + .join("\n") + }) github.issues.removeLabel({ issue_number, From e05691332f9bcdb029b6ad614131692a6231603f Mon Sep 17 00:00:00 2001 From: Ahmed Abdelbaset Date: Mon, 5 Aug 2024 23:24:00 +0300 Subject: [PATCH 08/13] failed test --- src/rules/no-phyisical-properties/test.ts | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/rules/no-phyisical-properties/test.ts b/src/rules/no-phyisical-properties/test.ts index ffcb25a..bcb320c 100644 --- a/src/rules/no-phyisical-properties/test.ts +++ b/src/rules/no-phyisical-properties/test.ts @@ -198,6 +198,15 @@ tester.run("no-physical-properties", noPhysicalProperties, { { messageId: NO_PHYSICAL_CLASSESS }, ], }, + { + name: "clsx('...', [1 && '...', { ...: false, ...: null }, ['...', ['...']]], '...')", + code: `clsx('pl-1', [1 && 'text-right', { 'text-left': false, 'mr-2': null }, ['pr-2', ['pl-2']]], 'mr-1')`, + output: `clsx('ps-1', [1 && 'text-end', { 'text-start': false, 'me-2': null }, ['pe-2', ['ps-2']]], 'me-1')`, + errors: [ + { messageId: NO_PHYSICAL_CLASSESS } + ], + only: true + }, { name: "should report if physical margin properties are used and fix them", code: `
text
`, From cc257d1947a641c5110f3124ab31a8ccf477302c Mon Sep 17 00:00:00 2001 From: Ahmed Abdelbaset Date: Mon, 5 Aug 2024 23:45:43 +0300 Subject: [PATCH 09/13] clsx test --- src/rules/no-phyisical-properties/test.ts | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/rules/no-phyisical-properties/test.ts b/src/rules/no-phyisical-properties/test.ts index bcb320c..9e2c684 100644 --- a/src/rules/no-phyisical-properties/test.ts +++ b/src/rules/no-phyisical-properties/test.ts @@ -199,13 +199,18 @@ tester.run("no-physical-properties", noPhysicalProperties, { ], }, { - name: "clsx('...', [1 && '...', { ...: false, ...: null }, ['...', ['...']]], '...')", - code: `clsx('pl-1', [1 && 'text-right', { 'text-left': false, 'mr-2': null }, ['pr-2', ['pl-2']]], 'mr-1')`, - output: `clsx('ps-1', [1 && 'text-end', { 'text-start': false, 'me-2': null }, ['pe-2', ['ps-2']]], 'me-1')`, + name: "clsx('...', [1 && '...', { ...: false, ...: null }, is && ['...', ['...']]], '...')", + code: `
`, + output: `
`, errors: [ - { messageId: NO_PHYSICAL_CLASSESS } + { messageId: NO_PHYSICAL_CLASSESS }, + { messageId: NO_PHYSICAL_CLASSESS }, + { messageId: NO_PHYSICAL_CLASSESS }, + { messageId: NO_PHYSICAL_CLASSESS }, + { messageId: NO_PHYSICAL_CLASSESS }, + { messageId: NO_PHYSICAL_CLASSESS }, + { messageId: NO_PHYSICAL_CLASSESS }, ], - only: true }, { name: "should report if physical margin properties are used and fix them", From 2d9cf7305c462384770fbc3df8281bd9009518d6 Mon Sep 17 00:00:00 2001 From: Ahmed Abdelbaset Date: Mon, 5 Aug 2024 23:48:45 +0300 Subject: [PATCH 10/13] log unimplemented --- package.json | 2 +- src/utils/ast.ts | 7 ++++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 10f50d4..47e5dc8 100644 --- a/package.json +++ b/package.json @@ -40,7 +40,7 @@ "lint": "eslint .", "test": "vitest --run", "test:watch": "vitest", - "test:coverage": "vitest --coverage", + "test:coverage": "vitest --run --coverage", "prepublishOnly": "npm run build", "gen-e2e": "tsx scripts/generate-e2e" }, diff --git a/src/utils/ast.ts b/src/utils/ast.ts index fa4eec0..255746e 100644 --- a/src/utils/ast.ts +++ b/src/utils/ast.ts @@ -36,6 +36,11 @@ export function extractTokenFromNode( return extractTokenFromExpression(expression, runner); } + if (!unimplemented.has(type)) { + console.log("Unimplemented: ", type, node); + unimplemented.add(type); + } + return []; } @@ -121,7 +126,7 @@ function extractTokenFromExpression( } if (!unimplemented.has(exp.type)) { - console.log("unimplemented: ", exp.type, exp); + console.log("Unimplemented: ", exp.type, exp); unimplemented.add(exp.type); } From 1676a6b670dbbd46ba070f7f00d35d0b23d4b69f Mon Sep 17 00:00:00 2001 From: Ahmed Abdelbaset Date: Tue, 6 Aug 2024 00:53:50 +0300 Subject: [PATCH 11/13] improve ts --- src/rules/no-phyisical-properties/test.ts | 2 +- src/utils/ast.ts | 128 ++++++++++++++++------ vitest.config.ts | 2 +- 3 files changed, 97 insertions(+), 35 deletions(-) diff --git a/src/rules/no-phyisical-properties/test.ts b/src/rules/no-phyisical-properties/test.ts index 9e2c684..a7eb3ab 100644 --- a/src/rules/no-phyisical-properties/test.ts +++ b/src/rules/no-phyisical-properties/test.ts @@ -1,5 +1,5 @@ -import * as vitest from "vitest" import { RuleTester } from "@typescript-eslint/rule-tester"; +import * as vitest from "vitest"; import { NO_PHYSICAL_CLASSESS, noPhysicalProperties } from "./rule"; RuleTester.afterAll = vitest.afterAll; diff --git a/src/utils/ast.ts b/src/utils/ast.ts index 255746e..bd822b7 100644 --- a/src/utils/ast.ts +++ b/src/utils/ast.ts @@ -1,4 +1,4 @@ -import type { TSESTree } from "@typescript-eslint/utils"; +import { TSESTree } from "@typescript-eslint/utils"; const unimplemented = new Set(); @@ -16,29 +16,25 @@ export function extractTokenFromNode( runner: "checker" | "fixer" ): (Token | undefined | null)[] { // value: Literal | JSXExpressionContainer | JSXElement | JSXFragment | null - const type = node.value?.type; - if (!type) return []; + const value = node.value; + if (!value) return []; - const nodeValue = node.value; - - if (isStringLiteral(nodeValue)) - return format( - nodeValue, - (n) => n.value, - (n) => n.raw - ); + if (value?.type === "Literal") { + if (typeof value.value !== "string") return []; // boolean, number, null, undefined, etc... + return format(value, value.value, value.raw); + } - if (type === "JSXExpressionContainer") { - const expression = node.value?.expression; + if (value.type === "JSXExpressionContainer") { + const expression = value?.expression; if (!expression || expression?.type === "JSXEmptyExpression") return []; return extractTokenFromExpression(expression, runner); } - if (!unimplemented.has(type)) { - console.log("Unimplemented: ", type, node); - unimplemented.add(type); + if (value.type === "JSXElement" || value.type === "JSXSpreadChild") { + // JSXElement is like => + return []; } return []; @@ -62,7 +58,7 @@ function extractTokenFromExpression( // const isFixer = runner === "fixer"; - if (exp.type === "Literal") { + if (is(exp, "Literal")) { if (typeof exp.value !== "string") return []; // boolean, number, null, undefined, etc... return format( @@ -72,7 +68,7 @@ function extractTokenFromExpression( ); } - if (exp?.type === "TemplateLiteral") { + if (is(exp, "TemplateLiteral")) { return format( exp.quasis, (q) => q.value.cooked, @@ -80,16 +76,16 @@ function extractTokenFromExpression( ); } - if (exp.type === "LogicalExpression") { + if (is(exp, "LogicalExpression")) { // isCondition && "..." return rerun(exp.right); } - if (exp.type === "ConditionalExpression") { + if (is(exp, "ConditionalExpression")) { return [...rerun(exp.consequent), ...rerun(exp.alternate)]; } - if (exp.type === "ArrayExpression") { + if (is(exp, "ArrayExpression")) { return exp.elements.flatMap((el) => { if (!el) return []; @@ -99,7 +95,7 @@ function extractTokenFromExpression( }); } - if (exp.type === "ObjectExpression") { + if (is(exp, "ObjectExpression")) { return exp.properties.flatMap((prop) => { if (prop.type === "SpreadElement") return rerun(prop.argument); @@ -115,7 +111,7 @@ function extractTokenFromExpression( }); } - if (exp.type === "CallExpression") { + if (is(exp, "CallExpression")) { return exp.arguments.flatMap((arg) => { if (arg.type === "SpreadElement") { return rerun(arg.argument); @@ -125,6 +121,49 @@ function extractTokenFromExpression( }); } + // if ( + // is(exp, "BinaryExpression") || + // is(exp, "Identifier") || + // is(exp, "MemberExpression") || + // is(exp, "TaggedTemplateExpression") + // ) { + // // Will be implemented + // return []; + // } + + // if ((unsupported as typeof exp.type[]).includes(exp.type)) { + // if ( + // is(exp, "ArrayPattern") || + // is(exp, "ObjectPattern") || + // is(exp, "ArrowFunctionExpression") || + // is(exp, "AssignmentExpression") || + // is(exp, "AwaitExpression") || + // is(exp, "ChainExpression") || + // is(exp, "ClassExpression") || + // is(exp, "FunctionExpression") || + // is(exp, "ImportExpression") || + // is(exp, "JSXElement") || + // is(exp, "JSXFragment") || + // is(exp, "MetaProperty") || + // is(exp, "NewExpression") || + // is(exp, "SequenceExpression") || + // is(exp, "Super") || + // is(exp, "ThisExpression") || + // is(exp, "UnaryExpression") || + // is(exp, "UpdateExpression") || + // is(exp, "VariableDeclaration") || + // is(exp, "VariableDeclarator") || + // is(exp, "WhileStatement") || + // is(exp, "YieldExpression") || + // is(exp, "TSAsExpression") || + // is(exp, "TSInstantiationExpression") || + // is(exp, "TSNonNullExpression") || + // is(exp, "TSSatisfiesExpression") || + // is(exp, "TSTypeAssertion") + // ) { + // return []; + // } + if (!unimplemented.has(exp.type)) { console.log("Unimplemented: ", exp.type, exp); unimplemented.add(exp.type); @@ -160,23 +199,23 @@ function format< | TSESTree.Expression | TSESTree.TemplateElement, >( - nodeOrToken: T | T[], - getValue: (t: T) => string, - getRaw: (t: T) => string + token: T | T[], + getValue: string | ((t: T) => string), + getRaw: string | ((t: T) => string) ): (T & { getValue: () => string; getRaw: () => string })[] { - if (Array.isArray(nodeOrToken)) { - return nodeOrToken.map((t) => ({ + if (Array.isArray(token)) { + return token.map((t) => ({ ...t, - getValue: () => getValue(t), - getRaw: getRaw ? () => getRaw(t) : () => getValue(t), + getValue: () => callOrValue(getValue, t), + getRaw: () => callOrValue(getRaw, t), })); } return [ { - ...nodeOrToken, - getValue: () => getValue(nodeOrToken), - getRaw: () => (getRaw ?? getValue)(nodeOrToken), + ...token, + getValue: () => callOrValue(getValue, token), + getRaw: () => callOrValue(getRaw, token), }, ] as const; } @@ -186,3 +225,26 @@ function isStringLiteral( ): value is TSESTree.StringLiteral { return value?.type === "Literal" && typeof value?.value === "string"; } + +function callOrValue(func: T | (() => T)): T; +function callOrValue( + func: T | ((arg: P) => T), + param: P +): T; +function callOrValue( + func: T | ((arg: P) => T), + param?: P +): T { + return typeof func === "function" ? func(param!) : func; +} + +function T(type: `${TSESTree.AST_NODE_TYPES}`) { + return type as TSESTree.AST_NODE_TYPES; +} + +function is( + exp: TSESTree.Expression, + type: `${T}` +): exp is Extract { + return exp.type === type; +} diff --git a/vitest.config.ts b/vitest.config.ts index 024d121..50fba4a 100644 --- a/vitest.config.ts +++ b/vitest.config.ts @@ -4,6 +4,6 @@ import { defineConfig } from "vitest/config"; export default defineConfig({ cacheDir: path.resolve(__dirname, "./node_modules/.cache/vitest"), test: { - include: ["src/**/test.ts"] + include: ["src/**/test.ts"], }, }); From e0a67c3efdde3d05fd8f6838761d9fd6561b60ac Mon Sep 17 00:00:00 2001 From: Ahmed Abdelbaset Date: Tue, 6 Aug 2024 00:53:50 +0300 Subject: [PATCH 12/13] improve ts and lint --- eslint.config.js | 6 -- src/rules/no-phyisical-properties/test.ts | 2 +- src/utils/ast.ts | 126 +++++++++++++++------- vitest.config.ts | 2 +- 4 files changed, 91 insertions(+), 45 deletions(-) diff --git a/eslint.config.js b/eslint.config.js index afaf91c..91e69cd 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -2,7 +2,6 @@ import js from "@eslint/js"; import eslintPlugin from "eslint-plugin-eslint-plugin"; -import globals from "globals"; import { config, configs } from "typescript-eslint"; import rtlFriendly from "./dist/index.js"; @@ -11,11 +10,6 @@ export default config( { ignores: ["dist/**/*"], }, - { - languageOptions: { - globals: globals.node, - }, - }, eslintPlugin.configs["flat/recommended"], js.configs.recommended, ...configs.recommended, diff --git a/src/rules/no-phyisical-properties/test.ts b/src/rules/no-phyisical-properties/test.ts index 9e2c684..a7eb3ab 100644 --- a/src/rules/no-phyisical-properties/test.ts +++ b/src/rules/no-phyisical-properties/test.ts @@ -1,5 +1,5 @@ -import * as vitest from "vitest" import { RuleTester } from "@typescript-eslint/rule-tester"; +import * as vitest from "vitest"; import { NO_PHYSICAL_CLASSESS, noPhysicalProperties } from "./rule"; RuleTester.afterAll = vitest.afterAll; diff --git a/src/utils/ast.ts b/src/utils/ast.ts index 255746e..9b9e919 100644 --- a/src/utils/ast.ts +++ b/src/utils/ast.ts @@ -1,4 +1,4 @@ -import type { TSESTree } from "@typescript-eslint/utils"; +import { TSESTree } from "@typescript-eslint/utils"; const unimplemented = new Set(); @@ -16,29 +16,25 @@ export function extractTokenFromNode( runner: "checker" | "fixer" ): (Token | undefined | null)[] { // value: Literal | JSXExpressionContainer | JSXElement | JSXFragment | null - const type = node.value?.type; - if (!type) return []; + const value = node.value; + if (!value) return []; - const nodeValue = node.value; - - if (isStringLiteral(nodeValue)) - return format( - nodeValue, - (n) => n.value, - (n) => n.raw - ); + if (value?.type === "Literal") { + if (typeof value.value !== "string") return []; // boolean, number, null, undefined, etc... + return format(value, value.value, value.raw); + } - if (type === "JSXExpressionContainer") { - const expression = node.value?.expression; + if (value.type === "JSXExpressionContainer") { + const expression = value?.expression; if (!expression || expression?.type === "JSXEmptyExpression") return []; return extractTokenFromExpression(expression, runner); } - if (!unimplemented.has(type)) { - console.log("Unimplemented: ", type, node); - unimplemented.add(type); + if (value.type === "JSXElement" || value.type === "JSXSpreadChild") { + // JSXElement is like => + return []; } return []; @@ -62,7 +58,7 @@ function extractTokenFromExpression( // const isFixer = runner === "fixer"; - if (exp.type === "Literal") { + if (is(exp, "Literal")) { if (typeof exp.value !== "string") return []; // boolean, number, null, undefined, etc... return format( @@ -72,7 +68,7 @@ function extractTokenFromExpression( ); } - if (exp?.type === "TemplateLiteral") { + if (is(exp, "TemplateLiteral")) { return format( exp.quasis, (q) => q.value.cooked, @@ -80,16 +76,16 @@ function extractTokenFromExpression( ); } - if (exp.type === "LogicalExpression") { + if (is(exp, "LogicalExpression")) { // isCondition && "..." return rerun(exp.right); } - if (exp.type === "ConditionalExpression") { + if (is(exp, "ConditionalExpression")) { return [...rerun(exp.consequent), ...rerun(exp.alternate)]; } - if (exp.type === "ArrayExpression") { + if (is(exp, "ArrayExpression")) { return exp.elements.flatMap((el) => { if (!el) return []; @@ -99,7 +95,7 @@ function extractTokenFromExpression( }); } - if (exp.type === "ObjectExpression") { + if (is(exp, "ObjectExpression")) { return exp.properties.flatMap((prop) => { if (prop.type === "SpreadElement") return rerun(prop.argument); @@ -115,7 +111,7 @@ function extractTokenFromExpression( }); } - if (exp.type === "CallExpression") { + if (is(exp, "CallExpression")) { return exp.arguments.flatMap((arg) => { if (arg.type === "SpreadElement") { return rerun(arg.argument); @@ -125,6 +121,49 @@ function extractTokenFromExpression( }); } + // if ( + // is(exp, "BinaryExpression") || + // is(exp, "Identifier") || + // is(exp, "MemberExpression") || + // is(exp, "TaggedTemplateExpression") + // ) { + // // Will be implemented + // return []; + // } + + // if ((unsupported as typeof exp.type[]).includes(exp.type)) { + // if ( + // is(exp, "ArrayPattern") || + // is(exp, "ObjectPattern") || + // is(exp, "ArrowFunctionExpression") || + // is(exp, "AssignmentExpression") || + // is(exp, "AwaitExpression") || + // is(exp, "ChainExpression") || + // is(exp, "ClassExpression") || + // is(exp, "FunctionExpression") || + // is(exp, "ImportExpression") || + // is(exp, "JSXElement") || + // is(exp, "JSXFragment") || + // is(exp, "MetaProperty") || + // is(exp, "NewExpression") || + // is(exp, "SequenceExpression") || + // is(exp, "Super") || + // is(exp, "ThisExpression") || + // is(exp, "UnaryExpression") || + // is(exp, "UpdateExpression") || + // is(exp, "VariableDeclaration") || + // is(exp, "VariableDeclarator") || + // is(exp, "WhileStatement") || + // is(exp, "YieldExpression") || + // is(exp, "TSAsExpression") || + // is(exp, "TSInstantiationExpression") || + // is(exp, "TSNonNullExpression") || + // is(exp, "TSSatisfiesExpression") || + // is(exp, "TSTypeAssertion") + // ) { + // return []; + // } + if (!unimplemented.has(exp.type)) { console.log("Unimplemented: ", exp.type, exp); unimplemented.add(exp.type); @@ -160,29 +199,42 @@ function format< | TSESTree.Expression | TSESTree.TemplateElement, >( - nodeOrToken: T | T[], - getValue: (t: T) => string, - getRaw: (t: T) => string + token: T | T[], + getValue: string | ((t: T) => string), + getRaw: string | ((t: T) => string) ): (T & { getValue: () => string; getRaw: () => string })[] { - if (Array.isArray(nodeOrToken)) { - return nodeOrToken.map((t) => ({ + if (Array.isArray(token)) { + return token.map((t) => ({ ...t, - getValue: () => getValue(t), - getRaw: getRaw ? () => getRaw(t) : () => getValue(t), + getValue: () => callOrValue(getValue, t), + getRaw: () => callOrValue(getRaw, t), })); } return [ { - ...nodeOrToken, - getValue: () => getValue(nodeOrToken), - getRaw: () => (getRaw ?? getValue)(nodeOrToken), + ...token, + getValue: () => callOrValue(getValue, token), + getRaw: () => callOrValue(getRaw, token), }, ] as const; } -function isStringLiteral( - value: TSESTree.JSXAttribute["value"] | TSESTree.Expression -): value is TSESTree.StringLiteral { - return value?.type === "Literal" && typeof value?.value === "string"; +function callOrValue(func: T | (() => T)): T; +function callOrValue( + func: T | ((arg: P) => T), + param: P +): T; +function callOrValue( + func: T | ((arg: P) => T), + param?: P +): T { + return typeof func === "function" ? func(param!) : func; +} + +function is( + exp: TSESTree.Expression, + type: `${T}` +): exp is Extract { + return exp.type === type; } diff --git a/vitest.config.ts b/vitest.config.ts index 024d121..50fba4a 100644 --- a/vitest.config.ts +++ b/vitest.config.ts @@ -4,6 +4,6 @@ import { defineConfig } from "vitest/config"; export default defineConfig({ cacheDir: path.resolve(__dirname, "./node_modules/.cache/vitest"), test: { - include: ["src/**/test.ts"] + include: ["src/**/test.ts"], }, }); From 3abb3bda80b9039b1a9ac5964d3000456f4434ea Mon Sep 17 00:00:00 2001 From: Ahmed Abdelbaset Date: Tue, 6 Aug 2024 01:02:36 +0300 Subject: [PATCH 13/13] fix rebase --- src/utils/ast.ts | 23 ----------------------- 1 file changed, 23 deletions(-) diff --git a/src/utils/ast.ts b/src/utils/ast.ts index a2e34c0..9b9e919 100644 --- a/src/utils/ast.ts +++ b/src/utils/ast.ts @@ -238,26 +238,3 @@ function is( ): exp is Extract { return exp.type === type; } - -function callOrValue(func: T | (() => T)): T; -function callOrValue( - func: T | ((arg: P) => T), - param: P -): T; -function callOrValue( - func: T | ((arg: P) => T), - param?: P -): T { - return typeof func === "function" ? func(param!) : func; -} - -function T(type: `${TSESTree.AST_NODE_TYPES}`) { - return type as TSESTree.AST_NODE_TYPES; -} - -function is( - exp: TSESTree.Expression, - type: `${T}` -): exp is Extract { - return exp.type === type; -}