@teqfw/di 0.22.0 → 0.30.1
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 +159 -197
- package/RELEASE.md +9 -0
- package/bin/release/clean.sh +1 -0
- package/dist/esm.js +1 -0
- package/dist/umd.js +1 -0
- package/package.json +7 -10
- package/rollup.config.js +21 -0
- package/src/Api/Container/Resolver.js +16 -0
- package/src/Api/Container.js +2 -2
- package/src/Container/A/Composer.js +1 -2
- package/src/Container/A/Parser/Chunk/Def.js +25 -30
- package/src/Container/A/Parser/Chunk/V02X.js +70 -0
- package/src/Container/Resolver.js +4 -2
- package/src/Container.js +6 -6
- package/src/Defs.js +6 -11
- package/dist/di.cjs.js +0 -205
- package/dist/di.esm.js +0 -206
- package/docs/README.md +0 -6
- package/docs/app.vue +0 -5
- package/docs/assets/css/layout.css +0 -51
- package/docs/assets/css/vars.css +0 -6
- package/docs/components/AppFooter.vue +0 -47
- package/docs/components/AppHeader.vue +0 -79
- package/docs/content/index.md +0 -272
- package/docs/content/test.md +0 -20
- package/docs/layouts/default.vue +0 -83
- package/docs/nuxt.config.mjs +0 -11
- package/docs/package-lock.json +0 -14873
- package/docs/package.json +0 -25
- package/docs/pages/[...slug].vue +0 -6
- package/docs/pages/index.vue +0 -6
- package/docs/public/.nojekyll +0 -0
- package/docs/public/CNAME +0 -1
- package/docs/public/favicon.ico +0 -0
- package/docs/public/img/github.svg +0 -1
- package/docs/public/img/npm.png +0 -0
- package/docs/public/img/teqfw_di_container_steps.png +0 -0
- package/docs/server/tsconfig.json +0 -3
- package/docs/tsconfig.json +0 -4
- package/index.cjs +0 -5
- package/index.mjs +0 -6
- package/webpack.config.mjs +0 -15
package/README.md
CHANGED
|
@@ -1,265 +1,227 @@
|
|
|
1
1
|
# @teqfw/di
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
npm package, all sources can be linked to each other through relative paths. However, if the sources are distributed
|
|
20
|
-
across different npm packages, addressing them becomes problematic:
|
|
21
|
-
|
|
22
|
-
```javascript
|
|
23
|
-
import something from '@vendor/package/src/Module.js'; // backend style
|
|
24
|
-
import something from 'https://domain.com/@vendor/package/src/Module.js'; // frontend style
|
|
25
|
-
```
|
|
3
|
+

|
|
4
|
+

