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",