@teqfw/di 0.35.0 → 1.0.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/.github/workflows/npm-publish.yml +48 -0
- package/AGENTS.md +6 -0
- package/CHANGELOG.md +9 -0
- package/README.md +31 -4
- package/doc/img/teqfw_di_components.drawio.png +0 -0
- package/doc/img/teqfw_di_container_steps.png +0 -0
- package/package.json +11 -9
- package/src/Container.js +0 -8
- package/src/Defs.js +0 -3
- package/src/Pre/Replace.js +80 -0
- package/teqfw.json +2 -3
- package/test/Container/A/Composer/A/SpecParser.test.mjs +98 -0
- package/test/Container/A/Parser/Chunk/Def.test.mjs +237 -0
- package/test/Container/A/Parser/Chunk/V02X.test.mjs +138 -0
- package/test/Container.test.mjs +141 -0
- package/test/_data/Container/basic/Config.js +3 -0
- package/test/_data/Container/basic/Logger.js +7 -0
- package/test/_data/Container/basic/Service.js +10 -0
- package/test/_data/Container/classes/Config.js +3 -0
- package/test/_data/Container/classes/Logger.js +7 -0
- package/test/_data/Container/classes/Service.js +15 -0
- package/test/_data/Container/loop/Config.js +7 -0
- package/test/_data/Container/loop/Logger.js +7 -0
- package/test/_data/Container/loop/Service.js +12 -0
- package/test/_data/Container/node/Config.js +3 -0
- package/test/_data/Container/node/Logger.js +7 -0
- package/test/_data/Container/node/Service.js +12 -0
- package/RELEASE.md +0 -109
- package/bin/release/clean.sh +0 -14
- package/dist/esm.js +0 -1
- package/dist/umd.js +0 -1
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
name: npm publication
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
workflow_dispatch:
|
|
5
|
+
release:
|
|
6
|
+
types: [ created ]
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
build:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
steps:
|
|
12
|
+
# Checkout repository code
|
|
13
|
+
- uses: actions/checkout@v4
|
|
14
|
+
|
|
15
|
+
# Setup Node.js environment
|
|
16
|
+
- uses: actions/setup-node@v4
|
|
17
|
+
with:
|
|
18
|
+
node-version: 20
|
|
19
|
+
|
|
20
|
+
# Install dependencies using package-lock.json
|
|
21
|
+
- run: npm ci
|
|
22
|
+
|
|
23
|
+
# Run unit tests
|
|
24
|
+
- run: npm run test
|
|
25
|
+
|
|
26
|
+
publish-npm:
|
|
27
|
+
needs: build
|
|
28
|
+
runs-on: ubuntu-latest
|
|
29
|
+
steps:
|
|
30
|
+
# Checkout repository code again for a separate job
|
|
31
|
+
- uses: actions/checkout@v4
|
|
32
|
+
|
|
33
|
+
# Setup Node.js and configure npm registry
|
|
34
|
+
- uses: actions/setup-node@v4
|
|
35
|
+
with:
|
|
36
|
+
node-version: 20
|
|
37
|
+
registry-url: https://registry.npmjs.org/
|
|
38
|
+
|
|
39
|
+
# Install dependencies again
|
|
40
|
+
- run: npm ci
|
|
41
|
+
|
|
42
|
+
# Build a code for browsers
|
|
43
|
+
- run: npm run rollup
|
|
44
|
+
|
|
45
|
+
# Publish package to npm registry with authentication token
|
|
46
|
+
- run: npm publish --access public
|
|
47
|
+
env:
|
|
48
|
+
NODE_AUTH_TOKEN: ${{ secrets.npm_token }}
|
package/AGENTS.md
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
# AGENTS
|
|
2
|
+
|
|
3
|
+
- Write all code comments, documentation, commit messages, and changelog entries in English.
|
|
4
|
+
- Record every project change in `CHANGELOG.md` under a new version `X.Y.Z` (the exact number is assigned at release).
|
|
5
|
+
- For the platform philosophy behind this library, see [PHILOSOPHY.md](./PHILOSOPHY.md).
|
|
6
|
+
|
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 1.0.0
|
|
4
|
+
- Started changelog for version 1.0.0.
|
|
5
|
+
- Added AGENTS.md with English-only guidelines and link to PHILOSOPHY.md.
|
|
6
|
+
- Switched tests from Mocha to Node's built-in runner.
|
|
7
|
+
- Updated package scripts and removed Mocha dependency.
|
|
8
|
+
- Documented breaking changes for v1.0.0: the container can no longer access itself, configuration must occur in the Composition Root, and legacy versions live in the `forerunner` branch.
|
|
9
|
+
|
package/README.md
CHANGED
|
@@ -3,6 +3,11 @@
|
|
|
3
3
|

|
|
4
4
|

