@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 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 help with unit testing and dependency mocking.
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 affecting production
138
- logic. Test mode safeguards this capability, ensuring that manual overrides are only permitted in designated test
139
- environments.
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@teqfw/di",
3
- "version": "0.35.0",
3
+ "version": "0.36.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",
@@ -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
+ }