diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..8746364 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,49 @@ +name: CI + +on: + pull_request: + types: [opened, synchronize, reopened, ready_for_review] + +jobs: + tests: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + - uses: pnpm/action-setup@v2 + with: + version: 8 + + - uses: actions/setup-node@v4 + with: + node-version: 18 + cache: 'pnpm' + + - name: Install dependencies + run: pnpm install + + - name: Unit Tests + run: pnpm run test + + Linting: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + - uses: pnpm/action-setup@v2 + with: + version: 8 + + - uses: actions/setup-node@v4 + with: + node-version: 18 + cache: 'pnpm' + + - name: Install dependencies + run: pnpm install + + - name: Building + run: pnpm run build + + - name: Linting + run: pnpm run lint diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml deleted file mode 100644 index 9c7db5d..0000000 --- a/.github/workflows/test.yml +++ /dev/null @@ -1,26 +0,0 @@ -name: tests - -on: - pull_request: - types: [opened, synchronize, reopened, ready_for_review] - -jobs: - tests: - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v4 - - uses: pnpm/action-setup@v2 - with: - version: 8 - - - uses: actions/setup-node@v4 - with: - node-version: 18 - cache: 'pnpm' - - - name: Install dependencies - run: pnpm install - - - name: CI-check - run: pnpm run test diff --git a/e2e/no-physical-properties.tsx b/e2e/no-physical-properties.tsx new file mode 100644 index 0000000..f7be22e --- /dev/null +++ b/e2e/no-physical-properties.tsx @@ -0,0 +1,479 @@ +declare const React; + +<> + <> +

Valid

+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ + + <> +

Valid with negative

+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ + + <> +

Valid important

+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ + + <> +

Valid with modifiers

+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ + + <> +

Valid with modifiers and negative

+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ + + <> +

Valid with modifiers and important

+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ + + <> +

Valid with modifiers, important and negative flags

+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ + + <> +

Invalid

+ {/* eslint-disable-next-line rtl-friendly/no-physical-properties */} +
+ {/* eslint-disable-next-line rtl-friendly/no-physical-properties */} +
+ {/* eslint-disable-next-line rtl-friendly/no-physical-properties */} +
+ {/* eslint-disable-next-line rtl-friendly/no-physical-properties */} +
+ {/* eslint-disable-next-line rtl-friendly/no-physical-properties */} +
+ {/* eslint-disable-next-line rtl-friendly/no-physical-properties */} +
+ {/* eslint-disable-next-line rtl-friendly/no-physical-properties */} +
+ {/* eslint-disable-next-line rtl-friendly/no-physical-properties */} +
+ {/* eslint-disable-next-line rtl-friendly/no-physical-properties */} +
+ {/* eslint-disable-next-line rtl-friendly/no-physical-properties */} +
+ {/* eslint-disable-next-line rtl-friendly/no-physical-properties */} +
+ {/* eslint-disable-next-line rtl-friendly/no-physical-properties */} +
+ {/* eslint-disable-next-line rtl-friendly/no-physical-properties */} +
+ {/* eslint-disable-next-line rtl-friendly/no-physical-properties */} +
+ {/* eslint-disable-next-line rtl-friendly/no-physical-properties */} +
+ {/* eslint-disable-next-line rtl-friendly/no-physical-properties */} +
+ {/* eslint-disable-next-line rtl-friendly/no-physical-properties */} +
+ {/* eslint-disable-next-line rtl-friendly/no-physical-properties */} +
+ {/* eslint-disable-next-line rtl-friendly/no-physical-properties */} +
+ {/* eslint-disable-next-line rtl-friendly/no-physical-properties */} +
+ + + <> +

Invalid with negative

