@webpieces/nx-webpieces-rules 0.4.391 → 0.4.392
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
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@webpieces/nx-webpieces-rules",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.392",
|
|
4
4
|
"description": "Nx-specific webpieces validation rules and graph tooling. Bundles all @webpieces rule packages with Nx graph validators and an inference plugin.",
|
|
5
5
|
"type": "commonjs",
|
|
6
6
|
"main": "./src/index.js",
|
|
@@ -21,11 +21,11 @@
|
|
|
21
21
|
"README.md"
|
|
22
22
|
],
|
|
23
23
|
"dependencies": {
|
|
24
|
-
"@webpieces/ai-hook-rules": "0.4.
|
|
25
|
-
"@webpieces/code-rules": "0.4.
|
|
26
|
-
"@webpieces/eslint-rules": "0.4.
|
|
27
|
-
"@webpieces/pr-gate": "0.4.
|
|
28
|
-
"@webpieces/rules-config": "0.4.
|
|
24
|
+
"@webpieces/ai-hook-rules": "0.4.392",
|
|
25
|
+
"@webpieces/code-rules": "0.4.392",
|
|
26
|
+
"@webpieces/eslint-rules": "0.4.392",
|
|
27
|
+
"@webpieces/pr-gate": "0.4.392",
|
|
28
|
+
"@webpieces/rules-config": "0.4.392",
|
|
29
29
|
"madge": "8.0.0"
|
|
30
30
|
},
|
|
31
31
|
"peerDependencies": {
|
|
@@ -228,8 +228,50 @@ function classDecorators(cls) {
|
|
|
228
228
|
const decorators = ts.getDecorators(cls);
|
|
229
229
|
return decorators ? [...decorators] : [];
|
|
230
230
|
}
|
|
231
|
+
/**
|
|
232
|
+
* Scope of an `@injectable(bindingScopeValues.Singleton|Transient)` SELF-binding, read from the
|
|
233
|
+
* decorator ARGUMENT (either `bindingScopeValues.Singleton` or the raw 'Singleton'/'Transient'
|
|
234
|
+
* string), NOT the decorator name like `@provideSingleton`.
|
|
235
|
+
*
|
|
236
|
+
* Returns null for a BARE `@injectable()` (no scope argument): that is NOT a self-binding — it just
|
|
237
|
+
* marks a class injectable for an explicit `bind(TOKEN).to(ThatClass)` elsewhere (e.g. SimpleCounter
|
|
238
|
+
* bound to a Counter token). Only `@injectable(<scope>)` with an argument opts a concrete class into
|
|
239
|
+
* autobind self-binding, the inject-by-type replacement for @provideSingleton.
|
|
240
|
+
*/
|
|
241
|
+
// webpieces-disable no-function-outside-class -- pure AST predicate, matching every sibling in this file
|
|
242
|
+
function injectableScope(decorator) {
|
|
243
|
+
const call = decoratorCall(decorator);
|
|
244
|
+
const arg = call?.arguments[0];
|
|
245
|
+
if (!arg)
|
|
246
|
+
return null;
|
|
247
|
+
const scopeName = ts.isPropertyAccessExpression(arg)
|
|
248
|
+
? arg.name.text
|
|
249
|
+
: ts.isStringLiteral(arg)
|
|
250
|
+
? arg.text
|
|
251
|
+
: '';
|
|
252
|
+
if (scopeName === 'Singleton')
|
|
253
|
+
return 'singleton';
|
|
254
|
+
if (scopeName === 'Transient')
|
|
255
|
+
return 'transient';
|
|
256
|
+
return null;
|
|
257
|
+
}
|
|
231
258
|
function collectDecoratorBindings(cls, checker, workspaceRoot, table) {
|
|
232
259
|
const file = (0, token_resolver_1.relativeFile)(workspaceRoot, cls.getSourceFile());
|
|
260
|
+
// A provide* decorator is the explicit binding; when present it wins over a plain @injectable on
|
|
261
|
+
// the same class (a transitional state where both appear). Only a class with @injectable and NO
|
|
262
|
+
// provide* decorator self-binds via autobind.
|
|
263
|
+
const PROVIDE_BINDERS = new Set([
|
|
264
|
+
'provideSingleton',
|
|
265
|
+
'provideTransient',
|
|
266
|
+
'provideFrameworkSingleton',
|
|
267
|
+
'provideFrameworkTransient',
|
|
268
|
+
'provideSingletonDefaultForApi',
|
|
269
|
+
'provideFrameworkSingletonDefaultForApi',
|
|
270
|
+
]);
|
|
271
|
+
const hasProvideBinding = classDecorators(cls).some((d) => {
|
|
272
|
+
const n = decoratorName(d);
|
|
273
|
+
return n !== null && PROVIDE_BINDERS.has(n);
|
|
274
|
+
});
|
|
233
275
|
for (const decorator of classDecorators(cls)) {
|
|
234
276
|
const name = decoratorName(decorator);
|
|
235
277
|
// provideFrameworkSingleton(As)/Transient are the framework-registry twins of
|
|
@@ -252,6 +294,16 @@ function collectDecoratorBindings(cls, checker, workspaceRoot, table) {
|
|
|
252
294
|
const token = (0, token_resolver_1.resolveTokenKey)(tokenExpr, checker, workspaceRoot);
|
|
253
295
|
table.add(new model_1.Binding(token.key, token.display, 'decorator', 'singleton', cls, '', file));
|
|
254
296
|
}
|
|
297
|
+
else if (name === 'injectable' && !hasProvideBinding) {
|
|
298
|
+
// @injectable(bindingScopeValues.Singleton|Transient) self-binds the concrete class under
|
|
299
|
+
// the app container's autobind (the inject-by-type replacement for @provideSingleton).
|
|
300
|
+
// A bare @injectable() (scope null) is skipped — it is bound via an explicit .to(token).
|
|
301
|
+
const scope = injectableScope(decorator);
|
|
302
|
+
if (scope !== null) {
|
|
303
|
+
const token = (0, token_resolver_1.classTokenKey)(cls, workspaceRoot);
|
|
304
|
+
table.add(new model_1.Binding(token.key, token.display, 'decorator', scope, cls, '', file));
|
|
305
|
+
}
|
|
306
|
+
}
|
|
255
307
|
}
|
|
256
308
|
}
|
|
257
309
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bindings.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/nx-webpieces-rules/src/lib/di-graph/bindings.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Binding Table (pass 1 of the DI graph analyzer)\n *\n * Scans every source file in the project's TypeScript program (skipping .d.ts and\n * node_modules) and collects all Inversify bindings into a Map<tokenKey, Binding[]>:\n *\n * - ContainerModule bodies: bind(TOKEN).to(Impl) / .toSelf() / .toConstantValue(x)\n * / .toDynamicValue(fn), with .inSingletonScope() etc.\n * - Decorators: @provideSingleton() / @provideTransient() (self-binding)\n * and @provideSingletonDefaultForApi(TOKEN)\n *\n * Arrays because multiInject tokens (e.g. HEADER_TYPES.PlatformHeadersExtension) are\n * bound once per ContainerModule across several packages.\n */\n\nimport * as ts from 'typescript';\nimport { Binding, BindingKind, DiScope } from './model';\nimport { classTokenKey, relativeFile, resolveTokenKey } from './token-resolver';\n\nconst BIND_METHOD_NAMES = new Set(['to', 'toSelf', 'toConstantValue', 'toDynamicValue']);\n\nexport class BindingTable {\n private readonly byToken = new Map<string, Binding[]>();\n /** providerTokenKey -> the class its get() resolves. See bindFrameworkProvider. */\n private readonly providerTargets = new Map<string, ts.ClassDeclaration>();\n\n add(binding: Binding): void {\n const list = this.byToken.get(binding.tokenKey);\n if (list) {\n list.push(binding);\n } else {\n this.byToken.set(binding.tokenKey, [binding]);\n }\n }\n\n lookup(tokenKey: string): Binding[] {\n return this.byToken.get(tokenKey) ?? [];\n }\n\n /** Record `bindFrameworkProvider(ProviderClass, TargetClass)`. */\n addProviderTarget(providerTokenKey: string, target: ts.ClassDeclaration): void {\n this.providerTargets.set(providerTokenKey, target);\n }\n\n /**\n * The class a Provider hands out, if this token is a registered Provider subclass.\n * The walker follows this so `Factory -> XProvider -> X` is visible in the design,\n * instead of the provider dead-ending as an opaque toDynamicValue leaf.\n */\n providerTarget(providerTokenKey: string): ts.ClassDeclaration | undefined {\n return this.providerTargets.get(providerTokenKey);\n }\n}\n\nfunction isAnalyzableFile(sourceFile: ts.SourceFile): boolean {\n if (sourceFile.isDeclarationFile) return false;\n if (sourceFile.fileName.includes('/node_modules/')) return false;\n return true;\n}\n\n/**\n * A class the walker treats as an EXTERNAL boundary: its declaration lives in a\n * `.d.ts` file or under `node_modules` — i.e. a published package outside this\n * nx workspace. The exact inverse of {@link isAnalyzableFile}'s file test. In an\n * nx monorepo internal libs resolve through tsconfig path mappings to real `.ts`\n * source, so they are NOT external and keep expanding; only third-party packages\n * (resolved to `.d.ts` in `node_modules`) trip this. Pass-2 renders such a class\n * as a leaf `external` node and stops — it does not descend into its ctor deps.\n */\nexport function isExternalClass(cls: ts.ClassDeclaration): boolean {\n const sourceFile = cls.getSourceFile();\n return sourceFile.isDeclarationFile || sourceFile.fileName.includes('/node_modules/');\n}\n\n/**\n * Walk `.inSingletonScope()` / `.inTransientScope()` suffixes above a binding call.\n *\n * No scope call at all means TRANSIENT, not \"unknown\": that is inversify's\n * `DEFAULT_DEFAULT_SCOPE`, and no `new Container(...)` in this workspace overrides `defaultScope`.\n */\nfunction scopeFromChain(bindingCall: ts.CallExpression): DiScope {\n let node: ts.Node = bindingCall;\n while (\n node.parent &&\n ts.isPropertyAccessExpression(node.parent) &&\n node.parent.parent &&\n ts.isCallExpression(node.parent.parent)\n ) {\n const methodName = node.parent.name.text;\n if (methodName === 'inSingletonScope') return 'singleton';\n if (methodName === 'inTransientScope') return 'transient';\n node = node.parent.parent;\n }\n return 'transient';\n}\n\n/**\n * If `expr` is (or resolves through the checker to) a class declaration, return it.\n */\nexport function resolveClassDeclaration(\n expr: ts.Expression,\n checker: ts.TypeChecker,\n): ts.ClassDeclaration | null {\n let symbol = checker.getSymbolAtLocation(expr);\n if (symbol && (symbol.flags & ts.SymbolFlags.Alias) !== 0) {\n symbol = checker.getAliasedSymbol(symbol);\n }\n for (const decl of symbol?.declarations ?? []) {\n if (ts.isClassDeclaration(decl)) return decl;\n }\n return null;\n}\n\n/**\n * Recognize `bind(TOKEN)` at the bottom of a fluent chain. Accepts a bare `bind(...)`\n * identifier call or `options.bind(...)` property call.\n */\nfunction asBindCall(expr: ts.Expression): ts.CallExpression | null {\n if (!ts.isCallExpression(expr)) return null;\n const callee = expr.expression;\n if (ts.isIdentifier(callee) && callee.text === 'bind') return expr;\n if (ts.isPropertyAccessExpression(callee) && callee.name.text === 'bind') return expr;\n return null;\n}\n\n/**\n * Handle one `<receiver>.to*(...)` call: if the receiver bottoms out at bind(TOKEN),\n * record the binding.\n */\nfunction collectBindCall(\n call: ts.CallExpression,\n checker: ts.TypeChecker,\n workspaceRoot: string,\n table: BindingTable,\n): void {\n if (!ts.isPropertyAccessExpression(call.expression)) return;\n const methodName = call.expression.name.text;\n if (!BIND_METHOD_NAMES.has(methodName)) return;\n\n const bindCall = asBindCall(call.expression.expression);\n if (!bindCall || bindCall.arguments.length === 0) return;\n\n const tokenExpr = bindCall.arguments[0];\n const file = relativeFile(workspaceRoot, call.getSourceFile());\n const scope = scopeFromChain(call);\n\n if (methodName === 'toSelf') {\n const cls = resolveClassDeclaration(tokenExpr, checker);\n const token = cls\n ? classTokenKey(cls, workspaceRoot)\n : resolveTokenKey(tokenExpr, checker, workspaceRoot);\n table.add(new Binding(token.key, tokenExpr.getText(), 'toSelf', scope, cls, '', file));\n return;\n }\n\n const token = resolveTokenKey(tokenExpr, checker, workspaceRoot);\n\n if (methodName === 'to') {\n const implExpr = call.arguments[0];\n const cls = implExpr ? resolveClassDeclaration(implExpr, checker) : null;\n const valueText = implExpr ? implExpr.getText() : '';\n table.add(new Binding(token.key, token.display, 'to', scope, cls, valueText, file));\n return;\n }\n\n const kind: BindingKind = methodName === 'toConstantValue' ? 'toConstantValue' : 'toDynamicValue';\n const valueExpr = call.arguments[0];\n const valueText = valueExpr ? firstLine(valueExpr.getText()) : '';\n const isApiBoundary = kind === 'toDynamicValue' && valueExpr ? isApiClientBoundary(valueExpr, checker) : false;\n table.add(new Binding(token.key, token.display, kind, scope, null, valueText, file, [], isApiBoundary));\n}\n\n/**\n * True when `expr` (the argument to `.toDynamicValue(...)` / an Angular\n * `useFactory`) builds an API-client proxy: it contains a `createApiClient(Api, ...)`\n * call whose first argument resolves to an @ApiPath-decorated contract class.\n *\n * This is the DI-graph boundary marker for generated (and external) API clients:\n * the walk renders such a binding as an `api` leaf and stops, rather than\n * descending into the client's own transport config (ClientConfig → ...).\n */\n// webpieces-disable no-function-outside-class -- pure AST predicate, matching every sibling in this file\nexport function isApiClientBoundary(expr: ts.Expression, checker: ts.TypeChecker): boolean {\n const call = findCreateApiClientCall(expr);\n if (!call || call.arguments.length === 0) return false;\n const apiClass = resolveClassDeclaration(call.arguments[0], checker);\n return apiClass ? hasApiPathDecorator(apiClass) : false;\n}\n\n/** Find the first `createApiClient(...)` call anywhere inside `node`, else null. */\n// webpieces-disable no-function-outside-class -- pure AST walker, matching every sibling in this file\nfunction findCreateApiClientCall(node: ts.Node): ts.CallExpression | null {\n if (ts.isCallExpression(node)) {\n const callee = node.expression;\n const name = ts.isIdentifier(callee)\n ? callee.text\n : ts.isPropertyAccessExpression(callee)\n ? callee.name.text\n : null;\n if (name === 'createApiClient') return node;\n }\n let found: ts.CallExpression | null = null;\n ts.forEachChild(node, (child: ts.Node) => {\n if (!found) found = findCreateApiClientCall(child);\n });\n return found;\n}\n\n/** True when the class carries the `@ApiPath(...)` contract decorator. */\n// webpieces-disable no-function-outside-class -- pure AST predicate, matching every sibling in this file\nfunction hasApiPathDecorator(cls: ts.ClassDeclaration): boolean {\n return classDecorators(cls).some((d: ts.Decorator) => decoratorName(d) === 'ApiPath');\n}\n\nfunction firstLine(text: string): string {\n const line = text.split('\\n')[0].trim();\n return line.length > 60 ? line.slice(0, 57) + '...' : line;\n}\n\n/** Return the decorator call expression when `decorator` is `@name(...)`, else null. */\nexport function decoratorCall(decorator: ts.Decorator): ts.CallExpression | null {\n return ts.isCallExpression(decorator.expression) ? decorator.expression : null;\n}\n\n/** The identifier name of a decorator like `@provideSingleton()` or `@inject(X)`. */\nexport function decoratorName(decorator: ts.Decorator): string | null {\n const call = decoratorCall(decorator);\n const callee = call ? call.expression : decorator.expression;\n if (ts.isIdentifier(callee)) return callee.text;\n if (ts.isPropertyAccessExpression(callee)) return callee.name.text;\n return null;\n}\n\nexport function classDecorators(cls: ts.ClassDeclaration): ts.Decorator[] {\n const decorators = ts.getDecorators(cls);\n return decorators ? [...decorators] : [];\n}\n\nfunction collectDecoratorBindings(\n cls: ts.ClassDeclaration,\n checker: ts.TypeChecker,\n workspaceRoot: string,\n table: BindingTable,\n): void {\n const file = relativeFile(workspaceRoot, cls.getSourceFile());\n for (const decorator of classDecorators(cls)) {\n const name = decoratorName(decorator);\n // provideFrameworkSingleton(As)/Transient are the framework-registry twins of\n // provideSingleton(As)/Transient (see @webpieces/core-context frameworkProvide.ts) —\n // same self/token binding, same scopes.\n if (\n name === 'provideSingleton' ||\n name === 'provideTransient' ||\n name === 'provideFrameworkSingleton' ||\n name === 'provideFrameworkTransient'\n ) {\n const token = classTokenKey(cls, workspaceRoot);\n const transient = name === 'provideTransient' || name === 'provideFrameworkTransient';\n const scope: DiScope = transient ? 'transient' : 'singleton';\n table.add(new Binding(token.key, token.display, 'decorator', scope, cls, '', file));\n } else if (name === 'provideSingletonDefaultForApi' || name === 'provideFrameworkSingletonDefaultForApi') {\n const call = decoratorCall(decorator);\n const tokenExpr = call?.arguments[0];\n if (!tokenExpr) continue;\n const token = resolveTokenKey(tokenExpr, checker, workspaceRoot);\n table.add(new Binding(token.key, token.display, 'decorator', 'singleton', cls, '', file));\n }\n }\n}\n\n/**\n * `bindFrameworkProvider(TOKEN, X)` — the Guice-style Provider registration in\n * @webpieces/core-context. Records that a `Provider<X>` injected under TOKEN yields X.\n *\n * The TOKEN is whatever names the provider (a Symbol, since `Provider<T>` is erased at runtime and\n * cannot be its own token). We never draw a node for it: a Provider is DI plumbing, not wiring.\n * The walker renders `Consumer -> X` directly, and X's OWN binding decides X's scope — and hence\n * whether the design draws one box (a lazy singleton) or a stack (a fresh instance per get()).\n */\n// webpieces-disable no-function-outside-class -- ts AST visitor, matching every sibling collector in this file\nfunction collectProviderBinding(\n call: ts.CallExpression,\n checker: ts.TypeChecker,\n workspaceRoot: string,\n table: BindingTable,\n): void {\n if (!ts.isIdentifier(call.expression) || call.expression.text !== 'bindFrameworkProvider') return;\n if (call.arguments.length < 2) return;\n\n // The token may be a Symbol, a class, anything — resolveTokenKey canonicalizes all of them.\n const token = resolveTokenKey(call.arguments[0], checker, workspaceRoot);\n const targetClass = resolveClassDeclaration(call.arguments[1], checker);\n if (!targetClass) return;\n\n table.addProviderTarget(token.key, targetClass);\n}\n\n/**\n * Pass 1: collect every binding in the program into a token-keyed table.\n */\nexport function collectBindings(\n program: ts.Program,\n checker: ts.TypeChecker,\n workspaceRoot: string,\n): BindingTable {\n const table = new BindingTable();\n\n for (const sourceFile of program.getSourceFiles()) {\n if (!isAnalyzableFile(sourceFile)) continue;\n\n const visit = (node: ts.Node): void => {\n if (ts.isCallExpression(node)) {\n collectBindCall(node, checker, workspaceRoot, table);\n collectProviderBinding(node, checker, workspaceRoot, table);\n } else if (ts.isClassDeclaration(node)) {\n collectDecoratorBindings(node, checker, workspaceRoot, table);\n }\n ts.forEachChild(node, visit);\n };\n visit(sourceFile);\n }\n\n return table;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"bindings.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/nx-webpieces-rules/src/lib/di-graph/bindings.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Binding Table (pass 1 of the DI graph analyzer)\n *\n * Scans every source file in the project's TypeScript program (skipping .d.ts and\n * node_modules) and collects all Inversify bindings into a Map<tokenKey, Binding[]>:\n *\n * - ContainerModule bodies: bind(TOKEN).to(Impl) / .toSelf() / .toConstantValue(x)\n * / .toDynamicValue(fn), with .inSingletonScope() etc.\n * - Decorators: @provideSingleton() / @provideTransient() (self-binding)\n * and @provideSingletonDefaultForApi(TOKEN)\n *\n * Arrays because multiInject tokens (e.g. HEADER_TYPES.PlatformHeadersExtension) are\n * bound once per ContainerModule across several packages.\n */\n\nimport * as ts from 'typescript';\nimport { Binding, BindingKind, DiScope } from './model';\nimport { classTokenKey, relativeFile, resolveTokenKey } from './token-resolver';\n\nconst BIND_METHOD_NAMES = new Set(['to', 'toSelf', 'toConstantValue', 'toDynamicValue']);\n\nexport class BindingTable {\n private readonly byToken = new Map<string, Binding[]>();\n /** providerTokenKey -> the class its get() resolves. See bindFrameworkProvider. */\n private readonly providerTargets = new Map<string, ts.ClassDeclaration>();\n\n add(binding: Binding): void {\n const list = this.byToken.get(binding.tokenKey);\n if (list) {\n list.push(binding);\n } else {\n this.byToken.set(binding.tokenKey, [binding]);\n }\n }\n\n lookup(tokenKey: string): Binding[] {\n return this.byToken.get(tokenKey) ?? [];\n }\n\n /** Record `bindFrameworkProvider(ProviderClass, TargetClass)`. */\n addProviderTarget(providerTokenKey: string, target: ts.ClassDeclaration): void {\n this.providerTargets.set(providerTokenKey, target);\n }\n\n /**\n * The class a Provider hands out, if this token is a registered Provider subclass.\n * The walker follows this so `Factory -> XProvider -> X` is visible in the design,\n * instead of the provider dead-ending as an opaque toDynamicValue leaf.\n */\n providerTarget(providerTokenKey: string): ts.ClassDeclaration | undefined {\n return this.providerTargets.get(providerTokenKey);\n }\n}\n\nfunction isAnalyzableFile(sourceFile: ts.SourceFile): boolean {\n if (sourceFile.isDeclarationFile) return false;\n if (sourceFile.fileName.includes('/node_modules/')) return false;\n return true;\n}\n\n/**\n * A class the walker treats as an EXTERNAL boundary: its declaration lives in a\n * `.d.ts` file or under `node_modules` — i.e. a published package outside this\n * nx workspace. The exact inverse of {@link isAnalyzableFile}'s file test. In an\n * nx monorepo internal libs resolve through tsconfig path mappings to real `.ts`\n * source, so they are NOT external and keep expanding; only third-party packages\n * (resolved to `.d.ts` in `node_modules`) trip this. Pass-2 renders such a class\n * as a leaf `external` node and stops — it does not descend into its ctor deps.\n */\nexport function isExternalClass(cls: ts.ClassDeclaration): boolean {\n const sourceFile = cls.getSourceFile();\n return sourceFile.isDeclarationFile || sourceFile.fileName.includes('/node_modules/');\n}\n\n/**\n * Walk `.inSingletonScope()` / `.inTransientScope()` suffixes above a binding call.\n *\n * No scope call at all means TRANSIENT, not \"unknown\": that is inversify's\n * `DEFAULT_DEFAULT_SCOPE`, and no `new Container(...)` in this workspace overrides `defaultScope`.\n */\nfunction scopeFromChain(bindingCall: ts.CallExpression): DiScope {\n let node: ts.Node = bindingCall;\n while (\n node.parent &&\n ts.isPropertyAccessExpression(node.parent) &&\n node.parent.parent &&\n ts.isCallExpression(node.parent.parent)\n ) {\n const methodName = node.parent.name.text;\n if (methodName === 'inSingletonScope') return 'singleton';\n if (methodName === 'inTransientScope') return 'transient';\n node = node.parent.parent;\n }\n return 'transient';\n}\n\n/**\n * If `expr` is (or resolves through the checker to) a class declaration, return it.\n */\nexport function resolveClassDeclaration(\n expr: ts.Expression,\n checker: ts.TypeChecker,\n): ts.ClassDeclaration | null {\n let symbol = checker.getSymbolAtLocation(expr);\n if (symbol && (symbol.flags & ts.SymbolFlags.Alias) !== 0) {\n symbol = checker.getAliasedSymbol(symbol);\n }\n for (const decl of symbol?.declarations ?? []) {\n if (ts.isClassDeclaration(decl)) return decl;\n }\n return null;\n}\n\n/**\n * Recognize `bind(TOKEN)` at the bottom of a fluent chain. Accepts a bare `bind(...)`\n * identifier call or `options.bind(...)` property call.\n */\nfunction asBindCall(expr: ts.Expression): ts.CallExpression | null {\n if (!ts.isCallExpression(expr)) return null;\n const callee = expr.expression;\n if (ts.isIdentifier(callee) && callee.text === 'bind') return expr;\n if (ts.isPropertyAccessExpression(callee) && callee.name.text === 'bind') return expr;\n return null;\n}\n\n/**\n * Handle one `<receiver>.to*(...)` call: if the receiver bottoms out at bind(TOKEN),\n * record the binding.\n */\nfunction collectBindCall(\n call: ts.CallExpression,\n checker: ts.TypeChecker,\n workspaceRoot: string,\n table: BindingTable,\n): void {\n if (!ts.isPropertyAccessExpression(call.expression)) return;\n const methodName = call.expression.name.text;\n if (!BIND_METHOD_NAMES.has(methodName)) return;\n\n const bindCall = asBindCall(call.expression.expression);\n if (!bindCall || bindCall.arguments.length === 0) return;\n\n const tokenExpr = bindCall.arguments[0];\n const file = relativeFile(workspaceRoot, call.getSourceFile());\n const scope = scopeFromChain(call);\n\n if (methodName === 'toSelf') {\n const cls = resolveClassDeclaration(tokenExpr, checker);\n const token = cls\n ? classTokenKey(cls, workspaceRoot)\n : resolveTokenKey(tokenExpr, checker, workspaceRoot);\n table.add(new Binding(token.key, tokenExpr.getText(), 'toSelf', scope, cls, '', file));\n return;\n }\n\n const token = resolveTokenKey(tokenExpr, checker, workspaceRoot);\n\n if (methodName === 'to') {\n const implExpr = call.arguments[0];\n const cls = implExpr ? resolveClassDeclaration(implExpr, checker) : null;\n const valueText = implExpr ? implExpr.getText() : '';\n table.add(new Binding(token.key, token.display, 'to', scope, cls, valueText, file));\n return;\n }\n\n const kind: BindingKind = methodName === 'toConstantValue' ? 'toConstantValue' : 'toDynamicValue';\n const valueExpr = call.arguments[0];\n const valueText = valueExpr ? firstLine(valueExpr.getText()) : '';\n const isApiBoundary = kind === 'toDynamicValue' && valueExpr ? isApiClientBoundary(valueExpr, checker) : false;\n table.add(new Binding(token.key, token.display, kind, scope, null, valueText, file, [], isApiBoundary));\n}\n\n/**\n * True when `expr` (the argument to `.toDynamicValue(...)` / an Angular\n * `useFactory`) builds an API-client proxy: it contains a `createApiClient(Api, ...)`\n * call whose first argument resolves to an @ApiPath-decorated contract class.\n *\n * This is the DI-graph boundary marker for generated (and external) API clients:\n * the walk renders such a binding as an `api` leaf and stops, rather than\n * descending into the client's own transport config (ClientConfig → ...).\n */\n// webpieces-disable no-function-outside-class -- pure AST predicate, matching every sibling in this file\nexport function isApiClientBoundary(expr: ts.Expression, checker: ts.TypeChecker): boolean {\n const call = findCreateApiClientCall(expr);\n if (!call || call.arguments.length === 0) return false;\n const apiClass = resolveClassDeclaration(call.arguments[0], checker);\n return apiClass ? hasApiPathDecorator(apiClass) : false;\n}\n\n/** Find the first `createApiClient(...)` call anywhere inside `node`, else null. */\n// webpieces-disable no-function-outside-class -- pure AST walker, matching every sibling in this file\nfunction findCreateApiClientCall(node: ts.Node): ts.CallExpression | null {\n if (ts.isCallExpression(node)) {\n const callee = node.expression;\n const name = ts.isIdentifier(callee)\n ? callee.text\n : ts.isPropertyAccessExpression(callee)\n ? callee.name.text\n : null;\n if (name === 'createApiClient') return node;\n }\n let found: ts.CallExpression | null = null;\n ts.forEachChild(node, (child: ts.Node) => {\n if (!found) found = findCreateApiClientCall(child);\n });\n return found;\n}\n\n/** True when the class carries the `@ApiPath(...)` contract decorator. */\n// webpieces-disable no-function-outside-class -- pure AST predicate, matching every sibling in this file\nfunction hasApiPathDecorator(cls: ts.ClassDeclaration): boolean {\n return classDecorators(cls).some((d: ts.Decorator) => decoratorName(d) === 'ApiPath');\n}\n\nfunction firstLine(text: string): string {\n const line = text.split('\\n')[0].trim();\n return line.length > 60 ? line.slice(0, 57) + '...' : line;\n}\n\n/** Return the decorator call expression when `decorator` is `@name(...)`, else null. */\nexport function decoratorCall(decorator: ts.Decorator): ts.CallExpression | null {\n return ts.isCallExpression(decorator.expression) ? decorator.expression : null;\n}\n\n/** The identifier name of a decorator like `@provideSingleton()` or `@inject(X)`. */\nexport function decoratorName(decorator: ts.Decorator): string | null {\n const call = decoratorCall(decorator);\n const callee = call ? call.expression : decorator.expression;\n if (ts.isIdentifier(callee)) return callee.text;\n if (ts.isPropertyAccessExpression(callee)) return callee.name.text;\n return null;\n}\n\nexport function classDecorators(cls: ts.ClassDeclaration): ts.Decorator[] {\n const decorators = ts.getDecorators(cls);\n return decorators ? [...decorators] : [];\n}\n\n/**\n * Scope of an `@injectable(bindingScopeValues.Singleton|Transient)` SELF-binding, read from the\n * decorator ARGUMENT (either `bindingScopeValues.Singleton` or the raw 'Singleton'/'Transient'\n * string), NOT the decorator name like `@provideSingleton`.\n *\n * Returns null for a BARE `@injectable()` (no scope argument): that is NOT a self-binding — it just\n * marks a class injectable for an explicit `bind(TOKEN).to(ThatClass)` elsewhere (e.g. SimpleCounter\n * bound to a Counter token). Only `@injectable(<scope>)` with an argument opts a concrete class into\n * autobind self-binding, the inject-by-type replacement for @provideSingleton.\n */\n// webpieces-disable no-function-outside-class -- pure AST predicate, matching every sibling in this file\nfunction injectableScope(decorator: ts.Decorator): DiScope | null {\n const call = decoratorCall(decorator);\n const arg = call?.arguments[0];\n if (!arg) return null;\n const scopeName = ts.isPropertyAccessExpression(arg)\n ? arg.name.text\n : ts.isStringLiteral(arg)\n ? arg.text\n : '';\n if (scopeName === 'Singleton') return 'singleton';\n if (scopeName === 'Transient') return 'transient';\n return null;\n}\n\nfunction collectDecoratorBindings(\n cls: ts.ClassDeclaration,\n checker: ts.TypeChecker,\n workspaceRoot: string,\n table: BindingTable,\n): void {\n const file = relativeFile(workspaceRoot, cls.getSourceFile());\n // A provide* decorator is the explicit binding; when present it wins over a plain @injectable on\n // the same class (a transitional state where both appear). Only a class with @injectable and NO\n // provide* decorator self-binds via autobind.\n const PROVIDE_BINDERS = new Set([\n 'provideSingleton',\n 'provideTransient',\n 'provideFrameworkSingleton',\n 'provideFrameworkTransient',\n 'provideSingletonDefaultForApi',\n 'provideFrameworkSingletonDefaultForApi',\n ]);\n const hasProvideBinding = classDecorators(cls).some((d: ts.Decorator) => {\n const n = decoratorName(d);\n return n !== null && PROVIDE_BINDERS.has(n);\n });\n for (const decorator of classDecorators(cls)) {\n const name = decoratorName(decorator);\n // provideFrameworkSingleton(As)/Transient are the framework-registry twins of\n // provideSingleton(As)/Transient (see @webpieces/core-context frameworkProvide.ts) —\n // same self/token binding, same scopes.\n if (\n name === 'provideSingleton' ||\n name === 'provideTransient' ||\n name === 'provideFrameworkSingleton' ||\n name === 'provideFrameworkTransient'\n ) {\n const token = classTokenKey(cls, workspaceRoot);\n const transient = name === 'provideTransient' || name === 'provideFrameworkTransient';\n const scope: DiScope = transient ? 'transient' : 'singleton';\n table.add(new Binding(token.key, token.display, 'decorator', scope, cls, '', file));\n } else if (name === 'provideSingletonDefaultForApi' || name === 'provideFrameworkSingletonDefaultForApi') {\n const call = decoratorCall(decorator);\n const tokenExpr = call?.arguments[0];\n if (!tokenExpr) continue;\n const token = resolveTokenKey(tokenExpr, checker, workspaceRoot);\n table.add(new Binding(token.key, token.display, 'decorator', 'singleton', cls, '', file));\n } else if (name === 'injectable' && !hasProvideBinding) {\n // @injectable(bindingScopeValues.Singleton|Transient) self-binds the concrete class under\n // the app container's autobind (the inject-by-type replacement for @provideSingleton).\n // A bare @injectable() (scope null) is skipped — it is bound via an explicit .to(token).\n const scope = injectableScope(decorator);\n if (scope !== null) {\n const token = classTokenKey(cls, workspaceRoot);\n table.add(new Binding(token.key, token.display, 'decorator', scope, cls, '', file));\n }\n }\n }\n}\n\n/**\n * `bindFrameworkProvider(TOKEN, X)` — the Guice-style Provider registration in\n * @webpieces/core-context. Records that a `Provider<X>` injected under TOKEN yields X.\n *\n * The TOKEN is whatever names the provider (a Symbol, since `Provider<T>` is erased at runtime and\n * cannot be its own token). We never draw a node for it: a Provider is DI plumbing, not wiring.\n * The walker renders `Consumer -> X` directly, and X's OWN binding decides X's scope — and hence\n * whether the design draws one box (a lazy singleton) or a stack (a fresh instance per get()).\n */\n// webpieces-disable no-function-outside-class -- ts AST visitor, matching every sibling collector in this file\nfunction collectProviderBinding(\n call: ts.CallExpression,\n checker: ts.TypeChecker,\n workspaceRoot: string,\n table: BindingTable,\n): void {\n if (!ts.isIdentifier(call.expression) || call.expression.text !== 'bindFrameworkProvider') return;\n if (call.arguments.length < 2) return;\n\n // The token may be a Symbol, a class, anything — resolveTokenKey canonicalizes all of them.\n const token = resolveTokenKey(call.arguments[0], checker, workspaceRoot);\n const targetClass = resolveClassDeclaration(call.arguments[1], checker);\n if (!targetClass) return;\n\n table.addProviderTarget(token.key, targetClass);\n}\n\n/**\n * Pass 1: collect every binding in the program into a token-keyed table.\n */\nexport function collectBindings(\n program: ts.Program,\n checker: ts.TypeChecker,\n workspaceRoot: string,\n): BindingTable {\n const table = new BindingTable();\n\n for (const sourceFile of program.getSourceFiles()) {\n if (!isAnalyzableFile(sourceFile)) continue;\n\n const visit = (node: ts.Node): void => {\n if (ts.isCallExpression(node)) {\n collectBindCall(node, checker, workspaceRoot, table);\n collectProviderBinding(node, checker, workspaceRoot, table);\n } else if (ts.isClassDeclaration(node)) {\n collectDecoratorBindings(node, checker, workspaceRoot, table);\n }\n ts.forEachChild(node, visit);\n };\n visit(sourceFile);\n }\n\n return table;\n}\n"]}
|