@enhance-eng/archangel 0.1.0 → 0.2.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 +5 -3
- package/archangel.schema.json +284 -8
- package/index.d.ts +261 -67
- package/package.json +20 -9
package/README.md
CHANGED
|
@@ -2,8 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
**Make architecture verifiable.**
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+

|
|
6
|
+
|
|
7
|
+
Architecture tests for TypeScript, checked in milliseconds. You write a table of what each part of
|
|
8
|
+
your code may import. Archangel tells you when the code breaks the table, and what to do about it. It checks
|
|
7
9
|
a large repository in about a second, so it can run in your editor as you type.
|
|
8
10
|
|
|
9
11
|
```sh
|
|
@@ -16,7 +18,7 @@ Archangel checks imports, import cycles, file and folder names, the files that f
|
|
|
16
18
|
and exports. It has presets for Feature-Sliced Design and Next.js, and you can write your own
|
|
17
19
|
conditions in TypeScript.
|
|
18
20
|
|
|
19
|
-
- [
|
|
21
|
+
- [Get started](https://github.com/enhance-labs-builds/archangel/blob/main/README.md#get-started)
|
|
20
22
|
- [Rules](https://github.com/enhance-labs-builds/archangel/blob/main/docs/rules.md)
|
|
21
23
|
- [Presets](https://github.com/enhance-labs-builds/archangel/blob/main/docs/presets.md)
|
|
22
24
|
- [Plugins](https://github.com/enhance-labs-builds/archangel/blob/main/docs/plugins.md)
|
package/archangel.schema.json
CHANGED
|
@@ -5,14 +5,32 @@
|
|
|
5
5
|
"description": "The elements of a repository, named sets of its files, and the rules between them. Archangel checks the same fields, with every problem at once.",
|
|
6
6
|
"type": "object",
|
|
7
7
|
"additionalProperties": false,
|
|
8
|
-
"required": [
|
|
9
|
-
"elements"
|
|
10
|
-
],
|
|
11
8
|
"properties": {
|
|
12
9
|
"$schema": {
|
|
13
10
|
"type": "string",
|
|
14
11
|
"description": "This schema, for editors; archangel ignores it."
|
|
15
12
|
},
|
|
13
|
+
"parts": {
|
|
14
|
+
"type": "object",
|
|
15
|
+
"description": "The parts of the code, by file pattern. A file is in one part at most.",
|
|
16
|
+
"minProperties": 1,
|
|
17
|
+
"additionalProperties": {
|
|
18
|
+
"oneOf": [
|
|
19
|
+
{
|
|
20
|
+
"type": "string",
|
|
21
|
+
"description": "A path pattern; `{name}` captures one instance, such as one feature."
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
"type": "array",
|
|
25
|
+
"items": {
|
|
26
|
+
"type": "string"
|
|
27
|
+
},
|
|
28
|
+
"minItems": 1,
|
|
29
|
+
"description": "Several path patterns."
|
|
30
|
+
}
|
|
31
|
+
]
|
|
32
|
+
}
|
|
33
|
+
},
|
|
16
34
|
"elements": {
|
|
17
35
|
"type": "object",
|
|
18
36
|
"description": "The elements: parts of the code by file pattern, or packages, by name.",
|
|
@@ -51,6 +69,13 @@
|
|
|
51
69
|
]
|
|
52
70
|
}
|
|
53
71
|
},
|
|
72
|
+
"groups": {
|
|
73
|
+
"type": "object",
|
|
74
|
+
"description": "The same as `selectors`: named sets of files, which may overlap.",
|
|
75
|
+
"additionalProperties": {
|
|
76
|
+
"$ref": "#/definitions/selector"
|
|
77
|
+
}
|
|
78
|
+
},
|
|
54
79
|
"selectors": {
|
|
55
80
|
"type": "object",
|
|
56
81
|
"description": "Named sets of files, which rules and other selectors use by name. A selector's name may not be an element's.",
|
|
@@ -58,6 +83,18 @@
|
|
|
58
83
|
"$ref": "#/definitions/selector"
|
|
59
84
|
}
|
|
60
85
|
},
|
|
86
|
+
"externals": {
|
|
87
|
+
"type": "object",
|
|
88
|
+
"description": "Named lists of outside packages, such as `pg`, `@aws-sdk/*` or `node:fs`.",
|
|
89
|
+
"additionalProperties": {
|
|
90
|
+
"type": "array",
|
|
91
|
+
"items": {
|
|
92
|
+
"type": "string"
|
|
93
|
+
},
|
|
94
|
+
"minItems": 1,
|
|
95
|
+
"description": "Package names. A package is in every set that lists it."
|
|
96
|
+
}
|
|
97
|
+
},
|
|
61
98
|
"rules": {
|
|
62
99
|
"type": "array",
|
|
63
100
|
"description": "The rules: a shorthand with `kind`, or a sentence of `files` and one condition.",
|
|
@@ -83,6 +120,10 @@
|
|
|
83
120
|
"type": "string",
|
|
84
121
|
"description": "Why the rule exists, shown with each violation."
|
|
85
122
|
},
|
|
123
|
+
"because": {
|
|
124
|
+
"type": "string",
|
|
125
|
+
"description": "The same as `reason`: why the rule exists. A rule has one of the two."
|
|
126
|
+
},
|
|
86
127
|
"link": {
|
|
87
128
|
"type": "string",
|
|
88
129
|
"description": "Where to read more, shown with each violation."
|
|
@@ -129,6 +170,10 @@
|
|
|
129
170
|
"type": "string",
|
|
130
171
|
"description": "Why the rule exists, shown with each violation."
|
|
131
172
|
},
|
|
173
|
+
"because": {
|
|
174
|
+
"type": "string",
|
|
175
|
+
"description": "The same as `reason`: why the rule exists. A rule has one of the two."
|
|
176
|
+
},
|
|
132
177
|
"link": {
|
|
133
178
|
"type": "string",
|
|
134
179
|
"description": "Where to read more, shown with each violation."
|
|
@@ -172,6 +217,10 @@
|
|
|
172
217
|
"type": "string",
|
|
173
218
|
"description": "Why the rule exists, shown with each violation."
|
|
174
219
|
},
|
|
220
|
+
"because": {
|
|
221
|
+
"type": "string",
|
|
222
|
+
"description": "The same as `reason`: why the rule exists. A rule has one of the two."
|
|
223
|
+
},
|
|
175
224
|
"link": {
|
|
176
225
|
"type": "string",
|
|
177
226
|
"description": "Where to read more, shown with each violation."
|
|
@@ -203,6 +252,116 @@
|
|
|
203
252
|
}
|
|
204
253
|
}
|
|
205
254
|
},
|
|
255
|
+
{
|
|
256
|
+
"type": "object",
|
|
257
|
+
"description": "An import table: for each row, what its files may import. Each row is a rule named `<table>.<row>`.",
|
|
258
|
+
"additionalProperties": false,
|
|
259
|
+
"required": [
|
|
260
|
+
"kind",
|
|
261
|
+
"name",
|
|
262
|
+
"rows"
|
|
263
|
+
],
|
|
264
|
+
"properties": {
|
|
265
|
+
"kind": {
|
|
266
|
+
"const": "mayImport"
|
|
267
|
+
},
|
|
268
|
+
"name": {
|
|
269
|
+
"type": "string",
|
|
270
|
+
"description": "The table's name, which starts the name of each row. It has no colon and no slash."
|
|
271
|
+
},
|
|
272
|
+
"rows": {
|
|
273
|
+
"type": "object",
|
|
274
|
+
"description": "Each row: a part, a group or a kind, and what its files may import.",
|
|
275
|
+
"additionalProperties": {
|
|
276
|
+
"type": "array",
|
|
277
|
+
"items": {
|
|
278
|
+
"type": "string"
|
|
279
|
+
},
|
|
280
|
+
"description": "Names of rows, parts, groups and externals sets, or path patterns."
|
|
281
|
+
}
|
|
282
|
+
},
|
|
283
|
+
"files": {
|
|
284
|
+
"$ref": "#/definitions/selector",
|
|
285
|
+
"description": "The files whose imports the table judges. By default, the files of its rows."
|
|
286
|
+
},
|
|
287
|
+
"kinds": {
|
|
288
|
+
"type": "string",
|
|
289
|
+
"description": "The pattern that gives each file its kind, with the capture `{kind}`."
|
|
290
|
+
},
|
|
291
|
+
"within": {
|
|
292
|
+
"$ref": "#/definitions/side",
|
|
293
|
+
"description": "An area: the rows judge only imports whose target is in it."
|
|
294
|
+
},
|
|
295
|
+
"everyRowMayImport": {
|
|
296
|
+
"type": "array",
|
|
297
|
+
"items": {
|
|
298
|
+
"type": "string"
|
|
299
|
+
},
|
|
300
|
+
"description": "Cells that every row also has."
|
|
301
|
+
},
|
|
302
|
+
"mayNotImportItself": {
|
|
303
|
+
"type": "array",
|
|
304
|
+
"items": {
|
|
305
|
+
"type": "string"
|
|
306
|
+
},
|
|
307
|
+
"description": "The rows that do not permit their own name."
|
|
308
|
+
},
|
|
309
|
+
"because": {
|
|
310
|
+
"type": "string",
|
|
311
|
+
"description": "The same as `reason`: why the rule exists. A rule has one of the two."
|
|
312
|
+
},
|
|
313
|
+
"reason": {
|
|
314
|
+
"type": "string",
|
|
315
|
+
"description": "Why the rule exists, shown with each violation."
|
|
316
|
+
},
|
|
317
|
+
"link": {
|
|
318
|
+
"type": "string",
|
|
319
|
+
"description": "Where to read more, shown with each violation."
|
|
320
|
+
},
|
|
321
|
+
"imports": {
|
|
322
|
+
"enum": [
|
|
323
|
+
"all",
|
|
324
|
+
"runtime",
|
|
325
|
+
"types"
|
|
326
|
+
],
|
|
327
|
+
"description": "Which imports the rule checks: all of them, those that stay at runtime, or type imports."
|
|
328
|
+
},
|
|
329
|
+
"names": {
|
|
330
|
+
"type": "array",
|
|
331
|
+
"items": {
|
|
332
|
+
"type": "string"
|
|
333
|
+
},
|
|
334
|
+
"minItems": 1,
|
|
335
|
+
"description": "Only imports that bring in one of these names."
|
|
336
|
+
},
|
|
337
|
+
"specifier": {
|
|
338
|
+
"enum": [
|
|
339
|
+
"relative",
|
|
340
|
+
"parent",
|
|
341
|
+
"alias",
|
|
342
|
+
"package"
|
|
343
|
+
],
|
|
344
|
+
"description": "Only imports whose specifier is written this way."
|
|
345
|
+
},
|
|
346
|
+
"dependency": {
|
|
347
|
+
"enum": [
|
|
348
|
+
"dev",
|
|
349
|
+
"undeclared"
|
|
350
|
+
],
|
|
351
|
+
"description": "Only imports of packages that the nearest package.json lists this way."
|
|
352
|
+
},
|
|
353
|
+
"instance": {
|
|
354
|
+
"enum": [
|
|
355
|
+
"same",
|
|
356
|
+
"different"
|
|
357
|
+
],
|
|
358
|
+
"description": "Only imports whose two ends compare this way, in the row's name."
|
|
359
|
+
},
|
|
360
|
+
"examples": {
|
|
361
|
+
"$ref": "#/definitions/examples"
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
},
|
|
206
365
|
{
|
|
207
366
|
"type": "object",
|
|
208
367
|
"description": "The files may not import the other side.",
|
|
@@ -224,6 +383,10 @@
|
|
|
224
383
|
"type": "string",
|
|
225
384
|
"description": "Why the rule exists, shown with each violation."
|
|
226
385
|
},
|
|
386
|
+
"because": {
|
|
387
|
+
"type": "string",
|
|
388
|
+
"description": "The same as `reason`: why the rule exists. A rule has one of the two."
|
|
389
|
+
},
|
|
227
390
|
"link": {
|
|
228
391
|
"type": "string",
|
|
229
392
|
"description": "Where to read more, shown with each violation."
|
|
@@ -342,6 +505,10 @@
|
|
|
342
505
|
"type": "string",
|
|
343
506
|
"description": "Why the rule exists, shown with each violation."
|
|
344
507
|
},
|
|
508
|
+
"because": {
|
|
509
|
+
"type": "string",
|
|
510
|
+
"description": "The same as `reason`: why the rule exists. A rule has one of the two."
|
|
511
|
+
},
|
|
345
512
|
"link": {
|
|
346
513
|
"type": "string",
|
|
347
514
|
"description": "Where to read more, shown with each violation."
|
|
@@ -358,8 +525,16 @@
|
|
|
358
525
|
"$ref": "#/definitions/examples"
|
|
359
526
|
},
|
|
360
527
|
"mayOnlyImport": {
|
|
361
|
-
"
|
|
362
|
-
|
|
528
|
+
"anyOf": [
|
|
529
|
+
{
|
|
530
|
+
"$ref": "#/definitions/side"
|
|
531
|
+
},
|
|
532
|
+
{
|
|
533
|
+
"type": "array",
|
|
534
|
+
"maxItems": 0
|
|
535
|
+
}
|
|
536
|
+
],
|
|
537
|
+
"description": "All that the files may import, besides each other. An empty list needs `within`."
|
|
363
538
|
},
|
|
364
539
|
"names": {
|
|
365
540
|
"type": "array",
|
|
@@ -392,6 +567,10 @@
|
|
|
392
567
|
"undeclared"
|
|
393
568
|
],
|
|
394
569
|
"description": "Only imports of packages that the importing file's nearest package.json lists only as development dependencies, or does not list at all."
|
|
570
|
+
},
|
|
571
|
+
"within": {
|
|
572
|
+
"$ref": "#/definitions/side",
|
|
573
|
+
"description": "The area: only imports whose other end is in it. On `mayOnlyImport` the target, and on `mayOnlyBeImportedBy` the importer."
|
|
395
574
|
}
|
|
396
575
|
}
|
|
397
576
|
},
|
|
@@ -416,6 +595,10 @@
|
|
|
416
595
|
"type": "string",
|
|
417
596
|
"description": "Why the rule exists, shown with each violation."
|
|
418
597
|
},
|
|
598
|
+
"because": {
|
|
599
|
+
"type": "string",
|
|
600
|
+
"description": "The same as `reason`: why the rule exists. A rule has one of the two."
|
|
601
|
+
},
|
|
419
602
|
"link": {
|
|
420
603
|
"type": "string",
|
|
421
604
|
"description": "Where to read more, shown with each violation."
|
|
@@ -466,6 +649,10 @@
|
|
|
466
649
|
"undeclared"
|
|
467
650
|
],
|
|
468
651
|
"description": "Only imports of packages that the importing file's nearest package.json lists only as development dependencies, or does not list at all."
|
|
652
|
+
},
|
|
653
|
+
"within": {
|
|
654
|
+
"$ref": "#/definitions/side",
|
|
655
|
+
"description": "The area: only imports whose other end is in it. On `mayOnlyImport` the target, and on `mayOnlyBeImportedBy` the importer."
|
|
469
656
|
}
|
|
470
657
|
}
|
|
471
658
|
},
|
|
@@ -490,6 +677,10 @@
|
|
|
490
677
|
"type": "string",
|
|
491
678
|
"description": "Why the rule exists, shown with each violation."
|
|
492
679
|
},
|
|
680
|
+
"because": {
|
|
681
|
+
"type": "string",
|
|
682
|
+
"description": "The same as `reason`: why the rule exists. A rule has one of the two."
|
|
683
|
+
},
|
|
493
684
|
"link": {
|
|
494
685
|
"type": "string",
|
|
495
686
|
"description": "Where to read more, shown with each violation."
|
|
@@ -544,6 +735,10 @@
|
|
|
544
735
|
"type": "string",
|
|
545
736
|
"description": "Why the rule exists, shown with each violation."
|
|
546
737
|
},
|
|
738
|
+
"because": {
|
|
739
|
+
"type": "string",
|
|
740
|
+
"description": "The same as `reason`: why the rule exists. A rule has one of the two."
|
|
741
|
+
},
|
|
547
742
|
"link": {
|
|
548
743
|
"type": "string",
|
|
549
744
|
"description": "Where to read more, shown with each violation."
|
|
@@ -585,6 +780,10 @@
|
|
|
585
780
|
"type": "string",
|
|
586
781
|
"description": "Why the rule exists, shown with each violation."
|
|
587
782
|
},
|
|
783
|
+
"because": {
|
|
784
|
+
"type": "string",
|
|
785
|
+
"description": "The same as `reason`: why the rule exists. A rule has one of the two."
|
|
786
|
+
},
|
|
588
787
|
"link": {
|
|
589
788
|
"type": "string",
|
|
590
789
|
"description": "Where to read more, shown with each violation."
|
|
@@ -645,6 +844,10 @@
|
|
|
645
844
|
"type": "string",
|
|
646
845
|
"description": "Why the rule exists, shown with each violation."
|
|
647
846
|
},
|
|
847
|
+
"because": {
|
|
848
|
+
"type": "string",
|
|
849
|
+
"description": "The same as `reason`: why the rule exists. A rule has one of the two."
|
|
850
|
+
},
|
|
648
851
|
"link": {
|
|
649
852
|
"type": "string",
|
|
650
853
|
"description": "Where to read more, shown with each violation."
|
|
@@ -692,6 +895,10 @@
|
|
|
692
895
|
"type": "string",
|
|
693
896
|
"description": "Why the rule exists, shown with each violation."
|
|
694
897
|
},
|
|
898
|
+
"because": {
|
|
899
|
+
"type": "string",
|
|
900
|
+
"description": "The same as `reason`: why the rule exists. A rule has one of the two."
|
|
901
|
+
},
|
|
695
902
|
"link": {
|
|
696
903
|
"type": "string",
|
|
697
904
|
"description": "Where to read more, shown with each violation."
|
|
@@ -739,6 +946,10 @@
|
|
|
739
946
|
"type": "string",
|
|
740
947
|
"description": "Why the rule exists, shown with each violation."
|
|
741
948
|
},
|
|
949
|
+
"because": {
|
|
950
|
+
"type": "string",
|
|
951
|
+
"description": "The same as `reason`: why the rule exists. A rule has one of the two."
|
|
952
|
+
},
|
|
742
953
|
"link": {
|
|
743
954
|
"type": "string",
|
|
744
955
|
"description": "Where to read more, shown with each violation."
|
|
@@ -773,6 +984,10 @@
|
|
|
773
984
|
"type": "string",
|
|
774
985
|
"description": "Why the rule exists, shown with each violation."
|
|
775
986
|
},
|
|
987
|
+
"because": {
|
|
988
|
+
"type": "string",
|
|
989
|
+
"description": "The same as `reason`: why the rule exists. A rule has one of the two."
|
|
990
|
+
},
|
|
776
991
|
"link": {
|
|
777
992
|
"type": "string",
|
|
778
993
|
"description": "Where to read more, shown with each violation."
|
|
@@ -807,6 +1022,10 @@
|
|
|
807
1022
|
"type": "string",
|
|
808
1023
|
"description": "Why the rule exists, shown with each violation."
|
|
809
1024
|
},
|
|
1025
|
+
"because": {
|
|
1026
|
+
"type": "string",
|
|
1027
|
+
"description": "The same as `reason`: why the rule exists. A rule has one of the two."
|
|
1028
|
+
},
|
|
810
1029
|
"link": {
|
|
811
1030
|
"type": "string",
|
|
812
1031
|
"description": "Where to read more, shown with each violation."
|
|
@@ -851,6 +1070,10 @@
|
|
|
851
1070
|
"type": "string",
|
|
852
1071
|
"description": "Why the rule exists, shown with each violation."
|
|
853
1072
|
},
|
|
1073
|
+
"because": {
|
|
1074
|
+
"type": "string",
|
|
1075
|
+
"description": "The same as `reason`: why the rule exists. A rule has one of the two."
|
|
1076
|
+
},
|
|
854
1077
|
"link": {
|
|
855
1078
|
"type": "string",
|
|
856
1079
|
"description": "Where to read more, shown with each violation."
|
|
@@ -885,6 +1108,10 @@
|
|
|
885
1108
|
"type": "string",
|
|
886
1109
|
"description": "Why the rule exists, shown with each violation."
|
|
887
1110
|
},
|
|
1111
|
+
"because": {
|
|
1112
|
+
"type": "string",
|
|
1113
|
+
"description": "The same as `reason`: why the rule exists. A rule has one of the two."
|
|
1114
|
+
},
|
|
888
1115
|
"link": {
|
|
889
1116
|
"type": "string",
|
|
890
1117
|
"description": "Where to read more, shown with each violation."
|
|
@@ -923,6 +1150,10 @@
|
|
|
923
1150
|
"type": "string",
|
|
924
1151
|
"description": "Why the rule exists, shown with each violation."
|
|
925
1152
|
},
|
|
1153
|
+
"because": {
|
|
1154
|
+
"type": "string",
|
|
1155
|
+
"description": "The same as `reason`: why the rule exists. A rule has one of the two."
|
|
1156
|
+
},
|
|
926
1157
|
"link": {
|
|
927
1158
|
"type": "string",
|
|
928
1159
|
"description": "Where to read more, shown with each violation."
|
|
@@ -1000,13 +1231,29 @@
|
|
|
1000
1231
|
"accepted": {
|
|
1001
1232
|
"type": "array",
|
|
1002
1233
|
"items": {
|
|
1003
|
-
"
|
|
1234
|
+
"anyOf": [
|
|
1235
|
+
{
|
|
1236
|
+
"type": "string",
|
|
1237
|
+
"description": "One line, such as `src/a.ts imports src/b.ts`, for which archangel writes the files."
|
|
1238
|
+
},
|
|
1239
|
+
{
|
|
1240
|
+
"$ref": "#/definitions/exampleFiles"
|
|
1241
|
+
}
|
|
1242
|
+
]
|
|
1004
1243
|
}
|
|
1005
1244
|
},
|
|
1006
1245
|
"rejected": {
|
|
1007
1246
|
"type": "array",
|
|
1008
1247
|
"items": {
|
|
1009
|
-
"
|
|
1248
|
+
"anyOf": [
|
|
1249
|
+
{
|
|
1250
|
+
"type": "string",
|
|
1251
|
+
"description": "One line, such as `src/a.ts imports src/b.ts`, for which archangel writes the files."
|
|
1252
|
+
},
|
|
1253
|
+
{
|
|
1254
|
+
"$ref": "#/definitions/exampleFiles"
|
|
1255
|
+
}
|
|
1256
|
+
]
|
|
1010
1257
|
}
|
|
1011
1258
|
}
|
|
1012
1259
|
}
|
|
@@ -1188,5 +1435,34 @@
|
|
|
1188
1435
|
"minLength": 1,
|
|
1189
1436
|
"description": "A name: text, `*` for any text, and `{FileName}`, `{fileName}`, `{file-name}`, `{file_name}` or `{FILE_NAME}` for the file's name in each style, or the same spellings of `{FolderName}` for the name of the folder that holds it."
|
|
1190
1437
|
}
|
|
1191
|
-
}
|
|
1438
|
+
},
|
|
1439
|
+
"anyOf": [
|
|
1440
|
+
{
|
|
1441
|
+
"properties": {
|
|
1442
|
+
"parts": true
|
|
1443
|
+
},
|
|
1444
|
+
"required": [
|
|
1445
|
+
"parts"
|
|
1446
|
+
]
|
|
1447
|
+
},
|
|
1448
|
+
{
|
|
1449
|
+
"properties": {
|
|
1450
|
+
"elements": true
|
|
1451
|
+
},
|
|
1452
|
+
"required": [
|
|
1453
|
+
"elements"
|
|
1454
|
+
]
|
|
1455
|
+
},
|
|
1456
|
+
{
|
|
1457
|
+
"properties": {
|
|
1458
|
+
"rules": {
|
|
1459
|
+
"type": "array",
|
|
1460
|
+
"minItems": 1
|
|
1461
|
+
}
|
|
1462
|
+
},
|
|
1463
|
+
"required": [
|
|
1464
|
+
"rules"
|
|
1465
|
+
]
|
|
1466
|
+
}
|
|
1467
|
+
]
|
|
1192
1468
|
}
|
package/index.d.ts
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
// The types of an archangel config. They check, as the config is written, that every rule names a
|
|
2
|
-
// declared
|
|
3
|
-
// rule names
|
|
4
|
-
// left to archangel, because checking them here makes TypeScript infer mutable
|
|
2
|
+
// declared part or group, that a sentence has exactly one condition, that a table's cells name
|
|
3
|
+
// something, and that an isolate rule names a part with a capture such as `{feature}`. The names
|
|
4
|
+
// inside `groups` are left to archangel, because checking them here makes TypeScript infer mutable
|
|
5
|
+
// lists for them. `parts` and `elements` are two spellings of one key, as are `groups` and
|
|
6
|
+
// `selectors`, and `because` and `reason`: a config uses one spelling of each.
|
|
5
7
|
|
|
6
8
|
import type { AnyCondition, Plugin } from './plugin.js';
|
|
7
9
|
|
|
@@ -29,6 +31,14 @@ type Captures<S> = S extends string
|
|
|
29
31
|
: false
|
|
30
32
|
: false;
|
|
31
33
|
|
|
34
|
+
/** The named sets of outside packages. */
|
|
35
|
+
export type Externals = Readonly<Record<string, readonly string[]>>;
|
|
36
|
+
|
|
37
|
+
/** Each externals set, as the package element it reads as. */
|
|
38
|
+
type ExternalElements<X extends Externals> = {
|
|
39
|
+
readonly [K in keyof X]: { readonly packages: readonly string[] };
|
|
40
|
+
};
|
|
41
|
+
|
|
32
42
|
/** The name of a declared element. */
|
|
33
43
|
export type ElementName<E extends Elements> = Extract<keyof E, string>;
|
|
34
44
|
|
|
@@ -99,23 +109,52 @@ export type Side<E extends Elements, S extends Selectors> =
|
|
|
99
109
|
/** Files by path, relative to the config folder, with their text. */
|
|
100
110
|
export type ExampleFiles = Readonly<Record<string, string>>;
|
|
101
111
|
|
|
102
|
-
/**
|
|
103
|
-
export
|
|
112
|
+
/** A one-line example, such as `'src/a.ts imports src/b.ts'`, or the files of an example. */
|
|
113
|
+
export type Example = string | ExampleFiles;
|
|
114
|
+
|
|
115
|
+
/** What the examples of a rule hold. */
|
|
116
|
+
export interface Examples {
|
|
117
|
+
/** Examples that the rule must accept. */
|
|
118
|
+
readonly accepted?: readonly Example[];
|
|
119
|
+
/** Examples that the rule must reject. */
|
|
120
|
+
readonly rejected?: readonly Example[];
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/** Why a rule exists, shown with each violation. A rule has one of the two spellings. */
|
|
124
|
+
type Because =
|
|
125
|
+
| { readonly because?: string; readonly reason?: never }
|
|
126
|
+
| { readonly reason?: string; readonly because?: never };
|
|
127
|
+
|
|
128
|
+
/** What every rule may have, and its reason. */
|
|
129
|
+
export type RuleOptions = CommonOptions & Because;
|
|
130
|
+
|
|
131
|
+
/** What every rule may have, without its reason. */
|
|
132
|
+
export interface CommonOptions {
|
|
104
133
|
/** The rule's name, for exceptions and the baseline; each rule gives a default. */
|
|
105
134
|
readonly name?: string;
|
|
106
|
-
/** Why the rule exists, shown with each violation. */
|
|
107
|
-
readonly reason?: string;
|
|
108
135
|
/** Where to read more, shown with each violation. */
|
|
109
136
|
readonly link?: string;
|
|
110
137
|
/** Which imports the rule checks: all of them, those that stay at runtime, or type imports. */
|
|
111
138
|
readonly imports?: 'all' | 'runtime' | 'types';
|
|
112
|
-
/**
|
|
113
|
-
readonly examples?:
|
|
114
|
-
readonly accepted?: readonly ExampleFiles[];
|
|
115
|
-
readonly rejected?: readonly ExampleFiles[];
|
|
116
|
-
};
|
|
139
|
+
/** Examples that the rule must accept and must reject, which `archangel test` checks. */
|
|
140
|
+
readonly examples?: Examples;
|
|
117
141
|
}
|
|
118
142
|
|
|
143
|
+
/**
|
|
144
|
+
* A rule name `N` that the config writes itself. A slash separates a preset's name from the names
|
|
145
|
+
* it declares, so the config's own name has one only after the name of a preset in `X`, the
|
|
146
|
+
* presets the config extends, and then replaces that preset's rule of the name. Any other name
|
|
147
|
+
* with a slash becomes the text of the type error.
|
|
148
|
+
*/
|
|
149
|
+
export type OwnRuleName<N extends string, X extends string> = N extends `${infer Prefix}/${string}`
|
|
150
|
+
? Prefix extends X
|
|
151
|
+
? N
|
|
152
|
+
: "a rule's own name has no slash, except after the name of a preset the config extends, to replace its rule"
|
|
153
|
+
: N;
|
|
154
|
+
|
|
155
|
+
/** A rule's `name`, checked by {@link OwnRuleName}. */
|
|
156
|
+
type Named<N extends string, X extends string> = { readonly name?: OwnRuleName<N, X> };
|
|
157
|
+
|
|
119
158
|
/** What limits an import condition to some imports. */
|
|
120
159
|
export interface ImportLimits {
|
|
121
160
|
/** Only imports of these names. */
|
|
@@ -182,15 +221,21 @@ type NoLimits = { readonly [K in keyof ImportLimits]?: never };
|
|
|
182
221
|
|
|
183
222
|
type NoChains = { readonly transitively?: false; readonly stopAt?: never };
|
|
184
223
|
|
|
224
|
+
type NoArea = { readonly within?: never };
|
|
225
|
+
|
|
226
|
+
/** The area whose imports an import condition judges: an import outside it passes the rule. */
|
|
227
|
+
type Area<E extends Elements, S extends Selectors> = { readonly within?: Side<E, S> };
|
|
228
|
+
|
|
185
229
|
/** A rule written as a sentence: `files`, and exactly one condition. */
|
|
186
|
-
export type Sentence<E extends Elements, S extends Selectors> = Omit<
|
|
230
|
+
export type Sentence<E extends Elements, S extends Selectors> = Omit<CommonOptions, 'imports'> & Because & {
|
|
187
231
|
readonly kind?: never;
|
|
188
232
|
/** The files the rule is about. */
|
|
189
233
|
readonly files: Files<E, S>;
|
|
190
234
|
} & (
|
|
191
|
-
| (Only<Side<E, S>, 'mayNotImport'> & ImportLimits & NoChains & Pick<
|
|
235
|
+
| (Only<Side<E, S>, 'mayNotImport'> & ImportLimits & NoChains & NoArea & Pick<CommonOptions, 'imports'>)
|
|
192
236
|
| (Only<Side<E, S>, 'mayNotImport'> &
|
|
193
|
-
NoLimits &
|
|
237
|
+
NoLimits &
|
|
238
|
+
NoArea & {
|
|
194
239
|
/** Follow chains of imports, not only direct ones. */
|
|
195
240
|
readonly transitively: true;
|
|
196
241
|
/** Files where a chain ends, such as `{ directive: 'use server' }`. */
|
|
@@ -198,24 +243,25 @@ export type Sentence<E extends Elements, S extends Selectors> = Omit<RuleOptions
|
|
|
198
243
|
/** A chain of type imports never runs, so a transitive rule checks no type imports. */
|
|
199
244
|
readonly imports?: 'all' | 'runtime';
|
|
200
245
|
})
|
|
201
|
-
| (Only<Side<E, S>, 'mayOnlyImport'> & ImportLimits & NoChains & Pick<
|
|
246
|
+
| (Only<Side<E, S>, 'mayOnlyImport'> & ImportLimits & NoChains & Area<E, S> & Pick<CommonOptions, 'imports'>)
|
|
202
247
|
| (Only<Side<E, S>, 'mayOnlyBeImportedBy'> &
|
|
203
248
|
ImportLimits &
|
|
204
249
|
NoChains &
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
| (Only<Side<E, S>, '
|
|
208
|
-
| (Only<Side<E, S>, '
|
|
209
|
-
| (Only<Side<E, S>, '
|
|
210
|
-
| (Only<Side<E, S>, '
|
|
211
|
-
| (Only<Side<E, S>, '
|
|
212
|
-
| (Only<Side<E, S>, '
|
|
250
|
+
Area<E, S> &
|
|
251
|
+
Pick<CommonOptions, 'imports'>)
|
|
252
|
+
| (Only<Side<E, S>, 'mustBeNamed'> & NoLimits & NoChains & NoArea & NoImports)
|
|
253
|
+
| (Only<Side<E, S>, 'mustContain'> & NoLimits & NoChains & NoArea & NoImports)
|
|
254
|
+
| (Only<Side<E, S>, 'mustNotExist'> & NoLimits & NoChains & NoArea & NoImports)
|
|
255
|
+
| (Only<Side<E, S>, 'mustExport'> & NoLimits & NoChains & NoArea & NoImports)
|
|
256
|
+
| (Only<Side<E, S>, 'mayNotExport'> & NoLimits & NoChains & NoArea & NoImports)
|
|
257
|
+
| (Only<Side<E, S>, 'mustOnlyReExport'> & NoLimits & NoChains & NoArea & NoImports)
|
|
258
|
+
| (Only<Side<E, S>, 'noCycles'> & NoLimits & NoChains & NoArea & Pick<CommonOptions, 'imports'>)
|
|
213
259
|
);
|
|
214
260
|
|
|
215
261
|
type NoImports = { readonly imports?: never };
|
|
216
262
|
|
|
217
263
|
/** A rule about folders: `folders`, and `mustBeNamed` or `mustContain`. */
|
|
218
|
-
export type FolderSentence = Omit<
|
|
264
|
+
export type FolderSentence = Omit<CommonOptions, 'imports'> & Because & {
|
|
219
265
|
readonly kind?: never;
|
|
220
266
|
readonly files?: never;
|
|
221
267
|
/**
|
|
@@ -226,6 +272,7 @@ export type FolderSentence = Omit<RuleOptions, 'imports'> & {
|
|
|
226
272
|
readonly folders: string | readonly string[];
|
|
227
273
|
} & NoLimits &
|
|
228
274
|
NoChains &
|
|
275
|
+
NoArea &
|
|
229
276
|
NoImports &
|
|
230
277
|
(
|
|
231
278
|
| (Only<never, 'mustBeNamed'>)
|
|
@@ -234,7 +281,7 @@ export type FolderSentence = Omit<RuleOptions, 'imports'> & {
|
|
|
234
281
|
);
|
|
235
282
|
|
|
236
283
|
/** A sentence of a rule of several sentences: the rule holds its name, reason and examples. */
|
|
237
|
-
type InnerSentence<T> = T extends unknown ? Omit<T, 'name' | 'reason' | 'link' | 'examples' | 'imports'> : never;
|
|
284
|
+
type InnerSentence<T> = T extends unknown ? Omit<T, 'name' | 'reason' | 'because' | 'link' | 'examples' | 'imports'> : never;
|
|
238
285
|
|
|
239
286
|
/** A shorthand as one sentence of a rule of several sentences. */
|
|
240
287
|
type InnerShorthand<E extends Elements> =
|
|
@@ -243,7 +290,7 @@ type InnerShorthand<E extends Elements> =
|
|
|
243
290
|
| { readonly kind: 'entry'; readonly element: NoInfer<ElementName<E>>; readonly files: readonly string[] };
|
|
244
291
|
|
|
245
292
|
/** A rule of several sentences, broken when any of them is, which needs a name. */
|
|
246
|
-
export type SeveralSentences<E extends Elements, S extends Selectors> = Omit<
|
|
293
|
+
export type SeveralSentences<E extends Elements, S extends Selectors> = Omit<CommonOptions, 'name'> & Because & {
|
|
247
294
|
readonly kind?: never;
|
|
248
295
|
readonly files?: never;
|
|
249
296
|
/** The rule's name, for exceptions and the baseline. */
|
|
@@ -266,9 +313,9 @@ export type ConditionName<P extends Plugins> = {
|
|
|
266
313
|
|
|
267
314
|
/** A rule that uses a plugin's condition, whose options the condition's schema checks. */
|
|
268
315
|
export type PluginRule<E extends Elements, S extends Selectors, P extends Plugins> = Omit<
|
|
269
|
-
|
|
316
|
+
CommonOptions,
|
|
270
317
|
'imports'
|
|
271
|
-
> & {
|
|
318
|
+
> & Because & NoArea & {
|
|
272
319
|
readonly kind?: never;
|
|
273
320
|
/** The files whose records the condition sees. */
|
|
274
321
|
readonly files: Files<E, S>;
|
|
@@ -278,7 +325,71 @@ export type PluginRule<E extends Elements, S extends Selectors, P extends Plugin
|
|
|
278
325
|
readonly options?: unknown;
|
|
279
326
|
};
|
|
280
327
|
|
|
281
|
-
/** A
|
|
328
|
+
/** A word for a table or a row: any text with no colon and no slash. */
|
|
329
|
+
type NoSeparator<T extends string> = T extends `${string}:${string}` | `${string}/${string}`
|
|
330
|
+
? 'the name of a table or a row has no colon or slash'
|
|
331
|
+
: T;
|
|
332
|
+
|
|
333
|
+
/** The name of a table. */
|
|
334
|
+
export type TableName<T extends string> = NoSeparator<T>;
|
|
335
|
+
|
|
336
|
+
/** What a table's rows are, before their names are checked. */
|
|
337
|
+
export type TableRows = Readonly<Record<string, readonly string[]>>;
|
|
338
|
+
|
|
339
|
+
/** What a cell may name: a row, a declared name, or a path pattern. */
|
|
340
|
+
type TableCell<Row extends string, Declared extends string> = Row | Declared | PathPattern;
|
|
341
|
+
|
|
342
|
+
/**
|
|
343
|
+
* The rows `R`, with each cell checked, and each row name checked against `Key`, the row names that
|
|
344
|
+
* the table permits.
|
|
345
|
+
*/
|
|
346
|
+
type CheckedRows<R extends TableRows, Key extends string, Declared extends string> = {
|
|
347
|
+
readonly [Row in keyof R]: Row extends string
|
|
348
|
+
? Row extends NoSeparator<Row>
|
|
349
|
+
? Row extends Key
|
|
350
|
+
? readonly TableCell<Extract<keyof R, string>, Declared>[]
|
|
351
|
+
: 'a row of a table without kinds is a declared part or group'
|
|
352
|
+
: NoSeparator<Row>
|
|
353
|
+
: never;
|
|
354
|
+
};
|
|
355
|
+
|
|
356
|
+
/** A name that a table can use as a row without kinds: a part or a group, not a set of packages. */
|
|
357
|
+
type RowName<E extends Elements, S extends Selectors> = FileElementName<E> | Extract<keyof S, string>;
|
|
358
|
+
|
|
359
|
+
/** Every name that a cell can use. */
|
|
360
|
+
type DeclaredName<E extends Elements, S extends Selectors> = ElementName<E> | Extract<keyof S, string>;
|
|
361
|
+
|
|
362
|
+
/** The options of a table with the rows `R`. */
|
|
363
|
+
export type TableOptions<E extends Elements, S extends Selectors, Row extends string> = Omit<CommonOptions, 'name'> &
|
|
364
|
+
Because &
|
|
365
|
+
ImportLimits & {
|
|
366
|
+
/** The files that the table judges as importers; without it, every file with a row. */
|
|
367
|
+
readonly files?: Files<E, S>;
|
|
368
|
+
/** The area whose imports the table judges, and without it, the whole repository. */
|
|
369
|
+
readonly within?: Side<E, S>;
|
|
370
|
+
/** Cells that every row has, such as a file that all code may use. */
|
|
371
|
+
readonly everyRowMayImport?: readonly TableCell<NoInfer<Row>, DeclaredName<E, S>>[];
|
|
372
|
+
/** Rows that may not import their own name, in the same instance or in another. */
|
|
373
|
+
readonly mayNotImportItself?: readonly NoInfer<Row>[];
|
|
374
|
+
};
|
|
375
|
+
|
|
376
|
+
/** A table, as an object. Archangel checks its names when the config loads. */
|
|
377
|
+
export type TableRule = RuleOptions &
|
|
378
|
+
ImportLimits & {
|
|
379
|
+
readonly kind: 'mayImport';
|
|
380
|
+
readonly name: string;
|
|
381
|
+
readonly rows: TableRows;
|
|
382
|
+
readonly kinds?: string;
|
|
383
|
+
readonly files?: unknown;
|
|
384
|
+
readonly within?: unknown;
|
|
385
|
+
readonly everyRowMayImport?: readonly string[];
|
|
386
|
+
readonly mayNotImportItself?: readonly string[];
|
|
387
|
+
};
|
|
388
|
+
|
|
389
|
+
/** A rule's body when its name comes first. */
|
|
390
|
+
type Body<T> = T extends unknown ? Omit<T, 'name'> & { readonly name?: never } : never;
|
|
391
|
+
|
|
392
|
+
/** A rule, as an object: one of the three shorthands, a table, a sentence, or a plugin rule. */
|
|
282
393
|
export type Rule<E extends Elements, S extends Selectors = {}, P extends Plugins = {}> =
|
|
283
394
|
| (RuleOptions & { readonly kind: 'layers'; readonly order: readonly NoInfer<ElementName<E>>[] })
|
|
284
395
|
| (RuleOptions & { readonly kind: 'isolate'; readonly element: NoInfer<CapturedElement<E>> })
|
|
@@ -287,26 +398,62 @@ export type Rule<E extends Elements, S extends Selectors = {}, P extends Plugins
|
|
|
287
398
|
readonly element: NoInfer<ElementName<E>>;
|
|
288
399
|
readonly files: readonly string[];
|
|
289
400
|
})
|
|
401
|
+
| TableRule
|
|
290
402
|
| Sentence<E, S>
|
|
291
403
|
| FolderSentence
|
|
292
404
|
| SeveralSentences<E, S>
|
|
293
405
|
| PluginRule<E, S, P>;
|
|
294
406
|
|
|
295
|
-
/**
|
|
296
|
-
|
|
407
|
+
/**
|
|
408
|
+
* The rule builders that a `rules` function receives, for a config that extends the presets
|
|
409
|
+
* named in `X`.
|
|
410
|
+
*/
|
|
411
|
+
export interface RuleBuilders<
|
|
412
|
+
E extends Elements,
|
|
413
|
+
S extends Selectors = {},
|
|
414
|
+
P extends Plugins = {},
|
|
415
|
+
X extends string = string,
|
|
416
|
+
> {
|
|
297
417
|
/** Each listed element may import only itself and later listed elements. */
|
|
298
|
-
layers
|
|
418
|
+
layers<const N extends string = string>(
|
|
419
|
+
order: readonly NoInfer<ElementName<E>>[],
|
|
420
|
+
options?: RuleOptions & Named<N, X>,
|
|
421
|
+
): Rule<E, S, P>;
|
|
299
422
|
/** The instances of the element may not import each other. */
|
|
300
|
-
isolate
|
|
423
|
+
isolate<const N extends string = string>(
|
|
424
|
+
element: NoInfer<CapturedElement<E>>,
|
|
425
|
+
options?: RuleOptions & Named<N, X>,
|
|
426
|
+
): Rule<E, S, P>;
|
|
301
427
|
/** Code outside an instance of the element may import it only through these files. */
|
|
302
|
-
entry(
|
|
428
|
+
entry<const N extends string = string>(
|
|
303
429
|
element: NoInfer<ElementName<E>>,
|
|
304
430
|
files: string | readonly string[],
|
|
305
|
-
options?: RuleOptions,
|
|
431
|
+
options?: RuleOptions & Named<N, X>,
|
|
432
|
+
): Rule<E, S, P>;
|
|
433
|
+
/** A table of what each row may import, whose rows are kinds of file, parts or groups. */
|
|
434
|
+
mayImport<const T extends string, const R extends TableRows>(
|
|
435
|
+
name: TableName<T>,
|
|
436
|
+
rows: R & CheckedRows<R, string, DeclaredName<E, S>>,
|
|
437
|
+
options: TableOptions<E, S, Extract<keyof R, string>> & {
|
|
438
|
+
/** The pattern that gives a file its kind, such as `**\/*.{kind}.ts`. */
|
|
439
|
+
readonly kinds: string;
|
|
440
|
+
},
|
|
441
|
+
): Rule<E, S, P>;
|
|
442
|
+
/** A table whose rows are all parts or groups. */
|
|
443
|
+
mayImport<const T extends string, const R extends TableRows>(
|
|
444
|
+
name: TableName<T>,
|
|
445
|
+
rows: R & CheckedRows<R, RowName<E, S>, DeclaredName<E, S>>,
|
|
446
|
+
options?: TableOptions<E, S, Extract<keyof R, string>> & { readonly kinds?: never },
|
|
447
|
+
): Rule<E, S, P>;
|
|
448
|
+
/** A rule with its name first: `files`, or `folders`, and exactly one condition. */
|
|
449
|
+
rule<const N extends string = string>(
|
|
450
|
+
name: OwnRuleName<N, X>,
|
|
451
|
+
sentence: Body<Sentence<E, S> | FolderSentence | SeveralSentences<E, S> | PluginRule<E, S, P>>,
|
|
306
452
|
): Rule<E, S, P>;
|
|
307
453
|
/** A rule as a sentence: `files`, or `folders`, and exactly one condition or plugin condition. */
|
|
308
|
-
rule(
|
|
309
|
-
sentence: Sentence<E, S> | FolderSentence | SeveralSentences<E, S> | PluginRule<E, S, P
|
|
454
|
+
rule<const N extends string = string>(
|
|
455
|
+
sentence: (Sentence<E, S> | FolderSentence | SeveralSentences<E, S> | PluginRule<E, S, P>) &
|
|
456
|
+
Named<N, X>,
|
|
310
457
|
): Rule<E, S, P>;
|
|
311
458
|
}
|
|
312
459
|
|
|
@@ -314,61 +461,108 @@ export interface RuleBuilders<E extends Elements, S extends Selectors = {}, P ex
|
|
|
314
461
|
* What a preset returns: part of a config, which `extends` puts under the config. Archangel names
|
|
315
462
|
* each element, selector and rule `<name>/<its name>`, and reads paths relative to `root`.
|
|
316
463
|
*/
|
|
317
|
-
export interface Preset {
|
|
464
|
+
export interface Preset<X extends string = string> {
|
|
318
465
|
/** The prefix of everything the preset declares, such as `fsd`. */
|
|
319
|
-
readonly name:
|
|
466
|
+
readonly name: X;
|
|
320
467
|
/** The package the preset comes from, which a problem its user cannot fix names. */
|
|
321
468
|
readonly package?: string;
|
|
322
469
|
/** The folder the preset applies to, relative to the config folder. */
|
|
323
470
|
readonly root?: string;
|
|
471
|
+
readonly parts?: Elements;
|
|
324
472
|
readonly elements?: Elements;
|
|
473
|
+
readonly groups?: Selectors;
|
|
325
474
|
readonly selectors?: Selectors;
|
|
475
|
+
readonly externals?: Externals;
|
|
326
476
|
readonly plugins?: Plugins;
|
|
327
477
|
/** The rules, each with a name. */
|
|
328
478
|
readonly rules?: readonly unknown[] | ((builders: RuleBuilders<Elements, Selectors, Plugins>) => readonly unknown[]);
|
|
329
479
|
}
|
|
330
480
|
|
|
331
481
|
/** The options of the Feature-Sliced Design preset. */
|
|
332
|
-
export interface FsdOptions {
|
|
482
|
+
export interface FsdOptions<X extends string = string> {
|
|
333
483
|
/** The folder that holds the layers, such as `src`. */
|
|
334
484
|
readonly root: string;
|
|
335
485
|
/** Turn on Steiger's four opt-in rules too. */
|
|
336
486
|
readonly strict?: boolean;
|
|
337
487
|
/** The prefix, for a second use in one repository; `fsd` by default. */
|
|
338
|
-
readonly name?:
|
|
488
|
+
readonly name?: X;
|
|
339
489
|
}
|
|
340
490
|
|
|
341
491
|
/** The Feature-Sliced Design preset: Steiger's rules for the layers under `root`. */
|
|
342
|
-
export declare function fsd(options: FsdOptions): Preset
|
|
492
|
+
export declare function fsd<const X extends string = 'fsd'>(options: FsdOptions<X>): Preset<X>;
|
|
343
493
|
|
|
344
494
|
/** The Next.js preset: code a client component reaches stays out of server-only code. */
|
|
345
|
-
export declare function nextjs
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
495
|
+
export declare function nextjs<const X extends string = 'nextjs'>(options?: {
|
|
496
|
+
readonly name?: X;
|
|
497
|
+
readonly root?: string;
|
|
498
|
+
}): Preset<X>;
|
|
499
|
+
|
|
500
|
+
/** The parts of the code, by file pattern. A part holds files, so it has no packages. */
|
|
501
|
+
export type PartSpecs = Readonly<Record<string, string | readonly string[]>>;
|
|
502
|
+
|
|
503
|
+
/** One spelling of the parts of the code. */
|
|
504
|
+
type PartsKey<E extends Elements> =
|
|
505
|
+
| {
|
|
506
|
+
/** The parts of the code, by file pattern. Each file is in one part. */
|
|
507
|
+
readonly parts?: E & PartSpecs;
|
|
508
|
+
readonly elements?: never;
|
|
509
|
+
}
|
|
510
|
+
| {
|
|
511
|
+
/** The same as `parts`, which may also hold packages. */
|
|
512
|
+
readonly elements?: E;
|
|
513
|
+
readonly parts?: never;
|
|
514
|
+
};
|
|
515
|
+
|
|
516
|
+
/** One spelling of the named sets of files. */
|
|
517
|
+
type GroupsKey<S extends Selectors> =
|
|
518
|
+
| {
|
|
519
|
+
/** Named sets of files, which can cross parts, and which rules use by name. */
|
|
520
|
+
readonly groups?: S;
|
|
521
|
+
readonly selectors?: never;
|
|
522
|
+
}
|
|
523
|
+
| {
|
|
524
|
+
/** The same as `groups`. */
|
|
525
|
+
readonly selectors?: S;
|
|
526
|
+
readonly groups?: never;
|
|
527
|
+
};
|
|
528
|
+
|
|
529
|
+
/**
|
|
530
|
+
* An archangel config, which extends the presets named in `X`, names its own rules with the names
|
|
531
|
+
* in `N`, and names the sets of outside packages in `O`.
|
|
532
|
+
*/
|
|
533
|
+
export type Config<
|
|
534
|
+
E extends Elements,
|
|
535
|
+
S extends Selectors = {},
|
|
536
|
+
P extends Plugins = {},
|
|
537
|
+
X extends string = string,
|
|
538
|
+
N extends string = string,
|
|
539
|
+
O extends Externals = {},
|
|
540
|
+
> = PartsKey<E> &
|
|
541
|
+
GroupsKey<S> & {
|
|
542
|
+
/** Named sets of outside packages, such as `{ driver: ['pg', '@aws-sdk/*', 'node:fs'] }`. */
|
|
543
|
+
readonly externals?: O;
|
|
544
|
+
/** Lines in `.gitignore` syntax that remove files from the check. */
|
|
545
|
+
readonly ignore?: readonly string[];
|
|
546
|
+
/** Specifiers that may stay unresolved, each with the reason; a specifier may end in `*`. */
|
|
547
|
+
readonly allowUnresolved?: Readonly<Record<string, string>>;
|
|
548
|
+
/** The plugins, by the name the rules use them by, as `plugin: '<name>/<condition>'`. */
|
|
549
|
+
readonly plugins?: P;
|
|
550
|
+
/** The presets the config extends; the config's own rules replace preset rules of one name. */
|
|
551
|
+
readonly extends?: readonly Preset<X>[];
|
|
552
|
+
/** Rule or table names to turn off after the presets are merged, such as `fsd/insignificant-slice`. */
|
|
553
|
+
readonly off?: readonly string[];
|
|
554
|
+
/** The rules, or a function that builds them. */
|
|
555
|
+
readonly rules?:
|
|
556
|
+
| readonly (Rule<E & ExternalElements<O>, S, P> & Named<N, X>)[]
|
|
557
|
+
| ((builders: RuleBuilders<E & ExternalElements<O>, S, P, X>) => readonly (Rule<E & ExternalElements<O>, S, P> & Named<N, X>)[]);
|
|
558
|
+
};
|
|
368
559
|
|
|
369
560
|
/** Declares an archangel config, with types that check its names as it is written. */
|
|
370
561
|
export declare function defineConfig<
|
|
371
562
|
const E extends Elements,
|
|
372
563
|
const S extends Selectors = {},
|
|
373
564
|
const P extends Plugins = {},
|
|
374
|
-
|
|
565
|
+
const X extends string = never,
|
|
566
|
+
const N extends string = string,
|
|
567
|
+
const O extends Externals = {},
|
|
568
|
+
>(config: Config<E, S, P, X, N, O>): Config<E, S, P, string, string, O>;
|
package/package.json
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@enhance-eng/archangel",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Make architecture verifiable. Architecture
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "Make architecture verifiable. Architecture tests for TypeScript, checked in milliseconds: imports, cycles, names, folder structure and exports.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"architecture",
|
|
7
|
+
"archunit",
|
|
8
|
+
"architecture-tests",
|
|
7
9
|
"lint",
|
|
8
10
|
"typescript",
|
|
9
11
|
"imports",
|
|
@@ -51,12 +53,21 @@
|
|
|
51
53
|
"pluralize": "8.0.0"
|
|
52
54
|
},
|
|
53
55
|
"optionalDependencies": {
|
|
54
|
-
"@enhance-eng/archangel-darwin-arm64": "0.
|
|
55
|
-
"@enhance-eng/archangel-darwin-x64": "0.
|
|
56
|
-
"@enhance-eng/archangel-linux-arm64-gnu": "0.
|
|
57
|
-
"@enhance-eng/archangel-linux-arm64-musl": "0.
|
|
58
|
-
"@enhance-eng/archangel-linux-x64-gnu": "0.
|
|
59
|
-
"@enhance-eng/archangel-linux-x64-musl": "0.
|
|
60
|
-
"@enhance-eng/archangel-win32-x64": "0.
|
|
56
|
+
"@enhance-eng/archangel-darwin-arm64": "0.2.0",
|
|
57
|
+
"@enhance-eng/archangel-darwin-x64": "0.2.0",
|
|
58
|
+
"@enhance-eng/archangel-linux-arm64-gnu": "0.2.0",
|
|
59
|
+
"@enhance-eng/archangel-linux-arm64-musl": "0.2.0",
|
|
60
|
+
"@enhance-eng/archangel-linux-x64-gnu": "0.2.0",
|
|
61
|
+
"@enhance-eng/archangel-linux-x64-musl": "0.2.0",
|
|
62
|
+
"@enhance-eng/archangel-win32-x64": "0.2.0"
|
|
63
|
+
},
|
|
64
|
+
"tsd": {
|
|
65
|
+
"compilerOptions": {
|
|
66
|
+
"paths": {
|
|
67
|
+
"@enhance-eng/archangel": [
|
|
68
|
+
"./index.d.ts"
|
|
69
|
+
]
|
|
70
|
+
}
|
|
71
|
+
}
|
|
61
72
|
}
|
|
62
73
|
}
|