@morriconeluca/eslint-plugin-faf 0.3.0 → 0.5.0

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
@@ -11,7 +11,7 @@ This plugin is designed for complex TypeScript/JavaScript codebases to ensure **
11
11
  - **Strict Enforcing of FAF Taxonomy**: Ensures your folder structure strictly maps to Layers, Categories, Fragments, and Fractal Branches.
12
12
  - **Law of Separation between Peers**: Prevents lateral dependency coupling between sibling modules without explicit hierarchy.
13
13
  - **Access Node Encapsulation**: Guarantees that internal Fragment files are never imported directly, enforcing consumption solely through the Fragment's barrel file (`index.ts`).
14
- - **High-Performance Architecture**: Features optimized caching (`dirCache`, `classifyCache`, `rolesCache`) to minimize disk I/O during linting runs.
14
+ - **High-Performance Architecture**: Features optimized caching (`dirCache`, `classifyCache`, `ancestorChainCache`, `rolesCache`) to minimize disk I/O during linting runs.
15
15
 
16
16
  ---
17
17
 
@@ -273,54 +273,84 @@ Determines horizontal import flow (Peer Separation). Each hierarchy is an array
273
273
 
274
274
  ### 1. `faf/naming-conventions`
275
275
 
276
- Verifies folders, files, and roles comply with FAF taxonomy:
276
+ Verifies pure lexical FAF naming conventions:
277
277
 
278
278
  - Enforces `kebab-case` naming for all folders and files in the logical domain.
279
- - Validates that internal Fragment files share their parent Fragment's name prefix (e.g. files in folder `button/` must be named `button.<role>.<ext>`).
280
- - Enforces role suffix validation on Fragment Nodes and Logical Nodes, restricted to a single Role segment per file name.
281
- - Requires every Fragment to contain at least one Fragment Node (its Master Node), and forbids two Fragment Nodes from sharing the same Role.
282
- - Assures route terminal Fragments contain the appropriate Master Node (e.g. a `.api.ts` file under `_apis`) and that the Fragment name follows the `<method>-<object>` pattern (e.g. `get-todo`), with the HTTP method restricted to `routeHierarchies[].httpMethods` or, by default, all standard HTTP methods.
283
- - Enforces FAF logical domain containment rules:
284
- - Root Fragments can only reside directly in the Root Container or another Root Fragment.
285
- - Layers cannot reside inside Fragments.
286
- - Fragments cannot be placed directly inside other Fragments (they must be nested within a Private Category), Fractal Branches, or nested Root Fragments.
287
- - Fractal Branches cannot be nested inside other Fractal Branches.
279
+ - Requires an underscore prefix for Layers/Categories/Sub-Categories, and forbids it for Fragments/Sub-Fragments/Root Fragments.
280
+ - Validates that a Fractal Branch's name matches its parent scope (`_<Scope>@shared`).
281
+ - Restricts a Logical Node's name to a single Role segment, whether it is a Fragment Node or a loose file inside a Category.
282
+ - For loose files inside a Category: validates the file extension against the category's whitelist, requires a role suffix on code files, and validates that the role matches the Category's configured role.
283
+
284
+ ### 2. `faf/logical-domain-placement`
285
+
286
+ Enforces FAF topological placement constraints between Logical Domains:
288
287
 
289
- ### 2. `faf/enforce-access-node`
288
+ - Root Fragments can only reside directly in the Root Container or another Root Fragment.
289
+ - Layers cannot reside inside Fragments, and cannot contain files or Fragments directly, except the terminal Layer of a configured Route Hierarchy.
290
+ - Fragments cannot be placed directly inside other Fragments (they must be nested within a Private Category), Layers (unless terminating a Route Hierarchy), Fractal Branches, or nested Root Fragments.
291
+ - Fractal Branches cannot be nested inside other Fractal Branches, and cannot contain files directly.
292
+
293
+ ### 3. `faf/enforce-access-node`
290
294
 
291
295
  Enforces Access Node (`index.ts` / `index.js`) integrity:
292
296
 