+ {/* eslint-disable-next-line rtl-friendly/no-physical-properties */} +
+ {/* eslint-disable-next-line rtl-friendly/no-physical-properties */} +
+ {/* eslint-disable-next-line rtl-friendly/no-physical-properties */} +
+ {/* eslint-disable-next-line rtl-friendly/no-physical-properties */} +
+ {/* eslint-disable-next-line rtl-friendly/no-physical-properties */} +
+ {/* eslint-disable-next-line rtl-friendly/no-physical-properties */} +
+ {/* eslint-disable-next-line rtl-friendly/no-physical-properties */} +
+ {/* eslint-disable-next-line rtl-friendly/no-physical-properties */} +
+ {/* eslint-disable-next-line rtl-friendly/no-physical-properties */} +
+ {/* eslint-disable-next-line rtl-friendly/no-physical-properties */} +
+ {/* eslint-disable-next-line rtl-friendly/no-physical-properties */} +
+ {/* eslint-disable-next-line rtl-friendly/no-physical-properties */} +
+ {/* eslint-disable-next-line rtl-friendly/no-physical-properties */} +
+ {/* eslint-disable-next-line rtl-friendly/no-physical-properties */} +
+ {/* eslint-disable-next-line rtl-friendly/no-physical-properties */} +
+ {/* eslint-disable-next-line rtl-friendly/no-physical-properties */} +
+ {/* eslint-disable-next-line rtl-friendly/no-physical-properties */} +
+ {/* eslint-disable-next-line rtl-friendly/no-physical-properties */} +
+ {/* eslint-disable-next-line rtl-friendly/no-physical-properties */} +
+ {/* eslint-disable-next-line rtl-friendly/no-physical-properties */} +
+ + + <> +

Invalid important

+ {/* eslint-disable-next-line rtl-friendly/no-physical-properties */} +
+ {/* eslint-disable-next-line rtl-friendly/no-physical-properties */} +
+ {/* eslint-disable-next-line rtl-friendly/no-physical-properties */} +
+ {/* eslint-disable-next-line rtl-friendly/no-physical-properties */} +
+ {/* eslint-disable-next-line rtl-friendly/no-physical-properties */} +
+ {/* eslint-disable-next-line rtl-friendly/no-physical-properties */} +
+ {/* eslint-disable-next-line rtl-friendly/no-physical-properties */} +
+ {/* eslint-disable-next-line rtl-friendly/no-physical-properties */} +
+ {/* eslint-disable-next-line rtl-friendly/no-physical-properties */} +
+ {/* eslint-disable-next-line rtl-friendly/no-physical-properties */} +
+ {/* eslint-disable-next-line rtl-friendly/no-physical-properties */} +
+ {/* eslint-disable-next-line rtl-friendly/no-physical-properties */} +
+ {/* eslint-disable-next-line rtl-friendly/no-physical-properties */} +
+ {/* eslint-disable-next-line rtl-friendly/no-physical-properties */} +
+ {/* eslint-disable-next-line rtl-friendly/no-physical-properties */} +
+ {/* eslint-disable-next-line rtl-friendly/no-physical-properties */} +
+ {/* eslint-disable-next-line rtl-friendly/no-physical-properties */} +
+ {/* eslint-disable-next-line rtl-friendly/no-physical-properties */} +
+ {/* eslint-disable-next-line rtl-friendly/no-physical-properties */} +
+ {/* eslint-disable-next-line rtl-friendly/no-physical-properties */} +
+ + + <> +

Invalid with modifiers

+ {/* eslint-disable-next-line rtl-friendly/no-physical-properties */} +
+ {/* eslint-disable-next-line rtl-friendly/no-physical-properties */} +
+ {/* eslint-disable-next-line rtl-friendly/no-physical-properties */} +
+ {/* eslint-disable-next-line rtl-friendly/no-physical-properties */} +
+ {/* eslint-disable-next-line rtl-friendly/no-physical-properties */} +
+ {/* eslint-disable-next-line rtl-friendly/no-physical-properties */} +
+ {/* eslint-disable-next-line rtl-friendly/no-physical-properties */} +
+ {/* eslint-disable-next-line rtl-friendly/no-physical-properties */} +
+ {/* eslint-disable-next-line rtl-friendly/no-physical-properties */} +
+ {/* eslint-disable-next-line rtl-friendly/no-physical-properties */} +
+ {/* eslint-disable-next-line rtl-friendly/no-physical-properties */} +
+ {/* eslint-disable-next-line rtl-friendly/no-physical-properties */} +
+ {/* eslint-disable-next-line rtl-friendly/no-physical-properties */} +
+ {/* eslint-disable-next-line rtl-friendly/no-physical-properties */} +
+ {/* eslint-disable-next-line rtl-friendly/no-physical-properties */} +
+ {/* eslint-disable-next-line rtl-friendly/no-physical-properties */} +
+ {/* eslint-disable-next-line rtl-friendly/no-physical-properties */} +
+ {/* eslint-disable-next-line rtl-friendly/no-physical-properties */} +
+ {/* eslint-disable-next-line rtl-friendly/no-physical-properties */} +
+ {/* eslint-disable-next-line rtl-friendly/no-physical-properties */} +
+ + + <> +

Invalid with modifiers and negative

