alloy-di 1.0.0 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +26 -0
- package/dist/lib/container.d.ts +6 -7
- package/dist/lib/container.js +1 -3
- package/dist/lib/decorators.d.ts +0 -1
- package/dist/lib/decorators.js +5 -7
- package/dist/lib/dependency-error.js +1 -3
- package/dist/lib/env-detection.js +2 -4
- package/dist/lib/lazy.js +1 -2
- package/dist/lib/providers.d.ts +0 -1
- package/dist/lib/providers.js +3 -5
- package/dist/lib/scope.js +1 -2
- package/dist/lib/service-identifiers.d.ts +0 -1
- package/dist/lib/service-identifiers.js +1 -2
- package/dist/lib/testing/mocking.d.ts +1 -3
- package/dist/lib/testing/mocking.js +1 -3
- package/dist/lib/testing/registry.js +1 -3
- package/dist/lib/types.js +1 -2
- package/dist/plugins/core/codegen.js +76 -50
- package/dist/plugins/core/decorators.js +31 -10
- package/dist/plugins/core/discovery-store.js +1 -3
- package/dist/plugins/core/identifier-resolver.js +1 -3
- package/dist/plugins/core/lazy.js +32 -41
- package/dist/plugins/core/scanner.js +50 -41
- package/dist/plugins/core/types.d.ts +0 -1
- package/dist/plugins/core/utils.js +13 -15
- package/dist/plugins/rollup-plugin/build-utils.js +1 -3
- package/dist/plugins/rollup-plugin/index.js +5 -6
- package/dist/plugins/vite-plugin/index.d.ts +16 -0
- package/dist/plugins/vite-plugin/index.js +120 -75
- package/dist/plugins/vite-plugin/manifest-utils.js +1 -3
- package/dist/plugins/vite-plugin/visualizer.d.ts +18 -0
- package/dist/plugins/vite-plugin/visualizer.js +287 -0
- package/dist/rollup.js +2 -4
- package/dist/runtime.js +1 -2
- package/dist/test.js +1 -3
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/vite.js +1 -3
- package/package.json +34 -29
package/README.md
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
## Highlights
|
|
6
6
|
|
|
7
7
|
- **Build-time graph** – services, scopes, and dependencies are resolved while bundling, so runtime work stays minimal.
|
|
8
|
+
- **Visualize your DI graph** – enable the Vite plugin’s `visualize` option to emit a Mermaid diagram (`./alloy-di.mmd` by default) that captures scopes, lazy edges, and tokens for easy review.
|
|
8
9
|
- **First-class lazy loading** – use `Lazy()` or provider-based lazy registrations to keep optional features in separate chunks.
|
|
9
10
|
- **Framework agnostic** – works anywhere Vite runs: React, Vue, Svelte, SSR, libraries, and plain TS apps.
|
|
10
11
|
- **Type safe** – generates `serviceIdentifiers` and manifest declarations for precise inference.
|
|
@@ -54,6 +55,31 @@ pnpm add -D alloy-di
|
|
|
54
55
|
|
|
55
56
|
Need manifests, providers, or testing utilities? See the docs site for complete guides.
|
|
56
57
|
|
|
58
|
+
## Visualize your dependency graph
|
|
59
|
+
|
|
60
|
+
Enable the Vite plugin’s `visualize` option to have Alloy emit a Mermaid diagram that reflects every discovered service, scope, lazy edge, and token. By default the graph is written to `./alloy-di.mmd`, but you can customize the output path, color palette, or layout direction to fit your workflow.
|
|
61
|
+
|
|
62
|
+
```ts
|
|
63
|
+
import { defineConfig } from "vite";
|
|
64
|
+
import alloy from "alloy-di/vite";
|
|
65
|
+
|
|
66
|
+
export default defineConfig({
|
|
67
|
+
plugins: [
|
|
68
|
+
alloy({
|
|
69
|
+
visualize: {
|
|
70
|
+
mermaid: {
|
|
71
|
+
outputPath: "./docs/di-graph.mmd",
|
|
72
|
+
direction: "TB",
|
|
73
|
+
includeLegend: false,
|
|
74
|
+
},
|
|
75
|
+
},
|
|
76
|
+
}),
|
|
77
|
+
],
|
|
78
|
+
});
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
Commit the artifact for PR reviews, or generate ad-hoc previews locally with any Mermaid-friendly tool (for example VS Code’s Mermaid extension, GitHub’s Markdown preview, or `npx @mermaid-js/mermaid-cli -i docs/di-graph.mmd -o graph.svg`). The diagram highlights scopes, lazy edges, factory nodes, and tokens so you can inspect DI wiring at a glance.
|
|
82
|
+
|
|
57
83
|
## Documentation
|
|
58
84
|
|
|
59
85
|
- **Website**: https://alloy-di.dev (generated from `/docs`)
|
package/dist/lib/container.d.ts
CHANGED
|
@@ -2,7 +2,6 @@ import { Constructor, Newable, Token } from "./types.js";
|
|
|
2
2
|
import { ServiceIdentifier } from "./service-identifiers.js";
|
|
3
3
|
|
|
4
4
|
//#region src/lib/container.d.ts
|
|
5
|
-
|
|
6
5
|
/**
|
|
7
6
|
* Runtime dependency injection container used by generated modules and tests.
|
|
8
7
|
*
|
|
@@ -10,12 +9,12 @@ import { ServiceIdentifier } from "./service-identifiers.js";
|
|
|
10
9
|
* performs singleton caching, and supports token-based value providers.
|
|
11
10
|
*/
|
|
12
11
|
declare class Container {
|
|
13
|
-
private singletons;
|
|
14
|
-
private pendingSingletons;
|
|
15
|
-
private instanceOverrides;
|
|
16
|
-
private metadataCache;
|
|
17
|
-
private valueProviders;
|
|
18
|
-
private factoryWarningCache;
|
|
12
|
+
private readonly singletons;
|
|
13
|
+
private readonly pendingSingletons;
|
|
14
|
+
private readonly instanceOverrides;
|
|
15
|
+
private readonly metadataCache;
|
|
16
|
+
private readonly valueProviders;
|
|
17
|
+
private readonly factoryWarningCache;
|
|
19
18
|
/**
|
|
20
19
|
* Resolve (and construct) the requested service.
|
|
21
20
|
*
|
package/dist/lib/container.js
CHANGED
|
@@ -5,7 +5,6 @@ import { dependenciesRegistry } from "./decorators.js";
|
|
|
5
5
|
import { DependencyResolutionError } from "./dependency-error.js";
|
|
6
6
|
import { getConstructorByIdentifier, getServiceIdentifier } from "./service-identifiers.js";
|
|
7
7
|
import { isDevEnvironment } from "./env-detection.js";
|
|
8
|
-
|
|
9
8
|
//#region src/lib/container.ts
|
|
10
9
|
function classifyDependency(value) {
|
|
11
10
|
if (isLazy(value)) return {
|
|
@@ -261,6 +260,5 @@ var Container = class {
|
|
|
261
260
|
return metadata;
|
|
262
261
|
}
|
|
263
262
|
};
|
|
264
|
-
|
|
265
263
|
//#endregion
|
|
266
|
-
export { Container };
|
|
264
|
+
export { Container };
|
package/dist/lib/decorators.d.ts
CHANGED
package/dist/lib/decorators.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { ServiceScope } from "./scope.js";
|
|
2
|
-
|
|
3
2
|
//#region src/lib/decorators.ts
|
|
4
3
|
/**
|
|
5
4
|
* Global registry for service metadata used by the runtime container.
|
|
@@ -27,16 +26,16 @@ const dependenciesRegistry = /* @__PURE__ */ new Map();
|
|
|
27
26
|
*/
|
|
28
27
|
function createDecoratorWithDeps(scope, depsOpt) {
|
|
29
28
|
if (typeof depsOpt === "function") {
|
|
30
|
-
const depsFn
|
|
29
|
+
const depsFn = depsOpt;
|
|
31
30
|
return (target) => {
|
|
32
31
|
dependenciesRegistry.set(target, {
|
|
33
32
|
scope,
|
|
34
|
-
dependencies: depsFn
|
|
33
|
+
dependencies: depsFn
|
|
35
34
|
});
|
|
36
35
|
};
|
|
37
36
|
}
|
|
38
|
-
const deps
|
|
39
|
-
const depsFn = () => deps
|
|
37
|
+
const deps = depsOpt;
|
|
38
|
+
const depsFn = () => deps;
|
|
40
39
|
return (target) => {
|
|
41
40
|
dependenciesRegistry.set(target, {
|
|
42
41
|
scope,
|
|
@@ -121,6 +120,5 @@ function deps(...items) {
|
|
|
121
120
|
function assertDeps(depsFn, klass) {
|
|
122
121
|
return klass;
|
|
123
122
|
}
|
|
124
|
-
|
|
125
123
|
//#endregion
|
|
126
|
-
export { Injectable, Singleton, assertDeps, dependenciesRegistry, deps };
|
|
124
|
+
export { Injectable, Singleton, assertDeps, dependenciesRegistry, deps };
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { isConstructor, isToken } from "./types.js";
|
|
2
2
|
import { isLazy } from "./lazy.js";
|
|
3
|
-
|
|
4
3
|
//#region src/lib/dependency-error.ts
|
|
5
4
|
function describeDependency(value) {
|
|
6
5
|
if (isConstructor(value)) return `constructor ${value.name || "<anonymous>"}`;
|
|
@@ -26,6 +25,5 @@ var DependencyResolutionError = class extends Error {
|
|
|
26
25
|
return `${this.message}\nResolution path: ${stackPath}${dependencyInfo}`;
|
|
27
26
|
}
|
|
28
27
|
};
|
|
29
|
-
|
|
30
28
|
//#endregion
|
|
31
|
-
export { DependencyResolutionError };
|
|
29
|
+
export { DependencyResolutionError };
|
|
@@ -16,8 +16,7 @@ function readImportMetaEnvFromRuntime() {
|
|
|
16
16
|
}
|
|
17
17
|
function readProcessNodeEnv() {
|
|
18
18
|
if (typeof process === "undefined") return;
|
|
19
|
-
|
|
20
|
-
return typeof nodeEnv === "string" ? nodeEnv : void 0;
|
|
19
|
+
return "development";
|
|
21
20
|
}
|
|
22
21
|
function getImportMetaEnv(overrides) {
|
|
23
22
|
if (overrides?.importMetaEnv === null) return;
|
|
@@ -39,6 +38,5 @@ function isDevEnvironment(overrides) {
|
|
|
39
38
|
if (typeof importMetaEnv?.NODE_ENV === "string") return importMetaEnv.NODE_ENV !== "production";
|
|
40
39
|
return true;
|
|
41
40
|
}
|
|
42
|
-
|
|
43
41
|
//#endregion
|
|
44
|
-
export { isDevEnvironment };
|
|
42
|
+
export { isDevEnvironment };
|
package/dist/lib/lazy.js
CHANGED
package/dist/lib/providers.d.ts
CHANGED
package/dist/lib/providers.js
CHANGED
|
@@ -2,7 +2,6 @@ import { ServiceScope } from "./scope.js";
|
|
|
2
2
|
import { isConstructor } from "./types.js";
|
|
3
3
|
import { Lazy } from "./lazy.js";
|
|
4
4
|
import { dependenciesRegistry } from "./decorators.js";
|
|
5
|
-
|
|
6
5
|
//#region src/lib/providers.ts
|
|
7
6
|
/**
|
|
8
7
|
* Hidden symbol used to attach lazy provider metadata to its placeholder constructor.
|
|
@@ -146,9 +145,9 @@ function applyProviders(container, definitions) {
|
|
|
146
145
|
detectProviderCycles(planEntries);
|
|
147
146
|
for (const definition of list) {
|
|
148
147
|
for (const valueProvider of definition.values ?? []) container.provideValue(valueProvider.token, valueProvider.value);
|
|
149
|
-
const registerService = (ctor, lifecycle
|
|
148
|
+
const registerService = (ctor, lifecycle, deps, factory) => {
|
|
150
149
|
dependenciesRegistry.set(ctor, {
|
|
151
|
-
scope: lifecycle
|
|
150
|
+
scope: lifecycle,
|
|
152
151
|
dependencies: normalizeDependencies(deps),
|
|
153
152
|
factory
|
|
154
153
|
});
|
|
@@ -161,6 +160,5 @@ function applyProviders(container, definitions) {
|
|
|
161
160
|
}
|
|
162
161
|
}
|
|
163
162
|
}
|
|
164
|
-
|
|
165
163
|
//#endregion
|
|
166
|
-
export { applyProviders, asClass, asLazyClass, asValue, defineProviders, lifecycle };
|
|
164
|
+
export { applyProviders, asClass, asLazyClass, asValue, defineProviders, lifecycle };
|
package/dist/lib/scope.js
CHANGED
|
@@ -49,6 +49,5 @@ function clearServiceIdentifierRegistry() {
|
|
|
49
49
|
ctorToIdentifier = /* @__PURE__ */ new WeakMap();
|
|
50
50
|
identifierToCtor.clear();
|
|
51
51
|
}
|
|
52
|
-
|
|
53
52
|
//#endregion
|
|
54
|
-
export { clearServiceIdentifierRegistry, getConstructorByIdentifier, getServiceIdentifier, registerServiceIdentifier };
|
|
53
|
+
export { clearServiceIdentifierRegistry, getConstructorByIdentifier, getServiceIdentifier, registerServiceIdentifier };
|
|
@@ -5,9 +5,7 @@ import { vi } from "vitest";
|
|
|
5
5
|
type MethodKeys<T> = { [K in keyof T]: T[K] extends ((...args: any[]) => any) ? K : never }[keyof T];
|
|
6
6
|
/** Typed mock shape returned for class auto-mocking. */
|
|
7
7
|
type MockOf<T> = Partial<T> & {
|
|
8
|
-
/** Map of method name -> vi spy function */
|
|
9
|
-
spies: Record<Extract<MethodKeys<T>, string>, ReturnType<typeof vi.fn>>;
|
|
10
|
-
/** Original constructor reference for introspection */
|
|
8
|
+
/** Map of method name -> vi spy function */spies: Record<Extract<MethodKeys<T>, string>, ReturnType<typeof vi.fn>>; /** Original constructor reference for introspection */
|
|
11
9
|
__target: Newable<T>;
|
|
12
10
|
};
|
|
13
11
|
//#endregion
|
|
@@ -2,7 +2,6 @@ import { isConstructor } from "../types.js";
|
|
|
2
2
|
import { isLazy } from "../lazy.js";
|
|
3
3
|
import { getRawDependencies } from "./registry.js";
|
|
4
4
|
import { vi } from "vitest";
|
|
5
|
-
|
|
6
5
|
//#region src/lib/testing/mocking.ts
|
|
7
6
|
/** Create a lightweight auto-mock instance for a class constructor. */
|
|
8
7
|
function mockClass(ctor) {
|
|
@@ -104,6 +103,5 @@ function buildMockCtorFrom(realCtor, mock) {
|
|
|
104
103
|
}
|
|
105
104
|
return MockCtor;
|
|
106
105
|
}
|
|
107
|
-
|
|
108
106
|
//#endregion
|
|
109
|
-
export { applyAutoMocks };
|
|
107
|
+
export { applyAutoMocks };
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { dependenciesRegistry } from "../decorators.js";
|
|
2
|
-
|
|
3
2
|
//#region src/lib/testing/registry.ts
|
|
4
3
|
/** Take a deep(ish) snapshot of current dependency registry state. */
|
|
5
4
|
function snapshotRegistry() {
|
|
@@ -14,6 +13,5 @@ function restoreRegistry(snapshot) {
|
|
|
14
13
|
function getRawDependencies(target) {
|
|
15
14
|
return (dependenciesRegistry.get(target)?.dependencies ?? (() => []))();
|
|
16
15
|
}
|
|
17
|
-
|
|
18
16
|
//#endregion
|
|
19
|
-
export { getRawDependencies, restoreRegistry, snapshotRegistry };
|
|
17
|
+
export { getRawDependencies, restoreRegistry, snapshotRegistry };
|
package/dist/lib/types.js
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
import { createClassKey, createSymbolKey, hashString, normalizeImportPath } from "./utils.js";
|
|
2
2
|
import { IdentifierResolver } from "./identifier-resolver.js";
|
|
3
3
|
import path from "node:path";
|
|
4
|
-
|
|
5
4
|
//#region src/plugins/core/codegen.ts
|
|
6
5
|
function escapeSingleQuotes(value) {
|
|
7
|
-
return value.
|
|
6
|
+
return value.replaceAll("'", "\\'");
|
|
8
7
|
}
|
|
9
8
|
/**
|
|
10
9
|
* Generates a unique export key for the service identifier map.
|
|
@@ -26,23 +25,22 @@ function resolveDependencyImports(metas) {
|
|
|
26
25
|
const importMap = /* @__PURE__ */ new Map();
|
|
27
26
|
const nameCounts = /* @__PURE__ */ new Map();
|
|
28
27
|
const getUniqueName = (name) => {
|
|
29
|
-
const count = nameCounts.get(name)
|
|
28
|
+
const count = nameCounts.get(name) ?? 0;
|
|
30
29
|
nameCounts.set(name, count + 1);
|
|
31
30
|
return count === 0 ? name : `${name}_${count}`;
|
|
32
31
|
};
|
|
33
|
-
for (const meta of metas)
|
|
34
|
-
if (
|
|
35
|
-
const
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
32
|
+
for (const meta of metas) {
|
|
33
|
+
if (!meta.referencedImports?.length) continue;
|
|
34
|
+
for (const ref of meta.referencedImports) {
|
|
35
|
+
if (ref.isTypeOnly) continue;
|
|
36
|
+
const normalizedPath = normalizeImportPath(ref.path.startsWith(".") ? path.resolve(path.dirname(meta.filePath), ref.path) : ref.path);
|
|
37
|
+
const key = `${normalizedPath}::${ref.originalName ?? "default"}`;
|
|
38
|
+
if (importMap.has(key)) continue;
|
|
39
|
+
importMap.set(key, {
|
|
41
40
|
localName: getUniqueName(ref.name),
|
|
42
41
|
importPath: normalizedPath,
|
|
43
42
|
originalName: ref.originalName
|
|
44
|
-
};
|
|
45
|
-
importMap.set(key, resolved);
|
|
43
|
+
});
|
|
46
44
|
}
|
|
47
45
|
}
|
|
48
46
|
return {
|
|
@@ -54,9 +52,9 @@ function reconstructDependencyExpression(dep, rewriter, contextDir) {
|
|
|
54
52
|
let expr = dep.expression;
|
|
55
53
|
for (const ident of dep.referencedIdentifiers) {
|
|
56
54
|
const replacement = rewriter(ident);
|
|
57
|
-
if (replacement && replacement !== ident) expr = expr.
|
|
55
|
+
if (replacement && replacement !== ident) expr = expr.replaceAll(new RegExp(`\\b${ident}\\b`, "g"), replacement);
|
|
58
56
|
}
|
|
59
|
-
if (dep.isLazy) expr = expr.
|
|
57
|
+
if (dep.isLazy) expr = expr.replaceAll(/import\s*\(\s*(['"])(.+?)\1\s*\)/g, (match, quote, importPath) => {
|
|
60
58
|
if (importPath.startsWith(".")) return `import(${quote}${normalizeImportPath(path.resolve(contextDir, importPath))}${quote})`;
|
|
61
59
|
return match;
|
|
62
60
|
});
|
|
@@ -89,12 +87,25 @@ function reconstructOptionsText(meta, importMap) {
|
|
|
89
87
|
return `{ ${parts.join(", ")} }`;
|
|
90
88
|
}
|
|
91
89
|
function buildImportsAndRegistrations(metas, lazyReferencedClassKeys, hasProviderModules) {
|
|
92
|
-
const activeMetas = metas
|
|
90
|
+
const activeMetas = filterActiveMetas(metas, lazyReferencedClassKeys);
|
|
93
91
|
const { dependencyImports, importMap } = resolveDependencyImports(activeMetas);
|
|
94
|
-
const
|
|
95
|
-
const
|
|
92
|
+
const resolvedRegistrations = enrichRegistrations(activeMetas, new IdentifierResolver(activeMetas), importMap);
|
|
93
|
+
const runtimeImports = computeRuntimeImports(resolvedRegistrations, hasProviderModules);
|
|
94
|
+
const runtimeImportStatement = formatRuntimeImportStatement(runtimeImports);
|
|
95
|
+
const stubsBlock = createStubBlock(dependencyImports, resolvedRegistrations, runtimeImports);
|
|
96
|
+
return {
|
|
97
|
+
runtimeImportStatement,
|
|
98
|
+
registrationsBlock: createRegistrationsBlock(buildRegistrationEntries(resolvedRegistrations)),
|
|
99
|
+
stubsBlock,
|
|
100
|
+
identifierExportBlock: createIdentifierExports(resolvedRegistrations)
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
function filterActiveMetas(metas, lazyReferencedClassKeys) {
|
|
104
|
+
return metas.filter((meta) => !lazyReferencedClassKeys.has(createClassKey(meta.filePath, meta.className)));
|
|
105
|
+
}
|
|
106
|
+
function enrichRegistrations(activeMetas, resolver, importMap) {
|
|
107
|
+
return activeMetas.map((meta) => {
|
|
96
108
|
const importName = resolver.resolve(meta.className, meta.filePath);
|
|
97
|
-
const isFactoryLazy = !!meta.metadata.factory;
|
|
98
109
|
const identifierConst = `${importName}Identifier`;
|
|
99
110
|
const exportKey = createIdentifierExportKey(meta, resolver);
|
|
100
111
|
const symbolDescription = meta.identifierKey ?? createSymbolDescription(meta);
|
|
@@ -102,53 +113,69 @@ function buildImportsAndRegistrations(metas, lazyReferencedClassKeys, hasProvide
|
|
|
102
113
|
return {
|
|
103
114
|
...meta,
|
|
104
115
|
importName,
|
|
105
|
-
isFactoryLazy,
|
|
116
|
+
isFactoryLazy: Boolean(meta.metadata.factory),
|
|
106
117
|
identifierConst,
|
|
107
118
|
exportKey,
|
|
108
119
|
symbolDescription,
|
|
109
120
|
optionsText
|
|
110
121
|
};
|
|
111
122
|
});
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
if (
|
|
117
|
-
|
|
118
|
-
|
|
123
|
+
}
|
|
124
|
+
function computeRuntimeImports(registrations, hasProviderModules) {
|
|
125
|
+
const imports = new Set(["Container", "dependenciesRegistry"]);
|
|
126
|
+
const needsLazyImport = registrations.some((m) => m.metadata.dependencies.some((d) => d.isLazy) || !!m.metadata.factory);
|
|
127
|
+
if (hasProviderModules) imports.add("applyProviders");
|
|
128
|
+
if (needsLazyImport) imports.add("Lazy");
|
|
129
|
+
if (registrations.length) imports.add("registerServiceIdentifier");
|
|
130
|
+
return imports;
|
|
131
|
+
}
|
|
132
|
+
function formatRuntimeImportStatement(imports) {
|
|
133
|
+
return `\nimport { ${Array.from(imports).join(", ")} } from 'alloy-di/runtime';\n`;
|
|
134
|
+
}
|
|
135
|
+
function createStubBlock(dependencyImports, registrations, runtimeImports) {
|
|
136
|
+
const statements = [];
|
|
119
137
|
const importedNames = new Set(runtimeImports);
|
|
120
138
|
for (const dep of dependencyImports) {
|
|
121
139
|
if (dep.importPath === "alloy-di/runtime" && dep.originalName && dep.localName === dep.originalName && runtimeImports.has(dep.originalName)) continue;
|
|
122
140
|
if (importedNames.has(dep.localName)) continue;
|
|
123
|
-
|
|
124
|
-
else if (dep.originalName === "*") stubDeclarations.push(`import * as ${dep.localName} from '${dep.importPath}';`);
|
|
125
|
-
else if (dep.originalName && dep.originalName !== dep.localName) stubDeclarations.push(`import { ${dep.originalName} as ${dep.localName} } from '${dep.importPath}';`);
|
|
126
|
-
else stubDeclarations.push(`import { ${dep.localName} } from '${dep.importPath}';`);
|
|
141
|
+
statements.push(createDependencyImportStatement(dep));
|
|
127
142
|
importedNames.add(dep.localName);
|
|
128
143
|
}
|
|
129
|
-
for (const meta of
|
|
144
|
+
for (const meta of registrations) {
|
|
130
145
|
if (meta.isFactoryLazy) {
|
|
131
|
-
|
|
146
|
+
statements.push(`class ${meta.importName} {}`);
|
|
132
147
|
continue;
|
|
133
148
|
}
|
|
134
149
|
if (importedNames.has(meta.importName)) continue;
|
|
135
|
-
|
|
136
|
-
if (meta.importName === meta.className) stubDeclarations.push(`import { ${meta.className} } from '${importPath}';`);
|
|
137
|
-
else stubDeclarations.push(`import { ${meta.className} as ${meta.importName} } from '${importPath}';`);
|
|
150
|
+
statements.push(createServiceImportStatement(meta));
|
|
138
151
|
importedNames.add(meta.importName);
|
|
139
152
|
}
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
return {
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
}
|
|
153
|
+
return statements.length ? `${statements.join("\n")}\n` : "";
|
|
154
|
+
}
|
|
155
|
+
function createDependencyImportStatement(dep) {
|
|
156
|
+
if (dep.originalName === "default") return `import ${dep.localName} from '${dep.importPath}';`;
|
|
157
|
+
if (dep.originalName === "*") return `import * as ${dep.localName} from '${dep.importPath}';`;
|
|
158
|
+
if (dep.originalName && dep.originalName !== dep.localName) return `import { ${dep.originalName} as ${dep.localName} } from '${dep.importPath}';`;
|
|
159
|
+
return `import { ${dep.localName} } from '${dep.importPath}';`;
|
|
160
|
+
}
|
|
161
|
+
function createServiceImportStatement(meta) {
|
|
162
|
+
const importPath = !/^(\/|[A-Za-z]:\\|\.|~)/.test(meta.filePath) && !meta.filePath.includes("\\") ? meta.filePath : normalizeImportPath(meta.filePath);
|
|
163
|
+
if (meta.importName === meta.className) return `import { ${meta.className} } from '${importPath}';`;
|
|
164
|
+
return `import { ${meta.className} as ${meta.importName} } from '${importPath}';`;
|
|
165
|
+
}
|
|
166
|
+
function buildRegistrationEntries(registrations) {
|
|
167
|
+
return registrations.map((m) => ({
|
|
168
|
+
ctorName: m.importName,
|
|
169
|
+
metaText: m.optionsText
|
|
170
|
+
}));
|
|
171
|
+
}
|
|
172
|
+
function createRegistrationsBlock(entries) {
|
|
173
|
+
if (!entries.length) return "const registrations = [];";
|
|
174
|
+
return `const registrations = [\n${entries.map((entry) => ` { ctor: ${entry.ctorName}, meta: ${entry.metaText} }`).join(",\n")}\n];`;
|
|
175
|
+
}
|
|
176
|
+
function createIdentifierExports(registrations) {
|
|
177
|
+
if (!registrations.length) return "export const serviceIdentifiers = {};\n";
|
|
178
|
+
return `${registrations.map((meta) => `const ${meta.identifierConst} = registerServiceIdentifier(${meta.importName}, Symbol.for('${escapeSingleQuotes(meta.symbolDescription)}'));`).join("\n")}\n\nexport const serviceIdentifiers = {\n${registrations.map((meta) => ` '${meta.exportKey}': ${meta.identifierConst}`).join(",\n")}\n};\n`;
|
|
152
179
|
}
|
|
153
180
|
/**
|
|
154
181
|
* Generates the virtual container module code.
|
|
@@ -283,6 +310,5 @@ ${serviceIdentifiers}
|
|
|
283
310
|
`;
|
|
284
311
|
}).join("\n");
|
|
285
312
|
}
|
|
286
|
-
|
|
287
313
|
//#endregion
|
|
288
|
-
export { generateContainerModule, generateContainerTypeDefinition, generateManifestTypeDefinition };
|
|
314
|
+
export { generateContainerModule, generateContainerTypeDefinition, generateManifestTypeDefinition };
|
|
@@ -1,38 +1,60 @@
|
|
|
1
1
|
import { ServiceScope } from "../../lib/scope.js";
|
|
2
2
|
import ts from "typescript";
|
|
3
|
-
|
|
4
3
|
//#region src/plugins/core/decorators.ts
|
|
5
|
-
function
|
|
4
|
+
function collectBindingIdentifiers(name, ignored) {
|
|
5
|
+
if (ts.isIdentifier(name)) {
|
|
6
|
+
ignored.add(name.text);
|
|
7
|
+
return;
|
|
8
|
+
}
|
|
9
|
+
name.elements.forEach((element) => {
|
|
10
|
+
if (ts.isOmittedExpression(element)) return;
|
|
11
|
+
collectBindingIdentifiers(element.name, ignored);
|
|
12
|
+
});
|
|
13
|
+
}
|
|
14
|
+
function isDynamicImportExpression(node) {
|
|
15
|
+
return node.expression.kind === ts.SyntaxKind.ImportKeyword;
|
|
16
|
+
}
|
|
17
|
+
function extractRefs(node, sourceFile, identifiers, ignored) {
|
|
6
18
|
if (ts.isPropertyAssignment(node)) {
|
|
7
|
-
extractRefs(node.initializer, sourceFile, identifiers);
|
|
8
|
-
if (ts.isComputedPropertyName(node.name)) extractRefs(node.name.expression, sourceFile, identifiers);
|
|
19
|
+
extractRefs(node.initializer, sourceFile, identifiers, ignored);
|
|
20
|
+
if (ts.isComputedPropertyName(node.name)) extractRefs(node.name.expression, sourceFile, identifiers, ignored);
|
|
9
21
|
return;
|
|
10
22
|
}
|
|
23
|
+
if (ts.isParameter(node)) {
|
|
24
|
+
collectBindingIdentifiers(node.name, ignored);
|
|
25
|
+
if (node.initializer) extractRefs(node.initializer, sourceFile, identifiers, ignored);
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
if (ts.isPropertyAccessExpression(node)) {
|
|
29
|
+
if (ts.isCallExpression(node.expression) && isDynamicImportExpression(node.expression) && ts.isIdentifier(node.name)) ignored.add(node.name.text);
|
|
30
|
+
}
|
|
11
31
|
if (ts.isIdentifier(node)) {
|
|
12
|
-
identifiers.add(node.text);
|
|
32
|
+
if (!ignored.has(node.text)) identifiers.add(node.text);
|
|
13
33
|
return;
|
|
14
34
|
}
|
|
15
35
|
if (ts.isCallExpression(node)) {
|
|
16
36
|
const name = node.expression.getText(sourceFile);
|
|
17
37
|
if (name === "Lazy" || name.endsWith(".Lazy")) {
|
|
18
|
-
node.arguments.forEach((arg) => extractRefs(arg, sourceFile, identifiers));
|
|
38
|
+
node.arguments.forEach((arg) => extractRefs(arg, sourceFile, identifiers, ignored));
|
|
19
39
|
return;
|
|
20
40
|
}
|
|
21
41
|
}
|
|
22
|
-
ts.forEachChild(node, (n) => extractRefs(n, sourceFile, identifiers));
|
|
42
|
+
ts.forEachChild(node, (n) => extractRefs(n, sourceFile, identifiers, ignored));
|
|
23
43
|
}
|
|
24
44
|
function createDependencyDescriptor(node, sourceFile) {
|
|
25
45
|
const expression = node.getText(sourceFile);
|
|
26
46
|
const referencedIdentifiers = /* @__PURE__ */ new Set();
|
|
47
|
+
const ignoredIdentifiers = /* @__PURE__ */ new Set();
|
|
27
48
|
let isLazy = false;
|
|
28
49
|
if (ts.isCallExpression(node)) {
|
|
29
50
|
const callName = node.expression.getText(sourceFile);
|
|
30
51
|
if (callName === "Lazy" || callName.endsWith(".Lazy")) isLazy = true;
|
|
31
52
|
}
|
|
32
|
-
extractRefs(node, sourceFile, referencedIdentifiers);
|
|
53
|
+
extractRefs(node, sourceFile, referencedIdentifiers, ignoredIdentifiers);
|
|
33
54
|
return {
|
|
34
55
|
expression,
|
|
35
56
|
referencedIdentifiers: Array.from(referencedIdentifiers),
|
|
57
|
+
ignoredIdentifiers: ignoredIdentifiers.size ? Array.from(ignoredIdentifiers) : void 0,
|
|
36
58
|
isLazy
|
|
37
59
|
};
|
|
38
60
|
}
|
|
@@ -85,6 +107,5 @@ function extractServiceMetadata(decoratorName, callExpression, sourceFile) {
|
|
|
85
107
|
dependencies
|
|
86
108
|
};
|
|
87
109
|
}
|
|
88
|
-
|
|
89
110
|
//#endregion
|
|
90
|
-
export { extractServiceMetadata };
|
|
111
|
+
export { extractServiceMetadata };
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { scanSource } from "./scanner.js";
|
|
2
|
-
|
|
3
2
|
//#region src/plugins/core/discovery-store.ts
|
|
4
3
|
/**
|
|
5
4
|
* Maintains a per-file cache of discovered DI metadata, lazy references, and
|
|
@@ -73,6 +72,5 @@ function createDiscoveryStore(options = {}) {
|
|
|
73
72
|
clear
|
|
74
73
|
};
|
|
75
74
|
}
|
|
76
|
-
|
|
77
75
|
//#endregion
|
|
78
|
-
export { createDiscoveryStore };
|
|
76
|
+
export { createDiscoveryStore };
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { createAliasName } from "./utils.js";
|
|
2
|
-
|
|
3
2
|
//#region src/plugins/core/identifier-resolver.ts
|
|
4
3
|
var IdentifierResolver = class {
|
|
5
4
|
counts;
|
|
@@ -17,6 +16,5 @@ var IdentifierResolver = class {
|
|
|
17
16
|
return this.count(className) > 1 ? createAliasName(className, importPath) : className;
|
|
18
17
|
}
|
|
19
18
|
};
|
|
20
|
-
|
|
21
19
|
//#endregion
|
|
22
|
-
export { IdentifierResolver };
|
|
20
|
+
export { IdentifierResolver };
|