@djodjonx/neosyringe-core 0.1.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 ADDED
@@ -0,0 +1,5 @@
1
+ # @djodjonx/neosyringe-core
2
+
3
+ The core analysis and generation engine for NeoSyringe. Used by the CLI and Plugin.
4
+
5
+ Not intended for direct use. See [Documentation](https://djodjonx.github.io/neosyringe/).
@@ -0,0 +1,462 @@
1
+ //#region rolldown:runtime
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __copyProps = (to, from, except, desc) => {
9
+ if (from && typeof from === "object" || typeof from === "function") {
10
+ for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
11
+ key = keys[i];
12
+ if (!__hasOwnProp.call(to, key) && key !== except) {
13
+ __defProp(to, key, {
14
+ get: ((k) => from[k]).bind(null, key),
15
+ enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
16
+ });
17
+ }
18
+ }
19
+ }
20
+ return to;
21
+ };
22
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
23
+ value: mod,
24
+ enumerable: true
25
+ }) : target, mod));
26
+
27
+ //#endregion
28
+ let typescript = require("typescript");
29
+ typescript = __toESM(typescript);
30
+ let node_crypto = require("node:crypto");
31
+ node_crypto = __toESM(node_crypto);
32
+ let node_path = require("node:path");
33
+ node_path = __toESM(node_path);
34
+
35
+ //#region src/analyzer/Analyzer.ts
36
+ /**
37
+ * Generates a unique, deterministic Token ID for a symbol.
38
+ * Uses the symbol name and a short hash of its relative file path.
39
+ */
40
+ function generateTokenId(symbol, sourceFile) {
41
+ const name = symbol.getName();
42
+ let relativePath = node_path.relative(process.cwd(), sourceFile.fileName);
43
+ relativePath = relativePath.split(node_path.sep).join("/");
44
+ return `${name}_${node_crypto.createHash("md5").update(relativePath).digest("hex").substring(0, 8)}`;
45
+ }
46
+ /**
47
+ * Analyzes TypeScript source code to extract the dependency injection graph.
48
+ *
49
+ * This class uses the TypeScript Compiler API to:
50
+ * 1. Locate `createContainer` calls.
51
+ * 2. Parse the fluent chain of `bind` and `register` calls.
52
+ * 3. Resolve symbols and types for services and their dependencies.
53
+ * 4. Build a `DependencyGraph` representing the system.
54
+ */
55
+ var Analyzer = class {
56
+ program;
57
+ checker;
58
+ /** Set of variable names that are parent containers (should not be added to main graph) */
59
+ parentContainerNames = /* @__PURE__ */ new Set();
60
+ /**
61
+ * Creates a new Analyzer instance.
62
+ * @param program - The TypeScript Program instance containing the source files to analyze.
63
+ */
64
+ constructor(program) {
65
+ this.program = program;
66
+ this.checker = program.getTypeChecker();
67
+ }
68
+ /**
69
+ * Extracts the dependency graph from the program's source files.
70
+ *
71
+ * It scans all non-declaration source files for container configurations.
72
+ *
73
+ * @returns A `DependencyGraph` containing all registered services and their dependencies.
74
+ */
75
+ extract() {
76
+ const graph = {
77
+ nodes: /* @__PURE__ */ new Map(),
78
+ roots: [],
79
+ buildArguments: []
80
+ };
81
+ for (const sourceFile of this.program.getSourceFiles()) {
82
+ if (sourceFile.isDeclarationFile) continue;
83
+ this.identifyParentContainers(sourceFile);
84
+ }
85
+ for (const sourceFile of this.program.getSourceFiles()) {
86
+ if (sourceFile.isDeclarationFile) continue;
87
+ this.visitNode(sourceFile, graph);
88
+ }
89
+ this.resolveAllDependencies(graph);
90
+ return graph;
91
+ }
92
+ /**
93
+ * First pass: identify containers used as parents so we can skip them in visitNode.
94
+ */
95
+ identifyParentContainers(node) {
96
+ if (typescript.isPropertyAssignment(node) && typescript.isIdentifier(node.name) && node.name.text === "useContainer" && typescript.isIdentifier(node.initializer)) this.parentContainerNames.add(node.initializer.text);
97
+ typescript.forEachChild(node, (child) => this.identifyParentContainers(child));
98
+ }
99
+ /**
100
+ * Visits an AST node to find container calls.
101
+ * @param node - The AST node to visit.
102
+ * @param graph - The graph to populate.
103
+ */
104
+ visitNode(node, graph) {
105
+ if (typescript.isCallExpression(node)) {
106
+ if (this.isDefineBuilderConfigCall(node)) {
107
+ const parent = node.parent;
108
+ if (typescript.isVariableDeclaration(parent) && typescript.isIdentifier(parent.name)) {
109
+ if (this.parentContainerNames.has(parent.name.text)) return;
110
+ graph.exportedVariableName = parent.name.text;
111
+ let current = parent;
112
+ while (current && !typescript.isVariableStatement(current)) current = current.parent;
113
+ if (current && typescript.isVariableStatement(current)) graph.variableStatementStart = current.getStart();
114
+ }
115
+ graph.defineBuilderConfigStart = node.getStart();
116
+ graph.defineBuilderConfigEnd = node.getEnd();
117
+ this.parseBuilderConfig(node, graph);
118
+ }
119
+ }
120
+ typescript.forEachChild(node, (child) => this.visitNode(child, graph));
121
+ }
122
+ isDefineBuilderConfigCall(node) {
123
+ const expression = node.expression;
124
+ if (typescript.isIdentifier(expression)) return expression.text === "defineBuilderConfig";
125
+ return false;
126
+ }
127
+ parseBuilderConfig(node, graph) {
128
+ const args = node.arguments;
129
+ if (args.length < 1) return;
130
+ const configObj = args[0];
131
+ if (!typescript.isObjectLiteralExpression(configObj)) return;
132
+ const nameProp = configObj.properties.find((p) => p.name && typescript.isIdentifier(p.name) && p.name.text === "name");
133
+ if (nameProp && typescript.isPropertyAssignment(nameProp) && typescript.isStringLiteral(nameProp.initializer)) graph.containerName = nameProp.initializer.text;
134
+ const injectionsProp = configObj.properties.find((p) => p.name && typescript.isIdentifier(p.name) && p.name.text === "injections");
135
+ if (injectionsProp && typescript.isPropertyAssignment(injectionsProp) && typescript.isArrayLiteralExpression(injectionsProp.initializer)) this.parseInjectionsArray(injectionsProp.initializer, graph);
136
+ const extendsProp = configObj.properties.find((p) => p.name && typescript.isIdentifier(p.name) && p.name.text === "extends");
137
+ if (extendsProp && typescript.isPropertyAssignment(extendsProp) && typescript.isArrayLiteralExpression(extendsProp.initializer)) this.parseExtendsArray(extendsProp.initializer, graph);
138
+ const useContainerProp = configObj.properties.find((p) => p.name && typescript.isIdentifier(p.name) && p.name.text === "useContainer");
139
+ if (useContainerProp && typescript.isPropertyAssignment(useContainerProp)) {
140
+ if (!graph.legacyContainers) graph.legacyContainers = [];
141
+ if (!graph.parentProvidedTokens) graph.parentProvidedTokens = /* @__PURE__ */ new Set();
142
+ const containerExpr = useContainerProp.initializer;
143
+ if (typescript.isIdentifier(containerExpr)) {
144
+ graph.legacyContainers.push(containerExpr.text);
145
+ this.extractParentContainerTokens(containerExpr, graph);
146
+ }
147
+ }
148
+ }
149
+ /**
150
+ * Extracts tokens provided by a parent container.
151
+ * Handles both NeoSyringe containers (defineBuilderConfig) and
152
+ * declared legacy containers (declareContainerTokens).
153
+ */
154
+ extractParentContainerTokens(containerIdentifier, graph) {
155
+ const symbol = this.checker.getSymbolAtLocation(containerIdentifier);
156
+ if (!symbol) return;
157
+ this.parentContainerNames.add(containerIdentifier.text);
158
+ const resolvedSymbol = this.resolveSymbol(symbol);
159
+ const declaration = resolvedSymbol.valueDeclaration ?? resolvedSymbol.declarations?.[0];
160
+ if (!declaration) return;
161
+ if (typescript.isVariableDeclaration(declaration) && declaration.initializer) {
162
+ const init = declaration.initializer;
163
+ if (typescript.isCallExpression(init)) {
164
+ if (this.isDefineBuilderConfigCall(init) || this.isDefinePartialConfigCall(init)) {
165
+ const parentGraph = {
166
+ nodes: /* @__PURE__ */ new Map(),
167
+ roots: []
168
+ };
169
+ this.parseBuilderConfig(init, parentGraph);
170
+ for (const tokenId of parentGraph.nodes.keys()) graph.parentProvidedTokens.add(tokenId);
171
+ if (parentGraph.parentProvidedTokens) for (const tokenId of parentGraph.parentProvidedTokens) graph.parentProvidedTokens.add(tokenId);
172
+ return;
173
+ }
174
+ if (this.isDeclareContainerTokensCall(init)) {
175
+ this.extractDeclaredTokens(init, graph);
176
+ return;
177
+ }
178
+ }
179
+ }
180
+ }
181
+ /**
182
+ * Checks if a call expression is declareContainerTokens<T>().
183
+ */
184
+ isDeclareContainerTokensCall(node) {
185
+ if (typescript.isIdentifier(node.expression)) return node.expression.text === "declareContainerTokens";
186
+ return false;
187
+ }
188
+ /**
189
+ * Extracts tokens from declareContainerTokens<{ Token: Type }>().
190
+ * The type argument contains the token names.
191
+ */
192
+ extractDeclaredTokens(node, graph) {
193
+ if (!node.typeArguments || node.typeArguments.length === 0) return;
194
+ const typeArg = node.typeArguments[0];
195
+ const properties = this.checker.getTypeFromTypeNode(typeArg).getProperties();
196
+ for (const prop of properties) {
197
+ const propType = this.checker.getTypeOfSymbol(prop);
198
+ if (propType) {
199
+ const tokenId = this.getTypeId(propType);
200
+ graph.parentProvidedTokens.add(tokenId);
201
+ } else graph.parentProvidedTokens.add(prop.getName());
202
+ }
203
+ }
204
+ parseExtendsArray(arrayLiteral, graph) {
205
+ for (const element of arrayLiteral.elements) if (typescript.isIdentifier(element)) this.parsePartialConfig(element, graph);
206
+ }
207
+ parsePartialConfig(identifier, graph) {
208
+ const symbol = this.checker.getSymbolAtLocation(identifier);
209
+ if (!symbol) return;
210
+ const resolvedSymbol = this.resolveSymbol(symbol);
211
+ const declaration = resolvedSymbol.valueDeclaration ?? resolvedSymbol.declarations?.[0];
212
+ if (!declaration) return;
213
+ if (typescript.isVariableDeclaration(declaration) && declaration.initializer && typescript.isCallExpression(declaration.initializer)) {
214
+ const callExpr = declaration.initializer;
215
+ if (this.isDefinePartialConfigCall(callExpr)) this.parseBuilderConfig(callExpr, graph);
216
+ }
217
+ if (typescript.isExportSpecifier(declaration)) {}
218
+ }
219
+ isDefinePartialConfigCall(node) {
220
+ const expression = node.expression;
221
+ if (typescript.isIdentifier(expression)) return expression.text === "definePartialConfig";
222
+ return false;
223
+ }
224
+ parseInjectionsArray(arrayLiteral, graph) {
225
+ for (const element of arrayLiteral.elements) if (typescript.isObjectLiteralExpression(element)) this.parseInjectionObject(element, graph);
226
+ }
227
+ parseInjectionObject(obj, graph) {
228
+ let tokenNode;
229
+ let providerNode;
230
+ let lifecycle = "singleton";
231
+ let useFactory = false;
232
+ let isScoped = false;
233
+ for (const prop of obj.properties) {
234
+ if (!typescript.isPropertyAssignment(prop) || !typescript.isIdentifier(prop.name)) continue;
235
+ if (prop.name.text === "token") tokenNode = prop.initializer;
236
+ else if (prop.name.text === "provider") providerNode = prop.initializer;
237
+ else if (prop.name.text === "lifecycle" && typescript.isStringLiteral(prop.initializer)) {
238
+ if (prop.initializer.text === "transient") lifecycle = "transient";
239
+ } else if (prop.name.text === "useFactory") {
240
+ if (prop.initializer.kind === typescript.SyntaxKind.TrueKeyword) useFactory = true;
241
+ } else if (prop.name.text === "scoped") {
242
+ if (prop.initializer.kind === typescript.SyntaxKind.TrueKeyword) isScoped = true;
243
+ }
244
+ }
245
+ if (!tokenNode) return;
246
+ if (providerNode && (typescript.isArrowFunction(providerNode) || typescript.isFunctionExpression(providerNode))) useFactory = true;
247
+ let tokenId;
248
+ let implementationSymbol;
249
+ let tokenSymbol;
250
+ let type = "autowire";
251
+ let isInterfaceToken = false;
252
+ let isValueToken = false;
253
+ let factorySource;
254
+ let resolvedTokenNode = tokenNode;
255
+ const resolved = this.resolveToInitializer(tokenNode);
256
+ if (resolved) resolvedTokenNode = resolved;
257
+ if (typescript.isCallExpression(resolvedTokenNode) && this.isUseInterfaceCall(resolvedTokenNode)) {
258
+ tokenId = this.extractInterfaceTokenId(resolvedTokenNode);
259
+ type = "explicit";
260
+ isInterfaceToken = true;
261
+ } else if (typescript.isCallExpression(resolvedTokenNode) && this.isUsePropertyCall(resolvedTokenNode)) {
262
+ const propertyInfo = this.extractPropertyTokenId(resolvedTokenNode);
263
+ tokenId = propertyInfo.tokenId;
264
+ type = "explicit";
265
+ isValueToken = true;
266
+ if (!providerNode) throw new Error(`useProperty(${propertyInfo.className}, '${propertyInfo.paramName}') requires a provider (factory).`);
267
+ useFactory = true;
268
+ } else {
269
+ const tokenType = this.checker.getTypeAtLocation(tokenNode);
270
+ tokenId = this.getTypeIdFromConstructor(tokenType);
271
+ if (typescript.isIdentifier(tokenNode)) tokenSymbol = this.checker.getSymbolAtLocation(tokenNode);
272
+ }
273
+ if (useFactory && providerNode) {
274
+ factorySource = providerNode.getText();
275
+ type = "factory";
276
+ if (tokenId) {
277
+ if (graph.nodes.has(tokenId) && !isScoped) throw new Error(`Duplicate registration: '${tokenId}' is already registered.`);
278
+ const definition = {
279
+ tokenId,
280
+ tokenSymbol: tokenSymbol ? this.resolveSymbol(tokenSymbol) : void 0,
281
+ registrationNode: obj,
282
+ type: "factory",
283
+ lifecycle,
284
+ isInterfaceToken,
285
+ isValueToken,
286
+ isFactory: true,
287
+ factorySource,
288
+ isScoped
289
+ };
290
+ graph.nodes.set(tokenId, {
291
+ service: definition,
292
+ dependencies: []
293
+ });
294
+ }
295
+ return;
296
+ }
297
+ if (providerNode) {
298
+ implementationSymbol = this.checker.getSymbolAtLocation(providerNode);
299
+ type = "explicit";
300
+ } else if (type === "explicit" && !providerNode) {
301
+ if (typescript.isIdentifier(tokenNode)) {
302
+ implementationSymbol = this.checker.getSymbolAtLocation(tokenNode);
303
+ type = "autowire";
304
+ }
305
+ } else if (typescript.isIdentifier(tokenNode)) {
306
+ implementationSymbol = this.checker.getSymbolAtLocation(tokenNode);
307
+ type = "autowire";
308
+ }
309
+ if (tokenId && implementationSymbol) {
310
+ if (graph.nodes.has(tokenId) && !isScoped) throw new Error(`Duplicate registration: '${tokenId}' is already registered.`);
311
+ const definition = {
312
+ tokenId,
313
+ implementationSymbol: this.resolveSymbol(implementationSymbol),
314
+ tokenSymbol: tokenSymbol ? this.resolveSymbol(tokenSymbol) : void 0,
315
+ registrationNode: obj,
316
+ type,
317
+ lifecycle,
318
+ isInterfaceToken: isInterfaceToken || typescript.isCallExpression(tokenNode) && this.isUseInterfaceCall(tokenNode),
319
+ isScoped
320
+ };
321
+ graph.nodes.set(tokenId, {
322
+ service: definition,
323
+ dependencies: []
324
+ });
325
+ }
326
+ }
327
+ isUseInterfaceCall(node) {
328
+ if (typescript.isIdentifier(node.expression)) return node.expression.text === "useInterface";
329
+ return false;
330
+ }
331
+ isUsePropertyCall(node) {
332
+ if (typescript.isIdentifier(node.expression)) return node.expression.text === "useProperty";
333
+ return false;
334
+ }
335
+ /**
336
+ * Resolves a node to its original initializer expression.
337
+ * Handles identifiers, property access, imports, and unwraps type assertions/casts.
338
+ */
339
+ resolveToInitializer(node) {
340
+ let expr = node;
341
+ while (typescript.isParenthesizedExpression(expr) || typescript.isAsExpression(expr) || typescript.isTypeAssertionExpression(expr) || typescript.isSatisfiesExpression && typescript.isSatisfiesExpression(expr)) expr = expr.expression;
342
+ if (typescript.isCallExpression(expr)) return expr;
343
+ if (typescript.isIdentifier(expr)) return this.resolveIdentifierToInitializer(expr);
344
+ if (typescript.isPropertyAccessExpression(expr)) return this.resolvePropertyAccessToInitializer(expr);
345
+ }
346
+ resolveIdentifierToInitializer(identifier) {
347
+ const symbol = this.checker.getSymbolAtLocation(identifier);
348
+ if (!symbol) return void 0;
349
+ const declarations = this.resolveSymbol(symbol).getDeclarations();
350
+ if (!declarations || declarations.length === 0) return void 0;
351
+ for (const decl of declarations) if (typescript.isVariableDeclaration(decl) && decl.initializer) return this.resolveToInitializer(decl.initializer) ?? decl.initializer;
352
+ }
353
+ resolvePropertyAccessToInitializer(node) {
354
+ const objectInitializer = this.resolveToInitializer(node.expression);
355
+ if (objectInitializer && typescript.isObjectLiteralExpression(objectInitializer)) {
356
+ const propName = node.name.text;
357
+ const prop = objectInitializer.properties.find((p) => p.name && typescript.isIdentifier(p.name) && p.name.text === propName);
358
+ if (prop && typescript.isPropertyAssignment(prop)) return this.resolveToInitializer(prop.initializer) ?? prop.initializer;
359
+ }
360
+ }
361
+ extractPropertyTokenId(node) {
362
+ if (node.arguments.length < 2) throw new Error("useProperty requires two arguments: (Class, paramName)");
363
+ const classArg = node.arguments[0];
364
+ const nameArg = node.arguments[1];
365
+ if (!typescript.isIdentifier(classArg)) throw new Error("useProperty first argument must be a class identifier.");
366
+ if (!typescript.isStringLiteral(nameArg)) throw new Error("useProperty second argument must be a string literal.");
367
+ const className = classArg.text;
368
+ const paramName = nameArg.text;
369
+ return {
370
+ tokenId: `PropertyToken:${className}.${paramName}`,
371
+ className,
372
+ paramName
373
+ };
374
+ }
375
+ extractInterfaceTokenId(node) {
376
+ if (!node.typeArguments || node.typeArguments.length === 0) throw new Error("useInterface must have a type argument.");
377
+ const typeNode = node.typeArguments[0];
378
+ const symbol = this.checker.getTypeFromTypeNode(typeNode).getSymbol();
379
+ if (!symbol) return "AnonymousInterface";
380
+ const declarations = symbol.getDeclarations();
381
+ if (declarations && declarations.length > 0) return generateTokenId(symbol, declarations[0].getSourceFile());
382
+ return symbol.getName();
383
+ }
384
+ getTypeIdFromConstructor(type) {
385
+ const constructSignatures = type.getConstructSignatures();
386
+ let instanceType;
387
+ if (constructSignatures.length > 0) instanceType = constructSignatures[0].getReturnType();
388
+ else instanceType = type;
389
+ return this.getTypeId(instanceType);
390
+ }
391
+ extractScope(optionsNode) {
392
+ for (const prop of optionsNode.properties) if (typescript.isPropertyAssignment(prop) && typescript.isIdentifier(prop.name) && prop.name.text === "scope" && typescript.isStringLiteral(prop.initializer)) {
393
+ if (prop.initializer.text === "transient") return "transient";
394
+ }
395
+ return "singleton";
396
+ }
397
+ /**
398
+ * Resolves dependencies for all nodes in the graph.
399
+ * @param graph - The dependency graph.
400
+ */
401
+ resolveAllDependencies(graph) {
402
+ for (const node of graph.nodes.values()) this.resolveDependencies(node, graph);
403
+ }
404
+ /**
405
+ * Resolves dependencies for a single service node by inspecting its constructor.
406
+ * @param node - The dependency node to resolve.
407
+ */
408
+ resolveDependencies(node, graph) {
409
+ if (node.service.isFactory || node.service.type === "factory") return;
410
+ const symbol = node.service.implementationSymbol;
411
+ if (!symbol) return;
412
+ const declarations = symbol.getDeclarations();
413
+ if (!declarations || declarations.length === 0) return;
414
+ const classDecl = declarations.find((d) => typescript.isClassDeclaration(d));
415
+ if (!classDecl) return;
416
+ const className = classDecl.name?.getText() ?? "Anonymous";
417
+ const constructor = classDecl.members.find((m) => typescript.isConstructorDeclaration(m));
418
+ if (!constructor) return;
419
+ for (const param of constructor.parameters) {
420
+ const paramName = param.name.getText();
421
+ const typeNode = param.type;
422
+ if (!typeNode) continue;
423
+ const type = this.checker.getTypeFromTypeNode(typeNode);
424
+ const propertyTokenId = `PropertyToken:${className}.${paramName}`;
425
+ if (graph.nodes.has(propertyTokenId)) {
426
+ node.dependencies.push(propertyTokenId);
427
+ continue;
428
+ }
429
+ const depTokenId = this.getTypeId(type);
430
+ node.dependencies.push(depTokenId);
431
+ }
432
+ }
433
+ /**
434
+ * Generates a unique Token ID for a given Type.
435
+ * Uses file path hash + name for consistency and collision avoidance.
436
+ *
437
+ * @param type - The TypeScript Type.
438
+ * @returns A string identifier for the token.
439
+ */
440
+ getTypeId(type) {
441
+ const symbol = type.getSymbol();
442
+ if (!symbol) return this.checker.typeToString(type);
443
+ const name = symbol.getName();
444
+ if (name === "__type" || name === "InterfaceToken" || name === "__brand") return this.checker.typeToString(type);
445
+ const declarations = symbol.getDeclarations();
446
+ if (declarations && declarations.length > 0) return generateTokenId(symbol, declarations[0].getSourceFile());
447
+ return symbol.getName();
448
+ }
449
+ /**
450
+ * Resolves a symbol, following aliases if necessary.
451
+ * @param symbol - The symbol to resolve.
452
+ * @returns The resolved symbol.
453
+ */
454
+ resolveSymbol(symbol) {
455
+ if (symbol.flags & typescript.SymbolFlags.Alias) return this.resolveSymbol(this.checker.getAliasedSymbol(symbol));
456
+ return symbol;
457
+ }
458
+ };
459
+
460
+ //#endregion
461
+ exports.Analyzer = Analyzer;
462
+ exports.generateTokenId = generateTokenId;
@@ -0,0 +1,108 @@
1
+ import { a as TokenId, i as ServiceDefinition, n as DependencyNode, r as RegistrationType, t as DependencyGraph } from "../types-DeLOEpiR.cjs";
2
+ import * as ts from "typescript";
3
+
4
+ //#region src/analyzer/Analyzer.d.ts
5
+ /**
6
+ * Generates a unique, deterministic Token ID for a symbol.
7
+ * Uses the symbol name and a short hash of its relative file path.
8
+ */
9
+ declare function generateTokenId(symbol: ts.Symbol, sourceFile: ts.SourceFile): string;
10
+ /**
11
+ * Analyzes TypeScript source code to extract the dependency injection graph.
12
+ *
13
+ * This class uses the TypeScript Compiler API to:
14
+ * 1. Locate `createContainer` calls.
15
+ * 2. Parse the fluent chain of `bind` and `register` calls.
16
+ * 3. Resolve symbols and types for services and their dependencies.
17
+ * 4. Build a `DependencyGraph` representing the system.
18
+ */
19
+ declare class Analyzer {
20
+ private program;
21
+ private checker;
22
+ /** Set of variable names that are parent containers (should not be added to main graph) */
23
+ private parentContainerNames;
24
+ /**
25
+ * Creates a new Analyzer instance.
26
+ * @param program - The TypeScript Program instance containing the source files to analyze.
27
+ */
28
+ constructor(program: ts.Program);
29
+ /**
30
+ * Extracts the dependency graph from the program's source files.
31
+ *
32
+ * It scans all non-declaration source files for container configurations.
33
+ *
34
+ * @returns A `DependencyGraph` containing all registered services and their dependencies.
35
+ */
36
+ extract(): DependencyGraph;
37
+ /**
38
+ * First pass: identify containers used as parents so we can skip them in visitNode.
39
+ */
40
+ private identifyParentContainers;
41
+ /**
42
+ * Visits an AST node to find container calls.
43
+ * @param node - The AST node to visit.
44
+ * @param graph - The graph to populate.
45
+ */
46
+ private visitNode;
47
+ private isDefineBuilderConfigCall;
48
+ private parseBuilderConfig;
49
+ /**
50
+ * Extracts tokens provided by a parent container.
51
+ * Handles both NeoSyringe containers (defineBuilderConfig) and
52
+ * declared legacy containers (declareContainerTokens).
53
+ */
54
+ private extractParentContainerTokens;
55
+ /**
56
+ * Checks if a call expression is declareContainerTokens<T>().
57
+ */
58
+ private isDeclareContainerTokensCall;
59
+ /**
60
+ * Extracts tokens from declareContainerTokens<{ Token: Type }>().
61
+ * The type argument contains the token names.
62
+ */
63
+ private extractDeclaredTokens;
64
+ private parseExtendsArray;
65
+ private parsePartialConfig;
66
+ private isDefinePartialConfigCall;
67
+ private parseInjectionsArray;
68
+ private parseInjectionObject;
69
+ private isUseInterfaceCall;
70
+ private isUsePropertyCall;
71
+ /**
72
+ * Resolves a node to its original initializer expression.
73
+ * Handles identifiers, property access, imports, and unwraps type assertions/casts.
74
+ */
75
+ private resolveToInitializer;
76
+ private resolveIdentifierToInitializer;
77
+ private resolvePropertyAccessToInitializer;
78
+ private extractPropertyTokenId;
79
+ private extractInterfaceTokenId;
80
+ private getTypeIdFromConstructor;
81
+ private extractScope;
82
+ /**
83
+ * Resolves dependencies for all nodes in the graph.
84
+ * @param graph - The dependency graph.
85
+ */
86
+ private resolveAllDependencies;
87
+ /**
88
+ * Resolves dependencies for a single service node by inspecting its constructor.
89
+ * @param node - The dependency node to resolve.
90
+ */
91
+ private resolveDependencies;
92
+ /**
93
+ * Generates a unique Token ID for a given Type.
94
+ * Uses file path hash + name for consistency and collision avoidance.
95
+ *
96
+ * @param type - The TypeScript Type.
97
+ * @returns A string identifier for the token.
98
+ */
99
+ private getTypeId;
100
+ /**
101
+ * Resolves a symbol, following aliases if necessary.
102
+ * @param symbol - The symbol to resolve.
103
+ * @returns The resolved symbol.
104
+ */
105
+ private resolveSymbol;
106
+ }
107
+ //#endregion
108
+ export { Analyzer, DependencyGraph, DependencyNode, RegistrationType, ServiceDefinition, TokenId, generateTokenId };
@@ -0,0 +1,108 @@
1
+ import { a as TokenId, i as ServiceDefinition, n as DependencyNode, r as RegistrationType, t as DependencyGraph } from "../types-EnrcFw9C.mjs";
2
+ import * as ts from "typescript";
3
+
4
+ //#region src/analyzer/Analyzer.d.ts
5
+ /**
6
+ * Generates a unique, deterministic Token ID for a symbol.
7
+ * Uses the symbol name and a short hash of its relative file path.
8
+ */
9
+ declare function generateTokenId(symbol: ts.Symbol, sourceFile: ts.SourceFile): string;
10
+ /**
11
+ * Analyzes TypeScript source code to extract the dependency injection graph.
12
+ *
13
+ * This class uses the TypeScript Compiler API to:
14
+ * 1. Locate `createContainer` calls.
15
+ * 2. Parse the fluent chain of `bind` and `register` calls.
16
+ * 3. Resolve symbols and types for services and their dependencies.
17
+ * 4. Build a `DependencyGraph` representing the system.
18
+ */
19
+ declare class Analyzer {
20
+ private program;
21
+ private checker;
22
+ /** Set of variable names that are parent containers (should not be added to main graph) */
23
+ private parentContainerNames;
24
+ /**
25
+ * Creates a new Analyzer instance.
26
+ * @param program - The TypeScript Program instance containing the source files to analyze.
27
+ */
28
+ constructor(program: ts.Program);
29
+ /**
30
+ * Extracts the dependency graph from the program's source files.
31
+ *
32
+ * It scans all non-declaration source files for container configurations.
33
+ *
34
+ * @returns A `DependencyGraph` containing all registered services and their dependencies.
35
+ */
36
+ extract(): DependencyGraph;
37
+ /**
38
+ * First pass: identify containers used as parents so we can skip them in visitNode.
39
+ */
40
+ private identifyParentContainers;
41
+ /**
42
+ * Visits an AST node to find container calls.
43
+ * @param node - The AST node to visit.
44
+ * @param graph - The graph to populate.
45
+ */
46
+ private visitNode;
47
+ private isDefineBuilderConfigCall;
48
+ private parseBuilderConfig;
49
+ /**
50
+ * Extracts tokens provided by a parent container.
51
+ * Handles both NeoSyringe containers (defineBuilderConfig) and
52
+ * declared legacy containers (declareContainerTokens).
53
+ */
54
+ private extractParentContainerTokens;
55
+ /**
56
+ * Checks if a call expression is declareContainerTokens<T>().
57
+ */
58
+ private isDeclareContainerTokensCall;
59
+ /**
60
+ * Extracts tokens from declareContainerTokens<{ Token: Type }>().
61
+ * The type argument contains the token names.
62
+ */
63
+ private extractDeclaredTokens;
64
+ private parseExtendsArray;
65
+ private parsePartialConfig;
66
+ private isDefinePartialConfigCall;
67
+ private parseInjectionsArray;
68
+ private parseInjectionObject;
69
+ private isUseInterfaceCall;
70
+ private isUsePropertyCall;
71
+ /**
72
+ * Resolves a node to its original initializer expression.
73
+ * Handles identifiers, property access, imports, and unwraps type assertions/casts.
74
+ */
75
+ private resolveToInitializer;
76
+ private resolveIdentifierToInitializer;
77
+ private resolvePropertyAccessToInitializer;
78
+ private extractPropertyTokenId;
79
+ private extractInterfaceTokenId;
80
+ private getTypeIdFromConstructor;
81
+ private extractScope;
82
+ /**
83
+ * Resolves dependencies for all nodes in the graph.
84
+ * @param graph - The dependency graph.
85
+ */
86
+ private resolveAllDependencies;
87
+ /**
88
+ * Resolves dependencies for a single service node by inspecting its constructor.
89
+ * @param node - The dependency node to resolve.
90
+ */
91
+ private resolveDependencies;
92
+ /**
93
+ * Generates a unique Token ID for a given Type.
94
+ * Uses file path hash + name for consistency and collision avoidance.
95
+ *
96
+ * @param type - The TypeScript Type.
97
+ * @returns A string identifier for the token.
98
+ */
99
+ private getTypeId;
100
+ /**
101
+ * Resolves a symbol, following aliases if necessary.
102
+ * @param symbol - The symbol to resolve.
103
+ * @returns The resolved symbol.
104
+ */
105
+ private resolveSymbol;
106
+ }
107
+ //#endregion
108
+ export { Analyzer, DependencyGraph, DependencyNode, RegistrationType, ServiceDefinition, TokenId, generateTokenId };