@teqfw/di 0.35.0 → 0.36.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +26 -4
- package/RELEASE.md +7 -0
- package/package.json +1 -1
- package/src/Pre/Replace.js +80 -0
package/README.md
CHANGED
|
@@ -124,7 +124,7 @@ const app = await container.get('App_Main$');
|
|
|
124
124
|
|
|
125
125
|
## Test Mode Support
|
|
126
126
|
|
|
127
|
-
`@teqfw/di` supports a dedicated **test mode** to
|
|
127
|
+
`@teqfw/di` supports a dedicated **test mode** to facilitate unit testing and dependency mocking.
|
|
128
128
|
|
|
129
129
|
When test mode is enabled via `container.enableTestMode()`, you can manually register singleton dependencies using the
|
|
130
130
|
`register(depId, obj)` method:
|
|
@@ -134,9 +134,31 @@ container.enableTestMode();
|
|
|
134
134
|
container.register('App_Service_Customer$', mockCustomerService);
|
|
135
135
|
```
|
|
136
136
|
|
|
137
|
-
This makes it easy to substitute real implementations with mocks or stubs during tests, without
|
|
138
|
-
logic.
|
|
139
|
-
|
|
137
|
+
This makes it easy to substitute real implementations with mocks or stubs during tests, without altering production
|
|
138
|
+
logic. Overrides are allowed only in test mode, ensuring clean separation of concerns.
|
|
139
|
+
|
|
140
|
+
### Mocking Node.js Built-in Modules
|
|
141
|
+
|
|
142
|
+
A powerful feature of `@teqfw/di` is the ability to mock **Node.js built-in libraries** such as `fs`, `path`, or
|
|
143
|
+
`process`. This is useful for isolating side effects and simulating system behavior:
|
|
144
|
+
|
|
145
|
+
```js
|
|
146
|
+
container.register('node:fs', {
|
|
147
|
+
existsSync: (path) => path.endsWith('.html'),
|
|
148
|
+
});
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
You can also register mocks for custom logic or environment-specific behavior:
|
|
152
|
+
|
|
153
|
+
```js
|
|
154
|
+
container.register('node:path', {
|
|
155
|
+
join: (...args) => args.join('/'),
|
|
156
|
+
resolve: (p) => `/abs/${p}`,
|
|
157
|
+
});
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
These mocks are injected transparently wherever such modules are used as dependencies, making it possible to write pure,
|
|
161
|
+
isolated, and deterministic unit tests — even for logic that relies on the filesystem or path resolution.
|
|
140
162
|
|
|
141
163
|
---
|
|
142
164
|
|
package/RELEASE.md
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
# @teqfw/di releases
|
|
2
2
|
|
|
3
|
+
## 0.36.0 – New Preprocessor for Dynamic Dependency Rewriting
|
|
4
|
+
|
|
5
|
+
- Introduced `TeqFw_Di_Pre_Replace`, a new preprocessor allowing runtime substitution of dependency identifiers before
|
|
6
|
+
object creation, enabling advanced customization scenarios.
|
|
7
|
+
- Improved documentation (`README.md`): added concrete examples of mocking native Node.js modules during testing, better
|
|
8
|
+
illustrating the power of test mode and late binding.
|
|
9
|
+
|
|
3
10
|
## 0.35.0 – Support for Node.js Module Mocking in Test Mode
|
|
4
11
|
|
|
5
12
|
- DI container (test mode): extended support to register not only singleton instances but also mock implementations of
|
package/package.json
CHANGED
|
@@ -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
|
+
}
|