@djodjonx/neo-syringe 1.2.0 → 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.
Files changed (42) hide show
  1. package/.github/workflows/docs.yml +59 -0
  2. package/CHANGELOG.md +14 -0
  3. package/README.md +72 -779
  4. package/dist/cli/index.cjs +15 -0
  5. package/dist/cli/index.mjs +15 -0
  6. package/dist/index.d.cts +1 -1
  7. package/dist/index.d.mts +1 -1
  8. package/dist/unplugin/index.cjs +31 -7
  9. package/dist/unplugin/index.d.cts +7 -5
  10. package/dist/unplugin/index.d.mts +7 -5
  11. package/dist/unplugin/index.mjs +31 -7
  12. package/docs/.vitepress/config.ts +109 -0
  13. package/docs/.vitepress/theme/custom.css +150 -0
  14. package/docs/.vitepress/theme/index.ts +17 -0
  15. package/docs/api/configuration.md +274 -0
  16. package/docs/api/functions.md +291 -0
  17. package/docs/api/types.md +158 -0
  18. package/docs/guide/basic-usage.md +267 -0
  19. package/docs/guide/cli.md +174 -0
  20. package/docs/guide/generated-code.md +284 -0
  21. package/docs/guide/getting-started.md +171 -0
  22. package/docs/guide/ide-plugin.md +203 -0
  23. package/docs/guide/injection-types.md +287 -0
  24. package/docs/guide/legacy-migration.md +333 -0
  25. package/docs/guide/lifecycle.md +223 -0
  26. package/docs/guide/parent-container.md +321 -0
  27. package/docs/guide/scoped-injections.md +271 -0
  28. package/docs/guide/what-is-neo-syringe.md +162 -0
  29. package/docs/guide/why-neo-syringe.md +219 -0
  30. package/docs/index.md +138 -0
  31. package/docs/public/logo.png +0 -0
  32. package/package.json +5 -3
  33. package/src/analyzer/types.ts +52 -52
  34. package/src/cli/index.ts +15 -0
  35. package/src/generator/Generator.ts +23 -1
  36. package/src/types.ts +1 -1
  37. package/src/unplugin/index.ts +13 -41
  38. package/tests/analyzer/AnalyzerDeclarative.test.ts +1 -1
  39. package/tests/e2e/container-integration.test.ts +19 -19
  40. package/tests/e2e/generated-code.test.ts +2 -2
  41. package/tsconfig.json +2 -1
  42. package/typedoc.json +0 -5