|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
`@teqfw/di` is a dependency injection container for standard JavaScript. This library is compatible with both browser
|
|
8
|
+
and Node.js environments.
|
|
9
|
+
|
|
10
|
+
**This library only supports ES6 modules ([the live demo](https://flancer64.github.io/demo-di-app/)).**
|
|
11
|
+
|
|
12
|
+
More samples:
|
|
13
|
+
|
|
14
|
+
* [demo-wa-esm-openai](https://github.com/flancer64/demo-wa-esm-openai)
|
|
15
|
+
* [pwa-wallet](https://github.com/flancer64/pwa-wallet)
|
|
16
|
+
* [spa-remote-console](https://github.com/flancer64/spa-remote-console)
|
|
17
|
+
* [demo-webauthn-pubkey](https://github.com/flancer64/demo-webauthn-pubkey)
|
|
18
|
+
* [tg-bot-habr-demo-grammy](https://github.com/flancer64/tg-bot-habr-demo-grammy)
|
|
26
19
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
20
|
+
This library is primarily designed to simplify the binding of code objects with minimal manual configuration required
|
|
21
|
+
for the object container. All instructions related to connections are encapsulated within the dependency identifiers
|
|
22
|
+
used in constructors or factory functions, as per the constructor injection scheme:
|
|
23
|
+
|
|
24
|
+
```js
|
|
25
|
+
export default class App_Main {
|
|
26
|
+
constructor(
|
|
27
|
+
{
|
|
28
|
+
App_Config$: config,
|
|
29
|
+
App_Logger$: logger,
|
|
30
|
+
App_Service_Customer$: servCustomer,
|
|
31
|
+
App_Service_Sale$: servSale,
|
|
32
|
+
}
|
|
33
|
+
) { ... }
|
|
30
34
|
|
|
31
|
-
```javascript
|
|
32
|
-
// constructor-based injection
|
|
33
|
-
class Service {
|
|
34
|
-
constructor(config, logger) {}
|
|
35
35
|
}
|
|
36
36
|
```
|
|
37
37
|
|
|
38
|
-
|
|
39
|
-
the source code itself. Now, they can be used without any changes, both on the front end and on the back end.
|
|
38
|
+
Corresponding files would look like this:
|
|
40
39
|
|
|
41
|
-
|
|
40
|
+
```
|
|
41
|
+
./src/
|
|
42
|
+
./Service/
|
|
43
|
+
./Customer.js
|
|
44
|
+
./Sale.js
|
|
45
|
+
./Config.js
|
|
46
|
+
./Logger.js
|
|
47
|
+
./Main.js
|
|
48
|
+
```
|
|
42
49
|
|
|
43
|
-
|
|
44
|
-
utilizes an object container, responsible for creating all the application's objects and their dependencies. The
|
|
45
|
-
`@teqfw/di` package provides precisely such an object container (`src/Container.js`). This object container is
|
|
46
|
-
initialized and configured at the outset of application execution, after which it assumes responsibility for creating
|
|
47
|
-
the remaining application objects:
|
|
50
|
+
Setting up object mapping is fairly simple:
|
|
48
51
|
|
|
49
|
-
```
|
|
52
|
+
```js
|
|
50
53
|
import Container from '@teqfw/di';
|
|
51
54
|
|
|
52
55
|
const container = new Container();
|
|
53
56
|
const resolver = container.getResolver();
|
|
54
|
-
resolver.addNamespaceRoot('App_',
|
|
55
|
-
resolver.addNamespaceRoot('Sample_Lib_', pathLib);
|
|
57
|
+
resolver.addNamespaceRoot('App_', '/path/to/src'); // or 'https://cdn.jsdelivr.net/npm/@vendor/pkg@latest/src'
|
|
56
58
|
const app = await container.get('App_Main$');
|
|
57
59
|
```
|
|
58
60
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
identifiers
|
|
62
|
-
|
|
63
|
-
## Namespaces
|
|
64
|
-
|
|
65
|
-
The primary purpose of namespaces is to address code elements within an application. In JavaScript (JS) applications,
|
|
66
|
-
code is organized into npm packages, within which the sources reside in files and directories. Each npm package and its
|
|
67
|
-
root directory can be linked to a namespace:
|
|
68
|
-
|
|
69
|
-
```
|
|
70
|
-
Vendor_Package_ => /home/user/app/node_modules/@vendor/package/src/....
|
|
71
|
-
```
|
|
72
|
-
|
|
73
|
-
This way, you can reference any ES6 module in any npm package:
|
|
61
|
+
While it could potentially be used with TypeScript, the current algorithm for composing
|
|
62
|
+
dependency identifiers, designed for JavaScript, may not be suitable for TypeScript. A different approach for composing
|
|
63
|
+
identifiers and mapping them to the corresponding objects in the transpiled codebase would be required, which is beyond
|
|
64
|
+
the current scope of the package.
|
|
74
65
|
|
|
75
|
-
|
|
76
|
-
Venodr_Package_Shared_Dto_Service_Save => /home/user/app/node_modules/@vendor/package/src/Shared/Dto/Service/Save.js
|
|
77
|
-
```
|
|
66
|
+
## Key Features
|
|
78
67
|
|
|
79
|
-
|
|
68
|
+
* **Late Binding**: Experience all the usual benefits of late binding at runtime including flexibility, testability,
|
|
69
|
+
modularity, manageability, and a clear separation of concerns.
|
|
70
|
+
* **ES6 Modules Integration**: Seamlessly utilize singletons and instances based on ES6 module exports.
|
|
71
|
+
* **Interface Usage in Standard JavaScript**: Take advantage of "interfaces" in standard JavaScript, with the added
|
|
72
|
+
benefit of dependency substitution.
|
|
73
|
+
* **Object Wrapping**: Enhance the functionality of created objects by adding wrappers (postprocessing).
|
|
80
74
|
|
|
81
|
-
|
|
82
|
-
Vendor_Package_ => /home/user/app/node_modules/@vendor/package/src // Linux style
|
|
83
|
-
Vendor_Package_ => C:\projects\app\node_modules\@vendor\package\src // Window style
|
|
84
|
-
Vendor_Package_ => https://unpkg.com/@vendor/package/src // Web style
|
|
85
|
-
```
|
|
75
|
+
## Installation
|
|
86
76
|
|
|
87
|
-
|
|
88
|
-
translate the namespace into the corresponding path to the source code file, contingent upon the runtime environment:
|
|
77
|
+
Installation instructions for Node.js:
|
|
89
78
|
|
|
90
|
-
```
|
|
91
|
-
|
|
92
|
-
Venodr_Package_Shared_Dto_Service_Save => C:\projects\app\node_modules\@vendor\package\src\Shared\Dto\Service\Save.js
|
|
93
|
-
Venodr_Package_Shared_Dto_Service_Save => https://unpkg.com/@vendor/package/src/Shared/Dto/Service/Save.js
|
|
79
|
+
```shell
|
|
80
|
+
$ npm i --save @teqfw/di
|
|
94
81
|
```
|
|
95
82
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
comprehend the necessary dependencies for creating a specific object, a distinct convention is employed - a dependency
|
|
100
|
-
specification. A dependency specification is an object where each key represents the identifier of the required
|
|
101
|
-
dependency:
|
|
83
|
+
```js
|
|
84
|
+
import Container from '@teqfw/di';
|
|
85
|
+
import {platform} from 'node:process';
|
|
102
86
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
) {}
|
|
111
|
-
}
|
|
87
|
+
/** @type {TeqFw_Di_Container} */
|
|
88
|
+
const container = new Container();
|
|
89
|
+
/** @type {TeqFw_Di_Container_Resolver} */
|
|
90
|
+
const resolver = res.getResolver();
|
|
91
|
+
resolver.setWindowsEnv(platform === 'win32');
|
|
92
|
+
resolver.addNamespaceRoot('App_', '...');
|
|
93
|
+
const app = await container.get('App_Main$');
|
|
112
94
|
```
|
|
113
95
|
|
|
114
|
-
|
|
96
|
+
Installation instructions for Web as ESM (~5Kb):
|
|
115
97
|
|
|
116
|
-
```
|
|
117
|
-
const App_Config = await container.get('App_Config');
|
|
118
|
-
const App_Logger = await container.get('App_Logger');
|
|
119
|
-
const spec = {App_Config, App_Logger};
|
|
120
|
-
const obj = new Service(spec);
|
|
121
|
-
```
|
|
98
|
+
```html
|
|
122
99
|
|
|
123
|
-
|
|
100
|
+
<script type="module">
|
|
101
|
+
import {default as Container} from 'https://cdn.jsdelivr.net/npm/@teqfw/di@latest/+esm';
|
|
124
102
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
103
|
+
/** @type {TeqFw_Di_Container} */
|
|
104
|
+
const container = new Container();
|
|
105
|
+
/** @type {TeqFw_Di_Container_Resolver} */
|
|
106
|
+
const resolver = res.getResolver();
|
|
107
|
+
resolver.addNamespaceRoot('App_', 'https://cdn.jsdelivr.net/npm/@flancer64/demo-di-app@0.2/src');
|
|
108
|
+
resolver.addNamespaceRoot('Sample_Lib_', 'https://cdn.jsdelivr.net/npm/@flancer64/demo-di-lib@0.3/src');
|
|
109
|
+
const app = await container.get('App_Main$');
|
|
110
|
+
...
|
|
111
|
+
</script>
|
|
130
112
|
```
|
|
131
113
|
|
|
132
|
-
|
|
114
|
+
Installation instructions for Web as UMD (~5Kb):
|
|
133
115
|
|
|
134
|
-
|
|
135
|
-
of [exporting](https://flancer32.com/es6-export-as-code-brick-b33a8efb3510):
|
|
116
|
+
```html
|
|
136
117
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
}
|
|
118
|
+
<script src="https://cdn.jsdelivr.net/npm/@teqfw/di@latest/dist/umd.js"></script>
|
|
119
|
+
<script type="module">
|
|
120
|
+
const {default: Container} = window.TeqFw_Di_Container;
|
|
121
|
+
/** @type {TeqFw_Di_Container} */
|
|
122
|
+
const container = new Container();
|
|
123
|
+
...
|
|
124
|
+
</script>
|
|
141
125
|
```
|
|
142
126
|
|
|
143
|
-
|
|
127
|
+
## Dependency ID Types
|
|
144
128
|
|
|
145
|
-
|
|
146
|
-
import obj1 from './mod.js';
|
|
147
|
-
import {obj2} from './mod.js';
|
|
148
|
-
````
|
|
129
|
+
Different Dependency IDs can be used for different imports, such as:
|
|
149
130
|
|
|
150
|
-
|
|
151
|
-
also a specific export within it, as illustrated in this example:
|
|
131
|
+
### Import whole module as ES module
|
|
152
132
|
|
|
153
|
-
```
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
const obj2 = 'Vendor_Package_Module.obj2';
|
|
133
|
+
```js
|
|
134
|
+
// App_Service
|
|
135
|
+
import * as Service from './App/Service.js';
|
|
157
136
|
```
|
|
158
137
|
|
|
159
|
-
|
|
138
|
+
### Import default export as is
|
|
160
139
|
|
|
161
|
-
```
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
{
|
|
165
|
-
'App_Config.default': config,
|
|
166
|
-
'App_Util.logger': logger
|
|
167
|
-
}
|
|
168
|
-
) {}
|
|
169
|
-
}
|
|
140
|
+
```js
|
|
141
|
+
// App_Service.default
|
|
142
|
+
import {default} from './App/Service.js';
|
|
170
143
|
```
|
|
171
144
|
|
|
172
|
-
|
|
145
|
+
### Import named export as is
|
|
173
146
|
|
|
174
|
-
|
|
175
|
-
|
|
147
|
+
```js
|
|
148
|
+
// App_Service.name
|
|
149
|
+
import {name} from './App/Service.js';
|
|
150
|
+
```
|
|
176
151
|
|
|
177
|
-
|
|
178
|
-
* **Instance**: A new object is created each time.
|
|
152
|
+
### Use default export as a singleton for container
|
|
179
153
|
|
|
180
|
-
|
|
181
|
-
|
|
154
|
+
```js
|
|
155
|
+
// App_Service$
|
|
156
|
+
import {default as Factory} from './App/Service.js';
|
|
182
157
|
|
|
183
|
-
|
|
184
|
-
const asIs = 'Vendor_Package_Module.default';
|
|
185
|
-
const asSingleton = 'Vendor_Package_Module.default$';
|
|
186
|
-
const asInstance = 'Vendor_Package_Module.default$$';
|
|
158
|
+
return res ?? (res = Factory({/* deps */}));
|
|
187
159
|
```
|
|
188
160
|
|
|
189
|
-
|
|
190
|
-
`TeqFw_Di_Container_Parser` object is responsible for applying the appropriate format within the required namespace.
|
|
161
|
+
### Create a new default export as Instance for each dependency
|
|
191
162
|
|
|
192
|
-
|
|
163
|
+
```js
|
|
164
|
+
// App_Service$$
|
|
165
|
+
import {default as Factory} from './App/Service.js';
|
|
193
166
|
|
|
194
|
-
|
|
167
|
+
return Factory({/* deps */});
|
|
168
|
+
```
|
|
195
169
|
|
|
196
|
-
|
|
170
|
+
### Use named export as singleton.
|
|
197
171
|
|
|
198
|
-
|
|
172
|
+
```js
|
|
173
|
+
// App_Service.name$
|
|
174
|
+
import {name} from './App/Service.js';
|
|
199
175
|
|
|
200
|
-
|
|
201
|
-
|
|
176
|
+
return res ?? (res = name({/* deps */}));
|
|
177
|
+
```
|
|
202
178
|
|
|
203
|
-
###
|
|
179
|
+
### Create a new named export as instance
|
|
204
180
|
|
|
205
|
-
|
|
206
|
-
|
|
181
|
+
```js
|
|
182
|
+
// App_Service.name$$
|
|
183
|
+
import {name} from './App/Service.js';
|
|
207
184
|
|
|
208
|
-
|
|
209
|
-
/** @type {TeqFw_Di_Api_Container_PreProcessor} */
|
|
210
|
-
const pre = container.getPreProcessor();
|
|
185
|
+
return name({/* deps */});
|
|
211
186
|
```
|
|
212
187
|
|
|
213
|
-
|
|
188
|
+
### Add custom wrappers to created objects in postprocessing
|
|
214
189
|
|
|
215
|
-
```
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
pre.addChunk(replace);
|
|
219
|
-
```
|
|
190
|
+
```js
|
|
191
|
+
// App_Service.name$$(proxy)
|
|
192
|
+
import {name} from './App/Service.js';
|
|
220
193
|
|
|
221
|
-
|
|
222
|
-
(
|
|
223
|
-
`OAuth2_Mod_Service`), depending on the npm packages included in the application compilation.
|
|
224
|
-
|
|
225
|
-
By using such replacements, you can implement the core functionality in one npm package, while in other npm packages,
|
|
226
|
-
you can implement the additional functionality required by the core package.
|
|
227
|
-
|
|
228
|
-
### Postprocessing
|
|
229
|
-
|
|
230
|
-
Since the container creates all objects in the application, it can also perform additional actions on newly created
|
|
231
|
-
objects, such as adding extra functionality to them in the form of a wrapper.
|
|
232
|
-
|
|
233
|
-
`@teqfw/di` enables you to add individual handlers to the post-processing stage and modify the result. For example, you
|
|
234
|
-
can wrap a finished object or perform various operations on it:
|
|
235
|
-
|
|
236
|
-
```javascript
|
|
237
|
-
// ./PostChunk.js
|
|
238
|
-
/**
|
|
239
|
-
* @implements TeqFw_Di_Api_Container_PostProcessor_Chunk
|
|
240
|
-
*/
|
|
241
|
-
export default {
|
|
242
|
-
modify: function (obj, originalId, stack) {
|
|
243
|
-
if (originalId.wrappers.indexOf('proxy') !== -1)
|
|
244
|
-
return new Proxy(obj, {
|
|
245
|
-
get: async function (base, name) { /* do something */ }
|
|
246
|
-
});
|
|
247
|
-
else return obj;
|
|
248
|
-
}
|
|
249
|
-
};
|
|
194
|
+
const res = name({/* deps */});
|
|
195
|
+
return proxy(res); // use a handler on the postprocessing
|
|
250
196
|
```
|
|
251
197
|
|
|
252
|
-
|
|
253
|
-
// ./main.js
|
|
254
|
-
import postChunk from './PostChunk.mjs';
|
|
198
|
+
### Brief overview
|
|
255
199
|
|
|
256
|
-
|
|
200
|
+
```js
|
|
201
|
+
export default class App_Main {
|
|
202
|
+
constructor(
|
|
203
|
+
{
|
|
204
|
+
App_Service: EsModule,
|
|
205
|
+
'App_Service.default': defaultExportAsIs,
|
|
206
|
+
'App_Service.name': namedExportAsIs,
|
|
207
|
+
App_Service$: defaultExportAsSingleton,
|
|
208
|
+
App_Service$$: defaultExportAsInstance,
|
|
209
|
+
'App_Service.name$': namedExportAsSingleton,
|
|
210
|
+
'App_Service.name$$': namedExportAsInstance,
|
|
211
|
+
'App_Service.name(factory)': factoryToCreateInstancesFromNamedExport,
|
|
212
|
+
}
|
|
213
|
+
) {
|
|
214
|
+
const {default: SrvDef, name: SrvName} = EsModule; // deconstruct the module and access the exports
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
}
|
|
257
218
|
```
|
|
258
219
|
|
|
259
|
-
##
|
|
220
|
+
## Summary
|
|
260
221
|
|
|
261
|
-
`@teqfw/di`
|
|
262
|
-
browser and Node.js
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
and
|
|
222
|
+
The `@teqfw/di` module provides a Dependency Injection feature for JavaScript, which requires minimal manual setup. This
|
|
223
|
+
library is functional in both browser and Node.js settings. The module utilizes late binding and an object container
|
|
224
|
+
methodology in JavaScript applications. Furthermore, it equips users with the ability to alter object behaviors through
|
|
225
|
+
pseudo-interfaces and wrappers. As a result, architectural solutions from other programming languages - namely Java,
|
|
226
|
+
PHP, and C# - can be harnessed effectively. This also contributes significantly by maximizing the efficiency of npm
|
|
227
|
+
packages and ES6 modules in JavaScript applications, especially within the Node.js environment.
|
package/RELEASE.md
CHANGED
package/bin/release/clean.sh
CHANGED
package/dist/esm.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var e={COMP_A:"A",COMP_F:"F",ID:"container",ID_FQN:"TeqFw_Di_Container$",LIFE_I:"I",LIFE_S:"S",isClass(e){const t=Object.getOwnPropertyDescriptor(e,"prototype");return t&&!t.writable}};const t=/(function)*\s*\w*\s*\(\s*\{([^\}]*)\}/s,o=/constructor\s*\(\s*\{([^\}]*)\}/s;function s(e){const t=[];try{const o=new Function(`{${e}}`,"return");o(new Proxy({},{get:(e,o)=>t.push(o)}))}catch(t){throw new Error(`Cannot analyze the deps specification:${e}\n\nPlease, be sure that spec does not contain extra ')' in a comments.\n\nError: ${t}`)}return t}function n(n){return"function"==typeof n?e.isClass(n)?function(e){const t=[],n=e.toString(),r=o.exec(n);return r&&t.push(...s(r[1])),t}(n):function(e){const o=[],n=e.toString(),r=t.exec(n);return r&&o.push(...s(r[2])),o}(n):[]}class r{constructor(){let t=!1;this.create=async function(o,s,r,i){if(r.includes(o.value))throw new Error(`Circular dependency for '${o.value}'. Parents are: ${JSON.stringify(r)}`);if(o.exportName){const a=[...r,o.value],{[o.exportName]:u}=s;if(o.composition===e.COMP_F){if("function"==typeof u){const s=n(u);s.length&&(c=`Deps for object '${o.value}' are: ${JSON.stringify(s)}`,t&&console.log(c));const r={};for(const e of s)r[e]=await i.compose(e,a);const l=e.isClass(u)?new u(r):u(r);return l instanceof Promise?await l:l}return Object.assign({},u)}return u}return s;var c},this.setDebug=function(e){t=e}}}class i{exportName;composition;life;moduleName;value;wrappers=[]}const c=/^((([A-Z])[A-Za-z0-9_]*)((\.)?([A-Za-z0-9_]*)((\$)?(\$)?)?)?(\(([A-Za-z0-9_,]*)\))?)$/;class a{canParse(e){return!0}parse(t){const o=new i;o.value=t;const s=c.exec(t);if(s&&(o.moduleName=s[2],"."===s[5]?"$"===s[7]||"$$"===s[7]?(o.composition=e.COMP_F,o.exportName=s[6],o.life="$"===s[7]?e.LIFE_S:e.LIFE_I):(o.composition=e.COMP_A,o.life=e.LIFE_S,o.exportName=""!==s[6]?s[6]:"default"):"$"===s[7]||"$$"===s[7]?(o.composition=e.COMP_F,o.exportName="default",o.life="$"===s[7]?e.LIFE_S:e.LIFE_I):(o.composition=void 0,o.exportName=void 0,o.life=void 0),s[11]&&(o.wrappers=s[11].split(","))),o.composition===e.COMP_A&&o.life===e.LIFE_I)throw new Error(`Export is not a function and should be used as a singleton only: '${o.value}'.`);return o}}class u{constructor(){let e=new a;const t=[];this.addChunk=function(e){t.push(e)},this.parse=function(o){let s;for(const e of t)if(e.canParse(o)){s=e.parse(o);break}return s||(s=e?.parse(o)),s},this.setDefaultChunk=function(t){e=t}}}class l{constructor(){const e=[];this.addChunk=function(t){e.push(t)},this.modify=function(t,o){let s=t;for(const n of e)s=n.modify(s,t,o);return s}}}class f{constructor(){const e=[];this.addChunk=function(t){e.push(t)},this.modify=async function(t,o,s){let n=t;for(const t of e)n=t.modify(n,o,s),n instanceof Promise&&(n=await n);return n}}}const p="ext",m="ns",d="root";class h{constructor(){const e={};let t=!1,o=[],s="/";this.addNamespaceRoot=function(s,n,r){const i=(t?n.replace(/^\\/,""):n).replace(/\\/g,"/"),c=t?`file://${i}`:i;e[s]={[p]:r??"js",[m]:s,[d]:c},o=Object.keys(e).sort(((e,t)=>t.localeCompare(e)))},this.resolve=function(t){let n,r,i;for(i of o)if(t.startsWith(i)){n=e[i][d],r=e[i].ext;break}if(n&&r){let e=t.replace(i,"");0===e.indexOf("_")&&(e=e.replace("_",""));const o=e.replaceAll("_",s);return`${n}${s}${o}.${r}`}return t},this.setWindowsEnv=function(e=!0){t=e,s=e?"\\":"/"}}}function $(e){return`${e.moduleName}#${e.exportName}`}class w{constructor(){let t=new r,o=!1,s=new u,n=new l,i=new f;const c={},a={};let p=new h;function m(){o&&console.log(...arguments)}this.get=async function(e){return this.compose(e,[])},this.compose=async function(o,r=[]){if(m(`Object '${o}' is requested.`),o===e.ID||o===e.ID_FQN)return m("Container itself is returned."),a[e.ID];const u=s.parse(o),l=n.modify(u,r);if(l.life===e.LIFE_S){const e=$(l);if(a[e])return m(`Existing singleton '${e}' is returned.`),a[e]}let f;c[l.moduleName]||(m(`ES6 module '${l.moduleName}' is not resolved yet`),c[l.moduleName]=p.resolve(l.moduleName));const d=c[l.moduleName];try{f=await import(d),m(`ES6 module '${l.moduleName}' is loaded from '${d}'.`)}catch(e){throw console.error(e?.message,`Object key: "${o}".`,`Path: "${d}".`,`Stack: ${JSON.stringify(r)}`),e}let h=await t.create(l,f,r,this);if(h=await i.modify(h,l,r),m(`Object '${o}' is created.`),l.life===e.LIFE_S){const e=$(l);a[e]=h,m(`Object '${o}' is saved as singleton.`)}return h},this.getParser=()=>s,this.getPreProcessor=()=>n,this.getPostProcessor=()=>i,this.getResolver=()=>p,this.setDebug=function(e){o=e,t.setDebug(e)},this.setParser=e=>s=e,this.setPreProcessor=e=>n=e,this.setPostProcessor=e=>i=e,this.setResolver=e=>p=e,a[e.ID]=this}}export{w as default};
|
package/dist/umd.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):(e="undefined"!=typeof globalThis?globalThis:e||self).TeqFw_Di_Container=t()}(this,(function(){"use strict";var e={COMP_A:"A",COMP_F:"F",ID:"container",ID_FQN:"TeqFw_Di_Container$",LIFE_I:"I",LIFE_S:"S",isClass(e){const t=Object.getOwnPropertyDescriptor(e,"prototype");return t&&!t.writable}};const t=/(function)*\s*\w*\s*\(\s*\{([^\}]*)\}/s,o=/constructor\s*\(\s*\{([^\}]*)\}/s;function n(e){const t=[];try{const o=new Function(`{${e}}`,"return");o(new Proxy({},{get:(e,o)=>t.push(o)}))}catch(t){throw new Error(`Cannot analyze the deps specification:${e}\n\nPlease, be sure that spec does not contain extra ')' in a comments.\n\nError: ${t}`)}return t}function s(s){return"function"==typeof s?e.isClass(s)?function(e){const t=[],s=e.toString(),r=o.exec(s);return r&&t.push(...n(r[1])),t}(s):function(e){const o=[],s=e.toString(),r=t.exec(s);return r&&o.push(...n(r[2])),o}(s):[]}class r{constructor(){let t=!1;this.create=async function(o,n,r,i){if(r.includes(o.value))throw new Error(`Circular dependency for '${o.value}'. Parents are: ${JSON.stringify(r)}`);if(o.exportName){const a=[...r,o.value],{[o.exportName]:u}=n;if(o.composition===e.COMP_F){if("function"==typeof u){const n=s(u);n.length&&(c=`Deps for object '${o.value}' are: ${JSON.stringify(n)}`,t&&console.log(c));const r={};for(const e of n)r[e]=await i.compose(e,a);const l=e.isClass(u)?new u(r):u(r);return l instanceof Promise?await l:l}return Object.assign({},u)}return u}return n;var c},this.setDebug=function(e){t=e}}}class i{exportName;composition;life;moduleName;value;wrappers=[]}const c=/^((([A-Z])[A-Za-z0-9_]*)((\.)?([A-Za-z0-9_]*)((\$)?(\$)?)?)?(\(([A-Za-z0-9_,]*)\))?)$/;class a{canParse(e){return!0}parse(t){const o=new i;o.value=t;const n=c.exec(t);if(n&&(o.moduleName=n[2],"."===n[5]?"$"===n[7]||"$$"===n[7]?(o.composition=e.COMP_F,o.exportName=n[6],o.life="$"===n[7]?e.LIFE_S:e.LIFE_I):(o.composition=e.COMP_A,o.life=e.LIFE_S,o.exportName=""!==n[6]?n[6]:"default"):"$"===n[7]||"$$"===n[7]?(o.composition=e.COMP_F,o.exportName="default",o.life="$"===n[7]?e.LIFE_S:e.LIFE_I):(o.composition=void 0,o.exportName=void 0,o.life=void 0),n[11]&&(o.wrappers=n[11].split(","))),o.composition===e.COMP_A&&o.life===e.LIFE_I)throw new Error(`Export is not a function and should be used as a singleton only: '${o.value}'.`);return o}}class u{constructor(){let e=new a;const t=[];this.addChunk=function(e){t.push(e)},this.parse=function(o){let n;for(const e of t)if(e.canParse(o)){n=e.parse(o);break}return n||(n=e?.parse(o)),n},this.setDefaultChunk=function(t){e=t}}}class l{constructor(){const e=[];this.addChunk=function(t){e.push(t)},this.modify=function(t,o){let n=t;for(const s of e)n=s.modify(n,t,o);return n}}}class f{constructor(){const e=[];this.addChunk=function(t){e.push(t)},this.modify=async function(t,o,n){let s=t;for(const t of e)s=t.modify(s,o,n),s instanceof Promise&&(s=await s);return s}}}const p="ext",d="ns",m="root";class h{constructor(){const e={};let t=!1,o=[],n="/";this.addNamespaceRoot=function(n,s,r){const i=(t?s.replace(/^\\/,""):s).replace(/\\/g,"/"),c=t?`file://${i}`:i;e[n]={[p]:r??"js",[d]:n,[m]:c},o=Object.keys(e).sort(((e,t)=>t.localeCompare(e)))},this.resolve=function(t){let s,r,i;for(i of o)if(t.startsWith(i)){s=e[i][m],r=e[i].ext;break}if(s&&r){let e=t.replace(i,"");0===e.indexOf("_")&&(e=e.replace("_",""));const o=e.replaceAll("_",n);return`${s}${n}${o}.${r}`}return t},this.setWindowsEnv=function(e=!0){t=e,n=e?"\\":"/"}}}function $(e){return`${e.moduleName}#${e.exportName}`}return class{constructor(){let t=new r,o=!1,n=new u,s=new l,i=new f;const c={},a={};let p=new h;function d(){o&&console.log(...arguments)}this.get=async function(e){return this.compose(e,[])},this.compose=async function(o,r=[]){if(d(`Object '${o}' is requested.`),o===e.ID||o===e.ID_FQN)return d("Container itself is returned."),a[e.ID];const u=n.parse(o),l=s.modify(u,r);if(l.life===e.LIFE_S){const e=$(l);if(a[e])return d(`Existing singleton '${e}' is returned.`),a[e]}let f;c[l.moduleName]||(d(`ES6 module '${l.moduleName}' is not resolved yet`),c[l.moduleName]=p.resolve(l.moduleName));const m=c[l.moduleName];try{f=await import(m),d(`ES6 module '${l.moduleName}' is loaded from '${m}'.`)}catch(e){throw console.error(e?.message,`Object key: "${o}".`,`Path: "${m}".`,`Stack: ${JSON.stringify(r)}`),e}let h=await t.create(l,f,r,this);if(h=await i.modify(h,l,r),d(`Object '${o}' is created.`),l.life===e.LIFE_S){const e=$(l);a[e]=h,d(`Object '${o}' is saved as singleton.`)}return h},this.getParser=()=>n,this.getPreProcessor=()=>s,this.getPostProcessor=()=>i,this.getResolver=()=>p,this.setDebug=function(e){o=e,t.setDebug(e)},this.setParser=e=>n=e,this.setPreProcessor=e=>s=e,this.setPostProcessor=e=>i=e,this.setResolver=e=>p=e,a[e.ID]=this}}}));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@teqfw/di",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.30.1",
|
|
4
4
|
"description": "Dependency Injection container for ES6 modules that works in both browser and Node.js apps.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"dependency injection",
|
|
@@ -27,19 +27,16 @@
|
|
|
27
27
|
"url": "git+https://github.com/teqfw/di.git"
|
|
28
28
|
},
|
|
29
29
|
"scripts": {
|
|
30
|
-
"
|
|
30
|
+
"rollup": "rollup -c",
|
|
31
31
|
"test": "mocha --recursive './test/**/*.test.mjs'"
|
|
32
32
|
},
|
|
33
33
|
"devDependencies": {
|
|
34
|
-
"
|
|
35
|
-
"
|
|
36
|
-
"
|
|
37
|
-
"
|
|
38
|
-
"webpack": "latest",
|
|
39
|
-
"webpack-cli": "latest"
|
|
34
|
+
"@rollup/plugin-node-resolve": "^15.2.3",
|
|
35
|
+
"mocha": "^10.7.0",
|
|
36
|
+
"rollup": "^2.79.1",
|
|
37
|
+
"rollup-plugin-terser": "^7.0.2"
|
|
40
38
|
},
|
|
41
|
-
"mocha":{
|
|
42
|
-
"require": "esm",
|
|
39
|
+
"mocha": {
|
|
43
40
|
"spec": "./test/**/*.test.mjs",
|
|
44
41
|
"timeout": 5000
|
|
45
42
|
}
|
package/rollup.config.js
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import resolve from '@rollup/plugin-node-resolve';
|
|
2
|
+
import {terser} from 'rollup-plugin-terser';
|
|
3
|
+
|
|
4
|
+
export default {
|
|
5
|
+
input: 'src/Container.js',
|
|
6
|
+
output: [
|
|
7
|
+
{
|
|
8
|
+
file: 'dist/esm.js',
|
|
9
|
+
format: 'es'
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
file: 'dist/umd.js',
|
|
13
|
+
format: 'umd',
|
|
14
|
+
name: 'TeqFw_Di_Container',
|
|
15
|
+
}
|
|
16
|
+
],
|
|
17
|
+
plugins: [
|
|
18
|
+
resolve(),
|
|
19
|
+
terser()
|
|
20
|
+
]
|
|
21
|
+
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Interface for the resolver to map a module name of the dependency ID into the path to the sources.
|
|
3
|
+
*
|
|
4
|
+
* This is not executable code, it is just for documentation purposes (similar to .h files in the C/C++ language).
|
|
5
|
+
* @interface
|
|
6
|
+
*/
|
|
7
|
+
export default class TeqFw_Di_Api_Container_Resolver {
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Convert the module name to the path of the source files .
|
|
11
|
+
* @param {string} moduleName 'Vendor_Package_Module'
|
|
12
|
+
* @return {string} '/home/user/app/node_modules/@vendor/package/src/Module.js'
|
|
13
|
+
*/
|
|
14
|
+
resolve(moduleName) {}
|
|
15
|
+
|
|
16
|
+
}
|
package/src/Api/Container.js
CHANGED
|
@@ -29,7 +29,7 @@ export default class TeqFw_Di_Api_Container {
|
|
|
29
29
|
getPostProcessor() {};
|
|
30
30
|
|
|
31
31
|
/**
|
|
32
|
-
* @return {TeqFw_Di_Container_Resolver}
|
|
32
|
+
* @return {TeqFw_Di_Container_Resolver} - the default resolver
|
|
33
33
|
*/
|
|
34
34
|
getResolver() {};
|
|
35
35
|
|
|
@@ -55,7 +55,7 @@ export default class TeqFw_Di_Api_Container {
|
|
|
55
55
|
setPostProcessor(data) {};
|
|
56
56
|
|
|
57
57
|
/**
|
|
58
|
-
* @param {
|
|
58
|
+
* @param {TeqFw_Di_Api_Container_Resolver} data
|
|
59
59
|
*/
|
|
60
60
|
setResolver(data) {};
|
|
61
61
|
};
|
|
@@ -35,7 +35,7 @@ export default class TeqFw_Di_Container_A_Composer {
|
|
|
35
35
|
// use export from the es6-module
|
|
36
36
|
const stackNew = [...stack, depId.value];
|
|
37
37
|
const {[depId.exportName]: exp} = module;
|
|
38
|
-
if (depId.composition === Defs.
|
|
38
|
+
if (depId.composition === Defs.COMP_F) {
|
|
39
39
|
if (typeof exp === 'function') {
|
|
40
40
|
// create deps for factory function
|
|
41
41
|
const deps = specParser(exp);
|
|
@@ -56,7 +56,6 @@ export default class TeqFw_Di_Container_A_Composer {
|
|
|
56
56
|
// just return the export (w/o factory function)
|
|
57
57
|
return exp;
|
|
58
58
|
} else {
|
|
59
|
-
// TODO: this is almost useless option
|
|
60
59
|
return module;
|
|
61
60
|
}
|
|
62
61
|
};
|