@alveolus/arch 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 +5 -0
- package/dist/bin.mjs +1 -1
- package/dist/{docs-DsQHpTtV.mjs → docs-B1Tzkjrn.mjs} +359 -49
- package/dist/docs-B1Tzkjrn.mjs.map +1 -0
- package/dist/index.d.mts +49 -15
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +1 -1
- package/docs/guide/existing-project.md +6 -3
- package/docs/guide/getting-started.md +14 -3
- package/docs/guide/project-layout.md +3 -1
- package/docs/rules/index.md +14 -3
- package/docs/rules/layers/no-impure-domain.md +2 -0
- package/docs/rules/layers/no-outward-import.md +9 -3
- package/docs/rules/strategic/no-cross-context-import.md +35 -2
- package/docs/rules/strategic/no-fat-shared-kernel.md +1 -0
- package/docs/rules/strategic/no-shared-state.md +100 -0
- package/docs/rules/strategic/no-unmapped-context.md +61 -25
- package/docs/rules/tactical/no-foreign-query-dependency.md +98 -3
- package/package.json +1 -1
- package/dist/docs-DsQHpTtV.mjs.map +0 -1
|
@@ -166,22 +166,26 @@ var Member = class {
|
|
|
166
166
|
kind;
|
|
167
167
|
visibility;
|
|
168
168
|
isStatic;
|
|
169
|
+
isReadonly;
|
|
169
170
|
isParameterProperty;
|
|
170
171
|
isCallable;
|
|
171
172
|
valueTypes;
|
|
172
173
|
parameterTypes;
|
|
173
174
|
returns;
|
|
175
|
+
holdsCollection;
|
|
174
176
|
constructor(props) {
|
|
175
177
|
this.name = props.name;
|
|
176
178
|
this.line = props.line;
|
|
177
179
|
this.kind = props.kind;
|
|
178
180
|
this.visibility = props.visibility;
|
|
179
181
|
this.isStatic = props.isStatic;
|
|
182
|
+
this.isReadonly = props.isReadonly;
|
|
180
183
|
this.isParameterProperty = props.isParameterProperty;
|
|
181
184
|
this.isCallable = props.isCallable;
|
|
182
185
|
this.valueTypes = props.valueTypes;
|
|
183
186
|
this.parameterTypes = props.parameterTypes;
|
|
184
187
|
this.returns = props.returns;
|
|
188
|
+
this.holdsCollection = props.holdsCollection;
|
|
185
189
|
}
|
|
186
190
|
get isPublicInstance() {
|
|
187
191
|
return this.visibility === "public" && !this.isStatic;
|
|
@@ -227,6 +231,18 @@ var GlobalUse = class {
|
|
|
227
231
|
}
|
|
228
232
|
};
|
|
229
233
|
//#endregion
|
|
234
|
+
//#region src/model/dependencies/wiring.ts
|
|
235
|
+
var Wiring = class {
|
|
236
|
+
line;
|
|
237
|
+
receiver;
|
|
238
|
+
links;
|
|
239
|
+
constructor(line, receiver, links) {
|
|
240
|
+
this.line = line;
|
|
241
|
+
this.receiver = receiver;
|
|
242
|
+
this.links = links;
|
|
243
|
+
}
|
|
244
|
+
};
|
|
245
|
+
//#endregion
|
|
230
246
|
//#region src/model/project.ts
|
|
231
247
|
var Project$1 = class {
|
|
232
248
|
directory;
|
|
@@ -257,6 +273,7 @@ var SourceFile = class {
|
|
|
257
273
|
statements;
|
|
258
274
|
throws;
|
|
259
275
|
globals;
|
|
276
|
+
wirings;
|
|
260
277
|
disables;
|
|
261
278
|
lines;
|
|
262
279
|
constructor(props) {
|
|
@@ -266,6 +283,7 @@ var SourceFile = class {
|
|
|
266
283
|
this.statements = props.statements;
|
|
267
284
|
this.throws = props.throws;
|
|
268
285
|
this.globals = props.globals;
|
|
286
|
+
this.wirings = props.wirings;
|
|
269
287
|
this.disables = props.disables;
|
|
270
288
|
this.lines = props.text.split(/\r?\n/);
|
|
271
289
|
}
|
|
@@ -586,6 +604,60 @@ var CoreApi = class {
|
|
|
586
604
|
}
|
|
587
605
|
};
|
|
588
606
|
//#endregion
|
|
607
|
+
//#region src/architecture/crossing.ts
|
|
608
|
+
var Crossings = class {
|
|
609
|
+
architecture;
|
|
610
|
+
constructor(architecture) {
|
|
611
|
+
this.architecture = architecture;
|
|
612
|
+
}
|
|
613
|
+
in(file) {
|
|
614
|
+
const crossings = /* @__PURE__ */ new Map();
|
|
615
|
+
for (const wiring of file.wirings) {
|
|
616
|
+
const crossing = this.of(wiring);
|
|
617
|
+
if (crossing !== void 0) crossings.set(`${crossing.line}:${crossing.expression}:${crossing.from}:${crossing.to}`, crossing);
|
|
618
|
+
}
|
|
619
|
+
return [...crossings.values()];
|
|
620
|
+
}
|
|
621
|
+
of(wiring) {
|
|
622
|
+
const receiver = this.architecture.locationOfPath(wiring.receiver);
|
|
623
|
+
if (!receiver.isInBoundedContext || receiver.context === void 0) return;
|
|
624
|
+
for (const link of wiring.links) {
|
|
625
|
+
const source = this.sourceOf(link, receiver);
|
|
626
|
+
if (source !== void 0) return {
|
|
627
|
+
expression: link.text,
|
|
628
|
+
from: receiver.context,
|
|
629
|
+
isOpenHostService: this.isOpenHostService(link, receiver),
|
|
630
|
+
line: wiring.line,
|
|
631
|
+
to: source
|
|
632
|
+
};
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
sourceOf(link, receiver) {
|
|
636
|
+
if (link.declaredIn !== void 0) {
|
|
637
|
+
const declared = this.architecture.locationOfPath(link.declaredIn);
|
|
638
|
+
if (declared.isOtherBoundedContextThan(receiver)) return declared.context;
|
|
639
|
+
}
|
|
640
|
+
return this.foreignTypesOf(link, receiver)[0]?.context;
|
|
641
|
+
}
|
|
642
|
+
isOpenHostService(link, receiver) {
|
|
643
|
+
const foreign = link.types.filter((type) => this.isForeignModel(type.declaredIn, receiver));
|
|
644
|
+
return foreign.length > 0 && foreign.every((type) => {
|
|
645
|
+
const declaration = this.architecture.classOf(type);
|
|
646
|
+
return declaration !== void 0 && this.architecture.implementsMarker(declaration, "OpenHostService");
|
|
647
|
+
});
|
|
648
|
+
}
|
|
649
|
+
foreignTypesOf(link, receiver) {
|
|
650
|
+
const locations = [];
|
|
651
|
+
for (const type of link.types) if (this.isForeignModel(type.declaredIn, receiver)) locations.push(this.architecture.locationOfPath(type.declaredIn ?? ""));
|
|
652
|
+
return locations;
|
|
653
|
+
}
|
|
654
|
+
isForeignModel(path, receiver) {
|
|
655
|
+
if (path === void 0) return false;
|
|
656
|
+
const location = this.architecture.locationOfPath(path);
|
|
657
|
+
return location.isOtherBoundedContextThan(receiver) && !location.isCompositionRoot;
|
|
658
|
+
}
|
|
659
|
+
};
|
|
660
|
+
//#endregion
|
|
589
661
|
//#region src/architecture/layers.ts
|
|
590
662
|
var Layers = class {
|
|
591
663
|
isLayer(name) {
|
|
@@ -769,6 +841,7 @@ var Architecture = class {
|
|
|
769
841
|
shape;
|
|
770
842
|
layout;
|
|
771
843
|
locations = /* @__PURE__ */ new Map();
|
|
844
|
+
crossings = new Crossings(this);
|
|
772
845
|
constructor(project, settings) {
|
|
773
846
|
this.project = project;
|
|
774
847
|
this.settings = settings;
|
|
@@ -790,6 +863,12 @@ var Architecture = class {
|
|
|
790
863
|
locationOf(file) {
|
|
791
864
|
return this.locate(file.path);
|
|
792
865
|
}
|
|
866
|
+
locationOfPath(path) {
|
|
867
|
+
return this.locate(path);
|
|
868
|
+
}
|
|
869
|
+
crossingsIn(file) {
|
|
870
|
+
return this.crossings.in(file);
|
|
871
|
+
}
|
|
793
872
|
locationOfTarget(target) {
|
|
794
873
|
if (target.visibility !== "analysed") return new Location({
|
|
795
874
|
area: "outside",
|
|
@@ -839,24 +918,22 @@ var Architecture = class {
|
|
|
839
918
|
};
|
|
840
919
|
//#endregion
|
|
841
920
|
//#region src/architecture/context-map.ts
|
|
842
|
-
var ContextMap = class
|
|
921
|
+
var ContextMap = class {
|
|
843
922
|
upstreams;
|
|
844
923
|
constructor(upstreams) {
|
|
845
924
|
this.upstreams = upstreams;
|
|
846
925
|
}
|
|
847
|
-
static ofEdges(edges) {
|
|
848
|
-
const upstreams = {};
|
|
849
|
-
for (const { from, to } of edges) {
|
|
850
|
-
const known = upstreams[from] ?? [];
|
|
851
|
-
if (!known.includes(to)) upstreams[from] = [...known, to];
|
|
852
|
-
}
|
|
853
|
-
return new ContextMap(upstreams);
|
|
854
|
-
}
|
|
855
926
|
get contexts() {
|
|
856
|
-
|
|
927
|
+
return Object.keys(this.upstreams);
|
|
928
|
+
}
|
|
929
|
+
get consumed() {
|
|
930
|
+
const names = /* @__PURE__ */ new Set();
|
|
857
931
|
for (const targets of Object.values(this.upstreams)) for (const target of targets) names.add(target);
|
|
858
932
|
return [...names];
|
|
859
933
|
}
|
|
934
|
+
selfConsumers() {
|
|
935
|
+
return this.contexts.filter((context) => this.allows(context, context));
|
|
936
|
+
}
|
|
860
937
|
allows(from, to) {
|
|
861
938
|
return (this.upstreams[from] ?? []).includes(to);
|
|
862
939
|
}
|
|
@@ -868,9 +945,6 @@ var ContextMap = class ContextMap {
|
|
|
868
945
|
if (found !== void 0) return found;
|
|
869
946
|
}
|
|
870
947
|
}
|
|
871
|
-
cycleThrough(from, to) {
|
|
872
|
-
return this.reaches(to, from, /* @__PURE__ */ new Set());
|
|
873
|
-
}
|
|
874
948
|
cycleFrom(context, path, visiting, done) {
|
|
875
949
|
if (done.has(context)) return;
|
|
876
950
|
if (visiting.has(context)) return [...path.slice(path.indexOf(context)), context];
|
|
@@ -882,21 +956,17 @@ var ContextMap = class ContextMap {
|
|
|
882
956
|
visiting.delete(context);
|
|
883
957
|
done.add(context);
|
|
884
958
|
}
|
|
885
|
-
reaches(from, target, seen) {
|
|
886
|
-
if (from === target) return true;
|
|
887
|
-
if (seen.has(from)) return false;
|
|
888
|
-
seen.add(from);
|
|
889
|
-
return (this.upstreams[from] ?? []).some((upstream) => this.reaches(upstream, target, seen));
|
|
890
|
-
}
|
|
891
959
|
};
|
|
892
960
|
//#endregion
|
|
893
961
|
//#region src/rules/framework/wording.ts
|
|
894
962
|
var Wording = class {
|
|
895
963
|
target(target, architecture) {
|
|
964
|
+
if (target.visibility === "dynamic") return `code loaded at runtime with ${target.path}`;
|
|
896
965
|
return `${architecture.relativePath(target.path)} (${this.location(architecture.locationOfTarget(target))})`;
|
|
897
966
|
}
|
|
898
967
|
location(location) {
|
|
899
968
|
if (location.unseen === "ignored") return "ignored by the analysis";
|
|
969
|
+
if (location.unseen === "dynamic") return "loaded at runtime";
|
|
900
970
|
if (location.unseen === "unresolved") return "not resolved by the analysis";
|
|
901
971
|
if (location.isOutside) return "outside the declared bounded contexts and shared kernel";
|
|
902
972
|
if (location.isAtRoot) return "at the root";
|
|
@@ -1100,6 +1170,7 @@ var NoOutwardImportRule = class extends ImportRule {
|
|
|
1100
1170
|
description: "A dependency pointing away from the domain, a file outside the layers or in the wrong folder of its layer.",
|
|
1101
1171
|
id: "layers/no-outward-import",
|
|
1102
1172
|
messages: {
|
|
1173
|
+
compositionRootImport: "The composition root imports {target}: it wires its own context and the shared kernel only.",
|
|
1103
1174
|
compositionRoots: "{context} has {count} composition roots ({names}): keep one, and move the rest into the layers.",
|
|
1104
1175
|
directlyInLayer: "The file sits directly in {layer}/: put it in the folder of its kind, such as {example}.",
|
|
1105
1176
|
drivenTooDeep: "The file is nested too deep: driven/ holds driven/<technology>/<folder>/, such as driven/pg/adapters/.",
|
|
@@ -1194,11 +1265,15 @@ var NoOutwardImportRule = class extends ImportRule {
|
|
|
1194
1265
|
return findings;
|
|
1195
1266
|
}
|
|
1196
1267
|
layerProblem(from, to, architecture) {
|
|
1197
|
-
if (from.isAtRoot) return to.isInBoundedContext && !to.isCompositionRoot ? {
|
|
1268
|
+
if (from.isAtRoot) return to.isOutside || to.isInBoundedContext && !to.isCompositionRoot ? {
|
|
1198
1269
|
data: {},
|
|
1199
1270
|
messageId: "rootImport"
|
|
1200
1271
|
} : void 0;
|
|
1201
|
-
if (from.isCompositionRoot
|
|
1272
|
+
if (from.isCompositionRoot) return to.isOutside ? {
|
|
1273
|
+
data: {},
|
|
1274
|
+
messageId: "compositionRootImport"
|
|
1275
|
+
} : void 0;
|
|
1276
|
+
if (to.isOtherBoundedContextThan(from)) return;
|
|
1202
1277
|
if (to.isCompositionRoot) return {
|
|
1203
1278
|
data: {},
|
|
1204
1279
|
messageId: "wiring"
|
|
@@ -1260,16 +1335,28 @@ var NoPortlessAdapterRule = class extends ClassRule {
|
|
|
1260
1335
|
var NoCrossContextImportRule = class extends ImportRule {
|
|
1261
1336
|
meta = {
|
|
1262
1337
|
contexts: "every",
|
|
1263
|
-
description: "An import from another bounded context that is not its open host service, a composition root that re-exports.",
|
|
1338
|
+
description: "An import from another bounded context that is not its open host service, a file the analysis does not see, a composition root that re-exports, a value of another context given in the wiring that is not its open host service.",
|
|
1264
1339
|
id: "strategic/no-cross-context-import",
|
|
1265
1340
|
messages: {
|
|
1341
|
+
dynamicLoad: "Loads code at runtime with {loader}: the analysis cannot tell which bounded context it reaches; use a static import.",
|
|
1266
1342
|
notOpenHostService: "Imports {target}: only an OpenHostService of another bounded context may be imported.",
|
|
1267
1343
|
outsideAntiCorruptionLayer: "Uses the open host service of {context} outside an AntiCorruptionLayer: a core context translates what it consumes in an anti-corruption layer.",
|
|
1268
1344
|
publishedLanguage: "Imports the published language of {context}: redeclare the fields you read in your own published-language/.",
|
|
1269
1345
|
reexport: "The composition root re-exports {names}: it exports its own module only, so that no other context reaches through it.",
|
|
1270
|
-
sharedKernel: "The shared kernel imports no bounded context, but imports {target}."
|
|
1346
|
+
sharedKernel: "The shared kernel imports no bounded context, but imports {target}.",
|
|
1347
|
+
unseen: "Imports {target}: the analysis cannot tell which bounded context it reaches; move the file into a bounded context or the shared kernel.",
|
|
1348
|
+
wiredModel: "Gives {expression}, from {to}, to {from}: only an OpenHostService of another bounded context may cross, in an import or in the wiring."
|
|
1271
1349
|
}
|
|
1272
1350
|
};
|
|
1351
|
+
check(architecture) {
|
|
1352
|
+
const findings = super.check(architecture);
|
|
1353
|
+
for (const file of this.filesOf(architecture)) for (const crossing of architecture.crossingsIn(file)) if (!crossing.isOpenHostService) findings.push(this.finding(file, crossing.line, crossing.expression, "wiredModel", {
|
|
1354
|
+
expression: crossing.expression,
|
|
1355
|
+
from: crossing.from,
|
|
1356
|
+
to: crossing.to
|
|
1357
|
+
}));
|
|
1358
|
+
return findings;
|
|
1359
|
+
}
|
|
1273
1360
|
appliesTo(file, architecture) {
|
|
1274
1361
|
const location = architecture.locationOf(file);
|
|
1275
1362
|
return location.isInBoundedContext || location.isInSharedKernel;
|
|
@@ -1280,6 +1367,8 @@ var NoCrossContextImportRule = class extends ImportRule {
|
|
|
1280
1367
|
const target = dependency.target;
|
|
1281
1368
|
if (target.kind !== "file") return;
|
|
1282
1369
|
const to = architecture.locationOfTarget(target);
|
|
1370
|
+
if (target.visibility === "dynamic") return this.finding(file, dependency.line, dependency.label, "dynamicLoad", { loader: target.path });
|
|
1371
|
+
if (to.isOutside) return this.finding(file, dependency.line, dependency.label, "unseen", { target: this.wording.target(target, architecture) });
|
|
1283
1372
|
if (!to.isOtherBoundedContextThan(from)) return;
|
|
1284
1373
|
if (from.isInSharedKernel) return this.finding(file, dependency.line, dependency.label, "sharedKernel", { target: this.wording.target(target, architecture) });
|
|
1285
1374
|
if (from.isCompositionRoot && to.isCompositionRoot) return;
|
|
@@ -1370,27 +1459,60 @@ var NoLeakyHostServiceRule = class extends ClassRule {
|
|
|
1370
1459
|
}
|
|
1371
1460
|
};
|
|
1372
1461
|
//#endregion
|
|
1462
|
+
//#region src/rules/strategic/no-shared-state.rule.ts
|
|
1463
|
+
var NoSharedStateRule = class extends ClassRule {
|
|
1464
|
+
meta = {
|
|
1465
|
+
contexts: "every",
|
|
1466
|
+
description: "A static field of the shared kernel that holds state: one without readonly, or one holding a collection.",
|
|
1467
|
+
id: "strategic/no-shared-state",
|
|
1468
|
+
messages: {
|
|
1469
|
+
collection: "{class}.{field} holds a collection in a static field: every context reaches the same one, a channel the context map does not show. The shared kernel shares a model, not state: integrate through an open host service.",
|
|
1470
|
+
mutable: "{class}.{field} is a static field without readonly: every context reaches the same one, a channel the context map does not show. The shared kernel shares a model, not state: integrate through an open host service."
|
|
1471
|
+
}
|
|
1472
|
+
};
|
|
1473
|
+
findingsFor(codeClass, file, architecture) {
|
|
1474
|
+
if (!architecture.locationOf(file).isInSharedKernel) return [];
|
|
1475
|
+
const findings = [];
|
|
1476
|
+
for (const member of codeClass.members) {
|
|
1477
|
+
const messageId = this.stateOf(member);
|
|
1478
|
+
if (messageId !== void 0) findings.push(this.finding(file, member.line, member.name, messageId, {
|
|
1479
|
+
class: codeClass.name,
|
|
1480
|
+
field: member.name
|
|
1481
|
+
}));
|
|
1482
|
+
}
|
|
1483
|
+
return findings;
|
|
1484
|
+
}
|
|
1485
|
+
stateOf(member) {
|
|
1486
|
+
if (!member.isStatic || member.kind !== "field") return;
|
|
1487
|
+
if (!member.isReadonly) return "mutable";
|
|
1488
|
+
return member.holdsCollection ? "collection" : void 0;
|
|
1489
|
+
}
|
|
1490
|
+
};
|
|
1491
|
+
//#endregion
|
|
1373
1492
|
//#region src/rules/strategic/no-unmapped-context.rule.ts
|
|
1374
1493
|
var NoUnmappedContextRule = class extends Rule {
|
|
1375
1494
|
meta = {
|
|
1376
1495
|
contexts: "every",
|
|
1377
|
-
description: "A bounded context consuming one the context map does not allow,
|
|
1496
|
+
description: "A bounded context consuming one the context map does not allow, through an import or through the wiring.",
|
|
1378
1497
|
id: "strategic/no-unmapped-context",
|
|
1379
1498
|
messages: {
|
|
1380
|
-
|
|
1381
|
-
|
|
1499
|
+
unmapped: "{from} consumes {to}, which the context map does not allow: reverse the dependency, or if {from} really is downstream of {to}, add {to} to contextMap.{from}.consumes.",
|
|
1500
|
+
unmappedWiring: "{from} receives {expression} from {to} here, which the context map does not allow: reverse the dependency, or if {from} really is downstream of {to}, add {to} to contextMap.{from}.consumes."
|
|
1382
1501
|
}
|
|
1383
1502
|
};
|
|
1384
1503
|
check(architecture) {
|
|
1385
|
-
const consumptions = this.consumptionsIn(architecture);
|
|
1386
|
-
const declared = architecture.contextMap;
|
|
1387
1504
|
const findings = [];
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1505
|
+
for (const consumption of this.consumptionsIn(architecture)) if (!architecture.contextMap.allows(consumption.from, consumption.to)) findings.push(this.consumptionFinding(consumption));
|
|
1506
|
+
findings.push(...this.unmappedWirings(architecture));
|
|
1507
|
+
return findings;
|
|
1508
|
+
}
|
|
1509
|
+
unmappedWirings(architecture) {
|
|
1510
|
+
const findings = [];
|
|
1511
|
+
for (const file of this.filesOf(architecture)) for (const crossing of architecture.crossingsIn(file)) if (!architecture.contextMap.allows(crossing.from, crossing.to)) findings.push(this.finding(file, crossing.line, crossing.expression, "unmappedWiring", {
|
|
1512
|
+
expression: crossing.expression,
|
|
1513
|
+
from: crossing.from,
|
|
1514
|
+
to: crossing.to
|
|
1515
|
+
}));
|
|
1394
1516
|
return findings;
|
|
1395
1517
|
}
|
|
1396
1518
|
consumptionsIn(architecture) {
|
|
@@ -1411,9 +1533,9 @@ var NoUnmappedContextRule = class extends Rule {
|
|
|
1411
1533
|
}
|
|
1412
1534
|
return consumptions;
|
|
1413
1535
|
}
|
|
1414
|
-
consumptionFinding(consumption
|
|
1536
|
+
consumptionFinding(consumption) {
|
|
1415
1537
|
const { dependency, file, from, to } = consumption;
|
|
1416
|
-
return this.finding(file, dependency.line, dependency.label,
|
|
1538
|
+
return this.finding(file, dependency.line, dependency.label, "unmapped", {
|
|
1417
1539
|
from,
|
|
1418
1540
|
to
|
|
1419
1541
|
});
|
|
@@ -1851,6 +1973,7 @@ const ruleIds = [
|
|
|
1851
1973
|
"strategic/no-leaky-host-service",
|
|
1852
1974
|
"strategic/no-unmapped-context",
|
|
1853
1975
|
"strategic/no-fat-shared-kernel",
|
|
1976
|
+
"strategic/no-shared-state",
|
|
1854
1977
|
"layers/no-impure-domain",
|
|
1855
1978
|
"layers/no-outward-import",
|
|
1856
1979
|
"layers/no-portless-adapter",
|
|
@@ -1871,6 +1994,7 @@ var RuleRegistry = class {
|
|
|
1871
1994
|
new NoLeakyHostServiceRule(),
|
|
1872
1995
|
new NoUnmappedContextRule(),
|
|
1873
1996
|
new NoFatSharedKernelRule(),
|
|
1997
|
+
new NoSharedStateRule(),
|
|
1874
1998
|
new NoImpureDomainRule(),
|
|
1875
1999
|
new NoOutwardImportRule(),
|
|
1876
2000
|
new NoPortlessAdapterRule(),
|
|
@@ -2109,6 +2233,7 @@ var Config = class {
|
|
|
2109
2233
|
contextMap;
|
|
2110
2234
|
ignored;
|
|
2111
2235
|
rules;
|
|
2236
|
+
layout;
|
|
2112
2237
|
constructor(config, projectDir) {
|
|
2113
2238
|
this.projectDir = resolve(projectDir);
|
|
2114
2239
|
this.rootDir = resolve(projectDir, config.root);
|
|
@@ -2118,7 +2243,7 @@ var Config = class {
|
|
|
2118
2243
|
this.applicationDependencies = this.domainDependencies.with(new AllowedPackages(config.applicationDependencies));
|
|
2119
2244
|
this.ignored = [...testFiles, ...config.ignore ?? []];
|
|
2120
2245
|
this.extraFolders = config.layout?.extraFolders ?? {};
|
|
2121
|
-
this.contextMap =
|
|
2246
|
+
this.contextMap = this.validContextMap(config.contextMap, Object.keys(config.boundedContexts));
|
|
2122
2247
|
this.rules = config.rules;
|
|
2123
2248
|
const sharedKernel = config.sharedKernel ?? "shared-kernel";
|
|
2124
2249
|
this.contextFolders = [...this.classifiedContexts(config.subdomains ?? {}, config.boundedContexts), {
|
|
@@ -2126,6 +2251,7 @@ var Config = class {
|
|
|
2126
2251
|
isSharedKernel: true,
|
|
2127
2252
|
name: "shared kernel"
|
|
2128
2253
|
}];
|
|
2254
|
+
this.layout = new Layout(this);
|
|
2129
2255
|
}
|
|
2130
2256
|
classifiedContexts(subdomains, boundedContexts) {
|
|
2131
2257
|
const types = [
|
|
@@ -2152,10 +2278,16 @@ var Config = class {
|
|
|
2152
2278
|
if (unclassified.length > 0) throw new Error(`boundedContexts declares ${unclassified.join(", ")}, which subdomains does not classify: list each context under subdomains.core, subdomains.supporting or subdomains.generic.`);
|
|
2153
2279
|
return folders;
|
|
2154
2280
|
}
|
|
2155
|
-
validContextMap(
|
|
2281
|
+
validContextMap(relations, contexts) {
|
|
2282
|
+
const upstreams = {};
|
|
2283
|
+
for (const [name, { consumes }] of Object.entries(relations)) upstreams[name] = consumes;
|
|
2156
2284
|
const map = new ContextMap(upstreams);
|
|
2157
|
-
const unknown = map.contexts.filter((name) => !contexts.includes(name));
|
|
2285
|
+
const unknown = [.../* @__PURE__ */ new Set([...map.contexts, ...map.consumed])].filter((name) => !contexts.includes(name));
|
|
2158
2286
|
if (unknown.length > 0) throw new Error(`contextMap names ${unknown.join(", ")}, which boundedContexts does not declare.`);
|
|
2287
|
+
const unlisted = contexts.filter((name) => !map.contexts.includes(name));
|
|
2288
|
+
if (unlisted.length > 0) throw new Error(`contextMap does not list ${unlisted.join(", ")}: every bounded context lists the contexts it consumes, consumes: [] when none.`);
|
|
2289
|
+
const selfConsumers = map.selfConsumers();
|
|
2290
|
+
if (selfConsumers.length > 0) throw new Error(`contextMap lists ${selfConsumers.join(", ")} as consuming itself: a context consumes other contexts only.`);
|
|
2159
2291
|
const cycle = map.cycle();
|
|
2160
2292
|
if (cycle !== void 0) throw new Error(`contextMap has a cycle: ${cycle.join(" → ")}. Two contexts that depend on each other can no longer change alone: reverse one dependency.`);
|
|
2161
2293
|
return map;
|
|
@@ -2164,6 +2296,10 @@ var Config = class {
|
|
|
2164
2296
|
const projectPath = relative(this.projectDir, path).split(sep).join("/");
|
|
2165
2297
|
return this.ignored.some((glob) => matchesGlob(projectPath, glob));
|
|
2166
2298
|
}
|
|
2299
|
+
readsWiring(path) {
|
|
2300
|
+
const location = this.layout.locate(path);
|
|
2301
|
+
return location.isAtRoot || location.isCompositionRoot;
|
|
2302
|
+
}
|
|
2167
2303
|
severityOf(rule) {
|
|
2168
2304
|
return this.rules?.[rule] ?? "error";
|
|
2169
2305
|
}
|
|
@@ -2182,7 +2318,7 @@ const schema = z.strictObject({
|
|
|
2182
2318
|
applicationDependencies: packageDependencies.exactOptional(),
|
|
2183
2319
|
boundedContexts: z.record(z.string(), z.string()),
|
|
2184
2320
|
compositionRoot: z.string().exactOptional(),
|
|
2185
|
-
contextMap: z.record(z.string(), z.array(z.string()))
|
|
2321
|
+
contextMap: z.record(z.string(), z.strictObject({ consumes: z.array(z.string()) })),
|
|
2186
2322
|
domainDependencies: packageDependencies.exactOptional(),
|
|
2187
2323
|
ignore: z.array(z.string()).exactOptional(),
|
|
2188
2324
|
layout: z.strictObject({ extraFolders: z.strictObject({
|
|
@@ -2268,6 +2404,7 @@ var ClassReader = class {
|
|
|
2268
2404
|
const type = member.getType();
|
|
2269
2405
|
const [signature] = type.getCallSignatures();
|
|
2270
2406
|
return [this.member(member, "field", {
|
|
2407
|
+
holdsCollection: this.types.holdsCollection(type),
|
|
2271
2408
|
isCallable: signature !== void 0,
|
|
2272
2409
|
returns: signature === void 0 ? void 0 : this.types.returnShapeOf(signature.getReturnType()),
|
|
2273
2410
|
valueTypes: this.types.classTypesIn(type)
|
|
@@ -2297,8 +2434,10 @@ var ClassReader = class {
|
|
|
2297
2434
|
const isStatic = Node.isStaticable(member) && member.isStatic();
|
|
2298
2435
|
const scope = Node.isScoped(member) ? member.getScope() : Scope.Public;
|
|
2299
2436
|
return new Member({
|
|
2437
|
+
holdsCollection: types.holdsCollection ?? false,
|
|
2300
2438
|
isCallable: types.isCallable ?? false,
|
|
2301
2439
|
isParameterProperty: false,
|
|
2440
|
+
isReadonly: Node.isReadonlyable(member) && member.isReadonly(),
|
|
2302
2441
|
isStatic,
|
|
2303
2442
|
kind,
|
|
2304
2443
|
line: member.getStartLineNumber(),
|
|
@@ -2314,8 +2453,10 @@ var ClassReader = class {
|
|
|
2314
2453
|
}
|
|
2315
2454
|
constructorParameter(parameter) {
|
|
2316
2455
|
return new Member({
|
|
2456
|
+
holdsCollection: this.types.holdsCollection(parameter.getType()),
|
|
2317
2457
|
isCallable: false,
|
|
2318
2458
|
isParameterProperty: parameter.isParameterProperty(),
|
|
2459
|
+
isReadonly: parameter.isReadonly(),
|
|
2319
2460
|
isStatic: false,
|
|
2320
2461
|
kind: "constructor parameter",
|
|
2321
2462
|
line: parameter.getStartLineNumber(),
|
|
@@ -2355,6 +2496,19 @@ var ClassReader = class {
|
|
|
2355
2496
|
//#endregion
|
|
2356
2497
|
//#region src/importer/ts-morph/readers/dependency-reader.ts
|
|
2357
2498
|
const everything = ["*"];
|
|
2499
|
+
const loaderPackages = [
|
|
2500
|
+
"module",
|
|
2501
|
+
"node:module",
|
|
2502
|
+
"vm",
|
|
2503
|
+
"node:vm"
|
|
2504
|
+
];
|
|
2505
|
+
const globalLoaders = ["eval", "Function"];
|
|
2506
|
+
const loaderMembers = [
|
|
2507
|
+
"eval",
|
|
2508
|
+
"Function",
|
|
2509
|
+
"require",
|
|
2510
|
+
"getBuiltinModule"
|
|
2511
|
+
];
|
|
2358
2512
|
var DependencyReader = class {
|
|
2359
2513
|
project;
|
|
2360
2514
|
scope;
|
|
@@ -2374,7 +2528,17 @@ var DependencyReader = class {
|
|
|
2374
2528
|
for (const declaration of file.getExportDeclarations()) if (declaration.hasModuleSpecifier()) references.push(this.fromDeclaration(declaration, "re-export"));
|
|
2375
2529
|
for (const declaration of file.getDescendantsOfKind(SyntaxKind.ImportEqualsDeclaration)) references.push(...this.fromImportEquals(declaration));
|
|
2376
2530
|
for (const node of file.getDescendantsOfKind(SyntaxKind.ImportType)) references.push(this.fromImportType(node));
|
|
2377
|
-
for (const call of file.getDescendantsOfKind(SyntaxKind.CallExpression))
|
|
2531
|
+
for (const call of file.getDescendantsOfKind(SyntaxKind.CallExpression)) {
|
|
2532
|
+
references.push(...this.fromCall(call));
|
|
2533
|
+
references.push(...this.fromLoader(call));
|
|
2534
|
+
}
|
|
2535
|
+
for (const construction of file.getDescendantsOfKind(SyntaxKind.NewExpression)) references.push(...this.fromLoader(construction));
|
|
2536
|
+
for (const reference of file.getPathReferenceDirectives()) references.push({
|
|
2537
|
+
...this.fromDirective(reference, file),
|
|
2538
|
+
isPath: true
|
|
2539
|
+
});
|
|
2540
|
+
for (const reference of file.getTypeReferenceDirectives()) references.push(this.fromDirective(reference, file));
|
|
2541
|
+
for (const declaration of file.getDescendantsOfKind(SyntaxKind.ModuleDeclaration)) references.push(...this.fromAugmentation(declaration));
|
|
2378
2542
|
return references;
|
|
2379
2543
|
}
|
|
2380
2544
|
fromDeclaration(declaration, form) {
|
|
@@ -2414,6 +2578,47 @@ var DependencyReader = class {
|
|
|
2414
2578
|
if (callee.isKind(SyntaxKind.Identifier) && callee.getText() === "require") return [this.reference(call, "require", argument, everything)];
|
|
2415
2579
|
return [];
|
|
2416
2580
|
}
|
|
2581
|
+
fromDirective(reference, file) {
|
|
2582
|
+
const line = file.getLineAndColumnAtPos(reference.getPos()).line;
|
|
2583
|
+
const specifier = reference.getFileName();
|
|
2584
|
+
return {
|
|
2585
|
+
form: "reference",
|
|
2586
|
+
line,
|
|
2587
|
+
names: everything,
|
|
2588
|
+
specifier,
|
|
2589
|
+
text: specifier
|
|
2590
|
+
};
|
|
2591
|
+
}
|
|
2592
|
+
fromAugmentation(declaration) {
|
|
2593
|
+
const name = declaration.getNameNode();
|
|
2594
|
+
if (!name.isKind(SyntaxKind.StringLiteral) || name.getLiteralValue().includes("*")) return [];
|
|
2595
|
+
return [this.reference(declaration, "augmentation", name, everything)];
|
|
2596
|
+
}
|
|
2597
|
+
fromLoader(call) {
|
|
2598
|
+
const loader = this.loaderOf(call.getExpression());
|
|
2599
|
+
if (loader === void 0) return [];
|
|
2600
|
+
return [{
|
|
2601
|
+
form: "dynamic load",
|
|
2602
|
+
line: call.getStartLineNumber(),
|
|
2603
|
+
names: [],
|
|
2604
|
+
specifier: loader,
|
|
2605
|
+
text: loader
|
|
2606
|
+
}];
|
|
2607
|
+
}
|
|
2608
|
+
loaderOf(callee) {
|
|
2609
|
+
const expression = this.unwrapped(callee);
|
|
2610
|
+
if (expression.isKind(SyntaxKind.Identifier)) return globalLoaders.includes(expression.getText()) && !this.isDeclaredInProject(expression) ? expression.getText() : void 0;
|
|
2611
|
+
if (expression.isKind(SyntaxKind.PropertyAccessExpression) && loaderMembers.includes(expression.getName())) return expression.getText();
|
|
2612
|
+
}
|
|
2613
|
+
unwrapped(expression) {
|
|
2614
|
+
if (expression.isKind(SyntaxKind.ParenthesizedExpression)) return this.unwrapped(expression.getExpression());
|
|
2615
|
+
if (expression.isKind(SyntaxKind.BinaryExpression) && expression.getOperatorToken().isKind(SyntaxKind.CommaToken)) return this.unwrapped(expression.getRight());
|
|
2616
|
+
return expression;
|
|
2617
|
+
}
|
|
2618
|
+
isDeclaredInProject(expression) {
|
|
2619
|
+
const declaration = expression.getSymbol()?.getDeclarations()[0];
|
|
2620
|
+
return declaration !== void 0 && this.isInsideProject(normalize(declaration.getSourceFile().getFilePath()));
|
|
2621
|
+
}
|
|
2417
2622
|
reference(node, form, specifierNode, names) {
|
|
2418
2623
|
const isLiteral = specifierNode.isKind(SyntaxKind.StringLiteral) || specifierNode.isKind(SyntaxKind.NoSubstitutionTemplateLiteral);
|
|
2419
2624
|
return {
|
|
@@ -2425,15 +2630,20 @@ var DependencyReader = class {
|
|
|
2425
2630
|
};
|
|
2426
2631
|
}
|
|
2427
2632
|
targetOf(reference, file) {
|
|
2633
|
+
if (reference.form === "dynamic load" || loaderPackages.includes(reference.specifier ?? "")) return {
|
|
2634
|
+
kind: "file",
|
|
2635
|
+
path: reference.text,
|
|
2636
|
+
visibility: "dynamic"
|
|
2637
|
+
};
|
|
2428
2638
|
const directory = file.getDirectoryPath();
|
|
2429
2639
|
if (reference.specifier === void 0) return {
|
|
2430
2640
|
kind: "file",
|
|
2431
2641
|
path: normalize(join(directory, reference.text)),
|
|
2432
2642
|
visibility: "unresolved"
|
|
2433
2643
|
};
|
|
2434
|
-
const resolved = this.resolve(reference.specifier, file);
|
|
2644
|
+
const resolved = reference.isPath === true ? this.referencedPath(reference.specifier, directory) : this.resolve(reference.specifier, file);
|
|
2435
2645
|
if (resolved !== void 0 && this.isInsideProject(resolved)) return this.fileTarget(resolved);
|
|
2436
|
-
if (reference.specifier.startsWith(".")) return {
|
|
2646
|
+
if (reference.isPath === true || reference.specifier.startsWith(".")) return {
|
|
2437
2647
|
kind: "file",
|
|
2438
2648
|
path: normalize(join(directory, reference.specifier)),
|
|
2439
2649
|
visibility: "unresolved"
|
|
@@ -2454,6 +2664,10 @@ var DependencyReader = class {
|
|
|
2454
2664
|
const resolved = ts.resolveModuleName(specifier, file.getFilePath(), this.project.getCompilerOptions(), this.project.getModuleResolutionHost()).resolvedModule?.resolvedFileName;
|
|
2455
2665
|
return resolved === void 0 ? void 0 : normalize(resolved);
|
|
2456
2666
|
}
|
|
2667
|
+
referencedPath(specifier, directory) {
|
|
2668
|
+
const path = normalize(join(directory, specifier));
|
|
2669
|
+
return this.project.getModuleResolutionHost().fileExists(path) ? path : void 0;
|
|
2670
|
+
}
|
|
2457
2671
|
packageNameOf(specifier) {
|
|
2458
2672
|
const segments = specifier.split("/");
|
|
2459
2673
|
const length = specifier.startsWith("@") ? 2 : 1;
|
|
@@ -2639,6 +2853,12 @@ var ThrowReader = class {
|
|
|
2639
2853
|
};
|
|
2640
2854
|
//#endregion
|
|
2641
2855
|
//#region src/importer/ts-morph/readers/type-reader.ts
|
|
2856
|
+
const collections = [
|
|
2857
|
+
"Map",
|
|
2858
|
+
"Set",
|
|
2859
|
+
"WeakMap",
|
|
2860
|
+
"WeakSet"
|
|
2861
|
+
];
|
|
2642
2862
|
var TypeReader = class {
|
|
2643
2863
|
packageNames;
|
|
2644
2864
|
constructor(packageNames) {
|
|
@@ -2661,6 +2881,12 @@ var TypeReader = class {
|
|
|
2661
2881
|
packageName: void 0
|
|
2662
2882
|
} : this.namedSymbol(symbol);
|
|
2663
2883
|
}
|
|
2884
|
+
holdsCollection(type) {
|
|
2885
|
+
if (type.isArray() || type.isTuple()) return !type.isReadonlyArray();
|
|
2886
|
+
if (collections.includes(type.getSymbol()?.getName() ?? "")) return true;
|
|
2887
|
+
if (type.getStringIndexType() !== void 0 || type.getNumberIndexType() !== void 0) return true;
|
|
2888
|
+
return type.isAnonymous() && type.getCallSignatures().length === 0 && type.getConstructSignatures().length === 0;
|
|
2889
|
+
}
|
|
2664
2890
|
returnShapeOf(type) {
|
|
2665
2891
|
if (type.getSymbol()?.getName() === "Promise") {
|
|
2666
2892
|
const [inner] = type.getTypeArguments();
|
|
@@ -2725,17 +2951,98 @@ var TypeReader = class {
|
|
|
2725
2951
|
}
|
|
2726
2952
|
};
|
|
2727
2953
|
//#endregion
|
|
2954
|
+
//#region src/importer/ts-morph/readers/wiring-reader.ts
|
|
2955
|
+
const references = [SyntaxKind.Identifier, SyntaxKind.PropertyAccessExpression];
|
|
2956
|
+
var WiringReader = class {
|
|
2957
|
+
types;
|
|
2958
|
+
scope;
|
|
2959
|
+
constructor(types, scope) {
|
|
2960
|
+
this.types = types;
|
|
2961
|
+
this.scope = scope;
|
|
2962
|
+
}
|
|
2963
|
+
read(file) {
|
|
2964
|
+
if (!this.scope.readsWiring(file.getFilePath())) return [];
|
|
2965
|
+
const wirings = [];
|
|
2966
|
+
for (const site of this.sitesOf(file)) {
|
|
2967
|
+
const receiver = this.declaringFileOf(site.receiver);
|
|
2968
|
+
if (receiver === void 0) continue;
|
|
2969
|
+
for (const value of site.values) for (const reference of this.referencesIn(value)) wirings.push(new Wiring(reference.getStartLineNumber(), receiver, this.linksOf(reference)));
|
|
2970
|
+
}
|
|
2971
|
+
return wirings;
|
|
2972
|
+
}
|
|
2973
|
+
sitesOf(file) {
|
|
2974
|
+
const sites = [];
|
|
2975
|
+
file.forEachDescendant((node) => {
|
|
2976
|
+
if (Node.isCallExpression(node) || Node.isNewExpression(node)) sites.push({
|
|
2977
|
+
receiver: node.getExpression(),
|
|
2978
|
+
values: node.getArguments()
|
|
2979
|
+
});
|
|
2980
|
+
else if (Node.isBinaryExpression(node) && node.getOperatorToken().isKind(SyntaxKind.EqualsToken)) sites.push({
|
|
2981
|
+
receiver: node.getLeft(),
|
|
2982
|
+
values: [node.getRight()]
|
|
2983
|
+
});
|
|
2984
|
+
else if ((Node.isVariableDeclaration(node) || Node.isPropertyDeclaration(node)) && node.getTypeNode() !== void 0) {
|
|
2985
|
+
const initializer = node.getInitializer();
|
|
2986
|
+
sites.push({
|
|
2987
|
+
receiver: node.getTypeNode(),
|
|
2988
|
+
values: initializer === void 0 ? [] : [initializer]
|
|
2989
|
+
});
|
|
2990
|
+
}
|
|
2991
|
+
});
|
|
2992
|
+
return sites;
|
|
2993
|
+
}
|
|
2994
|
+
referencesIn(value) {
|
|
2995
|
+
const found = [];
|
|
2996
|
+
const visit = (node) => {
|
|
2997
|
+
if (this.isOutermostReference(node)) found.push(node);
|
|
2998
|
+
node.forEachChild(visit);
|
|
2999
|
+
};
|
|
3000
|
+
visit(value);
|
|
3001
|
+
return found;
|
|
3002
|
+
}
|
|
3003
|
+
isOutermostReference(node) {
|
|
3004
|
+
if (!references.includes(node.getKind())) return false;
|
|
3005
|
+
const parent = node.getParent();
|
|
3006
|
+
if (parent === void 0 || Node.isPropertyAccessExpression(parent)) return false;
|
|
3007
|
+
if (Node.isTypeNode(parent) || Node.isQualifiedName(parent) || Node.isExpressionWithTypeArguments(parent)) return false;
|
|
3008
|
+
return Node.isShorthandPropertyAssignment(parent) || !(Node.hasName(parent) && parent.getNameNode() === node);
|
|
3009
|
+
}
|
|
3010
|
+
linksOf(reference) {
|
|
3011
|
+
if (Node.isPropertyAccessExpression(reference)) return [...this.linksOf(reference.getExpression()), this.link(reference, reference.getNameNode())];
|
|
3012
|
+
if (Node.isIdentifier(reference)) return [this.link(reference, reference)];
|
|
3013
|
+
return [];
|
|
3014
|
+
}
|
|
3015
|
+
link(expression, name) {
|
|
3016
|
+
return {
|
|
3017
|
+
declaredIn: this.declaringFileOf(name),
|
|
3018
|
+
text: expression.getText(),
|
|
3019
|
+
types: this.types.classTypesIn(expression.getType())
|
|
3020
|
+
};
|
|
3021
|
+
}
|
|
3022
|
+
declaringFileOf(node) {
|
|
3023
|
+
if (node === void 0) return;
|
|
3024
|
+
const named = Node.isTypeNode(node) ? node.getType().getAliasSymbol() ?? node.getType().getSymbol() : this.nameOf(node).getSymbol();
|
|
3025
|
+
const declaration = (named?.isAlias() === true ? named.getAliasedSymbol() : named)?.getDeclarations()[0];
|
|
3026
|
+
return declaration === void 0 ? void 0 : normalize(declaration.getSourceFile().getFilePath());
|
|
3027
|
+
}
|
|
3028
|
+
nameOf(node) {
|
|
3029
|
+
return Node.isPropertyAccessExpression(node) ? node.getNameNode() : node;
|
|
3030
|
+
}
|
|
3031
|
+
};
|
|
3032
|
+
//#endregion
|
|
2728
3033
|
//#region src/importer/ts-morph/ts-morph-importer.ts
|
|
2729
3034
|
var TsMorphImporter = class TsMorphImporter extends Importer {
|
|
2730
3035
|
sources;
|
|
2731
3036
|
classes;
|
|
3037
|
+
types;
|
|
2732
3038
|
statements = new StatementReader();
|
|
2733
3039
|
disables = new DisableReader();
|
|
2734
3040
|
throws = new ThrowReader();
|
|
2735
3041
|
constructor(sources) {
|
|
2736
3042
|
super();
|
|
2737
3043
|
this.sources = sources;
|
|
2738
|
-
this.
|
|
3044
|
+
this.types = new TypeReader(new PackageNames(sources.getFileSystem()));
|
|
3045
|
+
this.classes = new ClassReader(this.types);
|
|
2739
3046
|
}
|
|
2740
3047
|
static fromTsConfig(tsConfigFilePath) {
|
|
2741
3048
|
if (!existsSync(tsConfigFilePath)) throw new Error(`No TypeScript configuration found at ${tsConfigFilePath}: pass --tsconfig, or set tsconfig in alveolus.config.ts.`);
|
|
@@ -2747,12 +3054,13 @@ var TsMorphImporter = class TsMorphImporter extends Importer {
|
|
|
2747
3054
|
}
|
|
2748
3055
|
read(scope) {
|
|
2749
3056
|
const dependencies = new DependencyReader(this.sources, scope);
|
|
3057
|
+
const wirings = new WiringReader(this.types, scope);
|
|
2750
3058
|
const globals = new GlobalReader(scope.projectDir);
|
|
2751
3059
|
const files = [];
|
|
2752
|
-
for (const file of this.sources.getSourceFiles()) if (this.isInside(scope.rootDir, file.getFilePath()) && !scope.isIgnored(file.getFilePath())) files.push(this.readFile(file, dependencies, globals));
|
|
3060
|
+
for (const file of this.sources.getSourceFiles()) if (this.isInside(scope.rootDir, file.getFilePath()) && !scope.isIgnored(file.getFilePath())) files.push(this.readFile(file, dependencies, globals, wirings));
|
|
2753
3061
|
return new Project$1(scope.projectDir, files);
|
|
2754
3062
|
}
|
|
2755
|
-
readFile(file, dependencies, globals) {
|
|
3063
|
+
readFile(file, dependencies, globals, wirings) {
|
|
2756
3064
|
const globalReferences = globals.read(file);
|
|
2757
3065
|
return new SourceFile({
|
|
2758
3066
|
classes: file.getClasses().map((declaration) => this.classes.read(declaration)),
|
|
@@ -2762,7 +3070,8 @@ var TsMorphImporter = class TsMorphImporter extends Importer {
|
|
|
2762
3070
|
path: normalize(file.getFilePath()),
|
|
2763
3071
|
statements: this.statements.read(file),
|
|
2764
3072
|
text: file.getFullText(),
|
|
2765
|
-
throws: this.throws.read(file)
|
|
3073
|
+
throws: this.throws.read(file),
|
|
3074
|
+
wirings: wirings.read(file)
|
|
2766
3075
|
});
|
|
2767
3076
|
}
|
|
2768
3077
|
globalUsesOf(references) {
|
|
@@ -2781,6 +3090,7 @@ const scaffolds = [
|
|
|
2781
3090
|
|
|
2782
3091
|
export default defineConfig({
|
|
2783
3092
|
boundedContexts: {},
|
|
3093
|
+
contextMap: {},
|
|
2784
3094
|
root: "src",
|
|
2785
3095
|
});
|
|
2786
3096
|
`,
|
|
@@ -2799,7 +3109,7 @@ The documentation is installed with the package: read it with \`npx alveolus exp
|
|
|
2799
3109
|
1. Before writing a class, read its building block: \`npx alveolus explain aggregates\`, \`entities\`, \`value-objects\`, \`domain-events\`, \`domain-errors\`, \`ports\`, \`repositories\`, \`command-handlers\`, \`query-handlers\`, \`result\`.
|
|
2800
3110
|
2. Where a file goes and what each layer may import: \`npx alveolus explain project-layout\`.
|
|
2801
3111
|
3. After each change, run \`npx alveolus arch check\`.
|
|
2802
|
-
4. On a violation, read the rule before changing the code: \`npx alveolus explain <rule>\`, with the rule id of the report, such as \`layers/no-impure-domain\`. Fix the cause: never turn a rule off, add a disable comment or edit \`alveolus.baseline.json\` by hand without asking.
|
|
3112
|
+
4. On a violation, read the rule before changing the code: \`npx alveolus explain <rule>\`, with the rule id of the report, such as \`layers/no-impure-domain\`. Fix the cause: never turn a rule off, add a disable comment, add a context to \`contextMap\` or edit \`alveolus.baseline.json\` by hand without asking.
|
|
2803
3113
|
`,
|
|
2804
3114
|
path: ".claude/skills/alveolus/SKILL.md"
|
|
2805
3115
|
},
|
|
@@ -3056,4 +3366,4 @@ var Docs = class Docs {
|
|
|
3056
3366
|
//#endregion
|
|
3057
3367
|
export { TsMorphImporter as a, Config as c, RuleRegistry as d, Rule as f, Baseline as h, Init as i, Report as l, AllowedPackages as m, Page as n, Importer as o, Architecture as p, Cli as r, ConfigLoader as s, Docs as t, Checker as u };
|
|
3058
3368
|
|
|
3059
|
-
//# sourceMappingURL=docs-
|
|
3369
|
+
//# sourceMappingURL=docs-B1Tzkjrn.mjs.map
|