eslint-plugin-unslop 0.5.3 → 0.6.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
@@ -26,10 +26,10 @@ export default [
26
26
  architecture: {
27
27
  utils: { shared: true },
28
28
  'repository/*': {
29
- imports: ['utils', 'models/*'],
29
+ imports: ['utils', 'models/+'],
30
30
  exports: ['^create\\w+Repo$', '^Repository[A-Z]\\w+$'],
31
31
  },
32
- 'models/*': {
32
+ models: {
33
33
  imports: ['utils'],
34
34
  },
35
35
  app: {
@@ -66,18 +66,42 @@ export default [unslop.configs.minimal]
66
66
 
67
67
  ## Architecture Settings
68
68
 
69
- All architecture rules read from `settings.unslop.architecture`. Each key is a module matcher (path segments, `*` per segment), and each value is a policy object:
69
+ All architecture rules read from `settings.unslop.architecture`. Each file first resolves to a canonical module path equal to its containing directory relative to the source root from `tsconfig.json`.
70
+
71
+ - `src/index.ts` -> `.`
72
+ - `src/models/index.ts` -> `models`
73
+ - `src/models/user/index.ts` -> `models/user`
74
+ - `src/models/user/internal.ts` -> `models/user`
75
+
76
+ Architecture keys are directory-shaped subtree selectors:
77
+
78
+ - `.` matches the source-root module
79
+ - `models` owns `models` and everything below it
80
+ - `models/*` owns each direct child subtree under `models`, such as `models/user` and `models/user/internal`
81
+
82
+ File-shaped keys like `index.ts` or `rules/public.ts` are not supported.
83
+
84
+ Each value is a policy object:
70
85
 
71
86
  ```ts
72
87
  {
73
- imports?: string[] // module matchers this module may import from; '*' allows all
74
- exports?: string[] // regex patterns symbols exported from index.ts/types.ts must match
88
+ imports?: string[] // exact module, direct child via /*, self-or-child via /+, or '*' for all
89
+ exports?: string[] // regex patterns symbols exported from entrypoints must match
75
90
  entrypoints?: string[] // public files allowed for external and test imports
76
91
  shared?: boolean // marks module as shared; enables no-false-sharing
77
92
  }
78
93
  ```
79
94
 
80
- Best match wins by fewest wildcards, then longest matcher, then declaration order. All architecture rules take no options - policy comes entirely from this shared settings block.
95
+ Architecture keys and import allowlists use different matching rules:
96
+
97
+ - keys assign ownership to subtrees
98
+ - `imports: ['models']` allows only the exact `models` module
99
+ - `imports: ['models/*']` allows only direct children like `models/user`
100
+ - `imports: ['models/+']` allows `models` and direct children like `models/user`
101
+
102
+ When multiple keys cover the same canonical module path, the winner is chosen by nearest owner first, then exact named path over wildcard path at the same depth, then longer selector path, then declaration order. Unmatched canonical module paths become anonymous modules with empty `imports`, empty `exports`, `shared: false`, and default `entrypoints: ['index.ts']`.
103
+
104
+ All architecture rules take no options. Policy comes entirely from this shared settings block.
81
105
 
82
106
  ## Rules
83
107
 
@@ -87,10 +111,10 @@ Customs control for your modules: you declare which modules are allowed to impor
87
111
 
88
112
  Deny-by-default for cross-module imports, so forgetting to declare a dependency is a loud error rather than a silent free-for-all. It also enforces:
89
113
 
90
- - cross-module imports must arrive through the public gate (`index.ts` or `types.ts`)
114
+ - cross-module imports must arrive through the public gate (configured entrypoints)
91
115
  - local cross-module namespace imports are forbidden (`import * as X from '<local-module>'`)
92
116
  - same-module relative imports can only go one level deeper - no tunnelling into internals
93
- - files that don't match any declared module are denied (fail-closed, not fail-silently)
117
+ - files that don't match any declared module become anonymous modules and are denied by default
94
118
 
95
119
  Alias imports are resolved via `compilerOptions.paths` from `tsconfig.json`.
96
120
 
@@ -104,7 +128,7 @@ This rule only checks recognized test filenames (`*.test.*`, `*.spec.*`, `*.*-te
104
128
 
105
129
  The customs declaration form for the other direction: what are you actually exporting from your module's public entrypoints?
106
130
 
107
- When a module defines `exports` regex patterns, every symbol exported from its `index.ts` or `types.ts` must match at least one pattern - otherwise it's stopped at the gate. Modules without `exports` are waved through by default, so you can adopt this gradually. Regardless of module policy, `export * from ...` is rejected in public entrypoints so symbol provenance stays explicit.
131
+ When a module defines `exports` regex patterns, every symbol exported from its entrypoints must match at least one pattern - otherwise it's stopped at the gate. Modules without `exports` are waved through by default, so you can adopt this gradually. Regardless of module policy, `export * from ...` is rejected in public entrypoints so symbol provenance stays explicit.
108
132
 
109
133
  ### `unslop/no-false-sharing`
110
134
 
@@ -117,7 +141,7 @@ settings: {
117
141
  unslop: {
118
142
  architecture: {
119
143
  utils: { shared: true },
120
- 'shared/*': { shared: true },
144
+ shared: { shared: true },
121
145
  },
122
146
  },
123
147
  }
@@ -131,7 +155,7 @@ src/shared/index.ts
131
155
  -> imported only by src/features/calendar/view.ts
132
156
  -> error: symbol "formatDate" has 1 consumer group(s) (group: features/calendar)
133
157
 
134
- src/shared/types.ts
158
+ src/shared/index.ts
135
159
  export type LegacyOptions = ...
136
160
  -> not imported by anyone
137
161
  -> error: symbol "LegacyOptions" has 0 consumer group(s) (no consumers found)