+ {/* eslint-disable-next-line rtl-friendly/no-physical-properties */} +
+ {/* eslint-disable-next-line rtl-friendly/no-physical-properties */} +
+ {/* eslint-disable-next-line rtl-friendly/no-physical-properties */} +
+ {/* eslint-disable-next-line rtl-friendly/no-physical-properties */} +
+ {/* eslint-disable-next-line rtl-friendly/no-physical-properties */} +
+ {/* eslint-disable-next-line rtl-friendly/no-physical-properties */} +
+ {/* eslint-disable-next-line rtl-friendly/no-physical-properties */} +
+ {/* eslint-disable-next-line rtl-friendly/no-physical-properties */} +
+ {/* eslint-disable-next-line rtl-friendly/no-physical-properties */} +
+ {/* eslint-disable-next-line rtl-friendly/no-physical-properties */} +
+ {/* eslint-disable-next-line rtl-friendly/no-physical-properties */} +
+ {/* eslint-disable-next-line rtl-friendly/no-physical-properties */} +
+ {/* eslint-disable-next-line rtl-friendly/no-physical-properties */} +
+ {/* eslint-disable-next-line rtl-friendly/no-physical-properties */} +
+ {/* eslint-disable-next-line rtl-friendly/no-physical-properties */} +
+ {/* eslint-disable-next-line rtl-friendly/no-physical-properties */} +
+ {/* eslint-disable-next-line rtl-friendly/no-physical-properties */} +
+ {/* eslint-disable-next-line rtl-friendly/no-physical-properties */} +
+ {/* eslint-disable-next-line rtl-friendly/no-physical-properties */} +
+ {/* eslint-disable-next-line rtl-friendly/no-physical-properties */} +
+ + + <> +

Invalid with modifiers and important

+ {/* eslint-disable-next-line rtl-friendly/no-physical-properties */} +
+ {/* eslint-disable-next-line rtl-friendly/no-physical-properties */} +
+ {/* eslint-disable-next-line rtl-friendly/no-physical-properties */} +
+ {/* eslint-disable-next-line rtl-friendly/no-physical-properties */} +
+ {/* eslint-disable-next-line rtl-friendly/no-physical-properties */} +
+ {/* eslint-disable-next-line rtl-friendly/no-physical-properties */} +
+ {/* eslint-disable-next-line rtl-friendly/no-physical-properties */} +
+ {/* eslint-disable-next-line rtl-friendly/no-physical-properties */} +
+ {/* eslint-disable-next-line rtl-friendly/no-physical-properties */} +
+ {/* eslint-disable-next-line rtl-friendly/no-physical-properties */} +
+ {/* eslint-disable-next-line rtl-friendly/no-physical-properties */} +
+ {/* eslint-disable-next-line rtl-friendly/no-physical-properties */} +
+ {/* eslint-disable-next-line rtl-friendly/no-physical-properties */} +
+ {/* eslint-disable-next-line rtl-friendly/no-physical-properties */} +
+ {/* eslint-disable-next-line rtl-friendly/no-physical-properties */} +
+ {/* eslint-disable-next-line rtl-friendly/no-physical-properties */} +
+ {/* eslint-disable-next-line rtl-friendly/no-physical-properties */} +
+ {/* eslint-disable-next-line rtl-friendly/no-physical-properties */} +
+ {/* eslint-disable-next-line rtl-friendly/no-physical-properties */} +
+ {/* eslint-disable-next-line rtl-friendly/no-physical-properties */} +
+ + + <> +

Invalid with modifiers, important and negative flags