|
|
5
5
|
|
|
6
|
+
> [!IMPORTANT]
|
|
7
|
+
> **Breaking Changes in v1.0.0**
|
|
8
|
+
>
|
|
9
|
+
> The library has been stable for a long time and is now promoted to its first major version. To improve security, the Object Container can no longer access itself, so all configuration must occur in the Composition Root. This restriction ensures that third-party plugins cannot override or modify the container's internal functionality. Legacy versions are maintained in the `forerunner` branch, and packages like `@teqfw/core` should depend on `@teqfw/di` versions below `1.0.0`.
|
|
10
|
+
|
|
6
11
|
`@teqfw/di` is a lightweight dependency injection container for standard JavaScript, enabling late binding of code
|
|
7
12
|
objects with minimal manual configuration. It integrates smoothly in both browser and Node.js environments, supporting
|
|
8
13
|
flexibility, modularity, and easier testing for your applications.
|
|
@@ -124,7 +129,7 @@ const app = await container.get('App_Main$');
|
|
|
124
129
|
|
|
125
130
|
## Test Mode Support
|
|
126
131
|
|
|
127
|
-
`@teqfw/di` supports a dedicated **test mode** to
|
|
132
|
+
`@teqfw/di` supports a dedicated **test mode** to facilitate unit testing and dependency mocking.
|
|
128
133
|
|
|
129
134
|
When test mode is enabled via `container.enableTestMode()`, you can manually register singleton dependencies using the
|
|
130
135
|
`register(depId, obj)` method:
|
|
@@ -134,9 +139,31 @@ container.enableTestMode();
|
|
|
134
139
|
container.register('App_Service_Customer$', mockCustomerService);
|
|
135
140
|
```
|
|
136
141
|
|
|
137
|
-
This makes it easy to substitute real implementations with mocks or stubs during tests, without
|
|
138
|
-
logic.
|
|
139
|
-
|
|
142
|
+
This makes it easy to substitute real implementations with mocks or stubs during tests, without altering production
|
|
143
|
+
logic. Overrides are allowed only in test mode, ensuring clean separation of concerns.
|
|
144
|
+
|
|
145
|
+
### Mocking Node.js Built-in Modules
|
|
146
|
+
|
|
147
|
+
A powerful feature of `@teqfw/di` is the ability to mock **Node.js built-in libraries** such as `fs`, `path`, or
|
|
148
|
+
`process`. This is useful for isolating side effects and simulating system behavior:
|
|
149
|
+
|
|
150
|
+
```js
|
|
151
|
+
container.register('node:fs', {
|
|
152
|
+
existsSync: (path) => path.endsWith('.html'),
|
|
153
|
+
});
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
You can also register mocks for custom logic or environment-specific behavior:
|
|
157
|
+
|
|
158
|
+
```js
|
|
159
|
+
container.register('node:path', {
|
|
160
|
+
join: (...args) => args.join('/'),
|
|
161
|
+
resolve: (p) => `/abs/${p}`,
|
|
162
|
+
});
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
These mocks are injected transparently wherever such modules are used as dependencies, making it possible to write pure,
|
|
166
|
+
isolated, and deterministic unit tests — even for logic that relies on the filesystem or path resolution.
|
|
140
167
|
|
|
141
168
|
---
|
|
142
169
|
|
|
Binary file
|
|
Binary file
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@teqfw/di",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "1.0.0",
|
|
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",
|
|
@@ -29,18 +29,20 @@
|
|
|
29
29
|
"scripts": {
|
|
30
30
|
"rollup": "rollup -c",
|
|
31
31
|
"eslint": "eslint './src/**/*.js'",
|
|
32
|
-
"test": "
|
|
32
|
+
"test": "node --test"
|
|
33
33
|
},
|
|
34
34
|
"devDependencies": {
|
|
35
|
-
"@eslint/js": "^9.
|
|
35
|
+
"@eslint/js": "^9.33.0",
|
|
36
36
|
"@rollup/plugin-node-resolve": "^16.0.1",
|
|
37
37
|
"@rollup/plugin-terser": "^0.4.4",
|
|
38
|
-
"eslint": "^9.
|
|
39
|
-
"
|
|
40
|
-
"rollup": "^4.36.0"
|
|
38
|
+
"eslint": "^9.33.0",
|
|
39
|
+
"rollup": "^4.47.1"
|
|
41
40
|
},
|
|
42
|
-
"
|
|
43
|
-
"
|
|
44
|
-
|
|
41
|
+
"engines": {
|
|
42
|
+
"node": ">=20"
|
|
43
|
+
},
|
|
44
|
+
"funding": {
|
|
45
|
+
"type": "individual",
|
|
46
|
+
"url": "https://github.com/sponsors/teqfw"
|
|
45
47
|
}
|
|
46
48
|
}
|
package/src/Container.js
CHANGED
|
@@ -76,11 +76,6 @@ export default class TeqFw_Di_Container {
|
|
|
76
76
|
|
|
77
77
|
this.get = async function (depId, stack = []) {
|
|
78
78
|
log(`Object '${depId}' is requested.`);
|
|
79
|
-
// return the container itself if requested
|
|
80
|
-
if ((depId === Defs.ID) || (depId === Defs.ID_FQN)) {
|
|
81
|
-
log('Container itself is returned.');
|
|
82
|
-
return _regSingles[Defs.ID];
|
|
83
|
-
}
|
|
84
79
|
// parse the `objectKey` and get the structured DTO
|
|
85
80
|
const parsed = _parser.parse(depId);
|
|
86
81
|
// modify the original key according to some rules (replacements, etc.)
|
|
@@ -188,8 +183,5 @@ export default class TeqFw_Di_Container {
|
|
|
188
183
|
this.setPostProcessor = (data) => _postProcessor = data;
|
|
189
184
|
|
|
190
185
|
this.setResolver = (data) => _resolver = data;
|
|
191
|
-
|
|
192
|
-
// MAIN
|
|
193
|
-
_regSingles[Defs.ID] = this;
|
|
194
186
|
}
|
|
195
187
|
};
|
package/src/Defs.js
CHANGED
|
@@ -5,9 +5,6 @@
|
|
|
5
5
|
export default {
|
|
6
6
|
CA: 'A', // composition: as-is
|
|
7
7
|
CF: 'F', // composition: factory
|
|
8
|
-
// TODO: we don't need an access to the container itself.
|
|
9
|
-
ID: 'container', // default ID for container itself
|
|
10
|
-
ID_FQN: 'TeqFw_Di_Container$', // default Full Qualified Name for container itself
|
|
11
8
|
LI: 'I', // lifestyle: instance
|
|
12
9
|
LS: 'S', // lifestyle: singleton
|
|
13
10
|
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pre-processor chunk that replaces module addresses during dependency resolution.
|
|
3
|
+
*
|
|
4
|
+
* This chunk allows dynamically overriding the module namespace used to resolve source code
|
|
5
|
+
* for a given dependency ID. Only the `moduleName` field of the `depId` is modified.
|
|
6
|
+
* All other metadata (such as lifestyle, export type, etc.) remains unchanged.
|
|
7
|
+
*
|
|
8
|
+
* This mechanism enables redirecting from one module implementation to another
|
|
9
|
+
* without changing aliases or component registration.
|
|
10
|
+
*
|
|
11
|
+
* Example:
|
|
12
|
+
* Replace module path 'Fl32_Cms_Back_Api_Adapter' with 'App_Cms_Adapter_Custom',
|
|
13
|
+
* while keeping the lifestyle and export configuration intact.
|
|
14
|
+
*
|
|
15
|
+
* @implements {TeqFw_Di_Api_Container_PreProcessor_Chunk}
|
|
16
|
+
*/
|
|
17
|
+
export default class TeqFw_Di_Pre_Replace {
|
|
18
|
+
|
|
19
|
+
constructor() {
|
|
20
|
+
// VARS
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Mapping of source module namespaces to their replacements.
|
|
24
|
+
* Keys and values are strings without export/lifestyle suffixes.
|
|
25
|
+
*
|
|
26
|
+
* Example:
|
|
27
|
+
* {
|
|
28
|
+
* 'Fl32_Cms_Back_Api_Adapter': 'App_Cms_Adapter_Custom'
|
|
29
|
+
* }
|
|
30
|
+
*
|
|
31
|
+
* This means: when resolving a module with `moduleName === 'Fl32_Cms_Back_Api_Adapter'`,
|
|
32
|
+
* use source code from `'App_Cms_Adapter_Custom'` instead.
|
|
33
|
+
*
|
|
34
|
+
* @type {Object<string, string>}
|
|
35
|
+
*/
|
|
36
|
+
const map = {};
|
|
37
|
+
|
|
38
|
+
// INSTANCE METHODS
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Register a single module address replacement.
|
|
42
|
+
*
|
|
43
|
+
* @param {string} orig - Original module namespace (e.g. 'X_Y_Z')
|
|
44
|
+
* @param {string} alter - Replacement module namespace (e.g. 'A_B_C')
|
|
45
|
+
*/
|
|
46
|
+
this.add = function (orig, alter) {
|
|
47
|
+
map[orig] = alter;
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
/* eslint-disable no-unused-vars */
|
|
51
|
+
/**
|
|
52
|
+
* Replace the `moduleName` of the dependency ID if a mapping exists.
|
|
53
|
+
*
|
|
54
|
+
* This function does not alter any other part of the `depId` (e.g. lifestyle, export kind).
|
|
55
|
+
*
|
|
56
|
+
* @param {TeqFw_Di_DepId} depId - Dependency ID after previous transformations.
|
|
57
|
+
* @param {TeqFw_Di_DepId} originalId - Original dependency ID before any processing.
|
|
58
|
+
* @param {string[]} stack - Stack of parent dependency IDs (for trace/debug).
|
|
59
|
+
* @returns {TeqFw_Di_DepId} - Transformed `depId` with updated `moduleName` if replaced.
|
|
60
|
+
*/
|
|
61
|
+
this.modify = function (depId, originalId, stack) {
|
|
62
|
+
let module = depId.moduleName;
|
|
63
|
+
const seen = new Set();
|
|
64
|
+
|
|
65
|
+
while (map[module] && !seen.has(module)) {
|
|
66
|
+
seen.add(module);
|
|
67
|
+
module = map[module];
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if (module !== depId.moduleName) {
|
|
71
|
+
return {
|
|
72
|
+
...depId,
|
|
73
|
+
moduleName: module,
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return depId;
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
}
|
package/teqfw.json
CHANGED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import parse from '../../../../../src/Container/A/Composer/A/SpecParser.js';
|
|
2
|
+
import {describe, it} from 'node:test';
|
|
3
|
+
import assert from 'node:assert';
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
describe('TeqFw_Di_Container_A_Composer_A_SpecParser', () => {
|
|
7
|
+
|
|
8
|
+
it('parses not a function', async () => {
|
|
9
|
+
const deps = parse('namedSingleton');
|
|
10
|
+
assert(deps, []);
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
describe('parses an arrow function', () => {
|
|
14
|
+
it('w/o arguments', async () => {
|
|
15
|
+
const fn = () => {};
|
|
16
|
+
const deps = parse(fn);
|
|
17
|
+
assert(deps, []);
|
|
18
|
+
});
|
|
19
|
+
it('with simple arguments', async () => {
|
|
20
|
+
const fn = ({container, logger, config}) => {};
|
|
21
|
+
const deps = parse(fn);
|
|
22
|
+
assert(deps, []);
|
|
23
|
+
});
|
|
24
|
+
it('with complex arguments', async () => {
|
|
25
|
+
const fn = (
|
|
26
|
+
{
|
|
27
|
+
Vnd_Pkg_Mod1,
|
|
28
|
+
Vnd_Pkg_Mod2$,
|
|
29
|
+
Vnd_Pkg_Mod3$$,
|
|
30
|
+
'Vnd_Pkg_Mod4.exp': exp,
|
|
31
|
+
}
|
|
32
|
+
) => {};
|
|
33
|
+
const deps = parse(fn);
|
|
34
|
+
assert(deps.length, 4);
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
describe('parses a regular function', () => {
|
|
39
|
+
it('w/o arguments', async () => {
|
|
40
|
+
const fn = function () {};
|
|
41
|
+
const deps = parse(fn);
|
|
42
|
+
assert(deps, []);
|
|
43
|
+
});
|
|
44
|
+
it('with simple arguments', async () => {
|
|
45
|
+
const fn = function ({container, logger, config}) {};
|
|
46
|
+
const deps = parse(fn);
|
|
47
|
+
assert(deps, []);
|
|
48
|
+
});
|
|
49
|
+
it('with complex arguments', async () => {
|
|
50
|
+
const fn = function (
|
|
51
|
+
{
|
|
52
|
+
Vnd_Pkg_Mod1,
|
|
53
|
+
Vnd_Pkg_Mod2$,
|
|
54
|
+
Vnd_Pkg_Mod3$$,
|
|
55
|
+
'Vnd_Pkg_Mod4.exp': exp,
|
|
56
|
+
}
|
|
57
|
+
) {};
|
|
58
|
+
const deps = parse(fn);
|
|
59
|
+
assert(deps.length, 4);
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
describe('parses a class', () => {
|
|
64
|
+
it('w/o arguments', async () => {
|
|
65
|
+
const Clazz = class {
|
|
66
|
+
constructor() { }
|
|
67
|
+
|
|
68
|
+
};
|
|
69
|
+
const deps = parse(Clazz);
|
|
70
|
+
assert(deps, []);
|
|
71
|
+
});
|
|
72
|
+
it('with simple arguments', async () => {
|
|
73
|
+
const Clazz = class {
|
|
74
|
+
constructor({container, logger, config}) { }
|
|
75
|
+
|
|
76
|
+
};
|
|
77
|
+
const deps = parse(Clazz);
|
|
78
|
+
assert(deps, []);
|
|
79
|
+
});
|
|
80
|
+
it('with complex arguments', async () => {
|
|
81
|
+
const Clazz = class {
|
|
82
|
+
constructor(
|
|
83
|
+
{
|
|
84
|
+
Vnd_Pkg_Mod1,
|
|
85
|
+
Vnd_Pkg_Mod2$,
|
|
86
|
+
Vnd_Pkg_Mod3$$,
|
|
87
|
+
'Vnd_Pkg_Mod4.exp': exp,
|
|
88
|
+
}
|
|
89
|
+
) { }
|
|
90
|
+
|
|
91
|
+
};
|
|
92
|
+
const deps = parse(Clazz);
|
|
93
|
+
assert(deps.length, 4);
|
|
94
|
+
});
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
});
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Default format for the objects:
|
|
3
|
+
* - Ns_Module: es6 module
|
|
4
|
+
* - Ns_Module$: default export, factory, singleton (the most frequently used case)
|
|
5
|
+
* - Ns_Module$$: default export, factory, instance
|
|
6
|
+
* - Ns_Module.default: default export, as-is, singleton
|
|
7
|
+
* - Ns_Module.name: named export, as-is, singleton
|
|
8
|
+
* - Ns_Module.name$: named export, factory, singleton (the most frequently used case)
|
|
9
|
+
* - Ns_Module.name$$: named export, factory, instance
|
|
10
|
+
* - Ns_Module$(proxy): default export, factory, singleton with one post wrapper
|
|
11
|
+
* - Ns_Module$(proxy,factory): default export, factory, singleton with two post wrappers
|
|
12
|
+
*/
|
|
13
|
+
import DefChunk from '../../../../../src/Container/A/Parser/Chunk/Def.js';
|
|
14
|
+
import {describe, it} from 'node:test';
|
|
15
|
+
import assert from 'node:assert';
|
|
16
|
+
import Defs from '../../../../../src/Defs.js';
|
|
17
|
+
|
|
18
|
+
describe('TeqFw_Di_Container_A_Parser_Chunk_Def', () => {
|
|
19
|
+
|
|
20
|
+
const chunk = new DefChunk();
|
|
21
|
+
|
|
22
|
+
describe('should parse:', () => {
|
|
23
|
+
|
|
24
|
+
describe('default export:', () => {
|
|
25
|
+
it('es6 module (Ns_Module)', () => {
|
|
26
|
+
/** @type {TeqFw_Di_DepId} */
|
|
27
|
+
const dto = chunk.parse('Ns_Module');
|
|
28
|
+
assert.strictEqual(dto.composition, undefined);
|
|
29
|
+
assert.strictEqual(dto.exportName, undefined);
|
|
30
|
+
assert.strictEqual(dto.life, undefined);
|
|
31
|
+
assert.strictEqual(dto.moduleName, 'Ns_Module');
|
|
32
|
+
assert.strictEqual(dto.origin, 'Ns_Module');
|
|
33
|
+
assert.strictEqual(dto.wrappers.length, 0);
|
|
34
|
+
});
|
|
35
|
+
it('factory, singleton (Ns_Module$)', () => {
|
|
36
|
+
/** @type {TeqFw_Di_DepId} */
|
|
37
|
+
const dto = chunk.parse('Ns_Module$');
|
|
38
|
+
assert.strictEqual(dto.composition, Defs.CF);
|
|
39
|
+
assert.strictEqual(dto.exportName, 'default');
|
|
40
|
+
assert.strictEqual(dto.life, Defs.LS);
|
|
41
|
+
assert.strictEqual(dto.moduleName, 'Ns_Module');
|
|
42
|
+
assert.strictEqual(dto.origin, 'Ns_Module$');
|
|
43
|
+
assert.strictEqual(dto.wrappers.length, 0);
|
|
44
|
+
});
|
|
45
|
+
it('factory, instance (Ns_Module$$)', () => {
|
|
46
|
+
/** @type {TeqFw_Di_DepId} */
|
|
47
|
+
const dto = chunk.parse('Ns_Module$$');
|
|
48
|
+
assert.strictEqual(dto.composition, Defs.CF);
|
|
49
|
+
assert.strictEqual(dto.exportName, 'default');
|
|
50
|
+
assert.strictEqual(dto.life, Defs.LI);
|
|
51
|
+
assert.strictEqual(dto.moduleName, 'Ns_Module');
|
|
52
|
+
assert.strictEqual(dto.origin, 'Ns_Module$$');
|
|
53
|
+
assert.strictEqual(dto.wrappers.length, 0);
|
|
54
|
+
});
|
|
55
|
+
it('default export, as-is, singleton (Ns_Module.)', () => {
|
|
56
|
+
/** @type {TeqFw_Di_DepId} */
|
|
57
|
+
const dto = chunk.parse('Ns_Module.');
|
|
58
|
+
assert.strictEqual(dto.composition, Defs.CA);
|
|
59
|
+
assert.strictEqual(dto.exportName, 'default');
|
|
60
|
+
assert.strictEqual(dto.life, Defs.LS);
|
|
61
|
+
assert.strictEqual(dto.moduleName, 'Ns_Module');
|
|
62
|
+
assert.strictEqual(dto.origin, 'Ns_Module.');
|
|
63
|
+
assert.strictEqual(dto.wrappers.length, 0);
|
|
64
|
+
});
|
|
65
|
+
it('factory, singleton, wrapper (Ns_Module$(proxy))', () => {
|
|
66
|
+
/** @type {TeqFw_Di_DepId} */
|
|
67
|
+
const dto = chunk.parse('Ns_Module$(proxy)');
|
|
68
|
+
assert.strictEqual(dto.composition, Defs.CF);
|
|
69
|
+
assert.strictEqual(dto.exportName, 'default');
|
|
70
|
+
assert.strictEqual(dto.life, Defs.LS);
|
|
71
|
+
assert.strictEqual(dto.moduleName, 'Ns_Module');
|
|
72
|
+
assert.strictEqual(dto.origin, 'Ns_Module$(proxy)');
|
|
73
|
+
assert.strictEqual(dto.wrappers.length, 1);
|
|
74
|
+
});
|
|
75
|
+
it('factory, singleton, wrappers (Ns_Module$(proxy,factory))', () => {
|
|
76
|
+
/** @type {TeqFw_Di_DepId} */
|
|
77
|
+
const dto = chunk.parse('Ns_Module$(proxy,factory)');
|
|
78
|
+
assert.strictEqual(dto.composition, Defs.CF);
|
|
79
|
+
assert.strictEqual(dto.exportName, 'default');
|
|
80
|
+
assert.strictEqual(dto.life, Defs.LS);
|
|
81
|
+
assert.strictEqual(dto.moduleName, 'Ns_Module');
|
|
82
|
+
assert.strictEqual(dto.origin, 'Ns_Module$(proxy,factory)');
|
|
83
|
+
assert.strictEqual(dto.wrappers.length, 2);
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
describe('named export:', () => {
|
|
88
|
+
it('as-is, singleton (Ns_Module.name)', () => {
|
|
89
|
+
/** @type {TeqFw_Di_DepId} */
|
|
90
|
+
const dto = chunk.parse('Ns_Module.name');
|
|
91
|
+
assert.strictEqual(dto.composition, Defs.CA);
|
|
92
|
+
assert.strictEqual(dto.exportName, 'name');
|
|
93
|
+
assert.strictEqual(dto.life, Defs.LS);
|
|
94
|
+
assert.strictEqual(dto.moduleName, 'Ns_Module');
|
|
95
|
+
assert.strictEqual(dto.origin, 'Ns_Module.name');
|
|
96
|
+
assert.strictEqual(dto.wrappers.length, 0);
|
|
97
|
+
});
|
|
98
|
+
it('factory, singleton (Ns_Module.name$)', () => {
|
|
99
|
+
/** @type {TeqFw_Di_DepId} */
|
|
100
|
+
const dto = chunk.parse('Ns_Module.name$');
|
|
101
|
+
assert.strictEqual(dto.composition, Defs.CF);
|
|
102
|
+
assert.strictEqual(dto.exportName, 'name');
|
|
103
|
+
assert.strictEqual(dto.life, Defs.LS);
|
|
104
|
+
assert.strictEqual(dto.moduleName, 'Ns_Module');
|
|
105
|
+
assert.strictEqual(dto.origin, 'Ns_Module.name$');
|
|
106
|
+
assert.strictEqual(dto.wrappers.length, 0);
|
|
107
|
+
});
|
|
108
|
+
it('factory, instance (Ns_Module.name$$)', () => {
|
|
109
|
+
/** @type {TeqFw_Di_DepId} */
|
|
110
|
+
const dto = chunk.parse('Ns_Module.name$$');
|
|
111
|
+
assert.strictEqual(dto.composition, Defs.CF);
|
|
112
|
+
assert.strictEqual(dto.exportName, 'name');
|
|
113
|
+
assert.strictEqual(dto.life, Defs.LI);
|
|
114
|
+
assert.strictEqual(dto.moduleName, 'Ns_Module');
|
|
115
|
+
assert.strictEqual(dto.origin, 'Ns_Module.name$$');
|
|
116
|
+
assert.strictEqual(dto.wrappers.length, 0);
|
|
117
|
+
});
|
|
118
|
+
it('factory, singleton.wrapper (Ns_Module.name$(proxy))', () => {
|
|
119
|
+
/** @type {TeqFw_Di_DepId} */
|
|
120
|
+
const dto = chunk.parse('Ns_Module.name$(proxy)');
|
|
121
|
+
assert.strictEqual(dto.composition, Defs.CF);
|
|
122
|
+
assert.strictEqual(dto.exportName, 'name');
|
|
123
|
+
assert.strictEqual(dto.life, Defs.LS);
|
|
124
|
+
assert.strictEqual(dto.moduleName, 'Ns_Module');
|
|
125
|
+
assert.strictEqual(dto.origin, 'Ns_Module.name$(proxy)');
|
|
126
|
+
assert.strictEqual(dto.wrappers.length, 1);
|
|
127
|
+
});
|
|
128
|
+
it('factory, singleton.wrappers (Ns_Module.name$(proxy,factory))', () => {
|
|
129
|
+
/** @type {TeqFw_Di_DepId} */
|
|
130
|
+
const dto = chunk.parse('Ns_Module.name$(proxy,factory)');
|
|
131
|
+
assert.strictEqual(dto.composition, Defs.CF);
|
|
132
|
+
assert.strictEqual(dto.exportName, 'name');
|
|
133
|
+
assert.strictEqual(dto.life, Defs.LS);
|
|
134
|
+
assert.strictEqual(dto.moduleName, 'Ns_Module');
|
|
135
|
+
assert.strictEqual(dto.origin, 'Ns_Module.name$(proxy,factory)');
|
|
136
|
+
assert.strictEqual(dto.wrappers.length, 2);
|
|
137
|
+
});
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
describe('named export with "#" separator:', () => {
|
|
141
|
+
it('as-is, singleton (Ns_Module#name)', () => {
|
|
142
|
+
const dto = chunk.parse('Ns_Module#name');
|
|
143
|
+
assert.strictEqual(dto.composition, Defs.CA);
|
|
144
|
+
assert.strictEqual(dto.exportName, 'name');
|
|
145
|
+
assert.strictEqual(dto.life, Defs.LS);
|
|
146
|
+
assert.strictEqual(dto.moduleName, 'Ns_Module');
|
|
147
|
+
assert.strictEqual(dto.origin, 'Ns_Module#name');
|
|
148
|
+
assert.strictEqual(dto.wrappers.length, 0);
|
|
149
|
+
});
|
|
150
|
+
it('factory, singleton (Ns_Module#name$)', () => {
|
|
151
|
+
const dto = chunk.parse('Ns_Module#name$');
|
|
152
|
+
assert.strictEqual(dto.composition, Defs.CF);
|
|
153
|
+
assert.strictEqual(dto.exportName, 'name');
|
|
154
|
+
assert.strictEqual(dto.life, Defs.LS);
|
|
155
|
+
assert.strictEqual(dto.moduleName, 'Ns_Module');
|
|
156
|
+
assert.strictEqual(dto.origin, 'Ns_Module#name$');
|
|
157
|
+
assert.strictEqual(dto.wrappers.length, 0);
|
|
158
|
+
});
|
|
159
|
+
it('factory, instance (Ns_Module#name$$)', () => {
|
|
160
|
+
const dto = chunk.parse('Ns_Module#name$$');
|
|
161
|
+
assert.strictEqual(dto.composition, Defs.CF);
|
|
162
|
+
assert.strictEqual(dto.exportName, 'name');
|
|
163
|
+
assert.strictEqual(dto.life, Defs.LI);
|
|
164
|
+
assert.strictEqual(dto.moduleName, 'Ns_Module');
|
|
165
|
+
assert.strictEqual(dto.origin, 'Ns_Module#name$$');
|
|
166
|
+
assert.strictEqual(dto.wrappers.length, 0);
|
|
167
|
+
});
|
|
168
|
+
it('factory, singleton with wrappers (Ns_Module#name$(proxy,factory))', () => {
|
|
169
|
+
const dto = chunk.parse('Ns_Module#name$(proxy,factory)');
|
|
170
|
+
assert.strictEqual(dto.composition, Defs.CF);
|
|
171
|
+
assert.strictEqual(dto.exportName, 'name');
|
|
172
|
+
assert.strictEqual(dto.life, Defs.LS);
|
|
173
|
+
assert.strictEqual(dto.moduleName, 'Ns_Module');
|
|
174
|
+
assert.strictEqual(dto.origin, 'Ns_Module#name$(proxy,factory)');
|
|
175
|
+
assert.strictEqual(dto.wrappers.length, 2);
|
|
176
|
+
});
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
describe('nodejs package:', () => {
|
|
180
|
+
it('es6 module (node:pkg)', () => {
|
|
181
|
+
const dto = chunk.parse('node:pkg');
|
|
182
|
+
assert.strictEqual(dto.isNodeModule, true);
|
|
183
|
+
assert.strictEqual(dto.moduleName, 'pkg');
|
|
184
|
+
assert.strictEqual(dto.composition, undefined);
|
|
185
|
+
assert.strictEqual(dto.exportName, undefined);
|
|
186
|
+
assert.strictEqual(dto.life, undefined);
|
|
187
|
+
assert.strictEqual(dto.origin, 'node:pkg');
|
|
188
|
+
});
|
|
189
|
+
it('default export, singleton (node:pkg$$)', () => {
|
|
190
|
+
const dto = chunk.parse('node:pkg$$');
|
|
191
|
+
assert.strictEqual(dto.isNodeModule, true);
|
|
192
|
+
assert.strictEqual(dto.moduleName, 'pkg');
|
|
193
|
+
assert.strictEqual(dto.composition, Defs.CF);
|
|
194
|
+
assert.strictEqual(dto.exportName, 'default');
|
|
195
|
+
assert.strictEqual(dto.life, Defs.LI);
|
|
196
|
+
assert.strictEqual(dto.origin, 'node:pkg$$');
|
|
197
|
+
});
|
|
198
|
+
it('named export (node:pkg.export)', () => {
|
|
199
|
+
const dto = chunk.parse('node:pkg.export');
|
|
200
|
+
assert.strictEqual(dto.isNodeModule, true);
|
|
201
|
+
assert.strictEqual(dto.moduleName, 'pkg');
|
|
202
|
+
assert.strictEqual(dto.composition, Defs.CA);
|
|
203
|
+
assert.strictEqual(dto.exportName, 'export');
|
|
204
|
+
assert.strictEqual(dto.life, Defs.LS);
|
|
205
|
+
assert.strictEqual(dto.origin, 'node:pkg.export');
|
|
206
|
+
});
|
|
207
|
+
it('named export, factory (node:pkg.export$$)', () => {
|
|
208
|
+
const dto = chunk.parse('node:pkg.export$$');
|
|
209
|
+
assert.strictEqual(dto.isNodeModule, true);
|
|
210
|
+
assert.strictEqual(dto.moduleName, 'pkg');
|
|
211
|
+
assert.strictEqual(dto.composition, Defs.CF);
|
|
212
|
+
assert.strictEqual(dto.exportName, 'export');
|
|
213
|
+
assert.strictEqual(dto.life, Defs.LI);
|
|
214
|
+
assert.strictEqual(dto.origin, 'node:pkg.export$$');
|
|
215
|
+
});
|
|
216
|
+
it('scoped package (node:@teqfw/db)', () => {
|
|
217
|
+
const dto = chunk.parse('node:@teqfw/db');
|
|
218
|
+
assert.strictEqual(dto.isNodeModule, true);
|
|
219
|
+
assert.strictEqual(dto.moduleName, '@teqfw/db');
|
|
220
|
+
assert.strictEqual(dto.composition, undefined);
|
|
221
|
+
assert.strictEqual(dto.exportName, undefined);
|
|
222
|
+
assert.strictEqual(dto.life, undefined);
|
|
223
|
+
assert.strictEqual(dto.origin, 'node:@teqfw/db');
|
|
224
|
+
});
|
|
225
|
+
it('scoped package export (node:@teqfw/db.export$$)', () => {
|
|
226
|
+
const dto = chunk.parse('node:@teqfw/db.export$$');
|
|
227
|
+
assert.strictEqual(dto.isNodeModule, true);
|
|
228
|
+
assert.strictEqual(dto.moduleName, '@teqfw/db');
|
|
229
|
+
assert.strictEqual(dto.composition, Defs.CF);
|
|
230
|
+
assert.strictEqual(dto.exportName, 'export');
|
|
231
|
+
assert.strictEqual(dto.life, Defs.LI);
|
|
232
|
+
assert.strictEqual(dto.origin, 'node:@teqfw/db.export$$');
|
|
233
|
+
});
|
|
234
|
+
});
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
});
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Default format for the objects:
|
|
3
|
+
* - Ns_Module: es6 module
|
|
4
|
+
* - Ns_Module$: default export, factory, singleton (the most frequently used case)
|
|
5
|
+
* - Ns_Module$I: default export, factory, instance
|
|
6
|
+
* - Ns_Module$AS: default export, as-is, singleton
|
|
7
|
+
* - Ns_Module.name: named export, as-is, singleton
|
|
8
|
+
* - Ns_Module.name$: named export, factory, singleton (the most frequently used case)
|
|
9
|
+
* - Ns_Module.name$I: named export, factory, instance
|
|
10
|
+
*/
|
|
11
|
+
import DefChunk from '../../../../../src/Container/A/Parser/Chunk/V02X.js';
|
|
12
|
+
import {describe, it} from 'node:test';
|
|
13
|
+
import assert from 'node:assert';
|
|
14
|
+
import Defs from '../../../../../src/Defs.js';
|
|
15
|
+
|
|
16
|
+
describe('TeqFw_Di_Container_A_Parser_Chunk_V02X', () => {
|
|
17
|
+
|
|
18
|
+
const chunk = new DefChunk();
|
|
19
|
+
|
|
20
|
+
describe('should parse:', () => {
|
|
21
|
+
|
|
22
|
+
describe('default export:', () => {
|
|
23
|
+
it('es6 module (Ns_Module)', () => {
|
|
24
|
+
/** @type {TeqFw_Di_DepId} */
|
|
25
|
+
const dto = chunk.parse('Ns_Module');
|
|
26
|
+
assert.strictEqual(dto.composition, undefined);
|
|
27
|
+
assert.strictEqual(dto.exportName, undefined);
|
|
28
|
+
assert.strictEqual(dto.life, undefined);
|
|
29
|
+
assert.strictEqual(dto.moduleName, 'Ns_Module');
|
|
30
|
+
assert.strictEqual(dto.origin, 'Ns_Module');
|
|
31
|
+
assert.strictEqual(dto.wrappers.length, 0);
|
|
32
|
+
});
|
|
33
|
+
it('factory, singleton (Ns_Module$)', () => {
|
|
34
|
+
/** @type {TeqFw_Di_DepId} */
|
|
35
|
+
const dto = chunk.parse('Ns_Module$');
|
|
36
|
+
assert.strictEqual(dto.composition, Defs.CF);
|
|
37
|
+
assert.strictEqual(dto.exportName, 'default');
|
|
38
|
+
assert.strictEqual(dto.life, Defs.LS);
|
|
39
|
+
assert.strictEqual(dto.moduleName, 'Ns_Module');
|
|
40
|
+
assert.strictEqual(dto.origin, 'Ns_Module$');
|
|
41
|
+
assert.strictEqual(dto.wrappers.length, 0);
|
|
42
|
+
});
|
|
43
|
+
it('factory, instance (Ns_Module$I)', () => {
|
|
44
|
+
/** @type {TeqFw_Di_DepId} */
|
|
45
|
+
const dto = chunk.parse('Ns_Module$I');
|
|
46
|
+
assert.strictEqual(dto.composition, Defs.CF);
|
|
47
|
+
assert.strictEqual(dto.exportName, 'default');
|
|
48
|
+
assert.strictEqual(dto.life, Defs.LI);
|
|
49
|
+
assert.strictEqual(dto.moduleName, 'Ns_Module');
|
|
50
|
+
assert.strictEqual(dto.origin, 'Ns_Module$I');
|
|
51
|
+
assert.strictEqual(dto.wrappers.length, 0);
|
|
52
|
+
});
|
|
53
|
+
it('default export, as-is, singleton (Ns_Module$AS)', () => {
|
|
54
|
+
/** @type {TeqFw_Di_DepId} */
|
|
55
|
+
const dto = chunk.parse('Ns_Module$AS');
|
|
56
|
+
assert.strictEqual(dto.composition, Defs.CA);
|
|
57
|
+
assert.strictEqual(dto.exportName, 'default');
|
|
58
|
+
assert.strictEqual(dto.life, Defs.LS);
|
|
59
|
+
assert.strictEqual(dto.moduleName, 'Ns_Module');
|
|
60
|
+
assert.strictEqual(dto.origin, 'Ns_Module$AS');
|
|
61
|
+
assert.strictEqual(dto.wrappers.length, 0);
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
describe('named export:', () => {
|
|
66
|
+
it('as-is, singleton (Ns_Module.name)', () => {
|
|
67
|
+
/** @type {TeqFw_Di_DepId} */
|
|
68
|
+
const dto = chunk.parse('Ns_Module.name');
|
|
69
|
+
assert.strictEqual(dto.composition, Defs.CA);
|
|
70
|
+
assert.strictEqual(dto.exportName, 'name');
|
|
71
|
+
assert.strictEqual(dto.life, Defs.LS);
|
|
72
|
+
assert.strictEqual(dto.moduleName, 'Ns_Module');
|
|
73
|
+
assert.strictEqual(dto.origin, 'Ns_Module.name');
|
|
74
|
+
assert.strictEqual(dto.wrappers.length, 0);
|
|
75
|
+
});
|
|
76
|
+
it('factory, singleton (Ns_Module.name$)', () => {
|
|
77
|
+
/** @type {TeqFw_Di_DepId} */
|
|
78
|
+
const dto = chunk.parse('Ns_Module.name$');
|
|
79
|
+
assert.strictEqual(dto.composition, Defs.CF);
|
|
80
|
+
assert.strictEqual(dto.exportName, 'name');
|
|
81
|
+
assert.strictEqual(dto.life, Defs.LS);
|
|
82
|
+
assert.strictEqual(dto.moduleName, 'Ns_Module');
|
|
83
|
+
assert.strictEqual(dto.origin, 'Ns_Module.name$');
|
|
84
|
+
assert.strictEqual(dto.wrappers.length, 0);
|
|
85
|
+
});
|
|
86
|
+
it('factory, instance (Ns_Module.name$I)', () => {
|
|
87
|
+
/** @type {TeqFw_Di_DepId} */
|
|
88
|
+
const dto = chunk.parse('Ns_Module.name$I');
|
|
89
|
+
assert.strictEqual(dto.composition, Defs.CF);
|
|
90
|
+
assert.strictEqual(dto.exportName, 'name');
|
|
91
|
+
assert.strictEqual(dto.life, Defs.LI);
|
|
92
|
+
assert.strictEqual(dto.moduleName, 'Ns_Module');
|
|
93
|
+
assert.strictEqual(dto.origin, 'Ns_Module.name$I');
|
|
94
|
+
assert.strictEqual(dto.wrappers.length, 0);
|
|
95
|
+
});
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
describe('nodejs package:', () => {
|
|
99
|
+
it('es6 module (node:pkg)', () => {
|
|
100
|
+
const dto = chunk.parse('node:pkg');
|
|
101
|
+
assert.strictEqual(dto.isNodeModule, true);
|
|
102
|
+
assert.strictEqual(dto.moduleName, 'pkg');
|
|
103
|
+
assert.strictEqual(dto.composition, undefined);
|
|
104
|
+
assert.strictEqual(dto.exportName, undefined);
|
|
105
|
+
assert.strictEqual(dto.life, undefined);
|
|
106
|
+
assert.strictEqual(dto.origin, 'node:pkg');
|
|
107
|
+
});
|
|
108
|
+
it('default export, singleton (node:pkg$)', () => {
|
|
109
|
+
const dto = chunk.parse('node:pkg$');
|
|
110
|
+
assert.strictEqual(dto.isNodeModule, true);
|
|
111
|
+
assert.strictEqual(dto.moduleName, 'pkg');
|
|
112
|
+
assert.strictEqual(dto.composition, Defs.CF);
|
|
113
|
+
assert.strictEqual(dto.exportName, 'default');
|
|
114
|
+
assert.strictEqual(dto.life, Defs.LS);
|
|
115
|
+
assert.strictEqual(dto.origin, 'node:pkg$');
|
|
116
|
+
});
|
|
117
|
+
it('named export (node:pkg.export)', () => {
|
|
118
|
+
const dto = chunk.parse('node:pkg.export');
|
|
119
|
+
assert.strictEqual(dto.isNodeModule, true);
|
|
120
|
+
assert.strictEqual(dto.moduleName, 'pkg');
|
|
121
|
+
assert.strictEqual(dto.composition, Defs.CA);
|
|
122
|
+
assert.strictEqual(dto.exportName, 'export');
|
|
123
|
+
assert.strictEqual(dto.life, Defs.LS);
|
|
124
|
+
assert.strictEqual(dto.origin, 'node:pkg.export');
|
|
125
|
+
});
|
|
126
|
+
it('named export, factory (node:pkg.export$)', () => {
|
|
127
|
+
const dto = chunk.parse('node:pkg.export$');
|
|
128
|
+
assert.strictEqual(dto.isNodeModule, true);
|
|
129
|
+
assert.strictEqual(dto.moduleName, 'pkg');
|
|
130
|
+
assert.strictEqual(dto.composition, Defs.CF);
|
|
131
|
+
assert.strictEqual(dto.exportName, 'export');
|
|
132
|
+
assert.strictEqual(dto.life, Defs.LS);
|
|
133
|
+
assert.strictEqual(dto.origin, 'node:pkg.export$');
|
|
134
|
+
});
|
|
135
|
+
});
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
});
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import {dirname, join} from 'node:path';
|
|
2
|
+
import assert from 'node:assert';
|
|
3
|
+
import {describe, it} from 'node:test';
|
|
4
|
+
import Container from '../src/Container.js';
|
|
5
|
+
import Defs from '../src/Defs.js';
|
|
6
|
+
|
|
7
|
+
const __dirname = dirname(import.meta.url);
|
|
8
|
+
const ROOT = join(__dirname, '_data', 'Container');
|
|
9
|
+
|
|
10
|
+
describe('TeqFw_Di_Container', () => {
|
|
11
|
+
|
|
12
|
+
describe('basic tests', () => {
|
|
13
|
+
it('has right classname', async () => {
|
|
14
|
+
const container = new Container();
|
|
15
|
+
assert.strictEqual(container.constructor.name, 'TeqFw_Di_Container');
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it('has all expected public methods', async () => {
|
|
19
|
+
const container = new Container();
|
|
20
|
+
const methods = Object.getOwnPropertyNames(container)
|
|
21
|
+
.filter(p => (typeof container[p] === 'function'));
|
|
22
|
+
assert.deepStrictEqual(methods.sort(), [
|
|
23
|
+
'enableTestMode',
|
|
24
|
+
'get',
|
|
25
|
+
'getParser',
|
|
26
|
+
'getPostProcessor',
|
|
27
|
+
'getPreProcessor',
|
|
28
|
+
'getResolver',
|
|
29
|
+
'register',
|
|
30
|
+
'setDebug',
|
|
31
|
+
'setParser',
|
|
32
|
+
'setPostProcessor',
|
|
33
|
+
'setPreProcessor',
|
|
34
|
+
'setResolver',
|
|
35
|
+
]);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it('does not contain itself inside', async () => {
|
|
39
|
+
const container = new Container();
|
|
40
|
+
await assert.rejects(
|
|
41
|
+
async () => {
|
|
42
|
+
await container.get('container');
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
code: 'ERR_MODULE_NOT_FOUND',
|
|
46
|
+
message: /^Cannot find package 'container'/
|
|
47
|
+
}
|
|
48
|
+
);
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
describe('creates objects', () => {
|
|
53
|
+
|
|
54
|
+
it('default export singleton (App_Service$)', async () => {
|
|
55
|
+
const container = new Container();
|
|
56
|
+
container.setDebug(true);
|
|
57
|
+
const resolver = container.getResolver();
|
|
58
|
+
const src = join(ROOT, 'classes');
|
|
59
|
+
resolver.addNamespaceRoot('App_', src, 'js');
|
|
60
|
+
const dep = await container.get('App_Service$');
|
|
61
|
+
assert(dep);
|
|
62
|
+
assert(Object.isFrozen(dep));
|
|
63
|
+
dep({boobs: 'big'});
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
describe('prevents loops', () => {
|
|
69
|
+
|
|
70
|
+
it('simple loop', async () => {
|
|
71
|
+
const container = new Container();
|
|
72
|
+
container.setDebug(true);
|
|
73
|
+
const resolver = container.getResolver();
|
|
74
|
+
const src = join(ROOT, 'loop');
|
|
75
|
+
resolver.addNamespaceRoot('App_', src, 'js');
|
|
76
|
+
try {
|
|
77
|
+
await container.get('App_Service$');
|
|
78
|
+
} catch (e) {
|
|
79
|
+
assert(e);
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
describe('register()', () => {
|
|
86
|
+
|
|
87
|
+
it('registers a new singleton successfully when test mode is enabled', async () => {
|
|
88
|
+
const container = new Container();
|
|
89
|
+
container.enableTestMode();
|
|
90
|
+
const obj = {hello: 'world'};
|
|
91
|
+
container.register('My_Test_Module$', obj);
|
|
92
|
+
const instance = await container.get('My_Test_Module$');
|
|
93
|
+
assert.strictEqual(instance, obj);
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
it('throws if already registered, even in test mode', async () => {
|
|
97
|
+
const container = new Container();
|
|
98
|
+
container.enableTestMode();
|
|
99
|
+
const obj = {hello: 'again'};
|
|
100
|
+
container.register('My_Test_Module$', obj);
|
|
101
|
+
assert.throws(() => {
|
|
102
|
+
container.register('My_Test_Module$', {oops: true});
|
|
103
|
+
}, /already registered/);
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
it('throws if test mode is not enabled', () => {
|
|
107
|
+
const container = new Container();
|
|
108
|
+
const obj = {unauthorized: true};
|
|
109
|
+
assert.throws(() => {
|
|
110
|
+
container.register('My_Test_Module$', obj);
|
|
111
|
+
}, /Use enableTestMode\(\) to allow it/);
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
it('throws if depId is missing (in test mode)', () => {
|
|
115
|
+
const container = new Container();
|
|
116
|
+
container.enableTestMode();
|
|
117
|
+
assert.throws(() => {
|
|
118
|
+
container.register(undefined, {});
|
|
119
|
+
}, /required/);
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
it('throws if object is missing (in test mode)', () => {
|
|
123
|
+
const container = new Container();
|
|
124
|
+
container.enableTestMode();
|
|
125
|
+
assert.throws(() => {
|
|
126
|
+
container.register('My_Missing_Obj$', null);
|
|
127
|
+
}, /required/);
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
it('throws if trying to register a non-singleton (in test mode)', () => {
|
|
131
|
+
const container = new Container();
|
|
132
|
+
container.enableTestMode();
|
|
133
|
+
assert.throws(() => {
|
|
134
|
+
container.register('My_Test_Module$$', {notAllowed: true});
|
|
135
|
+
}, /Only node modules & singletons can be registered/);
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
});
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export default class App_Service {
|
|
2
|
+
constructor(
|
|
3
|
+
{
|
|
4
|
+
App_Logger$: logger,
|
|
5
|
+
App_Config: config,
|
|
6
|
+
'node:http2': http2,
|
|
7
|
+
}
|
|
8
|
+
) {
|
|
9
|
+
const {constants: {HTTP2_HEADER_CONTENT_TYPE}} = http2;
|
|
10
|
+
return function (opts) {
|
|
11
|
+
logger.info(`Service '${config.appName}' is running with: ${JSON.stringify(opts)}`);
|
|
12
|
+
logger.info(`Header: ${HTTP2_HEADER_CONTENT_TYPE}`);
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
}
|
package/RELEASE.md
DELETED
|
@@ -1,109 +0,0 @@
|
|
|
1
|
-
# @teqfw/di releases
|
|
2
|
-
|
|
3
|
-
## 0.35.0 – Support for Node.js Module Mocking in Test Mode
|
|
4
|
-
|
|
5
|
-
- DI container (test mode): extended support to register not only singleton instances but also mock implementations of
|
|
6
|
-
native Node.js modules, enabling more flexible and robust unit testing.
|
|
7
|
-
- Added missing property `TeqFw_Di_DepId.isNodeModule` to properly identify Node.js modules in the dependency resolution
|
|
8
|
-
process.
|
|
9
|
-
- Improved documentation (`README.md` and `PHILOSOPHY.md`) to clarify the design philosophy of TeqFW and explain the
|
|
10
|
-
immutability and test mode features in greater detail.
|
|
11
|
-
|
|
12
|
-
## 0.34.0 – Strict Mode and Test Support
|
|
13
|
-
|
|
14
|
-
- Removed outdated interface `TeqFw_Di_Api_Container` and associated documentation.
|
|
15
|
-
- Unified the `get()` and `compose()` methods for dependency retrieval.
|
|
16
|
-
- Introduced strict mode by default: manual registration of dependencies is now **disabled** unless explicitly allowed.
|
|
17
|
-
- Added `enableTestMode()` method to the container, enabling safe, test-specific manual registration of singleton
|
|
18
|
-
dependencies.
|
|
19
|
-
- Improved error messages and safeguards against accidental overwrites in `register()`.
|
|
20
|
-
- Updated internal naming and documentation for better clarity and alignment with the platform's principles.
|
|
21
|
-
- Cleaned up ESLint and JSDoc inconsistencies in internal files.
|
|
22
|
-
|
|
23
|
-
## 0.33.0 – Cleanup and Enhancements
|
|
24
|
-
|
|
25
|
-
- Removed deprecated documentation.
|
|
26
|
-
- Updated ESLint rules for consistency with current platform practices.
|
|
27
|
-
- Introduced `#` as a separator (in addition to `.`) between namespace and export names.
|
|
28
|
-
|
|
29
|
-
## 0.32.0 - added support for the `node:` prefix
|
|
30
|
-
|
|
31
|
-
- **Added ability** to manually register objects in the container (`register`).
|
|
32
|
-
- **Optimized dependency parsing**, added support for the `node:` prefix.
|
|
33
|
-
- **Updated singleton handling**, fixed issues with `Defs.LS`.
|
|
34
|
-
- **Added protection against object modification**, freezing objects when possible (`Object.freeze`).
|
|
35
|
-
- **Updated dependencies** (Mocha, Rollup, Rollup plugins).
|
|
36
|
-
|
|
37
|
-
## 0.31.0
|
|
38
|
-
|
|
39
|
-
* Added optional `stack` parameter to the `get` method for improved dependency tracking and debugging.
|
|
40
|
-
* Fixed browser example in the README by updating the usage of `window.TeqFw_Di_Container` for compatibility.
|
|
41
|
-
|
|
42
|
-
## 0.30.2
|
|
43
|
-
|
|
44
|
-
* Enhanced the README with updates from ChatGPT.
|
|
45
|
-
* Unified JSDoc annotations.
|
|
46
|
-
|
|
47
|
-
## 0.30.1
|
|
48
|
-
|
|
49
|
-
* Improve the README.
|
|
50
|
-
|
|
51
|
-
## 0.30.0
|
|
52
|
-
|
|
53
|
-
* New format of the depId for default parser (`Ns_Module.export$$(post)`).
|
|
54
|
-
* The rollup is added.
|
|
55
|
-
|
|
56
|
-
## 0.22.0
|
|
57
|
-
|
|
58
|
-
* Add Windows paths to the Resolver.
|
|
59
|
-
|
|
60
|
-
## 0.21.1
|
|
61
|
-
|
|
62
|
-
* Fix the dependency key signature in `TeqFw_Di_Container_A_Parser_Chunk_Def`.
|
|
63
|
-
|
|
64
|
-
## 0.21.0
|
|
65
|
-
|
|
66
|
-
* Restructured modules in the package.
|
|
67
|
-
* Documentation update.
|
|
68
|
-
|
|
69
|
-
## 0.20.1
|
|
70
|
-
|
|
71
|
-
* Changed regex for parameter extraction in the Spec Analyzer.
|
|
72
|
-
* Removed leading namespace separator in the Resolver.
|
|
73
|
-
* Added `teqfw.json` descriptor to add npm-package to DI container as a sources root in `teqfw/web`.
|
|
74
|
-
|
|
75
|
-
## 0.20.0
|
|
76
|
-
|
|
77
|
-
* Fully redesigned package with simplified composition of objects in the container. Spec Analyzer is used instead of a
|
|
78
|
-
proxy object.
|
|
79
|
-
|
|
80
|
-
## 0.12.1
|
|
81
|
-
|
|
82
|
-
* Hotfix for Windows delimiters.
|
|
83
|
-
|
|
84
|
-
## 0.12.0
|
|
85
|
-
|
|
86
|
-
* Standardized comments style for improved code readability and maintenance.
|
|
87
|
-
|
|
88
|
-
## 0.11.0
|
|
89
|
-
|
|
90
|
-
* Restructure `/@teqfw/di/replace` node in `teqfw.json`.
|
|
91
|
-
* Remove `TeqFw_Di_Shared_Api_Enum_Area` enumeration.
|
|
92
|
-
* Fix example code (`npm run example`).
|
|
93
|
-
|
|
94
|
-
## 0.10.0
|
|
95
|
-
|
|
96
|
-
* Improve error messaging.
|
|
97
|
-
* Use '.' instead of '#' in depIDs (Vnd_Plugin#export => Vnd_Plugin.export). Both variants are available for now.
|
|
98
|
-
* Experimental proxy for deps are added (Vnd_Plugin.export@@).
|
|
99
|
-
|
|
100
|
-
## 0.9.0
|
|
101
|
-
|
|
102
|
-
* `TeqFw_Di_Shared_Api_Enum_Area` enumeration is added;
|
|
103
|
-
|
|
104
|
-
## 0.8.0
|
|
105
|
-
|
|
106
|
-
* docs for plugin's teq-descriptor (see in `main` branch);
|
|
107
|
-
* use object notation instead of array notation in namespace replacement statements of
|
|
108
|
-
teq-descriptor (`@teqfw/di.replace` node format is changed in `./teqfw.json`);
|
|
109
|
-
* array is used as a container for upline dependencies in the 'SpecProxy' (object was);
|
package/bin/release/clean.sh
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
##
|
|
3
|
-
# Clean development files from release branch
|
|
4
|
-
##
|
|
5
|
-
DIR_ROOT=${DIR_ROOT:-$(cd "$(dirname "$0")/../../" && pwd)}
|
|
6
|
-
|
|
7
|
-
rm -fr "${DIR_ROOT}/demo/"
|
|
8
|
-
rm -fr "${DIR_ROOT}/doc/"
|
|
9
|
-
rm -fr "${DIR_ROOT}/docs/"
|
|
10
|
-
rm -fr "${DIR_ROOT}/example/"
|
|
11
|
-
rm -fr "${DIR_ROOT}/node_modules/"
|
|
12
|
-
rm -fr "${DIR_ROOT}/package-lock.json"
|
|
13
|
-
rm -fr "${DIR_ROOT}/test/"
|
|
14
|
-
rm -fr "${DIR_ROOT}/tmp/"
|
package/dist/esm.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
var e={CA:"A",CF:"F",ID:"container",ID_FQN:"TeqFw_Di_Container$",LI:"I",LS:"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.origin))throw new Error(`Circular dependency for '${o.origin}'. Parents are: ${JSON.stringify(r)}`);if(o.exportName){const a=[...r,o.origin],{[o.exportName]:l}=n;if(o.composition===e.CF){if("function"==typeof l){const n=s(l);n.length&&(c=`Deps for object '${o.origin}' are: ${JSON.stringify(n)}`,t&&console.log(c));const r={};for(const e of n)r[e]=await i.get(e,a);const u=e.isClass(l)?new l(r):l(r);return u instanceof Promise?await u:u}return Object.assign({},l)}return l}return n;var c},this.setDebug=function(e){t=e}}}class i{exportName;composition;isNodeModule;life;moduleName;origin;wrappers=[]}const c=/^(node:)?(@?[A-Za-z0-9_-]+\/?[A-Za-z0-9_-]*)(([.#])?([A-Za-z0-9_]*)((\$)?(\$)?)?)?(\(([A-Za-z0-9_,]*)\))?$/;class a{canParse(){return!0}parse(t){const o=new i;o.origin=t;const n=c.exec(t);if(n&&(o.isNodeModule=Boolean(n[1]),o.moduleName=n[2].replace(/^node:/,""),"."===n[4]||"#"===n[4]?"$"===n[6]||"$$"===n[6]?(o.composition=e.CF,o.exportName=n[5],o.life="$"===n[6]?e.LS:e.LI):(o.composition=e.CA,o.life=e.LS,o.exportName=""!==n[5]?n[5]:"default"):"$"===n[6]||"$$"===n[6]?(o.composition=e.CF,o.exportName="default",o.life="$"===n[6]?e.LS:e.LI):(o.composition=void 0,o.exportName=void 0,o.life=void 0),n[10]&&(o.wrappers=n[10].split(","))),o.composition===e.CA&&o.life===e.LI)throw new Error(`Export is not a function and should be used as a singleton only: '${o.origin}'.`);return o}}class l{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 u{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 d="ext",p="ns",h="root";class m{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]={[d]:r??"js",[p]:n,[h]: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][h],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 g(e){return`${e.moduleName}#${e.exportName}`}class ${constructor(){let t=new r,o=!1,n=new l,s=new f,i=new u,c=!1;const a={},d={},p={};let h=new m;function $(){o&&console.log(...arguments)}this.get=async function(o,r=[]){if($(`Object '${o}' is requested.`),o===e.ID||o===e.ID_FQN)return $("Container itself is returned."),d[e.ID];const l=n.parse(o),u=i.modify(l,r);if(u.life===e.LS){const e=g(u);if(d[e])return $(`Existing singleton '${e}' is returned.`),d[e]}if(u.isNodeModule&&c){const e=u.origin;if(p[e])return $(`Existing nodejs lib '${e}' is returned.`),p[e]}let f;a[u.moduleName]||($(`ES6 module '${u.moduleName}' is not resolved yet`),a[u.moduleName]=h.resolve(u.moduleName));const m=a[u.moduleName];try{f=await import(m),$(`ES6 module '${u.moduleName}' is loaded from '${m}'.`)}catch(e){throw console.error(e?.message,`Object key: "${o}".`,`Path: "${m}".`,`Stack: ${JSON.stringify(r)}`),e}let w=await t.create(u,f,r,this);var y;if(null===(y=w)||"object"!=typeof y&&"function"!=typeof y||"[object Module]"===Object.prototype.toString.call(y)||Object.isFrozen(y)||Object.freeze(w),w=await s.modify(w,u,r),$(`Object '${o}' is created.`),u.life===e.LS){const e=g(u);d[e]=w,$(`Object '${o}' is saved as singleton.`)}return w},this.enableTestMode=function(){c=!0,$("Test mode enabled")},this.getParser=()=>n,this.getPreProcessor=()=>i,this.getPostProcessor=()=>s,this.getResolver=()=>h,this.register=function(t,o){if(!c)throw new Error("Use enableTestMode() to allow it");if(!t||!o)throw new Error("Both params are required");const s=n.parse(t);if(s.life!==e.LS&&!s.isNodeModule)throw new Error(`Only node modules & singletons can be registered: '${t}'`);if(s.life===e.LS){const e=g(s);if(d[e])throw new Error(`'${t}' is already registered`);d[e]=o}else if(s.isNodeModule){const e=s.origin;if(p[e])throw new Error(`'${t}' is already registered`);p[e]=o}$(`'${t}' is registered`)},this.setDebug=function(e){o=e,t.setDebug(e)},this.setParser=e=>n=e,this.setPreProcessor=e=>i=e,this.setPostProcessor=e=>s=e,this.setResolver=e=>h=e,d[e.ID]=this}}export{$ as default};
|
package/dist/umd.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
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={CA:"A",CF:"F",ID:"container",ID_FQN:"TeqFw_Di_Container$",LI:"I",LS:"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.origin))throw new Error(`Circular dependency for '${o.origin}'. Parents are: ${JSON.stringify(r)}`);if(o.exportName){const a=[...r,o.origin],{[o.exportName]:l}=n;if(o.composition===e.CF){if("function"==typeof l){const n=s(l);n.length&&(c=`Deps for object '${o.origin}' are: ${JSON.stringify(n)}`,t&&console.log(c));const r={};for(const e of n)r[e]=await i.get(e,a);const u=e.isClass(l)?new l(r):l(r);return u instanceof Promise?await u:u}return Object.assign({},l)}return l}return n;var c},this.setDebug=function(e){t=e}}}class i{exportName;composition;isNodeModule;life;moduleName;origin;wrappers=[]}const c=/^(node:)?(@?[A-Za-z0-9_-]+\/?[A-Za-z0-9_-]*)(([.#])?([A-Za-z0-9_]*)((\$)?(\$)?)?)?(\(([A-Za-z0-9_,]*)\))?$/;class a{canParse(){return!0}parse(t){const o=new i;o.origin=t;const n=c.exec(t);if(n&&(o.isNodeModule=Boolean(n[1]),o.moduleName=n[2].replace(/^node:/,""),"."===n[4]||"#"===n[4]?"$"===n[6]||"$$"===n[6]?(o.composition=e.CF,o.exportName=n[5],o.life="$"===n[6]?e.LS:e.LI):(o.composition=e.CA,o.life=e.LS,o.exportName=""!==n[5]?n[5]:"default"):"$"===n[6]||"$$"===n[6]?(o.composition=e.CF,o.exportName="default",o.life="$"===n[6]?e.LS:e.LI):(o.composition=void 0,o.exportName=void 0,o.life=void 0),n[10]&&(o.wrappers=n[10].split(","))),o.composition===e.CA&&o.life===e.LI)throw new Error(`Export is not a function and should be used as a singleton only: '${o.origin}'.`);return o}}class l{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 u{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 d="ext",p="ns",h="root";class m{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]={[d]:r??"js",[p]:n,[h]: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][h],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 g(e){return`${e.moduleName}#${e.exportName}`}return class{constructor(){let t=new r,o=!1,n=new l,s=new f,i=new u,c=!1;const a={},d={},p={};let h=new m;function w(){o&&console.log(...arguments)}this.get=async function(o,r=[]){if(w(`Object '${o}' is requested.`),o===e.ID||o===e.ID_FQN)return w("Container itself is returned."),d[e.ID];const l=n.parse(o),u=i.modify(l,r);if(u.life===e.LS){const e=g(u);if(d[e])return w(`Existing singleton '${e}' is returned.`),d[e]}if(u.isNodeModule&&c){const e=u.origin;if(p[e])return w(`Existing nodejs lib '${e}' is returned.`),p[e]}let f;a[u.moduleName]||(w(`ES6 module '${u.moduleName}' is not resolved yet`),a[u.moduleName]=h.resolve(u.moduleName));const m=a[u.moduleName];try{f=await import(m),w(`ES6 module '${u.moduleName}' is loaded from '${m}'.`)}catch(e){throw console.error(e?.message,`Object key: "${o}".`,`Path: "${m}".`,`Stack: ${JSON.stringify(r)}`),e}let $=await t.create(u,f,r,this);var y;if(null===(y=$)||"object"!=typeof y&&"function"!=typeof y||"[object Module]"===Object.prototype.toString.call(y)||Object.isFrozen(y)||Object.freeze($),$=await s.modify($,u,r),w(`Object '${o}' is created.`),u.life===e.LS){const e=g(u);d[e]=$,w(`Object '${o}' is saved as singleton.`)}return $},this.enableTestMode=function(){c=!0,w("Test mode enabled")},this.getParser=()=>n,this.getPreProcessor=()=>i,this.getPostProcessor=()=>s,this.getResolver=()=>h,this.register=function(t,o){if(!c)throw new Error("Use enableTestMode() to allow it");if(!t||!o)throw new Error("Both params are required");const s=n.parse(t);if(s.life!==e.LS&&!s.isNodeModule)throw new Error(`Only node modules & singletons can be registered: '${t}'`);if(s.life===e.LS){const e=g(s);if(d[e])throw new Error(`'${t}' is already registered`);d[e]=o}else if(s.isNodeModule){const e=s.origin;if(p[e])throw new Error(`'${t}' is already registered`);p[e]=o}w(`'${t}' is registered`)},this.setDebug=function(e){o=e,t.setDebug(e)},this.setParser=e=>n=e,this.setPreProcessor=e=>i=e,this.setPostProcessor=e=>s=e,this.setResolver=e=>h=e,d[e.ID]=this}}}));
|