@trebired/code-discipline 0.1.0 → 1.0.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/CHANGELOG.md +14 -10
- package/README.md +141 -147
- package/dist/checks/fix-folderization.d.ts +17 -0
- package/dist/checks/fix-folderization.d.ts.map +1 -0
- package/dist/checks/fix-folderization.js +223 -0
- package/dist/checks/fix-folderization.js.map +1 -0
- package/dist/checks/index.d.ts +21 -2
- package/dist/checks/index.d.ts.map +1 -1
- package/dist/checks/index.js +104 -12
- package/dist/checks/index.js.map +1 -1
- package/dist/checks/rules/folderize-compound-files.d.ts.map +1 -1
- package/dist/checks/rules/folderize-compound-files.js +15 -33
- package/dist/checks/rules/folderize-compound-files.js.map +1 -1
- package/dist/checks/rules/folderize-plan.d.ts +16 -0
- package/dist/checks/rules/folderize-plan.d.ts.map +1 -0
- package/dist/checks/rules/folderize-plan.js +80 -0
- package/dist/checks/rules/folderize-plan.js.map +1 -0
- package/dist/checks/rules/max-file-lines.d.ts.map +1 -1
- package/dist/checks/rules/max-file-lines.js +2 -1
- package/dist/checks/rules/max-file-lines.js.map +1 -1
- package/dist/checks/types.d.ts +31 -20
- package/dist/checks/types.d.ts.map +1 -1
- package/dist/cli/run-cli.d.ts.map +1 -1
- package/dist/cli/run-cli.js +40 -16
- package/dist/cli/run-cli.js.map +1 -1
- package/dist/config/normalize-check-options.d.ts.map +1 -1
- package/dist/config/normalize-check-options.js +8 -1
- package/dist/config/normalize-check-options.js.map +1 -1
- package/dist/config/normalize-rule-options.d.ts +21 -7
- package/dist/config/normalize-rule-options.d.ts.map +1 -1
- package/dist/config/normalize-rule-options.js +43 -18
- package/dist/config/normalize-rule-options.js.map +1 -1
- package/dist/config/normalize-sync-imports-options.d.ts.map +1 -1
- package/dist/config/normalize-sync-imports-options.js +18 -6
- package/dist/config/normalize-sync-imports-options.js.map +1 -1
- package/dist/imports/aliases.d.ts +2 -1
- package/dist/imports/aliases.d.ts.map +1 -1
- package/dist/imports/aliases.js +18 -14
- package/dist/imports/aliases.js.map +1 -1
- package/dist/imports/check-sync-imports.d.ts +6 -0
- package/dist/imports/check-sync-imports.d.ts.map +1 -0
- package/dist/imports/check-sync-imports.js +55 -0
- package/dist/imports/check-sync-imports.js.map +1 -0
- package/dist/imports/module-specifiers.d.ts +20 -0
- package/dist/imports/module-specifiers.d.ts.map +1 -0
- package/dist/imports/module-specifiers.js +69 -0
- package/dist/imports/module-specifiers.js.map +1 -0
- package/dist/imports/rewrite.d.ts +3 -2
- package/dist/imports/rewrite.d.ts.map +1 -1
- package/dist/imports/rewrite.js +27 -90
- package/dist/imports/rewrite.js.map +1 -1
- package/dist/imports/sync-imports.d.ts.map +1 -1
- package/dist/imports/sync-imports.js +50 -5
- package/dist/imports/sync-imports.js.map +1 -1
- package/dist/imports/types.d.ts +18 -24
- package/dist/imports/types.d.ts.map +1 -1
- package/dist/index.d.ts +7 -7
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -4
- package/dist/index.js.map +1 -1
- package/dist/shared/constants.d.ts +4 -6
- package/dist/shared/constants.d.ts.map +1 -1
- package/dist/shared/constants.js +4 -6
- package/dist/shared/constants.js.map +1 -1
- package/dist/shared/errors.d.ts +8 -2
- package/dist/shared/errors.d.ts.map +1 -1
- package/dist/shared/errors.js +13 -1
- package/dist/shared/errors.js.map +1 -1
- package/dist/shared/logging-types.d.ts +7 -1
- package/dist/shared/logging-types.d.ts.map +1 -1
- package/dist/shared/logging.d.ts +2 -3
- package/dist/shared/logging.d.ts.map +1 -1
- package/dist/shared/logging.js.map +1 -1
- package/package.json +2 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,19 +2,23 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to `@trebired/code-discipline` will be documented here.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
## 1.0.0
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
-
|
|
10
|
-
-
|
|
11
|
-
-
|
|
12
|
-
-
|
|
13
|
-
-
|
|
14
|
-
-
|
|
7
|
+
- introduced the breaking `enabled` / `stop` / `fix` rule model across the package
|
|
8
|
+
- removed `severity` from discipline and sync-import config
|
|
9
|
+
- removed suffix-based folderization and replaced it with structural folder grouping
|
|
10
|
+
- removed `suffixes` from `folderizeCompoundFiles`
|
|
11
|
+
- removed nested `syncImports.imports`
|
|
12
|
+
- removed `rewrite` as a sync config option in favor of `syncImports.fix`
|
|
13
|
+
- replaced `keepRelative` with `allowRelative`
|
|
14
|
+
- made `check` fully read-only
|
|
15
|
+
- made `sync` mutate imports and `tsconfig.json` only when `syncImports.fix` is `true`
|
|
16
|
+
- added explicit `fix` support for folderization moves and affected import rewrites
|
|
17
|
+
- added `fixCodeDiscipline()` as the structural mutation API
|
|
18
|
+
- added blocking-or-warning behavior through `stop` instead of severity levels
|
|
19
|
+
- bumped the package to `1.0.0` for the config and API cleanup
|
|
15
20
|
|
|
16
21
|
## 0.1.0
|
|
17
22
|
|
|
18
23
|
- initial public release
|
|
19
24
|
- added source scanning, alias generation, tsconfig path syncing, and in-place import rewriting
|
|
20
|
-
- added configurable alias strategies, keep-relative policies, and restrained logging adapters
|
package/README.md
CHANGED
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
# @trebired/code-discipline
|
|
2
2
|
|
|
3
|
-
Configurable
|
|
3
|
+
Configurable repository discipline checks, structural fixes, and import syncing for Bun and Node.js projects.
|
|
4
4
|
|
|
5
|
-
`@trebired/code-discipline`
|
|
6
|
-
|
|
7
|
-
The package is intentionally focused. It helps with repository structure rules, file-shape rules, and import hygiene. It does not try to be a full linter, formatter, or build system.
|
|
5
|
+
`@trebired/code-discipline` is intentionally focused. It helps you keep a source tree disciplined without turning into a full linter, formatter, or build system.
|
|
8
6
|
|
|
9
7
|
## Install
|
|
10
8
|
|
|
@@ -14,59 +12,105 @@ Runtime support: Bun 1+ and Node.js 18+.
|
|
|
14
12
|
npm install @trebired/code-discipline
|
|
15
13
|
```
|
|
16
14
|
|
|
17
|
-
##
|
|
18
|
-
|
|
19
|
-
```ts
|
|
20
|
-
import { checkCodeDiscipline } from "@trebired/code-discipline";
|
|
21
|
-
|
|
22
|
-
const result = await checkCodeDiscipline({
|
|
23
|
-
projectRoot: "/repo",
|
|
24
|
-
sourceRoot: "src",
|
|
25
|
-
rules: {
|
|
26
|
-
maxFileLines: {
|
|
27
|
-
enabled: true,
|
|
28
|
-
max: 500,
|
|
29
|
-
},
|
|
30
|
-
folderizeCompoundFiles: {
|
|
31
|
-
enabled: true,
|
|
32
|
-
suffixes: ["start", "service"],
|
|
33
|
-
separators: ["_", "-"],
|
|
34
|
-
},
|
|
35
|
-
},
|
|
36
|
-
});
|
|
15
|
+
## Why This Package
|
|
37
16
|
|
|
38
|
-
|
|
39
|
-
console.error(result.violations);
|
|
40
|
-
process.exit(1);
|
|
41
|
-
}
|
|
42
|
-
```
|
|
17
|
+
Some repository rules are not really single-file lint rules. They are about the shape of the tree:
|
|
43
18
|
|
|
44
|
-
|
|
19
|
+
- files growing too large
|
|
20
|
+
- compound filenames that want to become folders
|
|
21
|
+
- alias drift between `tsconfig.json` and source imports
|
|
22
|
+
- blocking or warning on repository policy in CI or startup flows
|
|
45
23
|
|
|
46
|
-
|
|
47
|
-
- `syncImports()`
|
|
48
|
-
- `defineCodeDisciplineConfig()`
|
|
49
|
-
- `loadCodeDisciplineConfig()`
|
|
24
|
+
That is the lane of this package.
|
|
50
25
|
|
|
51
|
-
|
|
26
|
+
## Commands
|
|
52
27
|
|
|
53
28
|
```sh
|
|
54
29
|
code-discipline check
|
|
55
30
|
code-discipline sync
|
|
31
|
+
code-discipline fix
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Command responsibilities stay clean:
|
|
35
|
+
|
|
36
|
+
- `check`: read-only validation and logging
|
|
37
|
+
- `sync`: import and `tsconfig.json` synchronization only
|
|
38
|
+
- `fix`: explicit folderization moves only
|
|
39
|
+
|
|
40
|
+
Both `sync` and `fix` are gated by rule config. They do not mutate anything unless their rule has `fix: true`.
|
|
41
|
+
|
|
42
|
+
## Config
|
|
43
|
+
|
|
44
|
+
Every rule uses the same control model:
|
|
45
|
+
|
|
46
|
+
```txt
|
|
47
|
+
enabled
|
|
48
|
+
= whether the rule runs
|
|
49
|
+
|
|
50
|
+
stop
|
|
51
|
+
= whether violations fail the result / exit non-zero
|
|
52
|
+
|
|
53
|
+
fix
|
|
54
|
+
= whether explicit mutation commands may change files
|
|
56
55
|
```
|
|
57
56
|
|
|
58
|
-
|
|
57
|
+
Example `code-discipline.config.json`:
|
|
59
58
|
|
|
60
|
-
|
|
59
|
+
```json
|
|
60
|
+
{
|
|
61
|
+
"sourceRoot": "src",
|
|
62
|
+
"sourceExtensions": [
|
|
63
|
+
".ts",
|
|
64
|
+
".tsx",
|
|
65
|
+
".js",
|
|
66
|
+
".jsx"
|
|
67
|
+
],
|
|
68
|
+
"excludeDirs": [
|
|
69
|
+
"node_modules",
|
|
70
|
+
"dist",
|
|
71
|
+
".vite"
|
|
72
|
+
],
|
|
73
|
+
"rules": {
|
|
74
|
+
"maxFileLines": {
|
|
75
|
+
"enabled": true,
|
|
76
|
+
"stop": true,
|
|
77
|
+
"max": 500
|
|
78
|
+
},
|
|
79
|
+
"folderizeCompoundFiles": {
|
|
80
|
+
"enabled": true,
|
|
81
|
+
"stop": true,
|
|
82
|
+
"fix": false,
|
|
83
|
+
"separators": [
|
|
84
|
+
"_",
|
|
85
|
+
"-"
|
|
86
|
+
]
|
|
87
|
+
},
|
|
88
|
+
"syncImports": {
|
|
89
|
+
"enabled": true,
|
|
90
|
+
"stop": true,
|
|
91
|
+
"fix": true,
|
|
92
|
+
"alias": {
|
|
93
|
+
"strategy": "relative-path-slug"
|
|
94
|
+
},
|
|
95
|
+
"allowRelative": [
|
|
96
|
+
"./"
|
|
97
|
+
]
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
```
|
|
61
102
|
|
|
62
|
-
|
|
63
|
-
- `folderizeCompoundFiles`
|
|
103
|
+
Breaking cleanup in `1.0.0`:
|
|
64
104
|
|
|
65
|
-
`
|
|
105
|
+
- `severity` was removed
|
|
106
|
+
- `suffixes` was removed
|
|
107
|
+
- `keepRelative` was replaced by `allowRelative`
|
|
108
|
+
- nested `syncImports.imports` was removed
|
|
109
|
+
- `rewrite` was removed as a config flag because `fix` controls mutation
|
|
66
110
|
|
|
67
|
-
|
|
111
|
+
## Checks
|
|
68
112
|
|
|
69
|
-
|
|
113
|
+
`checkCodeDiscipline()` is read-only. It never moves files, rewrites imports, or updates `tsconfig.json`.
|
|
70
114
|
|
|
71
115
|
```ts
|
|
72
116
|
import { checkCodeDiscipline } from "@trebired/code-discipline";
|
|
@@ -76,29 +120,38 @@ const result = await checkCodeDiscipline({
|
|
|
76
120
|
rules: {
|
|
77
121
|
maxFileLines: {
|
|
78
122
|
enabled: true,
|
|
123
|
+
stop: true,
|
|
79
124
|
max: 500,
|
|
80
|
-
severity: "error",
|
|
81
125
|
},
|
|
82
126
|
folderizeCompoundFiles: {
|
|
83
127
|
enabled: true,
|
|
84
|
-
|
|
128
|
+
stop: false,
|
|
85
129
|
separators: ["_", "-"],
|
|
86
|
-
|
|
130
|
+
},
|
|
131
|
+
syncImports: {
|
|
132
|
+
enabled: true,
|
|
133
|
+
stop: true,
|
|
134
|
+
fix: false,
|
|
135
|
+
alias: {
|
|
136
|
+
strategy: "relative-path-slug",
|
|
137
|
+
},
|
|
138
|
+
allowRelative: ["./"],
|
|
87
139
|
},
|
|
88
140
|
},
|
|
89
141
|
});
|
|
90
142
|
```
|
|
91
143
|
|
|
92
|
-
|
|
144
|
+
Result shape:
|
|
93
145
|
|
|
94
146
|
```ts
|
|
95
147
|
{
|
|
96
148
|
ok: boolean;
|
|
97
149
|
warnings: number;
|
|
98
|
-
|
|
150
|
+
failures: number;
|
|
99
151
|
violations: Array<{
|
|
100
|
-
rule: "max-file-lines" | "folderize-compound-files";
|
|
101
|
-
|
|
152
|
+
rule: "max-file-lines" | "folderize-compound-files" | "sync-imports";
|
|
153
|
+
stop: boolean;
|
|
154
|
+
fix: boolean;
|
|
102
155
|
filePath: string;
|
|
103
156
|
message: string;
|
|
104
157
|
details: Record<string, unknown>;
|
|
@@ -107,135 +160,76 @@ The result shape stays simple:
|
|
|
107
160
|
}
|
|
108
161
|
```
|
|
109
162
|
|
|
110
|
-
|
|
163
|
+
## Folderization
|
|
111
164
|
|
|
112
|
-
|
|
165
|
+
`folderizeCompoundFiles` is structural. It no longer depends on configured suffix lists.
|
|
113
166
|
|
|
114
|
-
|
|
167
|
+
Same-directory groups:
|
|
115
168
|
|
|
116
|
-
```
|
|
117
|
-
|
|
118
|
-
|
|
169
|
+
```txt
|
|
170
|
+
src/api/user_route.ts
|
|
171
|
+
src/api/user_schema.ts
|
|
172
|
+
src/api/user_controller.ts
|
|
119
173
|
```
|
|
120
174
|
|
|
121
|
-
|
|
175
|
+
suggest:
|
|
122
176
|
|
|
123
|
-
|
|
177
|
+
```txt
|
|
178
|
+
src/api/user/route.ts
|
|
179
|
+
src/api/user/schema.ts
|
|
180
|
+
src/api/user/controller.ts
|
|
181
|
+
```
|
|
124
182
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
"maxFileLines": {
|
|
130
|
-
"enabled": true,
|
|
131
|
-
"max": 500
|
|
132
|
-
},
|
|
133
|
-
"folderizeCompoundFiles": {
|
|
134
|
-
"enabled": true,
|
|
135
|
-
"suffixes": ["start", "service"],
|
|
136
|
-
"separators": ["_", "-"]
|
|
137
|
-
},
|
|
138
|
-
"syncImports": {
|
|
139
|
-
"alias": {
|
|
140
|
-
"strategy": "relative-path-slug"
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
}
|
|
183
|
+
Repeated folder-prefix files are also detected:
|
|
184
|
+
|
|
185
|
+
```txt
|
|
186
|
+
src/api/user/user_route.ts
|
|
145
187
|
```
|
|
146
188
|
|
|
147
|
-
|
|
189
|
+
suggests:
|
|
148
190
|
|
|
149
|
-
|
|
191
|
+
```txt
|
|
192
|
+
src/api/user/route.ts
|
|
193
|
+
```
|
|
150
194
|
|
|
151
|
-
|
|
195
|
+
`check` only reports these candidates.
|
|
152
196
|
|
|
153
|
-
|
|
154
|
-
- use the API when another tool wants to control configuration or reporting dynamically
|
|
197
|
+
`fix` may apply them only when `folderizeCompoundFiles.fix` is `true`, and it rewrites affected relative imports after the move.
|
|
155
198
|
|
|
156
199
|
## Import Sync
|
|
157
200
|
|
|
158
|
-
`syncImports()`
|
|
201
|
+
`syncImports()` and `code-discipline sync` use one flat `syncImports` config.
|
|
159
202
|
|
|
160
203
|
```ts
|
|
161
204
|
import { syncImports } from "@trebired/code-discipline";
|
|
162
205
|
|
|
163
206
|
const result = await syncImports({
|
|
164
207
|
projectRoot: "/repo",
|
|
165
|
-
|
|
166
|
-
tsconfigPath: "/repo/tsconfig.json",
|
|
167
|
-
});
|
|
168
|
-
```
|
|
169
|
-
|
|
170
|
-
Default behavior:
|
|
171
|
-
|
|
172
|
-
- `sourceRoot: "src"`
|
|
173
|
-
- `tsconfigPath: "<projectRoot>/tsconfig.json"`
|
|
174
|
-
- `sourceExtensions: [".ts", ".tsx", ".js", ".jsx"]`
|
|
175
|
-
- `excludeDirs: ["node_modules", "dist", ".vite"]`
|
|
176
|
-
- `imports.rewrite: true`
|
|
177
|
-
- `imports.keepRelative: ["./"]`
|
|
178
|
-
- `alias.prefix: "#"`
|
|
179
|
-
- `alias.strategy: "random"`
|
|
180
|
-
- `alias.randomLength: 12`
|
|
181
|
-
|
|
182
|
-
By default, same-directory imports such as `./local` stay relative. Imports that walk upward, such as `../shared/util`, are eligible for rewrite when they resolve to a file under the configured source root.
|
|
183
|
-
|
|
184
|
-
Alias strategies:
|
|
185
|
-
|
|
186
|
-
- `"random"`
|
|
187
|
-
- `"relative-path-slug"`
|
|
188
|
-
- `"relative-path-hash"`
|
|
189
|
-
- custom function
|
|
190
|
-
|
|
191
|
-
Example custom strategy:
|
|
192
|
-
|
|
193
|
-
```ts
|
|
194
|
-
await syncImports({
|
|
195
|
-
projectRoot: "/repo",
|
|
208
|
+
fix: true,
|
|
196
209
|
alias: {
|
|
197
|
-
strategy
|
|
198
|
-
return `@${input.relativeFromSourceRoot.replace(/\//g, "__")}`;
|
|
199
|
-
},
|
|
210
|
+
strategy: "relative-path-slug",
|
|
200
211
|
},
|
|
212
|
+
allowRelative: ["./"],
|
|
201
213
|
});
|
|
202
214
|
```
|
|
203
215
|
|
|
204
|
-
|
|
216
|
+
Behavior:
|
|
205
217
|
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
This package is a better fit when you want checks such as:
|
|
218
|
+
- `check` reports import-policy drift in read-only mode
|
|
219
|
+
- `sync` rewrites imports and updates `tsconfig.json` only when `syncImports.fix` is `true`
|
|
220
|
+
- `allowRelative: ["./"]` keeps same-folder relative imports
|
|
221
|
+
- upward relative imports can be reported or rewritten through the configured alias policy
|
|
211
222
|
|
|
212
|
-
|
|
213
|
-
- filename patterns that imply a cleaner folder layout
|
|
214
|
-
- path alias synchronization across many files
|
|
215
|
-
- startup, CI, or pre-build gates based on repository conventions
|
|
223
|
+
## Logging
|
|
216
224
|
|
|
217
|
-
|
|
225
|
+
When you provide a logger, discipline results are emitted through it. Trebired-style loggers are supported directly, and the package falls back safely when no logger is provided.
|
|
218
226
|
|
|
219
|
-
##
|
|
227
|
+
## Public API
|
|
220
228
|
|
|
221
229
|
- `checkCodeDiscipline()`
|
|
230
|
+
- `fixCodeDiscipline()`
|
|
231
|
+
- `syncImports()`
|
|
222
232
|
- `defineCodeDisciplineConfig()`
|
|
223
233
|
- `loadCodeDisciplineConfig()`
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
- `syncTsconfigAliases()`
|
|
227
|
-
- `rewriteSourceImports()`
|
|
228
|
-
- `resolveRelativeImport()`
|
|
229
|
-
- `createRandomAlias()`
|
|
230
|
-
- `createRelativePathHashAlias()`
|
|
231
|
-
- `createRelativePathSlugAlias()`
|
|
232
|
-
|
|
233
|
-
The package also exports the public TypeScript types for options, results, violations, alias strategies, keep-relative callbacks, log events, and source scan rows.
|
|
234
|
-
|
|
235
|
-
## What This Package Does Not Do
|
|
236
|
-
|
|
237
|
-
- it does not build or compile TypeScript
|
|
238
|
-
- it does not move files automatically for folderization violations
|
|
239
|
-
- it does not rewrite emitted build output
|
|
240
|
-
- it does not depend on ESLint
|
|
241
|
-
- it does not assume a framework-specific runtime layout
|
|
234
|
+
|
|
235
|
+
The package also exports the public TypeScript types for rule config, results, violations, alias strategies, logging adapters, and source scan rows.
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { CodeDisciplineViolation, FixCodeDisciplineResult, NormalizedCheckCodeDisciplineOptions } from "./types.js";
|
|
2
|
+
import type { ScannedSourceFile } from "../imports/types.js";
|
|
3
|
+
import type { NormalizedSyncImportsLogger } from "../shared/logging-types.js";
|
|
4
|
+
type PlannedMove = {
|
|
5
|
+
fromAbsolutePath: string;
|
|
6
|
+
fromRelativePath: string;
|
|
7
|
+
toAbsolutePath: string;
|
|
8
|
+
toRelativePath: string;
|
|
9
|
+
};
|
|
10
|
+
declare function createFolderizationViolation(filePath: string, suggestedPath: string, options: NormalizedCheckCodeDisciplineOptions, details: Record<string, unknown>): CodeDisciplineViolation;
|
|
11
|
+
declare function buildMovePlan(sourceFiles: ScannedSourceFile[], options: NormalizedCheckCodeDisciplineOptions): {
|
|
12
|
+
moves: PlannedMove[];
|
|
13
|
+
violations: CodeDisciplineViolation[];
|
|
14
|
+
};
|
|
15
|
+
declare function fixFolderization(sourceFiles: ScannedSourceFile[], options: NormalizedCheckCodeDisciplineOptions, logger: NormalizedSyncImportsLogger): Promise<FixCodeDisciplineResult>;
|
|
16
|
+
export { buildMovePlan, createFolderizationViolation, fixFolderization };
|
|
17
|
+
//# sourceMappingURL=fix-folderization.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fix-folderization.d.ts","sourceRoot":"","sources":["../../src/checks/fix-folderization.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACV,uBAAuB,EACvB,uBAAuB,EACvB,oCAAoC,EACrC,MAAM,YAAY,CAAC;AACpB,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAC7D,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,4BAA4B,CAAC;AAO9E,KAAK,WAAW,GAAG;IACjB,gBAAgB,EAAE,MAAM,CAAC;IACzB,gBAAgB,EAAE,MAAM,CAAC;IACzB,cAAc,EAAE,MAAM,CAAC;IACvB,cAAc,EAAE,MAAM,CAAC;CACxB,CAAC;AAEF,iBAAS,4BAA4B,CACnC,QAAQ,EAAE,MAAM,EAChB,aAAa,EAAE,MAAM,EACrB,OAAO,EAAE,oCAAoC,EAC7C,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC/B,uBAAuB,CAUzB;AAED,iBAAS,aAAa,CACpB,WAAW,EAAE,iBAAiB,EAAE,EAChC,OAAO,EAAE,oCAAoC,GAC5C;IAAE,KAAK,EAAE,WAAW,EAAE,CAAC;IAAC,UAAU,EAAE,uBAAuB,EAAE,CAAA;CAAE,CAqBjE;AAsKD,iBAAe,gBAAgB,CAC7B,WAAW,EAAE,iBAAiB,EAAE,EAChC,OAAO,EAAE,oCAAoC,EAC7C,MAAM,EAAE,2BAA2B,GAClC,OAAO,CAAC,uBAAuB,CAAC,CAyElC;AAED,OAAO,EAAE,aAAa,EAAE,4BAA4B,EAAE,gBAAgB,EAAE,CAAC"}
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
import fs from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { applyTextReplacements, collectModuleSpecifiers } from "../imports/module-specifiers.js";
|
|
4
|
+
import { isRelativeImportSpecifier } from "../imports/resolve.js";
|
|
5
|
+
import { FileConflictError, FixFailureError, RewriteFailureError } from "../shared/errors.js";
|
|
6
|
+
import { ensureDotExtension, pathExists, stripKnownExtension, toPosixPath } from "../shared/utils.js";
|
|
7
|
+
import { planFolderizeCompoundFiles } from "./rules/folderize-plan.js";
|
|
8
|
+
function createFolderizationViolation(filePath, suggestedPath, options, details) {
|
|
9
|
+
return {
|
|
10
|
+
rule: "folderize-compound-files",
|
|
11
|
+
stop: options.rules.folderizeCompoundFiles.stop,
|
|
12
|
+
fix: options.rules.folderizeCompoundFiles.fix,
|
|
13
|
+
filePath,
|
|
14
|
+
message: `file can be grouped under ${suggestedPath}`,
|
|
15
|
+
suggestedPath,
|
|
16
|
+
details,
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
function buildMovePlan(sourceFiles, options) {
|
|
20
|
+
const candidates = planFolderizeCompoundFiles(sourceFiles, options);
|
|
21
|
+
const moves = candidates.map((candidate) => ({
|
|
22
|
+
fromAbsolutePath: candidate.absolutePath,
|
|
23
|
+
fromRelativePath: candidate.relativeFromProjectRoot,
|
|
24
|
+
toAbsolutePath: candidate.suggestedAbsolutePath,
|
|
25
|
+
toRelativePath: candidate.suggestedPath,
|
|
26
|
+
}));
|
|
27
|
+
const violations = candidates.map((candidate) => createFolderizationViolation(candidate.relativeFromProjectRoot, candidate.suggestedPath, options, {
|
|
28
|
+
mode: candidate.mode,
|
|
29
|
+
prefix: candidate.prefix,
|
|
30
|
+
remainder: candidate.remainder,
|
|
31
|
+
separator: candidate.separator,
|
|
32
|
+
}));
|
|
33
|
+
return { moves, violations };
|
|
34
|
+
}
|
|
35
|
+
function resolveRelativeFromInventory(specifier, sourceFilePath, sourceExtensions, knownFiles) {
|
|
36
|
+
if (!isRelativeImportSpecifier(specifier))
|
|
37
|
+
return null;
|
|
38
|
+
const basePath = path.resolve(path.dirname(sourceFilePath), specifier);
|
|
39
|
+
const exactCandidates = [basePath];
|
|
40
|
+
const fileCandidates = sourceExtensions.map((extension) => `${basePath}${ensureDotExtension(extension)}`);
|
|
41
|
+
const indexCandidates = sourceExtensions.map((extension) => path.join(basePath, `index${ensureDotExtension(extension)}`));
|
|
42
|
+
for (const candidate of [...exactCandidates, ...fileCandidates, ...indexCandidates]) {
|
|
43
|
+
if (knownFiles.has(candidate)) {
|
|
44
|
+
return candidate;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
function formatRelativeSpecifier(originalSpecifier, fromAbsolutePath, toAbsolutePath, sourceExtensions) {
|
|
50
|
+
const hadExplicitExtension = sourceExtensions.some((extension) => originalSpecifier.toLowerCase().endsWith(extension.toLowerCase()));
|
|
51
|
+
let relativePath = toPosixPath(path.relative(path.dirname(fromAbsolutePath), toAbsolutePath));
|
|
52
|
+
if (!relativePath.startsWith("."))
|
|
53
|
+
relativePath = `./${relativePath}`;
|
|
54
|
+
if (hadExplicitExtension) {
|
|
55
|
+
return relativePath;
|
|
56
|
+
}
|
|
57
|
+
const withoutExtension = stripKnownExtension(relativePath, sourceExtensions);
|
|
58
|
+
const targetBasename = path.basename(toAbsolutePath);
|
|
59
|
+
const originalWithoutExtension = stripKnownExtension(originalSpecifier, sourceExtensions);
|
|
60
|
+
const originalUsesIndex = /(^|\/)index$/.test(originalWithoutExtension);
|
|
61
|
+
if (targetBasename.startsWith("index.") && !originalUsesIndex) {
|
|
62
|
+
return withoutExtension.replace(/\/index$/, "") || ".";
|
|
63
|
+
}
|
|
64
|
+
return withoutExtension;
|
|
65
|
+
}
|
|
66
|
+
async function planImportRewritesForFolderizationMoves(sourceFiles, moves, options) {
|
|
67
|
+
if (moves.length === 0) {
|
|
68
|
+
return {
|
|
69
|
+
rewrittenByPath: new Map(),
|
|
70
|
+
rewrittenFiles: 0,
|
|
71
|
+
rewrittenImports: 0,
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
const movedPaths = new Map(moves.map((move) => [move.fromAbsolutePath, move.toAbsolutePath]));
|
|
75
|
+
const knownFiles = new Set(sourceFiles.map((file) => file.absolutePath));
|
|
76
|
+
const rewrittenByPath = new Map();
|
|
77
|
+
let rewrittenFiles = 0;
|
|
78
|
+
let rewrittenImports = 0;
|
|
79
|
+
for (const file of sourceFiles) {
|
|
80
|
+
const originalText = await fs.readFile(file.absolutePath, "utf8");
|
|
81
|
+
const futureFilePath = movedPaths.get(file.absolutePath) ?? file.absolutePath;
|
|
82
|
+
const replacements = [];
|
|
83
|
+
try {
|
|
84
|
+
for (const occurrence of collectModuleSpecifiers(originalText, file.absolutePath)) {
|
|
85
|
+
if (!isRelativeImportSpecifier(occurrence.specifier))
|
|
86
|
+
continue;
|
|
87
|
+
const resolvedTarget = resolveRelativeFromInventory(occurrence.specifier, file.absolutePath, options.sourceExtensions, knownFiles);
|
|
88
|
+
if (!resolvedTarget)
|
|
89
|
+
continue;
|
|
90
|
+
const futureTargetPath = movedPaths.get(resolvedTarget) ?? resolvedTarget;
|
|
91
|
+
const sourceMoved = futureFilePath !== file.absolutePath;
|
|
92
|
+
const targetMoved = futureTargetPath !== resolvedTarget;
|
|
93
|
+
if (!sourceMoved && !targetMoved)
|
|
94
|
+
continue;
|
|
95
|
+
replacements.push({
|
|
96
|
+
start: occurrence.start,
|
|
97
|
+
end: occurrence.end,
|
|
98
|
+
value: formatRelativeSpecifier(occurrence.specifier, futureFilePath, futureTargetPath, options.sourceExtensions),
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
catch (error) {
|
|
103
|
+
throw new RewriteFailureError(file.relativeFromProjectRoot, error);
|
|
104
|
+
}
|
|
105
|
+
const next = applyTextReplacements(originalText, replacements);
|
|
106
|
+
if (next.count === 0)
|
|
107
|
+
continue;
|
|
108
|
+
rewrittenByPath.set(futureFilePath, next);
|
|
109
|
+
rewrittenFiles += 1;
|
|
110
|
+
rewrittenImports += next.count;
|
|
111
|
+
}
|
|
112
|
+
return { rewrittenByPath, rewrittenFiles, rewrittenImports };
|
|
113
|
+
}
|
|
114
|
+
async function validateMovePlan(moves) {
|
|
115
|
+
const seenTargets = new Set();
|
|
116
|
+
const movingFrom = new Set(moves.map((move) => move.fromAbsolutePath));
|
|
117
|
+
for (const move of moves) {
|
|
118
|
+
if (move.fromAbsolutePath === move.toAbsolutePath)
|
|
119
|
+
continue;
|
|
120
|
+
if (seenTargets.has(move.toAbsolutePath)) {
|
|
121
|
+
throw new FileConflictError(move.toRelativePath, {
|
|
122
|
+
reason: "duplicate-target",
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
seenTargets.add(move.toAbsolutePath);
|
|
126
|
+
if (movingFrom.has(move.toAbsolutePath))
|
|
127
|
+
continue;
|
|
128
|
+
if (await pathExists(move.toAbsolutePath)) {
|
|
129
|
+
throw new FileConflictError(move.toRelativePath, {
|
|
130
|
+
reason: "existing-target",
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
async function moveFiles(moves) {
|
|
136
|
+
let movedFiles = 0;
|
|
137
|
+
for (const move of moves) {
|
|
138
|
+
if (move.fromAbsolutePath === move.toAbsolutePath)
|
|
139
|
+
continue;
|
|
140
|
+
await fs.mkdir(path.dirname(move.toAbsolutePath), { recursive: true });
|
|
141
|
+
await fs.rename(move.fromAbsolutePath, move.toAbsolutePath);
|
|
142
|
+
movedFiles += 1;
|
|
143
|
+
}
|
|
144
|
+
return movedFiles;
|
|
145
|
+
}
|
|
146
|
+
async function removeEmptyDirectories(directories) {
|
|
147
|
+
const sorted = [...new Set(directories)].sort((left, right) => right.length - left.length);
|
|
148
|
+
for (const directoryPath of sorted) {
|
|
149
|
+
try {
|
|
150
|
+
await fs.rmdir(directoryPath);
|
|
151
|
+
}
|
|
152
|
+
catch {
|
|
153
|
+
// The directory still contains files, which is expected in many cases.
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
async function fixFolderization(sourceFiles, options, logger) {
|
|
158
|
+
const { moves, violations } = buildMovePlan(sourceFiles, options);
|
|
159
|
+
const warnings = violations.filter((violation) => !violation.stop).length;
|
|
160
|
+
const failures = violations.length - warnings;
|
|
161
|
+
if (!options.rules.folderizeCompoundFiles.enabled || moves.length === 0) {
|
|
162
|
+
logger.info("fix-folderization-unchanged", "no folderization moves required", {
|
|
163
|
+
moves: 0,
|
|
164
|
+
});
|
|
165
|
+
return {
|
|
166
|
+
ok: true,
|
|
167
|
+
moved_files: 0,
|
|
168
|
+
rewritten_files: 0,
|
|
169
|
+
rewritten_imports: 0,
|
|
170
|
+
warnings,
|
|
171
|
+
failures,
|
|
172
|
+
violations,
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
if (!options.rules.folderizeCompoundFiles.fix) {
|
|
176
|
+
logger.warn("fix-folderization-disabled", "folderization fix is disabled", {
|
|
177
|
+
candidates: moves.length,
|
|
178
|
+
});
|
|
179
|
+
return {
|
|
180
|
+
ok: failures === 0,
|
|
181
|
+
moved_files: 0,
|
|
182
|
+
rewritten_files: 0,
|
|
183
|
+
rewritten_imports: 0,
|
|
184
|
+
warnings,
|
|
185
|
+
failures,
|
|
186
|
+
violations,
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
await validateMovePlan(moves);
|
|
190
|
+
const sourceDirectories = moves.map((move) => path.dirname(move.fromAbsolutePath));
|
|
191
|
+
try {
|
|
192
|
+
const rewriteState = await planImportRewritesForFolderizationMoves(sourceFiles, moves, options);
|
|
193
|
+
const movedFiles = await moveFiles(moves);
|
|
194
|
+
for (const [filePath, next] of rewriteState.rewrittenByPath) {
|
|
195
|
+
await fs.writeFile(filePath, next.text);
|
|
196
|
+
}
|
|
197
|
+
await removeEmptyDirectories(sourceDirectories);
|
|
198
|
+
logger.success("fix-folderization-finished", `folderized files=${movedFiles} rewrittenImports=${rewriteState.rewrittenImports}`, {
|
|
199
|
+
movedFiles,
|
|
200
|
+
rewrittenFiles: rewriteState.rewrittenFiles,
|
|
201
|
+
rewrittenImports: rewriteState.rewrittenImports,
|
|
202
|
+
});
|
|
203
|
+
return {
|
|
204
|
+
ok: true,
|
|
205
|
+
moved_files: movedFiles,
|
|
206
|
+
rewritten_files: rewriteState.rewrittenFiles,
|
|
207
|
+
rewritten_imports: rewriteState.rewrittenImports,
|
|
208
|
+
warnings,
|
|
209
|
+
failures: 0,
|
|
210
|
+
violations,
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
catch (error) {
|
|
214
|
+
if (error instanceof FileConflictError || error instanceof RewriteFailureError) {
|
|
215
|
+
throw error;
|
|
216
|
+
}
|
|
217
|
+
throw new FixFailureError("Folderization fix failed", {
|
|
218
|
+
cause: error instanceof Error ? error.message : String(error),
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
export { buildMovePlan, createFolderizationViolation, fixFolderization };
|
|
223
|
+
//# sourceMappingURL=fix-folderization.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fix-folderization.js","sourceRoot":"","sources":["../../src/checks/fix-folderization.ts"],"names":[],"mappings":"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"}
|