@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 CHANGED
@@ -1,12 +1,14 @@
1
- # @rnzeus/eslint-plugin-style
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-style
103
+ npm install -D @rnzeus/eslint-plugin
54
104
  # or
55
- yarn add -D @rnzeus/eslint-plugin-style
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 preset
117
+ ### Import presets
68
118
 
69
119
  ```js
70
- const styles = require('@rnzeus/eslint-plugin-style/configs/styles');
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('@rnzeus/themis/eslint/rnfsd');
85
- const styles = require('@rnzeus/eslint-plugin-style/configs/styles');
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 | Status |
186
- |-----|------|
187
- Unused styles | ✅ |
188
- Whole styles object (`styles={styles}`) | ❌ |
189
- Array styles | ✅ |
190
- Conditional styles | ✅ |
191
- Dynamic styles | ⚠️ (directive required) |
192
- Extra style props | ✅ (configurable) |
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 | Status |
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