@webpieces/nx-webpieces-rules 0.3.244 → 0.3.246
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/package.json +6 -6
- package/src/executors/di-graph-generate/executor.js +42 -12
- package/src/executors/di-graph-generate/executor.js.map +1 -1
- package/src/lib/di-graph/analyzer-strategy.d.ts +47 -0
- package/src/lib/di-graph/analyzer-strategy.js +86 -0
- package/src/lib/di-graph/analyzer-strategy.js.map +1 -0
- package/src/lib/di-graph/analyzer.d.ts +128 -5
- package/src/lib/di-graph/analyzer.js +190 -65
- package/src/lib/di-graph/analyzer.js.map +1 -1
- package/src/lib/di-graph/angular-analyzer.d.ts +28 -0
- package/src/lib/di-graph/angular-analyzer.js +139 -0
- package/src/lib/di-graph/angular-analyzer.js.map +1 -0
- package/src/lib/di-graph/angular-providers.d.ts +32 -0
- package/src/lib/di-graph/angular-providers.js +177 -0
- package/src/lib/di-graph/angular-providers.js.map +1 -0
- package/src/lib/di-graph/angular-roots.d.ts +21 -0
- package/src/lib/di-graph/angular-roots.js +150 -0
- package/src/lib/di-graph/angular-roots.js.map +1 -0
- package/src/lib/di-graph/dot.js +8 -5
- package/src/lib/di-graph/dot.js.map +1 -1
- package/src/lib/di-graph/mermaid.js +12 -7
- package/src/lib/di-graph/mermaid.js.map +1 -1
- package/src/lib/di-graph/model.d.ts +17 -3
- package/src/lib/di-graph/model.js +19 -2
- package/src/lib/di-graph/model.js.map +1 -1
- package/src/lib/framework-resolver.d.ts +8 -4
- package/src/lib/framework-resolver.js +10 -6
- package/src/lib/framework-resolver.js.map +1 -1
- package/src/lib/graph-loader.d.ts +5 -0
- package/src/lib/graph-loader.js +5 -0
- package/src/lib/graph-loader.js.map +1 -1
- package/src/lib/graph-metadata.d.ts +19 -0
- package/src/lib/graph-metadata.js +40 -1
- package/src/lib/graph-metadata.js.map +1 -1
- package/src/lib/graph-sorter.d.ts +3 -1
- package/src/lib/graph-sorter.js.map +1 -1
- package/src/lib/graph-visualizer.js +24 -16
- package/src/lib/graph-visualizer.js.map +1 -1
|
@@ -15,7 +15,14 @@
|
|
|
15
15
|
* (the tops of the local DAG).
|
|
16
16
|
*/
|
|
17
17
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
18
|
+
exports.InversifyDesignBuilder = exports.DiDesignBuilder = exports.Injection = exports.ParamInjection = void 0;
|
|
19
|
+
exports.readParamDecorators = readParamDecorators;
|
|
18
20
|
exports.isControllerClass = isControllerClass;
|
|
21
|
+
exports.findConstructor = findConstructor;
|
|
22
|
+
exports.projectClasses = projectClasses;
|
|
23
|
+
exports.byClassName = byClassName;
|
|
24
|
+
exports.assignLevels = assignLevels;
|
|
25
|
+
exports.buildDesign = buildDesign;
|
|
19
26
|
exports.buildDiGraph = buildDiGraph;
|
|
20
27
|
const tslib_1 = require("tslib");
|
|
21
28
|
const ts = tslib_1.__importStar(require("typescript"));
|
|
@@ -36,6 +43,7 @@ class ParamInjection {
|
|
|
36
43
|
this.unmanaged = unmanaged;
|
|
37
44
|
}
|
|
38
45
|
}
|
|
46
|
+
exports.ParamInjection = ParamInjection;
|
|
39
47
|
function readParamDecorators(param) {
|
|
40
48
|
let expr = null;
|
|
41
49
|
let multi = false;
|
|
@@ -102,12 +110,50 @@ function projectClasses(program, workspaceRoot, projectRoot) {
|
|
|
102
110
|
}
|
|
103
111
|
return classes;
|
|
104
112
|
}
|
|
113
|
+
/**
|
|
114
|
+
* One normalized injection site, framework-agnostic — produced by a builder's
|
|
115
|
+
* `collectInjections(cls)` hook and processed by the shared base. Inversify
|
|
116
|
+
* emits `token`/`type` from constructor params; Angular emits `angularToken`
|
|
117
|
+
* from constructor params AND `inject()` field initializers.
|
|
118
|
+
*
|
|
119
|
+
* Modes:
|
|
120
|
+
* - `token` Inversify `@inject`/`@multiInject`: table lookup; an unbound
|
|
121
|
+
* token becomes an `unresolved` node (no class fallback).
|
|
122
|
+
* - `type` Inversify bare typed param: resolve the declared TYPE to a
|
|
123
|
+
* class (inject-by-type); no table lookup.
|
|
124
|
+
* - `angularToken` Angular `inject(T)` / `@Inject(T)` / bare typed ctor param:
|
|
125
|
+
* table lookup FIRST (provider table), then fall back to
|
|
126
|
+
* resolving the token expression as a class, else unresolved.
|
|
127
|
+
*/
|
|
128
|
+
class Injection {
|
|
129
|
+
mode;
|
|
130
|
+
/** Token/type expression (modes `token`/`angularToken`, or the type identifier for `type`). */
|
|
131
|
+
expr;
|
|
132
|
+
multi;
|
|
133
|
+
optional;
|
|
134
|
+
paramName;
|
|
135
|
+
paramType;
|
|
136
|
+
constructor(mode, expr, paramName, paramType, multi = false, optional = false) {
|
|
137
|
+
this.mode = mode;
|
|
138
|
+
this.expr = expr;
|
|
139
|
+
this.paramName = paramName;
|
|
140
|
+
this.paramType = paramType;
|
|
141
|
+
this.multi = multi;
|
|
142
|
+
this.optional = optional;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
exports.Injection = Injection;
|
|
105
146
|
/**
|
|
106
147
|
* Builds ONE self-contained `DiDesign` for a single root. A fresh instance is
|
|
107
148
|
* created per root so its maps (visited/classIds/usedIds/...) are scoped to
|
|
108
|
-
* that root's tree — a dependency shared by two
|
|
109
|
-
* (and duplicated) into each
|
|
110
|
-
*
|
|
149
|
+
* that root's tree — a dependency shared by two roots is therefore walked
|
|
150
|
+
* (and duplicated) into each root's design, not hidden under whichever root
|
|
151
|
+
* reached it first.
|
|
152
|
+
*
|
|
153
|
+
* Framework-agnostic base: subclasses implement {@link collectInjections} (and
|
|
154
|
+
* override {@link rootKindOf} for Angular components); everything else — node
|
|
155
|
+
* ids, leaf/unresolved labeling, token→binding resolution, factory-dep edges,
|
|
156
|
+
* level assignment — is shared so Inversify and Angular render identically.
|
|
111
157
|
*/
|
|
112
158
|
class DiDesignBuilder {
|
|
113
159
|
checker;
|
|
@@ -125,6 +171,10 @@ class DiDesignBuilder {
|
|
|
125
171
|
this.workspaceRoot = workspaceRoot;
|
|
126
172
|
this.design = design;
|
|
127
173
|
}
|
|
174
|
+
/** Node kind for a root/reached class — `controller` (Inversify) or `component` (Angular). */
|
|
175
|
+
rootKindOf(cls) {
|
|
176
|
+
return isControllerClass(cls) ? 'controller' : 'class';
|
|
177
|
+
}
|
|
128
178
|
addRoot(cls) {
|
|
129
179
|
const id = this.classNode(cls);
|
|
130
180
|
this.design.root = id;
|
|
@@ -145,8 +195,7 @@ class DiDesignBuilder {
|
|
|
145
195
|
this.classIds.set(cls, id);
|
|
146
196
|
const declaredScope = this.classScope(cls);
|
|
147
197
|
const scope = declaredScope !== 'unknown' ? declaredScope : scopeHint;
|
|
148
|
-
|
|
149
|
-
this.design.nodes.push(new model_1.DiNode(id, className, kind, scope, file));
|
|
198
|
+
this.design.nodes.push(new model_1.DiNode(id, className, this.rootKindOf(cls), scope, file));
|
|
150
199
|
return id;
|
|
151
200
|
}
|
|
152
201
|
claimId(preferred, file) {
|
|
@@ -173,108 +222,176 @@ class DiDesignBuilder {
|
|
|
173
222
|
}
|
|
174
223
|
return 'unknown';
|
|
175
224
|
}
|
|
176
|
-
|
|
225
|
+
/**
|
|
226
|
+
* Leaf box for a constant/dynamic binding. B0: the box is labeled by the
|
|
227
|
+
* DECLARED param TYPE (e.g. `FirestoreConfig`, `ClientConfig`) — the DI
|
|
228
|
+
* contract — while the bound expression (`buildConfigFromEnv(...)` /
|
|
229
|
+
* `TOKEN (dynamic)`) is kept as `detail`. A dynamic leaf also fans out to
|
|
230
|
+
* each of its `useFactory` `deps` (Angular; empty for Inversify).
|
|
231
|
+
*/
|
|
232
|
+
leafNode(binding, kind, paramType) {
|
|
177
233
|
const existing = this.leafIds.get(binding);
|
|
178
234
|
if (existing)
|
|
179
235
|
return existing;
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
const className = kind === 'dynamic'
|
|
183
|
-
? `${binding.tokenDisplay} (dynamic)`
|
|
184
|
-
: binding.valueText !== '' ? binding.valueText : binding.tokenDisplay;
|
|
236
|
+
const detail = kind === 'dynamic' ? `${binding.tokenDisplay} (dynamic)` : binding.valueText;
|
|
237
|
+
const className = paramType !== '' ? paramType : detail !== '' ? detail : binding.tokenDisplay;
|
|
185
238
|
const id = this.claimId(className, binding.file);
|
|
186
239
|
this.leafIds.set(binding, id);
|
|
187
|
-
this.design.nodes.push(new model_1.DiNode(id, className, kind, binding.scope, binding.file));
|
|
240
|
+
this.design.nodes.push(new model_1.DiNode(id, className, kind, binding.scope, binding.file, 0, detail));
|
|
241
|
+
if (kind === 'dynamic')
|
|
242
|
+
this.expandFactoryDeps(id, binding);
|
|
188
243
|
return id;
|
|
189
244
|
}
|
|
190
|
-
|
|
191
|
-
|
|
245
|
+
/** Edges from a `useFactory` leaf to each declared `deps: [...]` token (Angular). */
|
|
246
|
+
expandFactoryDeps(leafId, binding) {
|
|
247
|
+
for (const dep of binding.factoryDeps) {
|
|
248
|
+
const bindings = this.table.lookup(dep.key);
|
|
249
|
+
if (bindings.length > 0) {
|
|
250
|
+
for (const depBinding of bindings) {
|
|
251
|
+
// The dep token IS the declared type — label the box with it.
|
|
252
|
+
const toId = this.bindingTarget(depBinding, dep.display);
|
|
253
|
+
this.design.edges.push(new model_1.DiEdge(leafId, toId, 'type', dep.display, dep.key, dep.display, dep.display));
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
else {
|
|
257
|
+
const toId = this.unresolvedNode(dep.display, binding.file, '');
|
|
258
|
+
this.design.edges.push(new model_1.DiEdge(leafId, toId, 'type', dep.display, dep.key, dep.display, dep.display));
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
/**
|
|
263
|
+
* `unresolved` placeholder box. B0: labeled by the declared param TYPE
|
|
264
|
+
* (`className`) with the resolving token expression kept as `detail` (and
|
|
265
|
+
* surfaced in `design.unresolved` for diagnostics).
|
|
266
|
+
*/
|
|
267
|
+
unresolvedNode(className, file, detail) {
|
|
268
|
+
const key = `${className}|${detail}|${file}`;
|
|
192
269
|
const existing = this.unresolvedIds.get(key);
|
|
193
270
|
if (existing)
|
|
194
271
|
return existing;
|
|
195
|
-
const id = this.claimId(
|
|
272
|
+
const id = this.claimId(className, file);
|
|
196
273
|
this.unresolvedIds.set(key, id);
|
|
197
|
-
this.design.nodes.push(new model_1.DiNode(id,
|
|
198
|
-
this.design.unresolved.push(
|
|
274
|
+
this.design.nodes.push(new model_1.DiNode(id, className, 'unresolved', 'unknown', file, 0, detail !== className ? detail : ''));
|
|
275
|
+
this.design.unresolved.push(detail !== '' ? detail : className);
|
|
199
276
|
return id;
|
|
200
277
|
}
|
|
201
278
|
walkClass(cls) {
|
|
202
279
|
if (this.visited.has(cls))
|
|
203
280
|
return;
|
|
204
281
|
this.visited.add(cls);
|
|
205
|
-
const ctor = findConstructor(cls);
|
|
206
|
-
if (!ctor)
|
|
207
|
-
return;
|
|
208
282
|
const fromId = this.classNode(cls);
|
|
209
|
-
for (const
|
|
210
|
-
this.
|
|
283
|
+
for (const injection of this.collectInjections(cls)) {
|
|
284
|
+
this.processInjection(fromId, cls, injection);
|
|
211
285
|
}
|
|
212
286
|
}
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
if (injection.
|
|
216
|
-
|
|
217
|
-
const paramName = ts.isIdentifier(param.name) ? param.name.text : param.name.getText();
|
|
218
|
-
const paramType = param.type ? param.type.getText() : '';
|
|
219
|
-
if (injection.expr) {
|
|
220
|
-
this.walkTokenParam(fromId, cls, injection, paramName, paramType);
|
|
287
|
+
/** Resolve one injection to a node and record the edge(s). Never throws. */
|
|
288
|
+
processInjection(fromId, cls, injection) {
|
|
289
|
+
if (injection.mode === 'type') {
|
|
290
|
+
this.processTypeInjection(fromId, cls, injection);
|
|
221
291
|
}
|
|
222
292
|
else {
|
|
223
|
-
this.
|
|
293
|
+
this.processTokenInjection(fromId, cls, injection);
|
|
224
294
|
}
|
|
225
295
|
}
|
|
226
|
-
|
|
296
|
+
/** Inversify `@inject`/`@multiInject` and Angular `inject()`/`@Inject`/bare token. */
|
|
297
|
+
processTokenInjection(fromId, cls, injection) {
|
|
227
298
|
const expr = injection.expr;
|
|
228
299
|
if (!expr)
|
|
229
300
|
return;
|
|
230
301
|
const token = (0, token_resolver_1.resolveTokenKey)(expr, this.checker, this.workspaceRoot);
|
|
231
302
|
const kind = injection.multi ? 'multiInject' : 'token';
|
|
232
303
|
const bindings = this.table.lookup(token.key);
|
|
233
|
-
if (bindings.length
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
const toId = this.unresolvedNode(token.display, file);
|
|
239
|
-
this.design.edges.push(new model_1.DiEdge(fromId, toId, kind, token.display, token.key, paramName, paramType));
|
|
304
|
+
if (bindings.length > 0) {
|
|
305
|
+
for (const binding of bindings) {
|
|
306
|
+
const toId = this.bindingTarget(binding, injection.paramType);
|
|
307
|
+
this.design.edges.push(new model_1.DiEdge(fromId, toId, kind, token.display, token.key, injection.paramName, injection.paramType));
|
|
308
|
+
}
|
|
240
309
|
return;
|
|
241
310
|
}
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
311
|
+
// @multiInject @optional with zero bindings is legal — no edge, no error.
|
|
312
|
+
if (injection.multi && injection.optional)
|
|
313
|
+
return;
|
|
314
|
+
// Angular: a token with no explicit provider may still be a bare @Injectable
|
|
315
|
+
// class — resolve the token expression as a class and inject it by type.
|
|
316
|
+
if (injection.mode === 'angularToken') {
|
|
317
|
+
const target = (0, bindings_1.resolveClassDeclaration)(expr, this.checker);
|
|
318
|
+
if (target) {
|
|
319
|
+
const classToken = (0, token_resolver_1.classTokenKey)(target, this.workspaceRoot);
|
|
320
|
+
const toId = this.classNode(target);
|
|
321
|
+
this.walkClass(target);
|
|
322
|
+
this.design.edges.push(new model_1.DiEdge(fromId, toId, 'type', classToken.display, classToken.key, injection.paramName, injection.paramType));
|
|
323
|
+
return;
|
|
324
|
+
}
|
|
245
325
|
}
|
|
326
|
+
const file = (0, token_resolver_1.relativeFile)(this.workspaceRoot, cls.getSourceFile());
|
|
327
|
+
const label = injection.paramType !== '' ? injection.paramType : token.display;
|
|
328
|
+
const toId = this.unresolvedNode(label, file, token.display);
|
|
329
|
+
this.design.edges.push(new model_1.DiEdge(fromId, toId, kind, token.display, token.key, injection.paramName, injection.paramType));
|
|
246
330
|
}
|
|
247
|
-
bindingTarget(binding) {
|
|
331
|
+
bindingTarget(binding, paramType) {
|
|
248
332
|
if (binding.implClass) {
|
|
249
333
|
const toId = this.classNode(binding.implClass, binding.scope);
|
|
250
334
|
this.walkClass(binding.implClass);
|
|
251
335
|
return toId;
|
|
252
336
|
}
|
|
253
337
|
if (binding.kind === 'toConstantValue')
|
|
254
|
-
return this.leafNode(binding, 'constant');
|
|
338
|
+
return this.leafNode(binding, 'constant', paramType);
|
|
255
339
|
if (binding.kind === 'toDynamicValue')
|
|
256
|
-
return this.leafNode(binding, 'dynamic');
|
|
340
|
+
return this.leafNode(binding, 'dynamic', paramType);
|
|
257
341
|
// .to(X) where X did not resolve to a class declaration.
|
|
258
|
-
|
|
342
|
+
const detail = binding.valueText !== '' ? binding.valueText : binding.tokenDisplay;
|
|
343
|
+
const label = paramType !== '' ? paramType : detail;
|
|
344
|
+
return this.unresolvedNode(label, binding.file, detail);
|
|
259
345
|
}
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
const
|
|
263
|
-
|
|
264
|
-
: null;
|
|
346
|
+
/** Inversify bare typed param — resolve the declared type directly to a class. */
|
|
347
|
+
processTypeInjection(fromId, cls, injection) {
|
|
348
|
+
const typeRef = injection.expr && ts.isIdentifier(injection.expr) ? injection.expr : null;
|
|
349
|
+
const target = typeRef ? (0, bindings_1.resolveClassDeclaration)(typeRef, this.checker) : null;
|
|
265
350
|
if (target) {
|
|
266
351
|
const token = (0, token_resolver_1.classTokenKey)(target, this.workspaceRoot);
|
|
267
352
|
const toId = this.classNode(target);
|
|
268
353
|
this.walkClass(target);
|
|
269
|
-
this.design.edges.push(new model_1.DiEdge(fromId, toId, 'type', token.display, token.key, paramName, paramType));
|
|
354
|
+
this.design.edges.push(new model_1.DiEdge(fromId, toId, 'type', token.display, token.key, injection.paramName, injection.paramType));
|
|
270
355
|
return;
|
|
271
356
|
}
|
|
272
357
|
// Bare param whose type is not a resolvable class (interface, primitive, etc.).
|
|
273
358
|
const file = (0, token_resolver_1.relativeFile)(this.workspaceRoot, cls.getSourceFile());
|
|
274
|
-
const
|
|
275
|
-
this.
|
|
359
|
+
const label = injection.paramType !== '' ? injection.paramType : injection.paramName;
|
|
360
|
+
const toId = this.unresolvedNode(label, file, '');
|
|
361
|
+
this.design.edges.push(new model_1.DiEdge(fromId, toId, 'type', injection.paramType, `type:${injection.paramType}`, injection.paramName, injection.paramType));
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
exports.DiDesignBuilder = DiDesignBuilder;
|
|
365
|
+
/**
|
|
366
|
+
* Inversify builder: injection sites are constructor params — `@inject`/
|
|
367
|
+
* `@multiInject` tokens or bare typed (inject-by-type) params.
|
|
368
|
+
*/
|
|
369
|
+
class InversifyDesignBuilder extends DiDesignBuilder {
|
|
370
|
+
collectInjections(cls) {
|
|
371
|
+
const ctor = findConstructor(cls);
|
|
372
|
+
if (!ctor)
|
|
373
|
+
return [];
|
|
374
|
+
const injections = [];
|
|
375
|
+
for (const param of ctor.parameters) {
|
|
376
|
+
const decorated = readParamDecorators(param);
|
|
377
|
+
if (decorated.unmanaged)
|
|
378
|
+
continue;
|
|
379
|
+
const paramName = ts.isIdentifier(param.name) ? param.name.text : param.name.getText();
|
|
380
|
+
const paramType = param.type ? param.type.getText() : '';
|
|
381
|
+
if (decorated.expr) {
|
|
382
|
+
injections.push(new Injection('token', decorated.expr, paramName, paramType, decorated.multi, decorated.optional));
|
|
383
|
+
}
|
|
384
|
+
else {
|
|
385
|
+
const typeRef = param.type && ts.isTypeReferenceNode(param.type) && ts.isIdentifier(param.type.typeName)
|
|
386
|
+
? param.type.typeName
|
|
387
|
+
: null;
|
|
388
|
+
injections.push(new Injection('type', typeRef, paramName, paramType));
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
return injections;
|
|
276
392
|
}
|
|
277
393
|
}
|
|
394
|
+
exports.InversifyDesignBuilder = InversifyDesignBuilder;
|
|
278
395
|
function byClassName(a, b) {
|
|
279
396
|
const nameA = a.name ? a.name.text : '';
|
|
280
397
|
const nameB = b.name ? b.name.text : '';
|
|
@@ -350,23 +467,28 @@ function assignLevels(design) {
|
|
|
350
467
|
}
|
|
351
468
|
design.maxLevel = maxLevel;
|
|
352
469
|
}
|
|
353
|
-
/**
|
|
354
|
-
|
|
470
|
+
/**
|
|
471
|
+
* Build one self-contained downward design tree for a single root class, using
|
|
472
|
+
* the builder produced by `makeBuilder` (Inversify or Angular). `rootKind` is
|
|
473
|
+
* the node kind for the root box (`controller`/`component`/`class`).
|
|
474
|
+
*/
|
|
475
|
+
function buildDesign(root, rootKind, workspaceRoot, makeBuilder) {
|
|
355
476
|
const className = root.name ? root.name.text : '<anonymous>';
|
|
356
|
-
const rootKind = isControllerClass(root) ? 'controller' : 'class';
|
|
357
477
|
const file = (0, token_resolver_1.relativeFile)(workspaceRoot, root.getSourceFile());
|
|
358
478
|
const design = new model_1.DiDesign(className, rootKind, file);
|
|
359
|
-
|
|
360
|
-
builder.addRoot(root);
|
|
479
|
+
makeBuilder(design).addRoot(root);
|
|
361
480
|
assignLevels(design);
|
|
362
481
|
return design;
|
|
363
482
|
}
|
|
364
483
|
/**
|
|
365
|
-
* Build the full DI graph for one project: one self-contained
|
|
366
|
-
*
|
|
367
|
-
*
|
|
484
|
+
* Build the full Inversify DI graph for one project: one self-contained
|
|
485
|
+
* `DiDesign` per @Controller root. `projectRoot` is workspace-relative.
|
|
486
|
+
*
|
|
487
|
+
* `includeLibraryRoots` (default false, the v1 executor path) roots ONLY on
|
|
488
|
+
* @Controller classes; when true, a controller-less project falls back to its
|
|
489
|
+
* top-of-DAG DI classes (the deferred library behavior, still unit-tested).
|
|
368
490
|
*/
|
|
369
|
-
function buildDiGraph(program, workspaceRoot, projectRoot, projectName) {
|
|
491
|
+
function buildDiGraph(program, workspaceRoot, projectRoot, projectName, includeLibraryRoots = false) {
|
|
370
492
|
const checker = program.getTypeChecker();
|
|
371
493
|
const table = (0, bindings_1.collectBindings)(program, checker, workspaceRoot);
|
|
372
494
|
const graph = new model_1.DiGraph(projectName);
|
|
@@ -374,9 +496,12 @@ function buildDiGraph(program, workspaceRoot, projectRoot, projectName) {
|
|
|
374
496
|
const controllers = classes.filter((cls) => isControllerClass(cls));
|
|
375
497
|
const roots = controllers.length > 0
|
|
376
498
|
? controllers
|
|
377
|
-
:
|
|
499
|
+
: includeLibraryRoots
|
|
500
|
+
? findLibraryRoots(classes, checker, table, workspaceRoot)
|
|
501
|
+
: [];
|
|
378
502
|
for (const root of [...roots].sort(byClassName)) {
|
|
379
|
-
|
|
503
|
+
const rootKind = isControllerClass(root) ? 'controller' : 'class';
|
|
504
|
+
graph.designs.push(buildDesign(root, rootKind, workspaceRoot, (design) => new InversifyDesignBuilder(checker, table, workspaceRoot, design)));
|
|
380
505
|
}
|
|
381
506
|
return graph;
|
|
382
507
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"analyzer.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/nx-webpieces-rules/src/lib/di-graph/analyzer.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * DI Graph Analyzer (pass 2)\n *\n * Walks constructor injection from a project's root classes down to leaves:\n *\n * - @inject(TOKEN) params → token lookup in the binding table → bound impl\n * - @multiInject(TOKEN) → fan-out edge to EVERY binding of that token\n * - bare typed params → checker resolves the type to a class (inject-by-type)\n * - toConstantValue/toDynamicValue bindings → leaf nodes, no recursion\n * - unresolvable tokens/types → kind \"unresolved\" nodes (generation never fails)\n *\n * Roots: @Controller() classes when the project has any; otherwise every\n * DI-registered class in the project that no other project class injects\n * (the tops of the local DAG).\n */\n\nimport * as ts from 'typescript';\nimport * as path from 'path';\nimport { Binding, DiDesign, DiEdge, DiGraph, DiNode, DiNodeKind, DiScope } from './model';\nimport {\n BindingTable,\n classDecorators,\n collectBindings,\n decoratorCall,\n decoratorName,\n resolveClassDeclaration,\n} from './bindings';\nimport { classTokenKey, relativeFile, resolveTokenKey } from './token-resolver';\n\nconst DI_DECORATORS = new Set(['provideSingleton', 'provideTransient', 'provideSingletonAs', 'injectable']);\n\nclass ParamInjection {\n expr: ts.Expression | null;\n multi: boolean;\n optional: boolean;\n unmanaged: boolean;\n\n constructor(expr: ts.Expression | null, multi: boolean, optional: boolean, unmanaged: boolean) {\n this.expr = expr;\n this.multi = multi;\n this.optional = optional;\n this.unmanaged = unmanaged;\n }\n}\n\nfunction readParamDecorators(param: ts.ParameterDeclaration): ParamInjection {\n let expr: ts.Expression | null = null;\n let multi = false;\n let optional = false;\n let unmanaged = false;\n for (const decorator of ts.getDecorators(param) ?? []) {\n const name = decoratorName(decorator);\n const call = decoratorCall(decorator);\n if (name === 'inject' && call?.arguments[0]) {\n expr = call.arguments[0];\n } else if (name === 'multiInject' && call?.arguments[0]) {\n expr = call.arguments[0];\n multi = true;\n } else if (name === 'optional') {\n optional = true;\n } else if (name === 'unmanaged') {\n unmanaged = true;\n }\n }\n return new ParamInjection(expr, multi, optional, unmanaged);\n}\n\nfunction hasDecoratorNamed(cls: ts.ClassDeclaration, names: Set<string>): boolean {\n for (const decorator of classDecorators(cls)) {\n const name = decoratorName(decorator);\n if (name && names.has(name)) return true;\n }\n return false;\n}\n\nexport function isControllerClass(cls: ts.ClassDeclaration): boolean {\n return hasDecoratorNamed(cls, new Set(['Controller']));\n}\n\nfunction isDiRegisteredClass(cls: ts.ClassDeclaration): boolean {\n return hasDecoratorNamed(cls, DI_DECORATORS);\n}\n\nfunction findConstructor(cls: ts.ClassDeclaration): ts.ConstructorDeclaration | null {\n for (const member of cls.members) {\n if (ts.isConstructorDeclaration(member) && member.body) return member;\n if (ts.isConstructorDeclaration(member)) return member;\n }\n return null;\n}\n\n/** All class declarations in files under the project root. */\nfunction projectClasses(\n program: ts.Program,\n workspaceRoot: string,\n projectRoot: string,\n): ts.ClassDeclaration[] {\n const prefix = projectRoot.endsWith('/') ? projectRoot : projectRoot + '/';\n const classes: ts.ClassDeclaration[] = [];\n for (const sourceFile of program.getSourceFiles()) {\n if (sourceFile.isDeclarationFile) continue;\n const rel = relativeFile(workspaceRoot, sourceFile);\n if (!rel.startsWith(prefix)) continue;\n const visit = (node: ts.Node): void => {\n if (ts.isClassDeclaration(node) && node.name) classes.push(node);\n ts.forEachChild(node, visit);\n };\n visit(sourceFile);\n }\n return classes;\n}\n\n/**\n * Builds ONE self-contained `DiDesign` for a single root. A fresh instance is\n * created per root so its maps (visited/classIds/usedIds/...) are scoped to\n * that root's tree — a dependency shared by two controllers is therefore walked\n * (and duplicated) into each controller's design, not hidden under whichever\n * controller reached it first.\n */\nclass DiDesignBuilder {\n private readonly checker: ts.TypeChecker;\n private readonly table: BindingTable;\n private readonly workspaceRoot: string;\n private readonly design: DiDesign;\n private readonly classIds = new Map<ts.ClassDeclaration, string>();\n private readonly leafIds = new Map<Binding, string>();\n private readonly unresolvedIds = new Map<string, string>();\n private readonly usedIds = new Set<string>();\n private readonly visited = new Set<ts.ClassDeclaration>();\n\n constructor(checker: ts.TypeChecker, table: BindingTable, workspaceRoot: string, design: DiDesign) {\n this.checker = checker;\n this.table = table;\n this.workspaceRoot = workspaceRoot;\n this.design = design;\n }\n\n addRoot(cls: ts.ClassDeclaration): void {\n const id = this.classNode(cls);\n this.design.root = id;\n this.walkClass(cls);\n }\n\n /**\n * Register (or fetch) the node for a class, returning its stable id.\n * `scopeHint` carries the scope of the module binding the class was reached\n * through (e.g. bind(TOKEN).to(X).inSingletonScope() where X is not self-bound).\n */\n private classNode(cls: ts.ClassDeclaration, scopeHint: DiScope = 'unknown'): string {\n const existing = this.classIds.get(cls);\n if (existing) return existing;\n\n const className = cls.name ? cls.name.text : '<anonymous>';\n const file = relativeFile(this.workspaceRoot, cls.getSourceFile());\n const id = this.claimId(className, file);\n this.classIds.set(cls, id);\n\n const declaredScope = this.classScope(cls);\n const scope = declaredScope !== 'unknown' ? declaredScope : scopeHint;\n const kind: DiNodeKind = isControllerClass(cls) ? 'controller' : 'class';\n this.design.nodes.push(new DiNode(id, className, kind, scope, file));\n return id;\n }\n\n private claimId(preferred: string, file: string): string {\n if (!this.usedIds.has(preferred)) {\n this.usedIds.add(preferred);\n return preferred;\n }\n const disambiguated = `${preferred}@${path.posix.dirname(file)}`;\n let id = disambiguated;\n let n = 2;\n while (this.usedIds.has(id)) {\n id = `${disambiguated}~${n}`;\n n++;\n }\n this.usedIds.add(id);\n return id;\n }\n\n /** Scope from the class's own decorator/module binding, if any. */\n private classScope(cls: ts.ClassDeclaration): DiScope {\n const token = classTokenKey(cls, this.workspaceRoot);\n for (const binding of this.table.lookup(token.key)) {\n if (binding.scope !== 'unknown') return binding.scope;\n }\n return 'unknown';\n }\n\n private leafNode(binding: Binding, kind: DiNodeKind): string {\n const existing = this.leafIds.get(binding);\n if (existing) return existing;\n\n // Dynamic values are factory functions — their source text is noise, so label\n // them by the token they satisfy. Constants keep their (short) expression text.\n const className = kind === 'dynamic'\n ? `${binding.tokenDisplay} (dynamic)`\n : binding.valueText !== '' ? binding.valueText : binding.tokenDisplay;\n const id = this.claimId(className, binding.file);\n this.leafIds.set(binding, id);\n this.design.nodes.push(new DiNode(id, className, kind, binding.scope, binding.file));\n return id;\n }\n\n private unresolvedNode(display: string, file: string): string {\n const key = `${display} ${file}`;\n const existing = this.unresolvedIds.get(key);\n if (existing) return existing;\n\n const id = this.claimId(display, file);\n this.unresolvedIds.set(key, id);\n this.design.nodes.push(new DiNode(id, display, 'unresolved', 'unknown', file));\n this.design.unresolved.push(display);\n return id;\n }\n\n walkClass(cls: ts.ClassDeclaration): void {\n if (this.visited.has(cls)) return;\n this.visited.add(cls);\n\n const ctor = findConstructor(cls);\n if (!ctor) return;\n const fromId = this.classNode(cls);\n\n for (const param of ctor.parameters) {\n this.walkParam(fromId, cls, param);\n }\n }\n\n private walkParam(fromId: string, cls: ts.ClassDeclaration, param: ts.ParameterDeclaration): void {\n const injection = readParamDecorators(param);\n if (injection.unmanaged) return;\n\n const paramName = ts.isIdentifier(param.name) ? param.name.text : param.name.getText();\n const paramType = param.type ? param.type.getText() : '';\n\n if (injection.expr) {\n this.walkTokenParam(fromId, cls, injection, paramName, paramType);\n } else {\n this.walkTypeParam(fromId, cls, param, paramName, paramType);\n }\n }\n\n private walkTokenParam(\n fromId: string,\n cls: ts.ClassDeclaration,\n injection: ParamInjection,\n paramName: string,\n paramType: string,\n ): void {\n const expr = injection.expr;\n if (!expr) return;\n const token = resolveTokenKey(expr, this.checker, this.workspaceRoot);\n const kind = injection.multi ? 'multiInject' : 'token';\n const bindings = this.table.lookup(token.key);\n\n if (bindings.length === 0) {\n // @multiInject @optional with zero bindings is legal — no edge, no error.\n if (injection.multi && injection.optional) return;\n const file = relativeFile(this.workspaceRoot, cls.getSourceFile());\n const toId = this.unresolvedNode(token.display, file);\n this.design.edges.push(new DiEdge(fromId, toId, kind, token.display, token.key, paramName, paramType));\n return;\n }\n\n for (const binding of bindings) {\n const toId = this.bindingTarget(binding);\n this.design.edges.push(new DiEdge(fromId, toId, kind, token.display, token.key, paramName, paramType));\n }\n }\n\n private bindingTarget(binding: Binding): string {\n if (binding.implClass) {\n const toId = this.classNode(binding.implClass, binding.scope);\n this.walkClass(binding.implClass);\n return toId;\n }\n if (binding.kind === 'toConstantValue') return this.leafNode(binding, 'constant');\n if (binding.kind === 'toDynamicValue') return this.leafNode(binding, 'dynamic');\n // .to(X) where X did not resolve to a class declaration.\n return this.unresolvedNode(binding.valueText !== '' ? binding.valueText : binding.tokenDisplay, binding.file);\n }\n\n private walkTypeParam(\n fromId: string,\n cls: ts.ClassDeclaration,\n param: ts.ParameterDeclaration,\n paramName: string,\n paramType: string,\n ): void {\n const typeName = param.type && ts.isTypeReferenceNode(param.type) ? param.type.typeName : null;\n const target = typeName && ts.isIdentifier(typeName)\n ? resolveClassDeclaration(typeName, this.checker)\n : null;\n\n if (target) {\n const token = classTokenKey(target, this.workspaceRoot);\n const toId = this.classNode(target);\n this.walkClass(target);\n this.design.edges.push(new DiEdge(fromId, toId, 'type', token.display, token.key, paramName, paramType));\n return;\n }\n\n // Bare param whose type is not a resolvable class (interface, primitive, etc.).\n const file = relativeFile(this.workspaceRoot, cls.getSourceFile());\n const toId = this.unresolvedNode(paramType !== '' ? paramType : paramName, file);\n this.design.edges.push(new DiEdge(fromId, toId, 'type', paramType, `type:${paramType}`, paramName, paramType));\n }\n}\n\nfunction byClassName(a: ts.ClassDeclaration, b: ts.ClassDeclaration): number {\n const nameA = a.name ? a.name.text : '';\n const nameB = b.name ? b.name.text : '';\n if (nameA !== nameB) return nameA < nameB ? -1 : 1;\n return a.getSourceFile().fileName < b.getSourceFile().fileName ? -1 : 1;\n}\n\n/**\n * Library-project roots: DI-registered classes in the project that no other\n * project class injects (tops of the local DAG).\n */\nfunction findLibraryRoots(\n classes: ts.ClassDeclaration[],\n checker: ts.TypeChecker,\n table: BindingTable,\n workspaceRoot: string,\n): ts.ClassDeclaration[] {\n const diClasses = classes.filter((cls: ts.ClassDeclaration) => isDiRegisteredClass(cls));\n const injected = new Set<ts.ClassDeclaration>();\n\n for (const cls of diClasses) {\n const ctor = findConstructor(cls);\n for (const param of ctor?.parameters ?? []) {\n const injection = readParamDecorators(param);\n if (injection.unmanaged) continue;\n if (injection.expr) {\n const token = resolveTokenKey(injection.expr, checker, workspaceRoot);\n for (const binding of table.lookup(token.key)) {\n if (binding.implClass) injected.add(binding.implClass);\n }\n } else if (param.type && ts.isTypeReferenceNode(param.type) && ts.isIdentifier(param.type.typeName)) {\n const target = resolveClassDeclaration(param.type.typeName, checker);\n if (target) injected.add(target);\n }\n }\n }\n\n return diClasses.filter((cls: ts.ClassDeclaration) => !injected.has(cls));\n}\n\n/**\n * Assign each node its BFS shortest-path depth from the design's root (root =\n * level 0, its direct injections = level 1, ...) and record the deepest level.\n * A node reached from multiple parents keeps its SHALLOWEST depth. Cycles are\n * safe: a node is levelled once, the first (shallowest) time BFS reaches it.\n */\nfunction assignLevels(design: DiDesign): void {\n const outgoing = new Map<string, string[]>();\n for (const e of design.edges) {\n const list = outgoing.get(e.from) ?? [];\n list.push(e.to);\n outgoing.set(e.from, list);\n }\n const nodeById = new Map<string, DiNode>(design.nodes.map((n: DiNode) => [n.id, n]));\n const levelById = new Map<string, number>();\n\n let frontier = [design.root];\n let level = 0;\n while (frontier.length > 0) {\n const next: string[] = [];\n for (const id of frontier) {\n if (levelById.has(id)) continue;\n levelById.set(id, level);\n for (const to of outgoing.get(id) ?? []) {\n if (!levelById.has(to)) next.push(to);\n }\n }\n frontier = next;\n level++;\n }\n\n let maxLevel = 0;\n for (const node of design.nodes) {\n node.level = levelById.get(node.id) ?? 0;\n if (node.level > maxLevel) maxLevel = node.level;\n }\n design.maxLevel = maxLevel;\n}\n\n/** Build one self-contained downward design tree for a single root class. */\nfunction buildDesign(\n root: ts.ClassDeclaration,\n checker: ts.TypeChecker,\n table: BindingTable,\n workspaceRoot: string,\n): DiDesign {\n const className = root.name ? root.name.text : '<anonymous>';\n const rootKind: DiNodeKind = isControllerClass(root) ? 'controller' : 'class';\n const file = relativeFile(workspaceRoot, root.getSourceFile());\n const design = new DiDesign(className, rootKind, file);\n const builder = new DiDesignBuilder(checker, table, workspaceRoot, design);\n builder.addRoot(root);\n assignLevels(design);\n return design;\n}\n\n/**\n * Build the full DI graph for one project: one self-contained `DiDesign` per\n * root — every @Controller class, or (when the project has no controllers)\n * every top-of-DAG DI class. `projectRoot` is workspace-relative.\n */\nexport function buildDiGraph(\n program: ts.Program,\n workspaceRoot: string,\n projectRoot: string,\n projectName: string,\n): DiGraph {\n const checker = program.getTypeChecker();\n const table = collectBindings(program, checker, workspaceRoot);\n const graph = new DiGraph(projectName);\n\n const classes = projectClasses(program, workspaceRoot, projectRoot);\n const controllers = classes.filter((cls: ts.ClassDeclaration) => isControllerClass(cls));\n const roots = controllers.length > 0\n ? controllers\n : findLibraryRoots(classes, checker, table, workspaceRoot);\n\n for (const root of [...roots].sort(byClassName)) {\n graph.designs.push(buildDesign(root, checker, table, workspaceRoot));\n }\n\n return graph;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"analyzer.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/nx-webpieces-rules/src/lib/di-graph/analyzer.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * DI Graph Analyzer (pass 2)\n *\n * Walks constructor injection from a project's root classes down to leaves:\n *\n * - @inject(TOKEN) params → token lookup in the binding table → bound impl\n * - @multiInject(TOKEN) → fan-out edge to EVERY binding of that token\n * - bare typed params → checker resolves the type to a class (inject-by-type)\n * - toConstantValue/toDynamicValue bindings → leaf nodes, no recursion\n * - unresolvable tokens/types → kind \"unresolved\" nodes (generation never fails)\n *\n * Roots: @Controller() classes when the project has any; otherwise every\n * DI-registered class in the project that no other project class injects\n * (the tops of the local DAG).\n */\n\nimport * as ts from 'typescript';\nimport * as path from 'path';\nimport { Binding, DiDesign, DiEdge, DiGraph, DiNode, DiNodeKind, DiScope } from './model';\nimport {\n BindingTable,\n classDecorators,\n collectBindings,\n decoratorCall,\n decoratorName,\n resolveClassDeclaration,\n} from './bindings';\nimport { classTokenKey, relativeFile, resolveTokenKey } from './token-resolver';\n\nconst DI_DECORATORS = new Set(['provideSingleton', 'provideTransient', 'provideSingletonAs', 'injectable']);\n\nexport class ParamInjection {\n expr: ts.Expression | null;\n multi: boolean;\n optional: boolean;\n unmanaged: boolean;\n\n constructor(expr: ts.Expression | null, multi: boolean, optional: boolean, unmanaged: boolean) {\n this.expr = expr;\n this.multi = multi;\n this.optional = optional;\n this.unmanaged = unmanaged;\n }\n}\n\nexport function readParamDecorators(param: ts.ParameterDeclaration): ParamInjection {\n let expr: ts.Expression | null = null;\n let multi = false;\n let optional = false;\n let unmanaged = false;\n for (const decorator of ts.getDecorators(param) ?? []) {\n const name = decoratorName(decorator);\n const call = decoratorCall(decorator);\n if (name === 'inject' && call?.arguments[0]) {\n expr = call.arguments[0];\n } else if (name === 'multiInject' && call?.arguments[0]) {\n expr = call.arguments[0];\n multi = true;\n } else if (name === 'optional') {\n optional = true;\n } else if (name === 'unmanaged') {\n unmanaged = true;\n }\n }\n return new ParamInjection(expr, multi, optional, unmanaged);\n}\n\nfunction hasDecoratorNamed(cls: ts.ClassDeclaration, names: Set<string>): boolean {\n for (const decorator of classDecorators(cls)) {\n const name = decoratorName(decorator);\n if (name && names.has(name)) return true;\n }\n return false;\n}\n\nexport function isControllerClass(cls: ts.ClassDeclaration): boolean {\n return hasDecoratorNamed(cls, new Set(['Controller']));\n}\n\nfunction isDiRegisteredClass(cls: ts.ClassDeclaration): boolean {\n return hasDecoratorNamed(cls, DI_DECORATORS);\n}\n\nexport function findConstructor(cls: ts.ClassDeclaration): ts.ConstructorDeclaration | null {\n for (const member of cls.members) {\n if (ts.isConstructorDeclaration(member) && member.body) return member;\n if (ts.isConstructorDeclaration(member)) return member;\n }\n return null;\n}\n\n/** All class declarations in files under the project root. */\nexport function projectClasses(\n program: ts.Program,\n workspaceRoot: string,\n projectRoot: string,\n): ts.ClassDeclaration[] {\n const prefix = projectRoot.endsWith('/') ? projectRoot : projectRoot + '/';\n const classes: ts.ClassDeclaration[] = [];\n for (const sourceFile of program.getSourceFiles()) {\n if (sourceFile.isDeclarationFile) continue;\n const rel = relativeFile(workspaceRoot, sourceFile);\n if (!rel.startsWith(prefix)) continue;\n const visit = (node: ts.Node): void => {\n if (ts.isClassDeclaration(node) && node.name) classes.push(node);\n ts.forEachChild(node, visit);\n };\n visit(sourceFile);\n }\n return classes;\n}\n\n/**\n * One normalized injection site, framework-agnostic — produced by a builder's\n * `collectInjections(cls)` hook and processed by the shared base. Inversify\n * emits `token`/`type` from constructor params; Angular emits `angularToken`\n * from constructor params AND `inject()` field initializers.\n *\n * Modes:\n * - `token` Inversify `@inject`/`@multiInject`: table lookup; an unbound\n * token becomes an `unresolved` node (no class fallback).\n * - `type` Inversify bare typed param: resolve the declared TYPE to a\n * class (inject-by-type); no table lookup.\n * - `angularToken` Angular `inject(T)` / `@Inject(T)` / bare typed ctor param:\n * table lookup FIRST (provider table), then fall back to\n * resolving the token expression as a class, else unresolved.\n */\nexport class Injection {\n mode: 'token' | 'type' | 'angularToken';\n /** Token/type expression (modes `token`/`angularToken`, or the type identifier for `type`). */\n expr: ts.Expression | ts.Identifier | null;\n multi: boolean;\n optional: boolean;\n paramName: string;\n paramType: string;\n\n constructor(\n mode: 'token' | 'type' | 'angularToken',\n expr: ts.Expression | ts.Identifier | null,\n paramName: string,\n paramType: string,\n multi = false,\n optional = false,\n ) {\n this.mode = mode;\n this.expr = expr;\n this.paramName = paramName;\n this.paramType = paramType;\n this.multi = multi;\n this.optional = optional;\n }\n}\n\n/**\n * Builds ONE self-contained `DiDesign` for a single root. A fresh instance is\n * created per root so its maps (visited/classIds/usedIds/...) are scoped to\n * that root's tree — a dependency shared by two roots is therefore walked\n * (and duplicated) into each root's design, not hidden under whichever root\n * reached it first.\n *\n * Framework-agnostic base: subclasses implement {@link collectInjections} (and\n * override {@link rootKindOf} for Angular components); everything else — node\n * ids, leaf/unresolved labeling, token→binding resolution, factory-dep edges,\n * level assignment — is shared so Inversify and Angular render identically.\n */\nexport abstract class DiDesignBuilder {\n protected readonly checker: ts.TypeChecker;\n protected readonly table: BindingTable;\n protected readonly workspaceRoot: string;\n protected readonly design: DiDesign;\n private readonly classIds = new Map<ts.ClassDeclaration, string>();\n private readonly leafIds = new Map<Binding, string>();\n private readonly unresolvedIds = new Map<string, string>();\n private readonly usedIds = new Set<string>();\n private readonly visited = new Set<ts.ClassDeclaration>();\n\n constructor(checker: ts.TypeChecker, table: BindingTable, workspaceRoot: string, design: DiDesign) {\n this.checker = checker;\n this.table = table;\n this.workspaceRoot = workspaceRoot;\n this.design = design;\n }\n\n /** Collect the injection sites for one class (constructor params, field inject(), ...). */\n protected abstract collectInjections(cls: ts.ClassDeclaration): Injection[];\n\n /** Node kind for a root/reached class — `controller` (Inversify) or `component` (Angular). */\n protected rootKindOf(cls: ts.ClassDeclaration): DiNodeKind {\n return isControllerClass(cls) ? 'controller' : 'class';\n }\n\n addRoot(cls: ts.ClassDeclaration): void {\n const id = this.classNode(cls);\n this.design.root = id;\n this.walkClass(cls);\n }\n\n /**\n * Register (or fetch) the node for a class, returning its stable id.\n * `scopeHint` carries the scope of the module binding the class was reached\n * through (e.g. bind(TOKEN).to(X).inSingletonScope() where X is not self-bound).\n */\n protected classNode(cls: ts.ClassDeclaration, scopeHint: DiScope = 'unknown'): string {\n const existing = this.classIds.get(cls);\n if (existing) return existing;\n\n const className = cls.name ? cls.name.text : '<anonymous>';\n const file = relativeFile(this.workspaceRoot, cls.getSourceFile());\n const id = this.claimId(className, file);\n this.classIds.set(cls, id);\n\n const declaredScope = this.classScope(cls);\n const scope = declaredScope !== 'unknown' ? declaredScope : scopeHint;\n this.design.nodes.push(new DiNode(id, className, this.rootKindOf(cls), scope, file));\n return id;\n }\n\n private claimId(preferred: string, file: string): string {\n if (!this.usedIds.has(preferred)) {\n this.usedIds.add(preferred);\n return preferred;\n }\n const disambiguated = `${preferred}@${path.posix.dirname(file)}`;\n let id = disambiguated;\n let n = 2;\n while (this.usedIds.has(id)) {\n id = `${disambiguated}~${n}`;\n n++;\n }\n this.usedIds.add(id);\n return id;\n }\n\n /** Scope from the class's own decorator/module binding, if any. */\n private classScope(cls: ts.ClassDeclaration): DiScope {\n const token = classTokenKey(cls, this.workspaceRoot);\n for (const binding of this.table.lookup(token.key)) {\n if (binding.scope !== 'unknown') return binding.scope;\n }\n return 'unknown';\n }\n\n /**\n * Leaf box for a constant/dynamic binding. B0: the box is labeled by the\n * DECLARED param TYPE (e.g. `FirestoreConfig`, `ClientConfig`) — the DI\n * contract — while the bound expression (`buildConfigFromEnv(...)` /\n * `TOKEN (dynamic)`) is kept as `detail`. A dynamic leaf also fans out to\n * each of its `useFactory` `deps` (Angular; empty for Inversify).\n */\n private leafNode(binding: Binding, kind: DiNodeKind, paramType: string): string {\n const existing = this.leafIds.get(binding);\n if (existing) return existing;\n\n const detail = kind === 'dynamic' ? `${binding.tokenDisplay} (dynamic)` : binding.valueText;\n const className = paramType !== '' ? paramType : detail !== '' ? detail : binding.tokenDisplay;\n const id = this.claimId(className, binding.file);\n this.leafIds.set(binding, id);\n this.design.nodes.push(new DiNode(id, className, kind, binding.scope, binding.file, 0, detail));\n if (kind === 'dynamic') this.expandFactoryDeps(id, binding);\n return id;\n }\n\n /** Edges from a `useFactory` leaf to each declared `deps: [...]` token (Angular). */\n private expandFactoryDeps(leafId: string, binding: Binding): void {\n for (const dep of binding.factoryDeps) {\n const bindings = this.table.lookup(dep.key);\n if (bindings.length > 0) {\n for (const depBinding of bindings) {\n // The dep token IS the declared type — label the box with it.\n const toId = this.bindingTarget(depBinding, dep.display);\n this.design.edges.push(new DiEdge(leafId, toId, 'type', dep.display, dep.key, dep.display, dep.display));\n }\n } else {\n const toId = this.unresolvedNode(dep.display, binding.file, '');\n this.design.edges.push(new DiEdge(leafId, toId, 'type', dep.display, dep.key, dep.display, dep.display));\n }\n }\n }\n\n /**\n * `unresolved` placeholder box. B0: labeled by the declared param TYPE\n * (`className`) with the resolving token expression kept as `detail` (and\n * surfaced in `design.unresolved` for diagnostics).\n */\n private unresolvedNode(className: string, file: string, detail: string): string {\n const key = `${className}|${detail}|${file}`;\n const existing = this.unresolvedIds.get(key);\n if (existing) return existing;\n\n const id = this.claimId(className, file);\n this.unresolvedIds.set(key, id);\n this.design.nodes.push(new DiNode(id, className, 'unresolved', 'unknown', file, 0, detail !== className ? detail : ''));\n this.design.unresolved.push(detail !== '' ? detail : className);\n return id;\n }\n\n protected walkClass(cls: ts.ClassDeclaration): void {\n if (this.visited.has(cls)) return;\n this.visited.add(cls);\n\n const fromId = this.classNode(cls);\n for (const injection of this.collectInjections(cls)) {\n this.processInjection(fromId, cls, injection);\n }\n }\n\n /** Resolve one injection to a node and record the edge(s). Never throws. */\n private processInjection(fromId: string, cls: ts.ClassDeclaration, injection: Injection): void {\n if (injection.mode === 'type') {\n this.processTypeInjection(fromId, cls, injection);\n } else {\n this.processTokenInjection(fromId, cls, injection);\n }\n }\n\n /** Inversify `@inject`/`@multiInject` and Angular `inject()`/`@Inject`/bare token. */\n private processTokenInjection(fromId: string, cls: ts.ClassDeclaration, injection: Injection): void {\n const expr = injection.expr;\n if (!expr) return;\n const token = resolveTokenKey(expr, this.checker, this.workspaceRoot);\n const kind = injection.multi ? 'multiInject' : 'token';\n const bindings = this.table.lookup(token.key);\n\n if (bindings.length > 0) {\n for (const binding of bindings) {\n const toId = this.bindingTarget(binding, injection.paramType);\n this.design.edges.push(\n new DiEdge(fromId, toId, kind, token.display, token.key, injection.paramName, injection.paramType),\n );\n }\n return;\n }\n\n // @multiInject @optional with zero bindings is legal — no edge, no error.\n if (injection.multi && injection.optional) return;\n\n // Angular: a token with no explicit provider may still be a bare @Injectable\n // class — resolve the token expression as a class and inject it by type.\n if (injection.mode === 'angularToken') {\n const target = resolveClassDeclaration(expr, this.checker);\n if (target) {\n const classToken = classTokenKey(target, this.workspaceRoot);\n const toId = this.classNode(target);\n this.walkClass(target);\n this.design.edges.push(\n new DiEdge(fromId, toId, 'type', classToken.display, classToken.key, injection.paramName, injection.paramType),\n );\n return;\n }\n }\n\n const file = relativeFile(this.workspaceRoot, cls.getSourceFile());\n const label = injection.paramType !== '' ? injection.paramType : token.display;\n const toId = this.unresolvedNode(label, file, token.display);\n this.design.edges.push(\n new DiEdge(fromId, toId, kind, token.display, token.key, injection.paramName, injection.paramType),\n );\n }\n\n private bindingTarget(binding: Binding, paramType: string): string {\n if (binding.implClass) {\n const toId = this.classNode(binding.implClass, binding.scope);\n this.walkClass(binding.implClass);\n return toId;\n }\n if (binding.kind === 'toConstantValue') return this.leafNode(binding, 'constant', paramType);\n if (binding.kind === 'toDynamicValue') return this.leafNode(binding, 'dynamic', paramType);\n // .to(X) where X did not resolve to a class declaration.\n const detail = binding.valueText !== '' ? binding.valueText : binding.tokenDisplay;\n const label = paramType !== '' ? paramType : detail;\n return this.unresolvedNode(label, binding.file, detail);\n }\n\n /** Inversify bare typed param — resolve the declared type directly to a class. */\n private processTypeInjection(fromId: string, cls: ts.ClassDeclaration, injection: Injection): void {\n const typeRef = injection.expr && ts.isIdentifier(injection.expr) ? injection.expr : null;\n const target = typeRef ? resolveClassDeclaration(typeRef, this.checker) : null;\n\n if (target) {\n const token = classTokenKey(target, this.workspaceRoot);\n const toId = this.classNode(target);\n this.walkClass(target);\n this.design.edges.push(\n new DiEdge(fromId, toId, 'type', token.display, token.key, injection.paramName, injection.paramType),\n );\n return;\n }\n\n // Bare param whose type is not a resolvable class (interface, primitive, etc.).\n const file = relativeFile(this.workspaceRoot, cls.getSourceFile());\n const label = injection.paramType !== '' ? injection.paramType : injection.paramName;\n const toId = this.unresolvedNode(label, file, '');\n this.design.edges.push(\n new DiEdge(fromId, toId, 'type', injection.paramType, `type:${injection.paramType}`, injection.paramName, injection.paramType),\n );\n }\n}\n\n/**\n * Inversify builder: injection sites are constructor params — `@inject`/\n * `@multiInject` tokens or bare typed (inject-by-type) params.\n */\nexport class InversifyDesignBuilder extends DiDesignBuilder {\n protected collectInjections(cls: ts.ClassDeclaration): Injection[] {\n const ctor = findConstructor(cls);\n if (!ctor) return [];\n\n const injections: Injection[] = [];\n for (const param of ctor.parameters) {\n const decorated = readParamDecorators(param);\n if (decorated.unmanaged) continue;\n\n const paramName = ts.isIdentifier(param.name) ? param.name.text : param.name.getText();\n const paramType = param.type ? param.type.getText() : '';\n\n if (decorated.expr) {\n injections.push(\n new Injection('token', decorated.expr, paramName, paramType, decorated.multi, decorated.optional),\n );\n } else {\n const typeRef =\n param.type && ts.isTypeReferenceNode(param.type) && ts.isIdentifier(param.type.typeName)\n ? param.type.typeName\n : null;\n injections.push(new Injection('type', typeRef, paramName, paramType));\n }\n }\n return injections;\n }\n}\n\nexport function byClassName(a: ts.ClassDeclaration, b: ts.ClassDeclaration): number {\n const nameA = a.name ? a.name.text : '';\n const nameB = b.name ? b.name.text : '';\n if (nameA !== nameB) return nameA < nameB ? -1 : 1;\n return a.getSourceFile().fileName < b.getSourceFile().fileName ? -1 : 1;\n}\n\n/**\n * Library-project roots: DI-registered classes in the project that no other\n * project class injects (tops of the local DAG).\n */\nfunction findLibraryRoots(\n classes: ts.ClassDeclaration[],\n checker: ts.TypeChecker,\n table: BindingTable,\n workspaceRoot: string,\n): ts.ClassDeclaration[] {\n const diClasses = classes.filter((cls: ts.ClassDeclaration) => isDiRegisteredClass(cls));\n const injected = new Set<ts.ClassDeclaration>();\n\n for (const cls of diClasses) {\n const ctor = findConstructor(cls);\n for (const param of ctor?.parameters ?? []) {\n const injection = readParamDecorators(param);\n if (injection.unmanaged) continue;\n if (injection.expr) {\n const token = resolveTokenKey(injection.expr, checker, workspaceRoot);\n for (const binding of table.lookup(token.key)) {\n if (binding.implClass) injected.add(binding.implClass);\n }\n } else if (param.type && ts.isTypeReferenceNode(param.type) && ts.isIdentifier(param.type.typeName)) {\n const target = resolveClassDeclaration(param.type.typeName, checker);\n if (target) injected.add(target);\n }\n }\n }\n\n return diClasses.filter((cls: ts.ClassDeclaration) => !injected.has(cls));\n}\n\n/**\n * Assign each node its BFS shortest-path depth from the design's root (root =\n * level 0, its direct injections = level 1, ...) and record the deepest level.\n * A node reached from multiple parents keeps its SHALLOWEST depth. Cycles are\n * safe: a node is levelled once, the first (shallowest) time BFS reaches it.\n */\nexport function assignLevels(design: DiDesign): void {\n const outgoing = new Map<string, string[]>();\n for (const e of design.edges) {\n const list = outgoing.get(e.from) ?? [];\n list.push(e.to);\n outgoing.set(e.from, list);\n }\n const nodeById = new Map<string, DiNode>(design.nodes.map((n: DiNode) => [n.id, n]));\n const levelById = new Map<string, number>();\n\n let frontier = [design.root];\n let level = 0;\n while (frontier.length > 0) {\n const next: string[] = [];\n for (const id of frontier) {\n if (levelById.has(id)) continue;\n levelById.set(id, level);\n for (const to of outgoing.get(id) ?? []) {\n if (!levelById.has(to)) next.push(to);\n }\n }\n frontier = next;\n level++;\n }\n\n let maxLevel = 0;\n for (const node of design.nodes) {\n node.level = levelById.get(node.id) ?? 0;\n if (node.level > maxLevel) maxLevel = node.level;\n }\n design.maxLevel = maxLevel;\n}\n\n/**\n * Build one self-contained downward design tree for a single root class, using\n * the builder produced by `makeBuilder` (Inversify or Angular). `rootKind` is\n * the node kind for the root box (`controller`/`component`/`class`).\n */\nexport function buildDesign(\n root: ts.ClassDeclaration,\n rootKind: DiNodeKind,\n workspaceRoot: string,\n makeBuilder: (design: DiDesign) => DiDesignBuilder,\n): DiDesign {\n const className = root.name ? root.name.text : '<anonymous>';\n const file = relativeFile(workspaceRoot, root.getSourceFile());\n const design = new DiDesign(className, rootKind, file);\n makeBuilder(design).addRoot(root);\n assignLevels(design);\n return design;\n}\n\n/**\n * Build the full Inversify DI graph for one project: one self-contained\n * `DiDesign` per @Controller root. `projectRoot` is workspace-relative.\n *\n * `includeLibraryRoots` (default false, the v1 executor path) roots ONLY on\n * @Controller classes; when true, a controller-less project falls back to its\n * top-of-DAG DI classes (the deferred library behavior, still unit-tested).\n */\nexport function buildDiGraph(\n program: ts.Program,\n workspaceRoot: string,\n projectRoot: string,\n projectName: string,\n includeLibraryRoots = false,\n): DiGraph {\n const checker = program.getTypeChecker();\n const table = collectBindings(program, checker, workspaceRoot);\n const graph = new DiGraph(projectName);\n\n const classes = projectClasses(program, workspaceRoot, projectRoot);\n const controllers = classes.filter((cls: ts.ClassDeclaration) => isControllerClass(cls));\n const roots =\n controllers.length > 0\n ? controllers\n : includeLibraryRoots\n ? findLibraryRoots(classes, checker, table, workspaceRoot)\n : [];\n\n for (const root of [...roots].sort(byClassName)) {\n const rootKind: DiNodeKind = isControllerClass(root) ? 'controller' : 'class';\n graph.designs.push(\n buildDesign(\n root,\n rootKind,\n workspaceRoot,\n (design: DiDesign) => new InversifyDesignBuilder(checker, table, workspaceRoot, design),\n ),\n );\n }\n\n return graph;\n}\n"]}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Angular DI Analyzer (pass 2, Angular flavor)
|
|
3
|
+
*
|
|
4
|
+
* Renders each Angular entry component's injection tree "from the page/root
|
|
5
|
+
* component on down". Reuses the shared {@link DiDesignBuilder} base (node ids,
|
|
6
|
+
* leaf/unresolved labeling, provider-table resolution, factory-dep edges, level
|
|
7
|
+
* assignment) via its `collectInjections` hook — only the front-half (roots,
|
|
8
|
+
* providers, injection-site discovery) is Angular-specific.
|
|
9
|
+
*
|
|
10
|
+
* Angular injection sites per class:
|
|
11
|
+
* 1. Constructor params — `@Inject(TOKEN)` (capital I), `forwardRef(() => X)`,
|
|
12
|
+
* `@Optional`/`@Self`/`@SkipSelf`/`@Host`, or a bare typed param (the class
|
|
13
|
+
* itself is the token).
|
|
14
|
+
* 2. Field initializers calling `inject()` — the standalone pattern
|
|
15
|
+
* (`private saveApi = inject(SaveApi)`); the field name is the edge label
|
|
16
|
+
* and the declared/token type labels the box.
|
|
17
|
+
*
|
|
18
|
+
* Every site is an `angularToken` injection: the provider table is consulted
|
|
19
|
+
* first, then the token expression is resolved as a bare `@Injectable` class,
|
|
20
|
+
* else it becomes an `unresolved` node. Generation never fails.
|
|
21
|
+
*/
|
|
22
|
+
import * as ts from 'typescript';
|
|
23
|
+
import { DiGraph } from './model';
|
|
24
|
+
/**
|
|
25
|
+
* Build the full Angular DI graph for one project: one self-contained `DiDesign`
|
|
26
|
+
* per entry component (bootstrap + routed). `projectRoot` is workspace-relative.
|
|
27
|
+
*/
|
|
28
|
+
export declare function buildAngularDiGraph(program: ts.Program, workspaceRoot: string, projectRoot: string, projectName: string): DiGraph;
|