Skip to content

Commit a934584

Browse files
authored
feat:eslint plugin (#16)
* eslint plugin ready to test * working eslint plugin * working v1 * published beta.1 lint package * add correct deps * update lockfile
1 parent 0b84899 commit a934584

28 files changed

+1118
-395
lines changed

eslint.config.js

Lines changed: 0 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -67,21 +67,4 @@ export default [
6767
},
6868

6969
/* 5 - JSX files */
70-
{
71-
files: ['**/*.jsx'],
72-
plugins: { node: nodePlugin, import: importPlugin },
73-
languageOptions: {
74-
ecmaVersion: 'latest',
75-
sourceType: 'module',
76-
globals: { ...nodeGlobals, ...browserGlobals },
77-
parserOptions: { ecmaFeatures: { jsx: true } },
78-
},
79-
rules: {
80-
'node/no-unsupported-features/es-syntax': 'off',
81-
'import/no-unresolved': 'error',
82-
'import/named': 'error',
83-
'import/default': 'error',
84-
'import/no-absolute-path': 'error',
85-
},
86-
},
8770
];

package.json

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,16 @@
88
"workspaces": [
99
"packages/*"
1010
],
11-
"packageManager": "pnpm@10.13.1+sha512.37ebf1a5c7a30d5fabe0c5df44ee8da4c965ca0c5af3dbab28c3a1681b70a256218d05c81c9c0dcf767ef6b8551eb5b960042b9ed4300c59242336377e01cfad",
11+
"packageManager": "pnpm@10.13.1",
1212
"references": [
1313
{
1414
"path": "packages/core"
1515
},
1616
{
1717
"path": "packages/cli"
18+
},
19+
{
20+
"path": "packages/eslint-plugin-react-zero-ui"
1821
}
1922
],
2023
"scripts": {
@@ -37,15 +40,16 @@
3740
"size": "npx esbuild ./packages/core/dist/index.js --bundle --minify --format=esm --external:react --define:process.env.NODE_ENV='\"production\"' | gzip -c | wc -c"
3841
},
3942
"devDependencies": {
40-
"@eslint/js": "^9.30.1",
41-
"@types/node": "^24.0.13",
42-
"esbuild": "^0.25.6",
43-
"eslint": "^9.30.1",
43+
"@eslint/js": "^9.32.0",
44+
"@types/node": "^24.1.0",
45+
"esbuild": "^0.25.8",
46+
"eslint": "^9.32.0",
4447
"eslint-plugin-import": "^2.32.0",
4548
"eslint-plugin-node": "^11.1.0",
49+
"eslint-plugin-react-zero-ui": "workspace:*",
4650
"prettier": "^3.6.2",
4751
"release-please": "^17.1.1",
4852
"tsx": "^4.20.3",
49-
"typescript": "^5.8.3"
53+
"typescript": "^5.9.2"
5054
}
51-
}
55+
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
'use client';
2+
import { useScopedUI } from '@react-zero-ui/core';
3+
4+
/**
5+
* This component is intentionally WRONG.
6+
* – Missing data-attr on <section>
7+
* – Missing ref attachment on <aside>
8+
*
9+
* The Zero-UI ESLint rule should flag both.
10+
*/
11+
export default function LintFailures() {
12+
// #1 Setter attached but no data-scope attr → missingAttr error
13+
const [scope, setScope] = useScopedUI<'off' | 'on'>('scope', 'off');
14+
15+
// #2 No ref at all → missingRef error
16+
const [, setDialog] = useScopedUI<'open' | 'closed'>('dialog', 'closed');
17+
18+
return (
19+
<main className="space-y-6 p-6">
20+
{/* ❌ lint error expected here */}
21+
<section
22+
ref={setScope.ref}
23+
className="scope-off:bg-red-100 scope-on:bg-red-600 scope-on:text-white p-4 rounded">
24+
<button
25+
className="border px-3 py-1"
26+
onClick={() => setScope((prev) => (prev === 'on' ? 'off' : 'on'))}>
27+
Toggle scope
28+
</button>
29+
</section>
30+
31+
{/* ❌ second lint error (missing .ref) */}
32+
<aside className="dialog-open:block dialog-closed:hidden">
33+
This dialog was never linked via <code>ref</code>
34+
</aside>
35+
</main>
36+
);
37+
}
38+
39+
/* ------------------------------------------------------------------ *
40+
| Correct version (for reference) |
41+
* ------------------------------------------------------------------ *
42+
const [scope, setScope] = useScopedUI<'off' | 'on'>('scope', 'off');
43+
<section
44+
data-scope={scope}
45+
ref={setScope.ref}
46+
>
47+
48+
</section>
49+
50+
const [, setDialog] = useScopedUI<'open'|'closed'>('dialog','closed');
51+
<aside ref={setDialog.ref} data-dialog="closed">…</aside>
52+
*/

packages/core/__tests__/fixtures/next/app/test/page.jsx

Lines changed: 0 additions & 209 deletions
This file was deleted.

packages/core/__tests__/fixtures/next/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
"@tailwindcss/postcss": "^4.1.10",
1717
"@types/node": "24.0.0",
1818
"@types/react": "19.1.7",
19+
"eslint-plugin-react-zero-ui": "0.0.1-beta.1",
1920
"postcss": "^8.5.5",
2021
"tailwindcss": "^4.1.10",
2122
"typescript": "5.8.3"

packages/core/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,4 +94,4 @@
9494
"@types/react": "^19.1.8",
9595
"tsx": "^4.20.3"
9696
}
97-
}
97+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
/* AUTO-GENERATED - DO NOT EDIT */
2+
export declare const bodyAttributes: {
3+
"data-child": "closed" | "open";
4+
"data-faq": "closed" | "open";
5+
"data-mobile": "false" | "true";
6+
"data-number": "1" | "2";
7+
"data-scope": "off" | "on";
8+
"data-theme": "dark" | "light";
9+
"data-theme-2": "dark" | "light";
10+
"data-theme-three": "dark" | "light";
11+
"data-toggle-boolean": "false" | "true";
12+
"data-toggle-function": "black" | "blue" | "green" | "red" | "white";
13+
"data-use-effect-theme": "dark" | "light";
14+
};
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
/* AUTO-GENERATED - DO NOT EDIT */
2+
export const bodyAttributes = {
3+
"data-child": "closed",
4+
"data-faq": "closed",
5+
"data-mobile": "false",
6+
"data-number": "1",
7+
"data-scope": "off",
8+
"data-theme": "light",
9+
"data-theme-2": "light",
10+
"data-theme-three": "light",
11+
"data-toggle-boolean": "true",
12+
"data-toggle-function": "white",
13+
"data-use-effect-theme": "light"
14+
};
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { type UISetterFn } from '@react-zero-ui/core';
2+
3+
export function ChildComponent({ setIsOpen }: { setIsOpen: UISetterFn }) {
4+
return (
5+
<div
6+
className="child-closed:bg-gray-100 child-open:bg-gray-900 child-open:text-white"
7+
data-testid="child-container">
8+
<button
9+
type="button"
10+
onClick={() => setIsOpen((prev) => (prev === 'closed' ? 'open' : 'closed'))}
11+
className="border-2 border-red-500"
12+
data-testid="child-toggle">
13+
Toggle Child
14+
</button>
15+
<div className="child-closed:bg-gray-100 child-open:bg-gray-900 flex">
16+
Child: <span className="child-open:block child-closed:hidden">Open</span> <span className="child-closed:block child-open:hidden">Closed</span>
17+
</div>
18+
</div>
19+
);
20+
}

0 commit comments

Comments
 (0)