+ {/* eslint-disable-next-line rtl-friendly/no-physical-properties */} +
+ {/* eslint-disable-next-line rtl-friendly/no-physical-properties */} +
+ {/* eslint-disable-next-line rtl-friendly/no-physical-properties */} +
+ {/* eslint-disable-next-line rtl-friendly/no-physical-properties */} +
+ {/* eslint-disable-next-line rtl-friendly/no-physical-properties */} +
+ {/* eslint-disable-next-line rtl-friendly/no-physical-properties */} +
+ {/* eslint-disable-next-line rtl-friendly/no-physical-properties */} +
+ {/* eslint-disable-next-line rtl-friendly/no-physical-properties */} +
+ {/* eslint-disable-next-line rtl-friendly/no-physical-properties */} +
+ {/* eslint-disable-next-line rtl-friendly/no-physical-properties */} +
+ {/* eslint-disable-next-line rtl-friendly/no-physical-properties */} +
+ {/* eslint-disable-next-line rtl-friendly/no-physical-properties */} +
+ {/* eslint-disable-next-line rtl-friendly/no-physical-properties */} +
+ {/* eslint-disable-next-line rtl-friendly/no-physical-properties */} +
+ {/* eslint-disable-next-line rtl-friendly/no-physical-properties */} +
+ {/* eslint-disable-next-line rtl-friendly/no-physical-properties */} +
+ {/* eslint-disable-next-line rtl-friendly/no-physical-properties */} +
+ {/* eslint-disable-next-line rtl-friendly/no-physical-properties */} +
+ {/* eslint-disable-next-line rtl-friendly/no-physical-properties */} +
+ {/* eslint-disable-next-line rtl-friendly/no-physical-properties */} +
+ +; diff --git a/jest.config.js b/jest.config.js index 08b935d..b0086e1 100644 --- a/jest.config.js +++ b/jest.config.js @@ -2,4 +2,18 @@ export default { preset: "ts-jest", testEnvironment: "node", testMatch: ["**/tests/**/*.ts"], + extensionsToTreatAsEsm: ['.ts'], + moduleNameMapper: { + '^(\\.{1,2}/.*)\\.js$': '$1', + }, + transform: { + // '^.+\\.[tj]sx?$' to process ts,js,tsx,jsx with `ts-jest` + // '^.+\\.m?[tj]sx?$' to process ts,js,tsx,jsx,mts,mjs,mtsx,mjsx with `ts-jest` + '^.+\\.tsx?$': [ + 'ts-jest', + { + useESM: true, + }, + ], + }, }; diff --git a/package.json b/package.json index 1dacc64..d78f101 100644 --- a/package.json +++ b/package.json @@ -34,12 +34,13 @@ "test:watch": "jest --watch", "test:coverage": "jest --coverage", "release": "semantic-release", - "add-rule": "ts-node scripts/add-rule", - "update": "ts-node scripts/update", - "prepublishOnly": "npm run build" + "add-rule": "tsx scripts/add-rule", + "update": "tsx scripts/update", + "prepublishOnly": "npm run build", + "gen-e2e": "tsx scripts/generate-e2e" }, "peerDependencies": { - "eslint": ">=5.16.0", + "eslint": ">=8.0.0", "tailwindcss": ">=3.3.0" }, "devDependencies": { diff --git a/scripts/generate-e2e.ts b/scripts/generate-e2e.ts new file mode 100644 index 0000000..5335d79 --- /dev/null +++ b/scripts/generate-e2e.ts @@ -0,0 +1,145 @@ +import { exec } from "child_process"; +import { mkdir, writeFile } from "fs/promises"; +import { logicalProperties } from "../src/configs/tw-logical-properties"; +import path from "path"; + +const modifiers = [ + "dark", + "dark:md:hover", + "@md", + "@md:dark:hover", + "group-has", + "[&>svg]", + "data-[state=active]", + "aria-[hidden]", + "group-hover/name", + "group-[:nth-of-type(3)_&]", + "*" +]; + +let valid = "

Valid

\n"; +let validWithNegative = "

Valid with negative

\n"; +let validWithImportant = "

Valid important

\n"; +let validWithModifier = "

Valid with modifiers

\n"; +let validWithModifierAndNegative = + "

Valid with modifiers and negative

\n"; +let validWithModifierAndImportant = + "

Valid with modifiers and important

\n"; +let validWithModifierAndNegativeAndImportant = + "

Valid with modifiers, important and negative flags

\n"; + +let invalid = "

Invalid

\n"; +let invalidWithNegative = "

Invalid with negative

\n"; +let invalidWithImportant = "

Invalid important

\n"; +let invalidWithModifier = "

Invalid with modifiers

\n"; +let invalidWithModifierAndNegative = + "

Invalid with modifiers and negative

\n"; +let invalidWithModifierAndImportant = + "

Invalid with modifiers and important

\n"; +let invalidWithModifierAndNegativeAndImportant = + "

Invalid with modifiers, important and negative flags

\n"; + +for (const { logical } of logicalProperties) { + valid += `
\n`; +} + +for (const { logical } of logicalProperties) { + validWithNegative += `
\n`; +} + +for (const { logical } of logicalProperties) { + validWithImportant += `
\n`; +} + +for (const { logical } of logicalProperties) { + const modifier = modifiers[Math.floor(Math.random() * modifiers.length)]; + validWithModifier += `
\n`; +} + +for (const { logical } of logicalProperties) { + const modifier = modifiers[Math.floor(Math.random() * modifiers.length)]; + validWithModifierAndNegative += `
\n`; +} + +for (const { logical } of logicalProperties) { + const modifier = modifiers[Math.floor(Math.random() * modifiers.length)]; + validWithModifierAndImportant += `
\n`; +} + +for (const { logical } of logicalProperties) { + const modifier = modifiers[Math.floor(Math.random() * modifiers.length)]; + validWithModifierAndNegativeAndImportant += `
\n`; +} + +// Invalid +for (const { physical } of logicalProperties) { + invalid += `{/* eslint-disable-next-line rtl-friendly/no-physical-properties */}\n
\n`; +} + +for (const { physical } of logicalProperties) { + invalidWithNegative += `{/* eslint-disable-next-line rtl-friendly/no-physical-properties */}\n
\n`; +} + +for (const { physical } of logicalProperties) { + invalidWithImportant += `{/* eslint-disable-next-line rtl-friendly/no-physical-properties */}\n
\n`; +} + +for (const { physical } of logicalProperties) { + const modifier = modifiers[Math.floor(Math.random() * modifiers.length)]; + invalidWithModifier += `{/* eslint-disable-next-line rtl-friendly/no-physical-properties */}\n
\n`; +} + +for (const { physical } of logicalProperties) { + const modifier = modifiers[Math.floor(Math.random() * modifiers.length)]; + invalidWithModifierAndNegative += `{/* eslint-disable-next-line rtl-friendly/no-physical-properties */}\n
\n`; +} + +for (const { physical } of logicalProperties) { + const modifier = modifiers[Math.floor(Math.random() * modifiers.length)]; + invalidWithModifierAndImportant += `{/* eslint-disable-next-line rtl-friendly/no-physical-properties */}\n
\n`; +} + +for (const { physical } of logicalProperties) { + const modifier = modifiers[Math.floor(Math.random() * modifiers.length)]; + invalidWithModifierAndNegativeAndImportant += `{/* eslint-disable-next-line rtl-friendly/no-physical-properties */}\n
\n`; +} + +await mkdir(path.join(process.cwd(), "e2e"), { recursive: true }); +await writeFile( + path.join(process.cwd(), "e2e", "no-physical-properties.tsx"), + ` +declare const React; + +<> + <>${valid} + + <>${validWithNegative} + + <>${validWithImportant} + + <>${validWithModifier} + + <>${validWithModifierAndNegative} + + <>${validWithModifierAndImportant} + + <>${validWithModifierAndNegativeAndImportant} + + <>${invalid} + + <>${invalidWithNegative} + + <>${invalidWithImportant} + + <>${invalidWithModifier} + + <>${invalidWithModifierAndNegative} + + <>${invalidWithModifierAndImportant} + + <>${invalidWithModifierAndNegativeAndImportant} + +` +); + +exec("biome check e2e --write") \ No newline at end of file diff --git a/src/index.ts b/src/index.ts index d83a97a..e61fc8f 100644 --- a/src/index.ts +++ b/src/index.ts @@ -3,9 +3,7 @@ import type { ESLint, Linter } from "eslint"; import noPhysicalProperties from "./rules/no-physical-properties.js"; import { createRequire } from "module"; -const pkg = createRequire(import.meta.url)( - "../package.json" -) as typeof import("../package.json"); +const pkg = createRequire(import.meta.url)("../package.json"); const rtlFriendly = { meta: { diff --git a/tests/rules/no-physical-properties.ts b/tests/rules/no-physical-properties.ts index ab33278..18f8aea 100644 --- a/tests/rules/no-physical-properties.ts +++ b/tests/rules/no-physical-properties.ts @@ -39,6 +39,10 @@ tester.run("no-physical-properties", logicalProperties, { name: "should work well with stacked modifiers", code: '
', }, + { + name: "should work well with : in the modifier", + code: '
', + }, ], invalid: [ { @@ -138,5 +142,11 @@ tester.run("no-physical-properties", logicalProperties, { output: '
', errors: [{ messageId: "noPhysicalProperties" }], }, + { + name: "should report if physical properties are used with : in the modifier", + code: '
', + output: '
', + errors: [{ messageId: "noPhysicalProperties" }], + }, ], }); diff --git a/tsconfig.json b/tsconfig.json index aed76f1..bbbddbd 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -9,11 +9,10 @@ "declarationMap": true, "esModuleInterop": true, "lib": ["ES2021"], + "noImplicitReturns": true, "module": "NodeNext", "moduleResolution": "NodeNext", - "noImplicitReturns": true, "pretty": true, - "resolveJsonModule": true, "skipLibCheck": true, "sourceMap": true, "target": "ES2021",