ditox 2.4.1 → 3.1.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/src/modules.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import {Container} from './container';
2
- import {injectable} from './utils';
3
2
  import {Token, token} from './tokens';
3
+ import {injectable} from './utils';
4
4
 
5
5
  type AnyObject = Record<string, any>;
6
6
  type EmptyObject = Record<string, never>;
@@ -73,19 +73,21 @@ export type AnyModuleDeclaration = ModuleDeclaration<Module<AnyObject>>;
73
73
  * Options for module binding.
74
74
  *
75
75
  * `scope` types:
76
- * - `singleton` - **This is the default**. The module is created and cached by the container which registered the factory.
77
- * - `scoped` - The module is created and cached by the container which starts resolving.
76
+ * - `singleton` - **This is the default**. The value is created and cached by the most distant parent container which owns the factory function.
77
+ * - `scoped` - The value is created and cached by the nearest container which owns the factory function.
78
78
  */
79
79
  export type BindModuleOptions = {
80
80
  scope?: 'scoped' | 'singleton';
81
81
  };
82
82
 
83
+ type ModuleDeclarationWithOptions = {
84
+ module: ModuleDeclaration<AnyObject>;
85
+ options: BindModuleOptions;
86
+ };
87
+
83
88
  export type ModuleBindingEntry =
84
89
  | ModuleDeclaration<AnyObject>
85
- | {
86
- module: ModuleDeclaration<AnyObject>;
87
- options: BindModuleOptions;
88
- };
90
+ | ModuleDeclarationWithOptions;
89
91
 
90
92
  /**
91
93
  * Binds the dependency module to the container
@@ -103,54 +105,94 @@ export function bindModule<T extends Module<AnyObject>>(
103
105
  moduleDeclaration: ModuleDeclaration<T>,
104
106
  options?: BindModuleOptions,
105
107
  ): void {
106
- const {token, imports, factory, beforeBinding, afterBinding} =
107
- moduleDeclaration;
108
- const exports = moduleDeclaration.exports;
108
+ const rootEntry: ModuleBindingEntry = {
109
+ module: moduleDeclaration,
110
+ options: options ?? {},
111
+ };
109
112
 
110
- const scope = options?.scope;
113
+ const bfsVisits = new Set<ModuleBindingEntry>([rootEntry]);
114
+ const bfsQueue: ModuleBindingEntry[] = [rootEntry];
115
+
116
+ let bfsIndex = 0;
117
+ while (bfsIndex < bfsQueue.length) {
118
+ const entry = bfsQueue[bfsIndex];
119
+
120
+ const m = 'module' in entry ? entry.module : entry;
121
+
122
+ m.imports?.forEach((depEntry) => {
123
+ if (!bfsVisits.has(depEntry)) {
124
+ bfsVisits.add(depEntry);
125
+ bfsQueue.push(depEntry);
126
+ }
127
+ });
128
+
129
+ bfsIndex++;
130
+ }
131
+
132
+ for (let i = 0; i < bfsQueue.length; i++) {
133
+ const entry = bfsQueue[i];
134
+ const m = 'module' in entry ? entry.module : entry;
135
+ m.beforeBinding?.(container);
136
+ }
111
137
 
112
- if (beforeBinding) {
113
- beforeBinding(container);
138
+ for (let i = 0; i < bfsQueue.length; i++) {
139
+ const entry = bfsQueue[i];
140
+ bindModuleEntry(container, entry);
114
141
  }
115
142
 
116
- if (imports) {
117
- bindModules(container, imports);
143
+ for (let i = bfsQueue.length - 1; i >= 0; i--) {
144
+ const entry = bfsQueue[i];
145
+ const m = 'module' in entry ? entry.module : entry;
146
+ m.afterBinding?.(container);
147
+ }
148
+ }
149
+
150
+ function bindModuleEntry(
151
+ container: Container,
152
+ entry: ModuleBindingEntry,
153
+ ): void {
154
+ let module: ModuleDeclaration<AnyObject>;
155
+ let options: BindModuleOptions | undefined;
156
+
157
+ if ('module' in entry) {
158
+ module = entry.module;
159
+ options = entry.options;
160
+ } else {
161
+ module = entry;
118
162
  }
119
163
 
164
+ const scope = options?.scope;
120
165
  const exportedValueTokens = new Set<Token<unknown>>();
166
+ const moduleExports = module.exports;
121
167
 
122
- if (exports) {
123
- const keys = Object.keys(exports);
168
+ if (moduleExports) {
169
+ const keys = Object.keys(moduleExports);
124
170
 
125
171
  keys.forEach((valueKey) => {
126
- const valueToken = exports[valueKey];
172
+ const valueToken = moduleExports[valueKey];
127
173
  if (valueToken) {
128
174
  exportedValueTokens.add(valueToken);
129
175
 
130
176
  container.bindFactory(
131
177
  valueToken,
132
- injectable((module) => module[valueKey], token),
178
+ injectable((module) => module[valueKey], module.token),
133
179
  {scope},
134
180
  );
135
181
  }
136
182
  });
137
183
  }
138
184
 
139
- container.bindFactory(token, factory, {
185
+ container.bindFactory(module.token, module.factory, {
140
186
  scope,
141
- onRemoved: (module) => {
142
- if (module.destroy) {
143
- module.destroy();
187
+ onRemoved: (moduleInstance) => {
188
+ if (moduleInstance.destroy) {
189
+ moduleInstance.destroy();
144
190
  }
145
191
 
146
192
  exportedValueTokens.forEach((valueToken) => container.remove(valueToken));
147
193
  exportedValueTokens.clear();
148
194
  },
149
195
  });
150
-
151
- if (afterBinding) {
152
- afterBinding(container);
153
- }
154
196
  }
155
197
 
156
198
  /**