297
+ - Requires a directory that isn't a Category, Layer, Fractal Branch, or Root Fragment to have an Access Node.
298
+ - Restricts Access Nodes to Fragments: an index file directly inside any other Logical Domain is forbidden.
299
+ - Requires every Fragment to contain at least one Fragment Node (its Master Node).
293
300
  - Restricts the Access Node to import/export only its own sibling Fragment Nodes.
294
301
  - Prevents import of nested Private Categories, Fractal Branches, or unrelated external paths.
295
302
 
296
- ### 3. `faf/no-direct-fragment-import`
303
+ ### 4. `faf/fragment-master-node`
304
+
305
+ Enforces Fragment Node identity and Master Node integrity:
306
+
307
+ - Validates that internal Fragment files share their parent Fragment's name prefix (e.g. files in folder `button/` must be named `button.<role>.<ext>`).
308
+ - Requires a role suffix on every Fragment Node, and forbids two Fragment Nodes from sharing the same Role.
309
+ - Assures a Fragment placed under a Category (or terminating a Route Hierarchy) contains a Master Node carrying the Role imposed by that Category/Route (e.g. a `.api.ts` file under `_apis`).
310
+ - Assures route terminal Fragments follow the `<method>-<object>` naming pattern (e.g. `get-todo`), with the HTTP method restricted to `routeHierarchies[].httpMethods` or, by default, all standard HTTP methods.
311
+
312
+ ### 5. `faf/no-direct-fragment-import`
297
313
 
298
314
  Applies **Fragment Encapsulation**:
299
315
 
300
316
  - Prevents external files from directly importing a Fragment's internal files. All imports must pass through the Fragment's Access Node (`index.ts`).
301
317
 
302
- ### 4. `faf/no-private-category-leak`
318
+ ### 6. `faf/no-private-category-leak`
303
319
 
304
320
  Applies **Private Category Encapsulation**:
305
321
 
306
322
  - Restricts consumption of elements inside a Private Category (e.g. `_components/` nested inside a Fragment) to the direct child Fragment Nodes of the owning Fragment/Root Fragment, and to sibling nodes within the same immediate sub-domain of the Private Category. Access does not extend to other sub-domains, nor to a Fractal Branch nested inside the Private Category (governed instead by its own encapsulation rule).
307
323
 
308
- ### 5. `faf/no-fractal-branch-leak`
324
+ ### 7. `faf/no-fractal-branch-leak`
309
325
 
310
326
  Applies **Fractal Branch Encapsulation**:
311
327
 
312
328
  - Fractal Branches named `_<Scope>@shared` are only importable by modules residing inside the parent Scope's subtree.
329
+ - A Fractal Branch itself must never depend, not even indirectly, on the subtree it serves: importing anything from its owner's subtree that isn't part of the branch itself is forbidden.
330
+
331
+ ### 8. `faf/no-peer-dependency`
332
+
333
+ Applies **Peer Isolation** via horizontal hierarchy resolution:
334
+
335
+ - **Inside a Fragment**: Flow is governed by the `roles` array order.
336
+ - **Between folders**: Flow is governed exclusively by `localHorizontalHierarchies` or `globalHorizontalHierarchies`; a sibling pair without a matching entry is denied in both directions.
313
337
 
314
- ### 6. `faf/no-peer-dependency`
338
+ Foreign Domain imports and Root Node dependency direction are handled by dedicated rules (see below).
315
339
 
316
- Applies **Peer Isolation**:
340
+ ### 9. `faf/foreign-domain-isolation`
317
341
 
318
- - Prevents sibling files and folders from importing each other unless an explicit hierarchy is configured:
319
- - **Inside a Fragment**: Flow is governed by the `roles` array order.
320
- - **Between folders**: Flow is governed by `localHorizontalHierarchies` or `globalHorizontalHierarchies`. If undefined, the linter falls back to mapped roles. Otherwise, peer isolation is strictly enforced.
321
- - **Root Nodes**: Flow is governed exclusively by the defined index order in `rootFragments.rootNodes`. Root Nodes are exempt from the Role naming convention, so relationships between them never fall back to the `roles` scale: any relationship without a matching `rootNodes` entry is denied.
342
+ Applies **Foreign Domain isolation**:
322
343
 