@@ -0,0 +1,274 @@
1
+ # Configuration
2
+
3
+ Configure Neo-Syringe in your project.
4
+
5
+ ## Build Plugin
6
+
7
+ ### Vite
8
+
9
+ ```typescript
10
+ // vite.config.ts
11
+ import { defineConfig } from 'vite';
12
+ import { neoSyringePlugin } from '@djodjonx/neo-syringe/plugin';
13
+
14
+ export default defineConfig({
15
+ plugins: [neoSyringePlugin.vite()]
16
+ });
17
+ ```
18
+
19
+ ### Rollup
20
+
21
+ ```typescript
22
+ // rollup.config.js
23
+ import { neoSyringePlugin } from '@djodjonx/neo-syringe/plugin';
24
+
25
+ export default {
26
+ input: 'src/main.ts',
27
+ output: {
28
+ file: 'dist/bundle.js',
29
+ format: 'esm'
30
+ },
31
+ plugins: [neoSyringePlugin.rollup()]
32
+ };
33
+ ```
34
+
35
+ ### Webpack
36
+
37
+ ```javascript
38
+ // webpack.config.js
39
+ const { webpack } = require('@djodjonx/neo-syringe/plugin');
40
+
41
+ module.exports = {
42
+ plugins: [webpack()]
43
+ };
44
+ ```
45
+
46
+ ### esbuild
47
+
48
+ ```typescript
49
+ // esbuild.config.js
50
+ import { neoSyringePlugin } from '@djodjonx/neo-syringe/plugin';
51
+ import esbuild from 'esbuild';
52
+
53
+ await esbuild.build({
54
+ entryPoints: ['src/main.ts'],
55
+ bundle: true,
56
+ outfile: 'dist/bundle.js',
57
+ plugins: [neoSyringePlugin.esbuild()]
58
+ });
59
+ ```
60
+
61
+ ### Rspack
62
+
63
+ ```javascript
64
+ // rspack.config.js
65
+ const { rspack } = require('@djodjonx/neo-syringe/plugin');
66
+
67
+ module.exports = {
68
+ plugins: [rspack()]
69
+ };
70
+ ```
71
+
72
+ ## TypeScript LSP Plugin
73
+
74
+ Add to `tsconfig.json` for IDE error detection:
75
+
76
+ ```json
77
+ {
78
+ "compilerOptions": {
79
+ "plugins": [
80
+ { "name": "@djodjonx/neo-syringe/lsp" }
81
+ ]
82
+ }
83
+ }
84
+ ```
85
+
86
+ ::: tip VS Code
87
+ Remember to select "Use Workspace Version" for TypeScript.
88
+ :::
89
+
90
+ ## BuilderConfig Options
91
+
92
+ ```typescript
93
+ defineBuilderConfig({
94
+ // Container name (for debugging)
95
+ name: 'AppContainer',
96
+
97
+ // List of injections
98
+ injections: [
99
+ { token: UserService },
100
+ { token: useInterface<ILogger>(), provider: ConsoleLogger }
101
+ ],
102
+
103
+ // Inherit from partial configs
104
+ extends: [loggingPartial, databasePartial],
105
+
106
+ // Parent container (Neo-Syringe or legacy)
107
+ useContainer: parentContainer
108
+ });
109
+ ```
110
+
111
+ ### name
112
+
113
+ Type: `string`
114
+
115
+ Optional. The container name appears in error messages.
116
+
117
+ ```typescript
118
+ defineBuilderConfig({
119
+ name: 'UserModule'
120
+ });
121
+
122
+ // Error: [UserModule] Service not found: XYZ
123
+ ```
124
+
125
+ ### injections
126
+
127
+ Type: `Injection[]`
128
+
129
+ Required. List of services to register.
130
+
131
+ ```typescript
132
+ defineBuilderConfig({
133
+ injections: [
134
+ // Class autowiring
135
+ { token: UserService },
136
+
137
+ // Interface binding
138
+ { token: useInterface<ILogger>(), provider: ConsoleLogger },
139
+
140
+ // Explicit provider
141
+ { token: BaseService, provider: ConcreteService },
142
+
143
+ // Factory
144
+ { token: useInterface<IConfig>(), provider: () => loadConfig() },
145
+
146
+ // Property token
147
+ { token: useProperty<string>(ApiService, 'apiUrl'), provider: () => 'http://...' },
148
+
149
+ // With lifecycle
150
+ { token: RequestContext, lifecycle: 'transient' },
151
+
152
+ // Scoped override
153
+ { token: useInterface<ILogger>(), provider: MockLogger, scoped: true }
154
+ ]
155
+ });
156
+ ```
157
+
158
+ ### extends
159
+
160
+ Type: `PartialConfig[]`
161
+
162
+ Optional. Inherit injections from partial configs.
163
+
164
+ ```typescript
165
+ const loggingPartial = definePartialConfig({
166
+ injections: [
167
+ { token: useInterface<ILogger>(), provider: ConsoleLogger }
168
+ ]
169
+ });
170
+
171
+ defineBuilderConfig({
172
+ extends: [loggingPartial],
173
+ injections: [
174
+ { token: UserService } // Can use ILogger
175
+ ]
176
+ });
177
+ ```
178
+
179
+ ### useContainer
180
+
181
+ Type: `Container | any`
182
+
183
+ Optional. Parent container for delegation.
184
+
185
+ ```typescript
186
+ // Neo-Syringe parent
187
+ defineBuilderConfig({
188
+ useContainer: sharedKernel,
189
+ injections: [...]
190
+ });
191
+
192
+ // Legacy container (tsyringe, etc.)
193
+ const legacy = declareContainerTokens<{...}>(tsyringeContainer);
194
+ defineBuilderConfig({
195
+ useContainer: legacy,
196
+ injections: [...]
197
+ });
198
+ ```
199
+
200
+ ## Injection Options
201
+
202
+ ```typescript
203
+ interface Injection<T> {
204
+ token: Token<T>;
205
+ provider?: Provider<T>;
206
+ useFactory?: boolean;
207
+ lifecycle?: 'singleton' | 'transient';
208
+ scoped?: boolean;
209
+ }
210
+ ```
211
+
212
+ ### token
213
+
214
+ Type: `Token<T>` (required)
215
+
216
+ What to register. Can be:
217
+
218
+ - Class constructor: `UserService`
219
+ - Interface token: `useInterface<ILogger>()`
220
+ - Property token: `useProperty<string>(ApiService, 'apiUrl')`
221
+
222
+ ### provider
223
+
224
+ Type: `Provider<T>`
225
+
226
+ What provides the instance. Can be:
227
+
228
+ - Class constructor: `ConsoleLogger`
229
+ - Factory function: `(container) => new Service()`
230
+
231
+ If omitted, the token itself is used (autowiring).
232
+
233
+ ### useFactory
234
+
235
+ Type: `boolean`
236
+
237
+ Force treating the provider as a factory function.
238
+
239
+ ```typescript
240
+ // Auto-detected (arrow function)
241
+ { provider: () => createService() }
242
+
243
+ // Explicit (regular function)
244
+ { provider: createService, useFactory: true }
245
+ ```
246
+
247
+ ### lifecycle
248
+
249
+ Type: `'singleton' | 'transient'`
250
+
251
+ Default: `'singleton'`
252
+
253
+ How instances are managed.
254
+
255
+ ```typescript
256
+ { token: UserService, lifecycle: 'singleton' } // One instance
257
+ { token: RequestContext, lifecycle: 'transient' } // New each time
258
+ ```
259
+
260
+ ### scoped
261
+
262
+ Type: `boolean`
263
+
264
+ Default: `false`
265
+
266
+ If `true`, resolve locally instead of delegating to parent.
267
+
268
+ ```typescript
269
+ // Override parent's ILogger with local MockLogger
270
+ { token: useInterface<ILogger>(), provider: MockLogger, scoped: true }
271
+ ```
272
+
273
+ See [Scoped Injections](/guide/scoped-injections) for details.
274
+
@@ -0,0 +1,291 @@
1
+ # Functions
2
+
3
+ API functions exported by Neo-Syringe.
4
+
5
+ ## defineBuilderConfig
6
+
7
+ ```typescript
8
+ function defineBuilderConfig(config: BuilderConfig): Container
9
+ ```
10
+
11
+ Define a container configuration. At runtime without the build plugin, this throws an error. At build time, it's replaced with the generated container.
12
+
13
+ ### Parameters
14
+
15
+ | Name | Type | Description |
16
+ |------|------|-------------|
17
+ | `config` | `BuilderConfig` | Container configuration |
18
+
19
+ ### Returns
20
+
21
+ `Container` - The generated container (after build)
22
+
23
+ ### Example
24
+
25
+ ```typescript
26
+ import { defineBuilderConfig, useInterface } from '@djodjonx/neo-syringe';
27
+
28
+ export const container = defineBuilderConfig({
29
+ name: 'AppContainer',
30
+ injections: [
31
+ { token: useInterface<ILogger>(), provider: ConsoleLogger },
32
+ { token: UserService }
33
+ ]
34
+ });
35
+ ```
36
+
37
+ ---
38
+
39
+ ## definePartialConfig
40
+
41
+ ```typescript
42
+ function definePartialConfig(config: PartialConfig): PartialConfig
43
+ ```
44
+
45
+ Define a reusable partial configuration that can be extended.
46
+
47
+ ### Parameters
48
+
49
+ | Name | Type | Description |
50
+ |------|------|-------------|
51
+ | `config` | `PartialConfig` | Partial configuration |
52
+
53
+ ### Returns
54
+
55
+ `PartialConfig` - The partial configuration
56
+
57
+ ### Example
58
+
59
+ ```typescript
60
+ import { definePartialConfig, useInterface } from '@djodjonx/neo-syringe';
61
+
62
+ export const loggingPartial = definePartialConfig({
63
+ injections: [
64
+ { token: useInterface<ILogger>(), provider: ConsoleLogger }
65
+ ]
66
+ });
67
+
68
+ // Use in main config
69
+ export const container = defineBuilderConfig({
70
+ extends: [loggingPartial],
71
+ injections: [
72
+ { token: UserService }
73
+ ]
74
+ });
75
+ ```
76
+
77
+ ---
78
+
79
+ ## useInterface
80
+
81
+ ```typescript
82
+ function useInterface<T>(): InterfaceToken<T>
83
+ ```
84
+
85
+ Create a token for an interface. At compile time, this generates a unique string ID.
86
+
87
+ ### Type Parameters
88
+
89
+ | Name | Description |
90
+ |------|-------------|
91
+ | `T` | The interface type |
92
+
93
+ ### Returns
94
+
95
+ `InterfaceToken<T>` - A token representing the interface
96
+
97
+ ### Example
98
+
99
+ ```typescript
100
+ import { useInterface } from '@djodjonx/neo-syringe';
101
+
102
+ interface ILogger {
103
+ log(msg: string): void;
104
+ }
105
+
106
+ interface IDatabase {
107
+ query(sql: string): any[];
108
+ }
109
+
110
+ // In configuration
111
+ {
112
+ injections: [
113
+ { token: useInterface<ILogger>(), provider: ConsoleLogger },
114
+ { token: useInterface<IDatabase>(), provider: PostgresDatabase }
115
+ ]
116
+ }
117
+
118
+ // Resolution
119
+ const logger = container.resolve(useInterface<ILogger>());
120
+ ```
121
+
122
+ ---
123
+
124
+ ## useProperty
125
+
126
+ ```typescript
127
+ function useProperty<T, C = unknown>(
128
+ targetClass: Constructor<C>,
129
+ paramName: string
130
+ ): PropertyToken<T, C>
131
+ ```
132
+
133
+ Create a token for a primitive constructor parameter.
134
+
135
+ ### Type Parameters
136
+
137
+ | Name | Description |
138
+ |------|-------------|
139
+ | `T` | The primitive type (string, number, boolean) |
140
+ | `C` | The class type |
141
+
142
+ ### Parameters
143
+
144
+ | Name | Type | Description |
145
+ |------|------|-------------|
146
+ | `targetClass` | `Constructor<C>` | The class that has this parameter |
147
+ | `paramName` | `string` | The parameter name |
148
+
149
+ ### Returns
150
+
151
+ `PropertyToken<T, C>` - A token for the primitive
152
+
153
+ ### Example
154
+
155
+ ```typescript
156
+ import { useProperty } from '@djodjonx/neo-syringe';
157
+
158
+ class ApiService {
159
+ constructor(
160
+ private apiUrl: string,
161
+ private timeout: number
162
+ ) {}
163
+ }
164
+
165
+ const apiUrl = useProperty<string>(ApiService, 'apiUrl');
166
+ const timeout = useProperty<number>(ApiService, 'timeout');
167
+
168
+ // In configuration
169
+ {
170
+ injections: [
171
+ { token: apiUrl, provider: () => 'https://api.example.com' },
172
+ { token: timeout, provider: () => 5000 },
173
+ { token: ApiService }
174
+ ]
175
+ }
176
+ ```
177
+
178
+ ---
179
+
180
+ ## declareContainerTokens
181
+
182
+ ```typescript
183
+ function declareContainerTokens<T>(container: any): any
184
+ ```
185
+
186
+ Declare tokens provided by a legacy container for type-safety and validation.
187
+
188
+ ### Type Parameters
189
+
190
+ | Name | Description |
191
+ |------|-------------|
192
+ | `T` | Object type mapping token names to types |
193
+
194
+ ### Parameters
195
+
196
+ | Name | Type | Description |
197
+ |------|------|-------------|
198
+ | `container` | `any` | The legacy container instance |
199
+
200
+ ### Returns
201
+
202
+ The container with declared types
203
+
204
+ ### Example
205
+
206
+ ```typescript
207
+ import { declareContainerTokens } from '@djodjonx/neo-syringe';
208
+ import { container as tsyringeContainer } from 'tsyringe';
209
+
210
+ // Declare what tsyringe provides
211
+ const legacy = declareContainerTokens<{
212
+ AuthService: AuthService;
213
+ UserRepository: UserRepository;
214
+ }>(tsyringeContainer);
215
+
216
+ // Use in configuration
217
+ export const container = defineBuilderConfig({
218
+ useContainer: legacy,
219
+ injections: [
220
+ { token: NewService } // Can depend on AuthService, UserRepository
221
+ ]
222
+ });
223
+ ```
224
+
225
+ ---
226
+
227
+ ## Container.resolve
228
+
229
+ ```typescript
230
+ resolve<T>(token: Token<T>): T
231
+ ```
232
+
233
+ Resolve a service from the container.
234
+
235
+ ### Type Parameters
236
+
237
+ | Name | Description |
238
+ |------|-------------|
239
+ | `T` | The service type |
240
+
241
+ ### Parameters
242
+
243
+ | Name | Type | Description |
244
+ |------|------|-------------|
245
+ | `token` | `Token<T>` | Class, interface token, or property token |
246
+
247
+ ### Returns
248
+
249
+ `T` - The resolved instance
250
+
251
+ ### Throws
252
+
253
+ `Error` - If the service is not found
254
+
255
+ ### Example
256
+
257
+ ```typescript
258
+ // By class
259
+ const userService = container.resolve(UserService);
260
+
261
+ // By interface
262
+ const logger = container.resolve(useInterface<ILogger>());
263
+
264
+ // By property (unusual, but possible)
265
+ const apiUrl = container.resolve(useProperty<string>(ApiService, 'apiUrl'));
266
+ ```
267
+
268
+ ---
269
+
270
+ ## Container.createChildContainer
271
+
272
+ ```typescript
273
+ createChildContainer(): Container
274
+ ```
275
+
276
+ Create a child container that inherits from this one.
277
+
278
+ ### Returns
279
+
280
+ `Container` - A new child container
281
+
282
+ ### Example
283
+
284
+ ```typescript
285
+ const parent = container;
286
+ const child = parent.createChildContainer();
287
+
288
+ // Child can resolve parent's services
289
+ const logger = child.resolve(useInterface<ILogger>());
290
+ ```
291
+
@@ -0,0 +1,158 @@
1
+ # Types
2
+
3
+ Type definitions for Neo-Syringe.
4
+
5
+ ## Lifecycle
6
+
7
+ ```typescript
8
+ type Lifecycle = 'singleton' | 'transient';
9
+ ```
10
+
11
+ Defines how instances are managed:
12
+
13
+ | Value | Description |
14
+ |-------|-------------|
15
+ | `singleton` | One instance per container (default) |
16
+ | `transient` | New instance on every `resolve()` |
17
+
18
+ ## Constructor
19
+
20
+ ```typescript
21
+ type Constructor<T = unknown> = new (...args: unknown[]) => T;
22
+ ```
23
+
24
+ Represents any class constructor.
25
+
26
+ ## Token
27
+
28
+ ```typescript
29
+ type Token<T = any> = Constructor<T> | InterfaceToken<T> | PropertyToken<T, any>;
30
+ ```
31
+
32
+ A token that can be resolved by the container:
33
+
34
+ - **Constructor**: A class reference (e.g., `UserService`)
35
+ - **InterfaceToken**: Created by `useInterface<T>()`
36
+ - **PropertyToken**: Created by `useProperty<T>(Class, 'param')`
37
+
38
+ ## InterfaceToken
39
+
40
+ ```typescript
41
+ type InterfaceToken<T> = {
42
+ __brand: 'InterfaceToken';
43
+ __type: T;
44
+ };
45
+ ```
46
+
47
+ A compile-time token for interfaces. Created by `useInterface<T>()`.
48
+
49
+ ::: warning Internal Type
50
+ You don't create this directly. Use `useInterface<T>()` instead.
51
+ :::
52
+
53
+ ## PropertyToken
54
+
55
+ ```typescript
56
+ type PropertyToken<T, C = unknown> = {
57
+ __brand: 'PropertyToken';
58
+ __type: T;
59
+ __class: C;
60
+ __name: string;
61
+ };
62
+ ```
63
+
64
+ A token for primitive values bound to a specific class parameter. Created by `useProperty<T>(Class, 'param')`.
65
+
66
+ ## Provider
67
+
68
+ ```typescript
69
+ type Provider<T> = Constructor<T> | Factory<T>;
70
+ ```
71
+
72
+ What creates instances:
73
+
74
+ - **Constructor**: A class to instantiate
75
+ - **Factory**: A function that creates the instance
76
+
77
+ ## Factory
78
+
79
+ ```typescript
80
+ type Factory<T> = (container: Container) => T;
81
+ ```
82
+
83
+ A function that receives the container and returns an instance.
84
+
85
+ ```typescript
86
+ const myFactory: Factory<IConfig> = (container) => ({
87
+ apiUrl: process.env.API_URL,
88
+ logger: container.resolve(useInterface<ILogger>())
89
+ });
90
+ ```
91
+
92
+ ## Injection
93
+
94
+ ```typescript
95
+ interface Injection<T = any> {
96
+ token: Token<T>;
97
+ provider?: Provider<T>;
98
+ useFactory?: boolean;
99
+ lifecycle?: Lifecycle;
100
+ scoped?: boolean;
101
+ }
102
+ ```
103
+
104
+ A single injection definition:
105
+
106
+ | Property | Type | Description |
107
+ |----------|------|-------------|
108
+ | `token` | `Token<T>` | **Required**. What to register |
109
+ | `provider` | `Provider<T>` | Optional. What provides the instance |
110
+ | `useFactory` | `boolean` | Explicit factory flag |
111
+ | `lifecycle` | `Lifecycle` | `'singleton'` (default) or `'transient'` |
112
+ | `scoped` | `boolean` | If `true`, resolve locally (override parent) |
113
+
114
+ ## PartialConfig
115
+
116
+ ```typescript
117
+ interface PartialConfig {
118
+ injections?: Injection[];
119
+ }
120
+ ```
121
+
122
+ A reusable configuration block.
123
+
124
+ ## BuilderConfig
125
+
126
+ ```typescript
127
+ interface BuilderConfig extends PartialConfig {
128
+ name?: string;
129
+ extends?: PartialConfig[];
130
+ useContainer?: any;
131
+ }
132
+ ```
133
+
134
+ Main configuration for a container:
135
+
136
+ | Property | Type | Description |
137
+ |----------|------|-------------|
138
+ | `name` | `string` | Container name (for debugging) |
139
+ | `injections` | `Injection[]` | List of injections |
140
+ | `extends` | `PartialConfig[]` | Inherit from partials |
141
+ | `useContainer` | `any` | Parent container |
142
+
143
+ ## Container
144
+
145
+ ```typescript
146
+ interface Container {
147
+ resolve<T>(token: Token<T>): T;
148
+ createChildContainer(): Container;
149
+ }
150
+ ```
151
+
152
+ The generated container interface:
153
+
154
+ | Method | Description |
155
+ |--------|-------------|
156
+ | `resolve<T>(token)` | Resolve a service by token |
157
+ | `createChildContainer()` | Create a child container |
158
+