@djodjonx/neo-syringe 1.1.5 → 1.2.2
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/.github/workflows/ci.yml +6 -5
- package/.github/workflows/docs.yml +59 -0
- package/CHANGELOG.md +27 -0
- package/README.md +74 -740
- package/dist/{GraphValidator-G0F4QiLk.cjs → GraphValidator-CV4VoJl0.cjs} +18 -10
- package/dist/{GraphValidator-C8ldJtNp.mjs → GraphValidator-DXqqkNdS.mjs} +18 -10
- package/dist/cli/index.cjs +16 -1
- package/dist/cli/index.mjs +16 -1
- package/dist/index.d.cts +31 -5
- package/dist/index.d.mts +31 -5
- package/dist/lsp/index.cjs +1 -1
- package/dist/lsp/index.mjs +1 -1
- package/dist/unplugin/index.cjs +33 -9
- package/dist/unplugin/index.d.cts +7 -5
- package/dist/unplugin/index.d.mts +7 -5
- package/dist/unplugin/index.mjs +33 -9
- package/docs/.vitepress/config.ts +109 -0
- package/docs/.vitepress/theme/custom.css +150 -0
- package/docs/.vitepress/theme/index.ts +17 -0
- package/docs/api/configuration.md +274 -0
- package/docs/api/functions.md +291 -0
- package/docs/api/types.md +158 -0
- package/docs/guide/basic-usage.md +267 -0
- package/docs/guide/cli.md +174 -0
- package/docs/guide/generated-code.md +284 -0
- package/docs/guide/getting-started.md +171 -0
- package/docs/guide/ide-plugin.md +203 -0
- package/docs/guide/injection-types.md +287 -0
- package/docs/guide/legacy-migration.md +333 -0
- package/docs/guide/lifecycle.md +223 -0
- package/docs/guide/parent-container.md +321 -0
- package/docs/guide/scoped-injections.md +271 -0
- package/docs/guide/what-is-neo-syringe.md +162 -0
- package/docs/guide/why-neo-syringe.md +219 -0
- package/docs/index.md +138 -0
- package/docs/public/logo.png +0 -0
- package/package.json +15 -12
- package/src/analyzer/Analyzer.ts +20 -10
- package/src/analyzer/types.ts +55 -49
- package/src/cli/index.ts +15 -0
- package/src/generator/Generator.ts +24 -2
- package/src/generator/GraphValidator.ts +6 -2
- package/src/types.ts +30 -4
- package/src/unplugin/index.ts +13 -41
- package/tests/analyzer/Analyzer.test.ts +4 -4
- package/tests/analyzer/AnalyzerDeclarative.test.ts +1 -1
- package/tests/analyzer/Factory.test.ts +2 -2
- package/tests/analyzer/Scoped.test.ts +434 -0
- package/tests/cli/cli.test.ts +91 -0
- package/tests/e2e/container-integration.test.ts +21 -21
- package/tests/e2e/generated-code.test.ts +7 -7
- package/tests/e2e/scoped.test.ts +370 -0
- package/tests/e2e/snapshots.test.ts +2 -2
- package/tests/e2e/standalone.test.ts +2 -2
- package/tests/generator/ExternalGenerator.test.ts +1 -1
- package/tests/generator/FactoryGenerator.test.ts +6 -6
- package/tests/generator/Generator.test.ts +2 -2
- package/tests/generator/GeneratorDeclarative.test.ts +1 -1
- package/tests/generator/GraphValidator.test.ts +1 -1
- package/tsconfig.json +2 -1
- package/typedoc.json +0 -5
|
@@ -202,7 +202,7 @@ it('should resolve class dependencies on interfaces', () => {
|
|
|
202
202
|
function defineBuilderConfig(config: any) { return config; }
|
|
203
203
|
class SingletonService {}
|
|
204
204
|
export const c = defineBuilderConfig({
|
|
205
|
-
injections: [{ token: SingletonService,
|
|
205
|
+
injections: [{ token: SingletonService, lifecycle: 'singleton' }]
|
|
206
206
|
});
|
|
207
207
|
`);
|
|
208
208
|
|
|
@@ -217,7 +217,7 @@ it('should resolve class dependencies on interfaces', () => {
|
|
|
217
217
|
function defineBuilderConfig(config: any) { return config; }
|
|
218
218
|
class TransientService {}
|
|
219
219
|
export const c = defineBuilderConfig({
|
|
220
|
-
injections: [{ token: TransientService,
|
|
220
|
+
injections: [{ token: TransientService, lifecycle: 'transient' }]
|
|
221
221
|
});
|
|
222
222
|
`);
|
|
223
223
|
|
|
@@ -652,10 +652,10 @@ it('should generate PropertyToken resolution', () => {
|
|
|
652
652
|
|
|
653
653
|
export const c = defineBuilderConfig({
|
|
654
654
|
injections: [
|
|
655
|
-
{ token: SingletonA,
|
|
655
|
+
{ token: SingletonA, lifecycle: 'singleton' },
|
|
656
656
|
{ token: SingletonB }, // Default singleton
|
|
657
|
-
{ token: TransientA,
|
|
658
|
-
{ token: TransientB,
|
|
657
|
+
{ token: TransientA, lifecycle: 'transient' },
|
|
658
|
+
{ token: TransientB, lifecycle: 'transient' }
|
|
659
659
|
]
|
|
660
660
|
});
|
|
661
661
|
`);
|
|
@@ -926,7 +926,7 @@ it('should generate PropertyToken resolution', () => {
|
|
|
926
926
|
);
|
|
927
927
|
|
|
928
928
|
// Should parse without syntax errors
|
|
929
|
-
const syntaxErrors = sourceFile.parseDiagnostics || [];
|
|
929
|
+
const syntaxErrors = (sourceFile as any).parseDiagnostics || [];
|
|
930
930
|
expect(syntaxErrors.length).toBe(0);
|
|
931
931
|
});
|
|
932
932
|
|
|
@@ -948,7 +948,7 @@ it('should generate PropertyToken resolution', () => {
|
|
|
948
948
|
ts.ScriptTarget.Latest,
|
|
949
949
|
true
|
|
950
950
|
);
|
|
951
|
-
const syntaxErrors = sourceFile.parseDiagnostics || [];
|
|
951
|
+
const syntaxErrors = (sourceFile as any).parseDiagnostics || [];
|
|
952
952
|
expect(syntaxErrors.length).toBe(0);
|
|
953
953
|
});
|
|
954
954
|
});
|
|
@@ -0,0 +1,370 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* E2E Tests for scoped: true functionality
|
|
3
|
+
*
|
|
4
|
+
* Tests the complete flow from configuration to generated code
|
|
5
|
+
* for scoped token overrides.
|
|
6
|
+
*/
|
|
7
|
+
import { describe, it, expect } from 'vitest';
|
|
8
|
+
import * as ts from 'typescript';
|
|
9
|
+
import { Analyzer } from '../../src/analyzer/Analyzer';
|
|
10
|
+
import { GraphValidator } from '../../src/generator/GraphValidator';
|
|
11
|
+
import { Generator } from '../../src/generator/Generator';
|
|
12
|
+
|
|
13
|
+
describe('E2E - Scoped Injections', () => {
|
|
14
|
+
const compileAndGenerate = (fileContent: string): string => {
|
|
15
|
+
const fileName = 'scoped-e2e.ts';
|
|
16
|
+
const compilerHost = ts.createCompilerHost({});
|
|
17
|
+
const originalGetSourceFile = compilerHost.getSourceFile;
|
|
18
|
+
|
|
19
|
+
compilerHost.getSourceFile = (name, languageVersion) => {
|
|
20
|
+
if (name === fileName) {
|
|
21
|
+
return ts.createSourceFile(fileName, fileContent, languageVersion);
|
|
22
|
+
}
|
|
23
|
+
return originalGetSourceFile(name, languageVersion);
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
const program = ts.createProgram([fileName], {}, compilerHost);
|
|
27
|
+
const analyzer = new Analyzer(program);
|
|
28
|
+
const graph = analyzer.extract();
|
|
29
|
+
|
|
30
|
+
const validator = new GraphValidator();
|
|
31
|
+
validator.validate(graph);
|
|
32
|
+
|
|
33
|
+
const generator = new Generator(graph);
|
|
34
|
+
return generator.generate();
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
describe('Basic scoped override', () => {
|
|
38
|
+
it('should compile and generate code for scoped token', () => {
|
|
39
|
+
const code = compileAndGenerate(`
|
|
40
|
+
function defineBuilderConfig(config: any) { return config; }
|
|
41
|
+
function useInterface<T>(): any { return null; }
|
|
42
|
+
|
|
43
|
+
interface ILogger { log(msg: string): void; }
|
|
44
|
+
class ConsoleLogger implements ILogger { log(msg: string) {} }
|
|
45
|
+
class FileLogger implements ILogger { log(msg: string) {} }
|
|
46
|
+
|
|
47
|
+
const sharedKernel = defineBuilderConfig({
|
|
48
|
+
name: 'SharedKernel',
|
|
49
|
+
injections: [
|
|
50
|
+
{ token: useInterface<ILogger>(), provider: ConsoleLogger }
|
|
51
|
+
]
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
class UserService {
|
|
55
|
+
constructor(private logger: ILogger) {}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export const userModule = defineBuilderConfig({
|
|
59
|
+
name: 'UserModule',
|
|
60
|
+
useContainer: sharedKernel,
|
|
61
|
+
injections: [
|
|
62
|
+
{ token: useInterface<ILogger>(), provider: FileLogger, scoped: true },
|
|
63
|
+
{ token: UserService }
|
|
64
|
+
]
|
|
65
|
+
});
|
|
66
|
+
`);
|
|
67
|
+
|
|
68
|
+
// Container should be generated
|
|
69
|
+
expect(code).toContain('class NeoContainer');
|
|
70
|
+
|
|
71
|
+
// FileLogger (scoped) should be used, not ConsoleLogger
|
|
72
|
+
expect(code).toContain('FileLogger');
|
|
73
|
+
|
|
74
|
+
// Should have factory for ILogger (may have filename prefix)
|
|
75
|
+
expect(code).toMatch(/create_.*ILogger/);
|
|
76
|
+
|
|
77
|
+
// UserService should depend on ILogger
|
|
78
|
+
expect(code).toContain('UserService');
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
it('should fail without scoped: true when overriding parent', () => {
|
|
82
|
+
expect(() => compileAndGenerate(`
|
|
83
|
+
function defineBuilderConfig(config: any) { return config; }
|
|
84
|
+
function useInterface<T>(): any { return null; }
|
|
85
|
+
|
|
86
|
+
interface ILogger {}
|
|
87
|
+
class ConsoleLogger implements ILogger {}
|
|
88
|
+
class FileLogger implements ILogger {}
|
|
89
|
+
|
|
90
|
+
const parent = defineBuilderConfig({
|
|
91
|
+
injections: [{ token: useInterface<ILogger>(), provider: ConsoleLogger }]
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
export const child = defineBuilderConfig({
|
|
95
|
+
useContainer: parent,
|
|
96
|
+
injections: [
|
|
97
|
+
{ token: useInterface<ILogger>(), provider: FileLogger } // Missing scoped: true
|
|
98
|
+
]
|
|
99
|
+
});
|
|
100
|
+
`)).toThrow(/Duplicate registration.*ILogger/);
|
|
101
|
+
});
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
describe('Scoped with scopes (singleton/transient)', () => {
|
|
105
|
+
it('should generate singleton for scoped singleton', () => {
|
|
106
|
+
const code = compileAndGenerate(`
|
|
107
|
+
function defineBuilderConfig(config: any) { return config; }
|
|
108
|
+
function useInterface<T>(): any { return null; }
|
|
109
|
+
|
|
110
|
+
interface ICache {}
|
|
111
|
+
class RedisCache implements ICache {}
|
|
112
|
+
class MemoryCache implements ICache {}
|
|
113
|
+
|
|
114
|
+
const parent = defineBuilderConfig({
|
|
115
|
+
injections: [{ token: useInterface<ICache>(), provider: RedisCache }]
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
export const c = defineBuilderConfig({
|
|
119
|
+
useContainer: parent,
|
|
120
|
+
injections: [
|
|
121
|
+
{ token: useInterface<ICache>(), provider: MemoryCache, lifecycle: 'singleton', scoped: true }
|
|
122
|
+
]
|
|
123
|
+
});
|
|
124
|
+
`);
|
|
125
|
+
|
|
126
|
+
// Should use instances cache for singleton
|
|
127
|
+
expect(code).toContain('this.instances.has');
|
|
128
|
+
expect(code).toContain('this.instances.set');
|
|
129
|
+
expect(code).toContain('MemoryCache');
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
it('should generate transient for scoped transient', () => {
|
|
133
|
+
const code = compileAndGenerate(`
|
|
134
|
+
function defineBuilderConfig(config: any) { return config; }
|
|
135
|
+
function useInterface<T>(): any { return null; }
|
|
136
|
+
|
|
137
|
+
interface IRequest {}
|
|
138
|
+
class RequestImpl implements IRequest {}
|
|
139
|
+
|
|
140
|
+
const parent = defineBuilderConfig({
|
|
141
|
+
injections: [{ token: useInterface<IRequest>(), provider: RequestImpl, lifecycle: 'singleton' }]
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
export const c = defineBuilderConfig({
|
|
145
|
+
name: 'RequestScope',
|
|
146
|
+
useContainer: parent,
|
|
147
|
+
injections: [
|
|
148
|
+
{ token: useInterface<IRequest>(), provider: RequestImpl, lifecycle: 'transient', scoped: true }
|
|
149
|
+
]
|
|
150
|
+
});
|
|
151
|
+
`);
|
|
152
|
+
|
|
153
|
+
// Transient should return directly without caching
|
|
154
|
+
expect(code).toMatch(/if \(token === ".*IRequest"\) \{\s*return create_/);
|
|
155
|
+
});
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
describe('Scoped with factory', () => {
|
|
159
|
+
it('should work with factory provider + scoped', () => {
|
|
160
|
+
const code = compileAndGenerate(`
|
|
161
|
+
function defineBuilderConfig(config: any) { return config; }
|
|
162
|
+
function useInterface<T>(): any { return null; }
|
|
163
|
+
|
|
164
|
+
interface IConfig { apiUrl: string; }
|
|
165
|
+
|
|
166
|
+
const parent = defineBuilderConfig({
|
|
167
|
+
injections: [{
|
|
168
|
+
token: useInterface<IConfig>(),
|
|
169
|
+
provider: () => ({ apiUrl: 'https://prod.api.com' })
|
|
170
|
+
}]
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
export const testModule = defineBuilderConfig({
|
|
174
|
+
name: 'TestModule',
|
|
175
|
+
useContainer: parent,
|
|
176
|
+
injections: [
|
|
177
|
+
{
|
|
178
|
+
token: useInterface<IConfig>(),
|
|
179
|
+
provider: () => ({ apiUrl: 'http://localhost:3000' }),
|
|
180
|
+
scoped: true
|
|
181
|
+
}
|
|
182
|
+
]
|
|
183
|
+
});
|
|
184
|
+
`);
|
|
185
|
+
|
|
186
|
+
expect(code).toContain('localhost:3000');
|
|
187
|
+
expect(code).toMatch(/create_.*IConfig/);
|
|
188
|
+
});
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
describe('Multi-level hierarchy', () => {
|
|
192
|
+
it('should work with 3-level container hierarchy', () => {
|
|
193
|
+
const code = compileAndGenerate(`
|
|
194
|
+
function defineBuilderConfig(config: any) { return config; }
|
|
195
|
+
function useInterface<T>(): any { return null; }
|
|
196
|
+
|
|
197
|
+
interface ILogger {}
|
|
198
|
+
interface IDatabase {}
|
|
199
|
+
|
|
200
|
+
class ConsoleLogger implements ILogger {}
|
|
201
|
+
class FileLogger implements ILogger {}
|
|
202
|
+
class PostgresDB implements IDatabase {}
|
|
203
|
+
|
|
204
|
+
// Level 1: Infrastructure
|
|
205
|
+
const infrastructure = defineBuilderConfig({
|
|
206
|
+
name: 'Infrastructure',
|
|
207
|
+
injections: [
|
|
208
|
+
{ token: useInterface<ILogger>(), provider: ConsoleLogger },
|
|
209
|
+
{ token: useInterface<IDatabase>(), provider: PostgresDB }
|
|
210
|
+
]
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
// Level 2: Domain (inherits all from infrastructure)
|
|
214
|
+
const domain = defineBuilderConfig({
|
|
215
|
+
name: 'Domain',
|
|
216
|
+
useContainer: infrastructure,
|
|
217
|
+
injections: []
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
// Level 3: TestModule (overrides ILogger only)
|
|
221
|
+
class TestService {
|
|
222
|
+
constructor(private logger: ILogger, private db: IDatabase) {}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
export const testModule = defineBuilderConfig({
|
|
226
|
+
name: 'TestModule',
|
|
227
|
+
useContainer: domain,
|
|
228
|
+
injections: [
|
|
229
|
+
{ token: useInterface<ILogger>(), provider: FileLogger, scoped: true },
|
|
230
|
+
{ token: TestService }
|
|
231
|
+
]
|
|
232
|
+
});
|
|
233
|
+
`);
|
|
234
|
+
|
|
235
|
+
// Should have local ILogger (FileLogger)
|
|
236
|
+
expect(code).toContain('FileLogger');
|
|
237
|
+
|
|
238
|
+
// Should NOT have local IDatabase (comes from parent)
|
|
239
|
+
expect(code).not.toContain('create_IDatabase');
|
|
240
|
+
|
|
241
|
+
// TestService should exist
|
|
242
|
+
expect(code).toContain('TestService');
|
|
243
|
+
});
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
describe('Class token scoped', () => {
|
|
247
|
+
it('should work with class token (not just interfaces)', () => {
|
|
248
|
+
const code = compileAndGenerate(`
|
|
249
|
+
function defineBuilderConfig(config: any) { return config; }
|
|
250
|
+
|
|
251
|
+
class Logger {
|
|
252
|
+
log(msg: string) { console.log(msg); }
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
class MockLogger extends Logger {
|
|
256
|
+
log(msg: string) { /* noop */ }
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
const parent = defineBuilderConfig({
|
|
260
|
+
injections: [{ token: Logger }]
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
export const testModule = defineBuilderConfig({
|
|
264
|
+
name: 'TestModule',
|
|
265
|
+
useContainer: parent,
|
|
266
|
+
injections: [
|
|
267
|
+
{ token: Logger, provider: MockLogger, scoped: true }
|
|
268
|
+
]
|
|
269
|
+
});
|
|
270
|
+
`);
|
|
271
|
+
|
|
272
|
+
expect(code).toContain('MockLogger');
|
|
273
|
+
expect(code).toContain('create_Logger');
|
|
274
|
+
});
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
describe('Multiple scoped tokens', () => {
|
|
278
|
+
it('should handle multiple scoped overrides', () => {
|
|
279
|
+
const code = compileAndGenerate(`
|
|
280
|
+
function defineBuilderConfig(config: any) { return config; }
|
|
281
|
+
function useInterface<T>(): any { return null; }
|
|
282
|
+
|
|
283
|
+
interface ILogger {}
|
|
284
|
+
interface ICache {}
|
|
285
|
+
interface IConfig {}
|
|
286
|
+
|
|
287
|
+
class ConsoleLogger implements ILogger {}
|
|
288
|
+
class RedisCache implements ICache {}
|
|
289
|
+
class ProdConfig implements IConfig {}
|
|
290
|
+
|
|
291
|
+
class MockLogger implements ILogger {}
|
|
292
|
+
class MemoryCache implements ICache {}
|
|
293
|
+
class TestConfig implements IConfig {}
|
|
294
|
+
|
|
295
|
+
const production = defineBuilderConfig({
|
|
296
|
+
name: 'Production',
|
|
297
|
+
injections: [
|
|
298
|
+
{ token: useInterface<ILogger>(), provider: ConsoleLogger },
|
|
299
|
+
{ token: useInterface<ICache>(), provider: RedisCache },
|
|
300
|
+
{ token: useInterface<IConfig>(), provider: ProdConfig }
|
|
301
|
+
]
|
|
302
|
+
});
|
|
303
|
+
|
|
304
|
+
export const testing = defineBuilderConfig({
|
|
305
|
+
name: 'Testing',
|
|
306
|
+
useContainer: production,
|
|
307
|
+
injections: [
|
|
308
|
+
{ token: useInterface<ILogger>(), provider: MockLogger, scoped: true },
|
|
309
|
+
{ token: useInterface<ICache>(), provider: MemoryCache, scoped: true },
|
|
310
|
+
{ token: useInterface<IConfig>(), provider: TestConfig, scoped: true }
|
|
311
|
+
]
|
|
312
|
+
});
|
|
313
|
+
`);
|
|
314
|
+
|
|
315
|
+
// All 3 mock implementations should be present
|
|
316
|
+
expect(code).toContain('MockLogger');
|
|
317
|
+
expect(code).toContain('MemoryCache');
|
|
318
|
+
expect(code).toContain('TestConfig');
|
|
319
|
+
|
|
320
|
+
// Should have factories for all 3 (may have filename prefix)
|
|
321
|
+
expect(code).toMatch(/create_.*ILogger/);
|
|
322
|
+
expect(code).toMatch(/create_.*ICache/);
|
|
323
|
+
expect(code).toMatch(/create_.*IConfig/);
|
|
324
|
+
});
|
|
325
|
+
});
|
|
326
|
+
|
|
327
|
+
describe('Scoped with dependencies', () => {
|
|
328
|
+
it('should resolve scoped dependencies correctly', () => {
|
|
329
|
+
const code = compileAndGenerate(`
|
|
330
|
+
function defineBuilderConfig(config: any) { return config; }
|
|
331
|
+
function useInterface<T>(): any { return null; }
|
|
332
|
+
|
|
333
|
+
interface ILogger {}
|
|
334
|
+
class ConsoleLogger implements ILogger {}
|
|
335
|
+
class FileLogger implements ILogger {}
|
|
336
|
+
|
|
337
|
+
class UserRepository {
|
|
338
|
+
constructor(private logger: ILogger) {}
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
class UserService {
|
|
342
|
+
constructor(private repo: UserRepository, private logger: ILogger) {}
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
const parent = defineBuilderConfig({
|
|
346
|
+
name: 'Parent',
|
|
347
|
+
injections: [
|
|
348
|
+
{ token: useInterface<ILogger>(), provider: ConsoleLogger }
|
|
349
|
+
]
|
|
350
|
+
});
|
|
351
|
+
|
|
352
|
+
export const child = defineBuilderConfig({
|
|
353
|
+
name: 'Child',
|
|
354
|
+
useContainer: parent,
|
|
355
|
+
injections: [
|
|
356
|
+
{ token: useInterface<ILogger>(), provider: FileLogger, scoped: true },
|
|
357
|
+
{ token: UserRepository },
|
|
358
|
+
{ token: UserService }
|
|
359
|
+
]
|
|
360
|
+
});
|
|
361
|
+
`);
|
|
362
|
+
|
|
363
|
+
// Both UserRepository and UserService should use local ILogger
|
|
364
|
+
expect(code).toContain('create_UserRepository');
|
|
365
|
+
expect(code).toContain('create_UserService');
|
|
366
|
+
expect(code).toContain('FileLogger');
|
|
367
|
+
});
|
|
368
|
+
});
|
|
369
|
+
});
|
|
370
|
+
|
|
@@ -91,8 +91,8 @@ describe('Generated Code Snapshots', () => {
|
|
|
91
91
|
export const c = defineBuilderConfig({
|
|
92
92
|
name: 'ScopesApp',
|
|
93
93
|
injections: [
|
|
94
|
-
{ token: SingletonService,
|
|
95
|
-
{ token: TransientService,
|
|
94
|
+
{ token: SingletonService, lifecycle: 'singleton' },
|
|
95
|
+
{ token: TransientService, lifecycle: 'transient' }
|
|
96
96
|
]
|
|
97
97
|
});
|
|
98
98
|
`);
|
|
@@ -130,8 +130,8 @@ describe('E2E - Neo-Syringe Standalone', () => {
|
|
|
130
130
|
|
|
131
131
|
export const container = defineBuilderConfig({
|
|
132
132
|
injections: [
|
|
133
|
-
{ token: SingletonService,
|
|
134
|
-
{ token: TransientService,
|
|
133
|
+
{ token: SingletonService, lifecycle: 'singleton' },
|
|
134
|
+
{ token: TransientService, lifecycle: 'transient' }
|
|
135
135
|
]
|
|
136
136
|
});
|
|
137
137
|
`);
|
|
@@ -18,7 +18,7 @@ describe('Generator - Factory Support', () => {
|
|
|
18
18
|
tokenId: 'IConfig',
|
|
19
19
|
registrationNode: {} as any,
|
|
20
20
|
type: 'factory',
|
|
21
|
-
|
|
21
|
+
lifecycle: 'singleton',
|
|
22
22
|
isInterfaceToken: true,
|
|
23
23
|
isFactory: true,
|
|
24
24
|
factorySource: '(container) => ({ apiUrl: "http://example.com" })'
|
|
@@ -50,7 +50,7 @@ describe('Generator - Factory Support', () => {
|
|
|
50
50
|
tokenId: 'IDatabase',
|
|
51
51
|
registrationNode: {} as any,
|
|
52
52
|
type: 'factory',
|
|
53
|
-
|
|
53
|
+
lifecycle: 'singleton',
|
|
54
54
|
isInterfaceToken: true,
|
|
55
55
|
isFactory: true,
|
|
56
56
|
factorySource: '() => ({ query: () => [] })'
|
|
@@ -78,7 +78,7 @@ describe('Generator - Factory Support', () => {
|
|
|
78
78
|
tokenId: 'IRequest',
|
|
79
79
|
registrationNode: {} as any,
|
|
80
80
|
type: 'factory',
|
|
81
|
-
|
|
81
|
+
lifecycle: 'transient',
|
|
82
82
|
isInterfaceToken: true,
|
|
83
83
|
isFactory: true,
|
|
84
84
|
factorySource: '() => ({ id: Math.random() })'
|
|
@@ -107,7 +107,7 @@ describe('Generator - Factory Support', () => {
|
|
|
107
107
|
tokenSymbol: createMockSymbol('UserService'),
|
|
108
108
|
registrationNode: {} as any,
|
|
109
109
|
type: 'autowire',
|
|
110
|
-
|
|
110
|
+
lifecycle: 'singleton',
|
|
111
111
|
isFactory: false
|
|
112
112
|
},
|
|
113
113
|
dependencies: []
|
|
@@ -131,7 +131,7 @@ describe('Generator - Factory Support', () => {
|
|
|
131
131
|
tokenId: 'IConfig',
|
|
132
132
|
registrationNode: {} as any,
|
|
133
133
|
type: 'factory',
|
|
134
|
-
|
|
134
|
+
lifecycle: 'singleton',
|
|
135
135
|
isInterfaceToken: true,
|
|
136
136
|
isFactory: true,
|
|
137
137
|
factorySource: '() => ({ env: "prod" })'
|
|
@@ -145,7 +145,7 @@ describe('Generator - Factory Support', () => {
|
|
|
145
145
|
tokenSymbol: createMockSymbol('AppService'),
|
|
146
146
|
registrationNode: {} as any,
|
|
147
147
|
type: 'autowire',
|
|
148
|
-
|
|
148
|
+
lifecycle: 'singleton',
|
|
149
149
|
isFactory: false
|
|
150
150
|
},
|
|
151
151
|
dependencies: ['IConfig']
|
|
@@ -16,14 +16,14 @@ function createMockSymbol(name: string, filePath: string): ts.Symbol {
|
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
// Helper to create a mock node
|
|
19
|
-
function createMockNode(id: TokenId, dependencies: TokenId[], implName: string, filePath: string,
|
|
19
|
+
function createMockNode(id: TokenId, dependencies: TokenId[], implName: string, filePath: string, lifecycle: 'singleton' | 'transient' = 'singleton'): DependencyNode {
|
|
20
20
|
return {
|
|
21
21
|
service: {
|
|
22
22
|
tokenId: id,
|
|
23
23
|
implementationSymbol: createMockSymbol(implName, filePath),
|
|
24
24
|
registrationNode: {} as ts.Node,
|
|
25
25
|
type: 'explicit',
|
|
26
|
-
|
|
26
|
+
lifecycle: lifecycle,
|
|
27
27
|
} as ServiceDefinition,
|
|
28
28
|
dependencies,
|
|
29
29
|
};
|
|
@@ -11,7 +11,7 @@ function createMockNode(id: TokenId, dependencies: TokenId[]): DependencyNode {
|
|
|
11
11
|
implementationSymbol: {} as ts.Symbol, // Mock
|
|
12
12
|
registrationNode: {} as ts.Node, // Mock
|
|
13
13
|
type: 'autowire',
|
|
14
|
-
|
|
14
|
+
lifecycle: 'singleton',
|
|
15
15
|
} as ServiceDefinition,
|
|
16
16
|
dependencies,
|
|
17
17
|
};
|
package/tsconfig.json
CHANGED