323
- ### 7. `faf/category-mutually-exclusive`
344
+ - Prevents Logical Nodes from importing a file from a Foreign Domain (e.g. `src/configs`).
345
+ - Prevents a Foreign Domain file from importing a Logical Node from the FAF tree.
346
+
347
+ ### 10. `faf/root-node-dependency-direction`
348
+
349
+ Enforces explicit, non-Role-based dependency direction between **Root Nodes**:
350
+
351
+ - Flow is governed exclusively by the defined index order in `rootFragments.rootNodes`. Root Nodes are exempt from the Role naming convention, so relationships between them (sibling or nested Root Fragments) never fall back to the `roles` scale: any relationship without a matching `rootNodes` entry is denied.
352
+
353
+ ### 11. `faf/category-mutually-exclusive`
324
354
 
325
355
  Ensures category purity:
326
356
 
@@ -329,12 +359,31 @@ Ensures category purity:
329
359
 
330
360
  ---
331
361
 
362
+ ## Migrating from 7 rules to 11
363
+
364
+ Starting with this release, five checks that used to live inside two large rules (`naming-conventions` and `no-peer-dependency`) have moved to dedicated rules with their own `id`. The underlying checks and their error messages are unchanged — only the `ruleId` reporting them is different. This is a breaking change for anyone who doesn't use `configs.recommended` (which picks up the new rules automatically) or who references a specific rule `id` directly, e.g. in `// eslint-disable-next-line faf/<id>` comments or a hand-written `rules` block.
365
+
366
+ | Old rule id | Check moved to |
367
+ | ------------------------------------------------------------------------------------------------------------------------------------ | ---------------------------------------- |
368
+ | `naming-conventions` — folder/file placement (Root Fragment, Layer, Fragment, Fractal Branch containment) | `logical-domain-placement` (new) |
369
+ | `naming-conventions` — Access Node existence, Master Node existence | `enforce-access-node` |
370
+ | `naming-conventions` — Fragment Node name prefix, role requirement/uniqueness, Category/Route Master Node role, route naming pattern | `fragment-master-node` (new) |
371
+ | `naming-conventions` — kebab-case, underscore prefix, Fractal Branch naming, single-Role-segment, Category loose-file naming | unchanged, stays in `naming-conventions` |
372
+ | `no-peer-dependency` — Foreign Domain imports (both directions) | `foreign-domain-isolation` (new) |
373
+ | `no-peer-dependency` — Root Node dependency direction (`rootFragments.rootNodes`) | `root-node-dependency-direction` (new) |
374
+ | `no-peer-dependency` — horizontal hierarchy resolution (Fragment Node roles, sibling folders) | unchanged, stays in `no-peer-dependency` |
375
+
376
+ `no-direct-fragment-import`, `no-private-category-leak`, `category-mutually-exclusive` are unaffected. `no-fractal-branch-leak` keeps its `id` and gains one new check: a Fractal Branch depending on the subtree it serves is now reported.
377
+
378
+ ---
379
+
332
380
  ## Performance Optimizations
333
381
 
334
382
  To prevent sluggish linting in large codebases, the core engine implements aggressive, state-cached optimizations:
335
383
 
336
384
  - **Directory Cache (`dirCache`)**: Inspects the physical disk exactly once per folder, storing the contents in-memory. It is optimized to perform exactly one system call on cache misses by removing redundant `fs.existsSync` checks.
337
385
  - **Classification Cache (`classifyCache`)**: Memoizes the structural type (`FolderType`) of each directory for the lifetime of the ESLint run.
386
+ - **Ancestor Chain Cache (`ancestorChainCache`)**: Memoizes the full chain of classified ancestors above a directory, so rules that walk upward from a file (folder taxonomy, topological placement) do so once per directory rather than once per file.
338
387
  - **Role Cache (`rolesCache`)**: Uses a `WeakMap` to cache the allowed roles per tree configuration as a native `Set`, avoiding memory leaks, redundant array flattening allocations, and speeding up role lookup checks to $O(1)$.
339
388
  - **Category Configuration Cache (`categoryConfigCache`)**: Memoizes resolved category configurations per folder path to eliminate repetitive directory walks and path parent traversals.
340
389
  - **Tree Configuration Cache (`treeConfigCache` / `treeConfigIncludingExcludedCache`)**: Memoizes resolved tree configurations per file path, reducing tree lookup checks to a simple map-lookup.