@rnzeus/eslint-plugin 0.1.3 → 0.1.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +125 -31
- package/dist/configs/base.cjs +592 -0
- package/dist/configs/base.cjs.map +1 -0
- package/dist/configs/imports.cjs +11 -0
- package/dist/configs/imports.cjs.map +1 -0
- package/dist/configs/styles.cjs +0 -423
- package/dist/configs/styles.cjs.map +1 -1
- package/dist/plugin.cjs +161 -0
- package/dist/plugin.cjs.map +1 -1
- package/package.json +4 -2
package/README.md
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
|
-
# @rnzeus/eslint-plugin
|
|
1
|
+
# @rnzeus/eslint-plugin
|
|
2
2
|
|
|
3
|
-
ESLint plugin for React / React Native projects focused on **style discipline**.
|
|
3
|
+
ESLint plugin for React / React Native projects focused on **style discipline** and **FSD import boundaries**.
|
|
4
4
|
|
|
5
5
|
This plugin is designed to solve real-world problems that appear during refactoring:
|
|
6
|
+
|
|
6
7
|
- unused styles left in `*.styles.ts` files
|
|
7
8
|
- inconsistent style naming
|
|
8
9
|
- unsafe dynamic style usage
|
|
9
10
|
- passing whole `styles` objects to components
|
|
11
|
+
- architectural erosion via unconstrained cross-slice imports (FSD)
|
|
10
12
|
|
|
11
13
|
The plugin is **static**, **runtime-free**, and optimized for **ESLint Flat Config (ESLint v9+)**.
|
|
12
14
|
|
|
@@ -15,9 +17,11 @@ The plugin is **static**, **runtime-free**, and optimized for **ESLint Flat Conf
|
|
|
15
17
|
## Features
|
|
16
18
|
|
|
17
19
|
### ✅ `rnzeus/styles-usage`
|
|
20
|
+
|
|
18
21
|
Ensures that all styles declared in `*.styles.ts(x)` files are actually used in the corresponding component file.
|
|
19
22
|
|
|
20
23
|
**What it checks:**
|
|
24
|
+
|
|
21
25
|
- detects unused style keys
|
|
22
26
|
- forbids passing the whole `styles` object (`styles={styles}`)
|
|
23
27
|
- supports arrays: `style={[styles.foo, condition && styles.bar]}`
|
|
@@ -26,6 +30,7 @@ Ensures that all styles declared in `*.styles.ts(x)` files are actually used in
|
|
|
26
30
|
- supports additional “style-like” props (configurable): e.g. `contentContainerStyle`
|
|
27
31
|
|
|
28
32
|
**File convention (required):**
|
|
33
|
+
|
|
29
34
|
```
|
|
30
35
|
Component.tsx
|
|
31
36
|
Component.styles.ts
|
|
@@ -34,15 +39,60 @@ Component.styles.ts
|
|
|
34
39
|
---
|
|
35
40
|
|
|
36
41
|
### ✅ `rnzeus/styles-naming`
|
|
42
|
+
|
|
37
43
|
Enforces **snake_case** naming for style keys.
|
|
38
44
|
|
|
39
45
|
**Example:**
|
|
46
|
+
|
|
40
47
|
```ts
|
|
41
48
|
// ❌ bad
|
|
42
|
-
itemName: {
|
|
49
|
+
itemName: {
|
|
50
|
+
}
|
|
43
51
|
|
|
44
52
|
// ✅ good
|
|
45
|
-
item_name: {
|
|
53
|
+
item_name: {
|
|
54
|
+
}
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
---
|
|
58
|
+
|
|
59
|
+
### ✅ `rnzeus/slice-imports`
|
|
60
|
+
|
|
61
|
+
Enforces **FSD import boundaries** for alias imports:
|
|
62
|
+
|
|
63
|
+
- **layer order**: lower layers cannot import higher layers
|
|
64
|
+
- **same-slice imports** must be **relative**
|
|
65
|
+
- **cross-slice** imports inside the same layer are allowed only via `@x/<currentSlice>`
|
|
66
|
+
|
|
67
|
+
This rule validates only alias imports starting with `@` (relative imports are ignored).
|
|
68
|
+
|
|
69
|
+
**Assumed structure (defaults):**
|
|
70
|
+
|
|
71
|
+
- `srcRoot`: `"src"`
|
|
72
|
+
- `layers`: `shared -> entities -> features -> widgets -> pages -> app`
|
|
73
|
+
|
|
74
|
+
**Examples (same slice must be relative):**
|
|
75
|
+
|
|
76
|
+
```ts
|
|
77
|
+
// file: src/features/auth/ui/Login.tsx
|
|
78
|
+
|
|
79
|
+
// ❌ bad (same slice via alias)
|
|
80
|
+
import { submit } from "@features/auth/model/submit";
|
|
81
|
+
|
|
82
|
+
// ✅ good
|
|
83
|
+
import { submit } from "../model/submit";
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
**Examples (cross-slice via @x):**
|
|
87
|
+
|
|
88
|
+
```ts
|
|
89
|
+
// file: src/entities/product/ui/ProductCard.tsx
|
|
90
|
+
|
|
91
|
+
// ❌ bad (direct cross-slice import inside same layer)
|
|
92
|
+
import { getCartTotal } from "@entities/cart/model/getCartTotal";
|
|
93
|
+
|
|
94
|
+
// ✅ good (cart exposes cross-slice API specifically for product slice)
|
|
95
|
+
import { getCartTotal } from "@entities/cart/@x/product";
|
|
46
96
|
```
|
|
47
97
|
|
|
48
98
|
---
|
|
@@ -50,9 +100,9 @@ item_name: {}
|
|
|
50
100
|
## Installation
|
|
51
101
|
|
|
52
102
|
```bash
|
|
53
|
-
npm install -D @rnzeus/eslint-plugin
|
|
103
|
+
npm install -D @rnzeus/eslint-plugin
|
|
54
104
|
# or
|
|
55
|
-
yarn add -D @rnzeus/eslint-plugin
|
|
105
|
+
yarn add -D @rnzeus/eslint-plugin
|
|
56
106
|
```
|
|
57
107
|
|
|
58
108
|
> Peer dependency: `eslint >= 9`
|
|
@@ -64,30 +114,28 @@ yarn add -D @rnzeus/eslint-plugin-style
|
|
|
64
114
|
This plugin exports **ready-to-use flat config presets**.
|
|
65
115
|
No `.default` access and no manual plugin wiring is required.
|
|
66
116
|
|
|
67
|
-
### Import
|
|
117
|
+
### Import presets
|
|
68
118
|
|
|
69
119
|
```js
|
|
70
|
-
const styles = require(
|
|
120
|
+
const styles = require("@rnzeus/eslint-plugin/configs/styles");
|
|
121
|
+
const imports = require("@rnzeus/eslint-plugin/configs/imports");
|
|
71
122
|
```
|
|
72
123
|
|
|
73
124
|
### Minimal setup
|
|
74
125
|
|
|
75
126
|
```js
|
|
76
|
-
module.exports = [
|
|
77
|
-
...styles,
|
|
78
|
-
];
|
|
127
|
+
module.exports = [...styles, ...imports];
|
|
79
128
|
```
|
|
80
129
|
|
|
81
130
|
### With other presets
|
|
82
131
|
|
|
83
132
|
```js
|
|
84
|
-
const rnfsd = require(
|
|
85
|
-
const
|
|
133
|
+
const rnfsd = require("@rnzeus/themis/eslint/rnfsd");
|
|
134
|
+
const base = require("@rnzeus/eslint-plugin/configs/base");
|
|
135
|
+
const styles = require("@rnzeus/eslint-plugin/configs/styles");
|
|
136
|
+
const imports = require("@rnzeus/eslint-plugin/configs/imports");
|
|
86
137
|
|
|
87
|
-
module.exports = [
|
|
88
|
-
...rnfsd,
|
|
89
|
-
...styles,
|
|
90
|
-
];
|
|
138
|
+
module.exports = [...rnfsd, ...styles, ...imports];
|
|
91
139
|
```
|
|
92
140
|
|
|
93
141
|
---
|
|
@@ -104,6 +152,7 @@ You can extend or override this list via rule options.
|
|
|
104
152
|
### Merge (default)
|
|
105
153
|
|
|
106
154
|
`type: "merge"` is the default behavior:
|
|
155
|
+
|
|
107
156
|
- takes plugin defaults
|
|
108
157
|
- adds your `styleProps`
|
|
109
158
|
- de-duplicates
|
|
@@ -166,51 +215,96 @@ You **must** explicitly document allowed keys using a directive comment:
|
|
|
166
215
|
```
|
|
167
216
|
|
|
168
217
|
Supported comment forms:
|
|
218
|
+
|
|
169
219
|
```ts
|
|
170
220
|
// rnzeus-styles-used: a | b
|
|
171
221
|
/* rnzeus-styles-used: a | b */
|
|
172
222
|
```
|
|
173
223
|
|
|
174
224
|
This makes dynamic styles:
|
|
225
|
+
|
|
175
226
|
- explicit
|
|
176
227
|
- reviewable
|
|
177
228
|
- safe during refactors
|
|
178
229
|
|
|
179
230
|
---
|
|
180
231
|
|
|
232
|
+
## Configure `slice-imports`
|
|
233
|
+
|
|
234
|
+
`rnzeus/slice-imports` supports these options:
|
|
235
|
+
|
|
236
|
+
- `srcRoot` (default: `"src"`): marker folder used to detect layer/slice from absolute filename
|
|
237
|
+
- `layers` (default: `["shared","entities","features","widgets","pages","app"]`): allowed layers in dependency order
|
|
238
|
+
|
|
239
|
+
Example (custom `srcRoot` and layers):
|
|
240
|
+
|
|
241
|
+
```js
|
|
242
|
+
const imports = require("@rnzeus/eslint-plugin/configs/imports");
|
|
243
|
+
|
|
244
|
+
module.exports = [
|
|
245
|
+
...imports,
|
|
246
|
+
{
|
|
247
|
+
rules: {
|
|
248
|
+
"rnzeus/slice-imports": [
|
|
249
|
+
"error",
|
|
250
|
+
{
|
|
251
|
+
srcRoot: "src",
|
|
252
|
+
layers: ["shared", "entities", "features", "widgets", "pages", "app"],
|
|
253
|
+
},
|
|
254
|
+
],
|
|
255
|
+
},
|
|
256
|
+
},
|
|
257
|
+
];
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
---
|
|
261
|
+
|
|
181
262
|
## Rules
|
|
182
263
|
|
|
183
264
|
### `rnzeus/styles-usage`
|
|
184
265
|
|
|
185
|
-
| Check
|
|
186
|
-
|
|
187
|
-
Unused styles
|
|
188
|
-
Whole styles object (`styles={styles}`) | ❌
|
|
189
|
-
Array styles
|
|
190
|
-
Conditional styles
|
|
191
|
-
Dynamic styles
|
|
192
|
-
Extra style props
|
|
266
|
+
| Check | Status |
|
|
267
|
+
| --------------------------------------- | ----------------------- |
|
|
268
|
+
| Unused styles | ✅ |
|
|
269
|
+
| Whole styles object (`styles={styles}`) | ❌ |
|
|
270
|
+
| Array styles | ✅ |
|
|
271
|
+
| Conditional styles | ✅ |
|
|
272
|
+
| Dynamic styles | ⚠️ (directive required) |
|
|
273
|
+
| Extra style props | ✅ (configurable) |
|
|
193
274
|
|
|
194
275
|
---
|
|
195
276
|
|
|
196
277
|
### `rnzeus/styles-naming`
|
|
197
278
|
|
|
198
|
-
| Rule
|
|
199
|
-
|
|
200
|
-
snake_case only | ✅
|
|
201
|
-
camelCase
|
|
202
|
-
PascalCase
|
|
279
|
+
| Rule | Status |
|
|
280
|
+
| --------------- | ------ |
|
|
281
|
+
| snake_case only | ✅ |
|
|
282
|
+
| camelCase | ❌ |
|
|
283
|
+
| PascalCase | ❌ |
|
|
203
284
|
|
|
204
285
|
Regex used:
|
|
286
|
+
|
|
205
287
|
```ts
|
|
206
|
-
/^[a-z][a-z0-9_]
|
|
288
|
+
/^[a-z][a-z0-9_]*$/;
|
|
207
289
|
```
|
|
208
290
|
|
|
209
291
|
---
|
|
210
292
|
|
|
293
|
+
### `rnzeus/slice-imports`
|
|
294
|
+
|
|
295
|
+
| Check | Status |
|
|
296
|
+
| ---------------------------------------- | ------- |
|
|
297
|
+
| Layer order (lower cannot import higher) | ✅ |
|
|
298
|
+
| Same slice must be relative | ✅ |
|
|
299
|
+
| Cross-slice via `@x/<currentSlice>` | ✅ |
|
|
300
|
+
| Relative imports | Ignored |
|
|
301
|
+
|
|
302
|
+
---
|
|
303
|
+
|
|
211
304
|
## Philosophy
|
|
212
305
|
|
|
213
306
|
This plugin intentionally:
|
|
307
|
+
|
|
214
308
|
- avoids runtime helpers
|
|
215
309
|
- avoids TypeScript compiler APIs
|
|
216
310
|
- avoids project-wide analysis
|