@teqfw/di 0.12.0 → 0.20.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/.eslintrc.mjs +0 -0
- package/README.md +27 -0
- package/RELEASE.md +13 -0
- package/bin/release/clean.sh +2 -1
- package/dist/di.cjs.js +205 -0
- package/dist/di.esm.js +206 -0
- package/index.cjs +5 -0
- package/index.mjs +6 -0
- package/package.json +12 -4
- package/src/Api/ObjectKey.js +35 -0
- package/src/Composer.js +68 -0
- package/src/Container.js +142 -0
- package/src/Defs.js +25 -0
- package/src/DepId/Parser.mjs +68 -0
- package/src/Parser/Def.js +63 -0
- package/src/Parser/Old.js +108 -0
- package/src/Parser.js +65 -0
- package/src/PreProcessor/Replace.js +48 -0
- package/src/PreProcessor.js +45 -0
- package/src/Resolver.js +64 -0
- package/src/Spec/Parser.mjs +101 -0
- package/src/SpecAnalyser.js +82 -0
- package/webpack.config.mjs +15 -0
- package/src/Back/Api/Dto/Plugin/Desc.mjs +0 -70
- package/src/Back/Api/Dto/Scanned.mjs +0 -12
- package/src/Back/Api/README.md +0 -1
- package/src/Back/Defaults.mjs +0 -11
- package/src/Back/Plugin/Scanner.mjs +0 -154
- package/src/Back/README.md +0 -1
- package/src/Shared/Api/Dto/Plugin/Desc/Autoload.mjs +0 -50
- package/src/Shared/Api/IProxy.mjs +0 -11
- package/src/Shared/Container.mjs +0 -333
- package/src/Shared/IdParser/Dto.mjs +0 -96
- package/src/Shared/IdParser.mjs +0 -187
- package/src/Shared/ModuleLoader.mjs +0 -38
- package/src/Shared/README.md +0 -1
- package/src/Shared/Resolver/FilepathNs.mjs +0 -51
- package/src/Shared/Resolver/LogicalNs.mjs +0 -136
- package/src/Shared/Resolver.mjs +0 -67
- package/src/Shared/SpecProxy.mjs +0 -110
- package/teqfw.json +0 -8
package/src/Shared/Container.mjs
DELETED
|
@@ -1,333 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Dependency Injection container for Tequila Framework.
|
|
3
|
-
*
|
|
4
|
-
* @namespace TeqFw_Di_Shared_Container
|
|
5
|
-
*/
|
|
6
|
-
// MODULE'S IMPORT
|
|
7
|
-
/* don't import ES-modules with nodejs dependencies (will not work in browsers) */
|
|
8
|
-
import DAutoload from './Api/Dto/Plugin/Desc/Autoload.mjs';
|
|
9
|
-
import IdParser from './IdParser.mjs';
|
|
10
|
-
import ModuleLoader from './ModuleLoader.mjs';
|
|
11
|
-
import ParsedId from './IdParser/Dto.mjs';
|
|
12
|
-
import Resolver from './Resolver.mjs';
|
|
13
|
-
import SpecProxy from './SpecProxy.mjs';
|
|
14
|
-
|
|
15
|
-
// MODULE'S VARS
|
|
16
|
-
const $parser = new IdParser();
|
|
17
|
-
|
|
18
|
-
// MODULE'S CLASSES
|
|
19
|
-
/**
|
|
20
|
-
*
|
|
21
|
-
*/
|
|
22
|
-
export default class TeqFw_Di_Shared_Container {
|
|
23
|
-
/**
|
|
24
|
-
* @param {Object} [spec]
|
|
25
|
-
* @param {TeqFw_Di_Shared_Resolver} [spec.namespaceResolver] custom resolver to map module names to file paths
|
|
26
|
-
*/
|
|
27
|
-
constructor(spec = {}) {
|
|
28
|
-
/** @type {TeqFw_Di_Shared_Resolver} Modules loader (given in constructor or empty one). */
|
|
29
|
-
const resolver = spec.namespaceResolver || new Resolver();
|
|
30
|
-
/** @type {TeqFw_Di_Shared_ModuleLoader} */
|
|
31
|
-
const moduleLoader = new ModuleLoader(resolver);
|
|
32
|
-
/**
|
|
33
|
-
* Storage for constructors (named or default exports of ES modules) to create new objects.
|
|
34
|
-
* Module name ('Vendor_Project_Module') is a key in the map.
|
|
35
|
-
*/
|
|
36
|
-
const factories = new Map();
|
|
37
|
-
/**
|
|
38
|
-
* Storage for created instances (singletons).
|
|
39
|
-
* Module name ('Vendor_Project_Module') or singleton name ('dbConnection') is a key in the map.
|
|
40
|
-
*/
|
|
41
|
-
const singletons = new Map();
|
|
42
|
-
/**
|
|
43
|
-
* Storage for ES modules replacements (interface => implementation).
|
|
44
|
-
* Sample: {['Vnd_Plug_Interface']:'Vnd_Plug_Impl', ...}
|
|
45
|
-
* @type {Object<string, string>}
|
|
46
|
-
*/
|
|
47
|
-
const replacements = {};
|
|
48
|
-
|
|
49
|
-
// set default instance of the DI container
|
|
50
|
-
singletons.set('container', this); // as singleton
|
|
51
|
-
singletons.set('TeqFw_Di_Shared_Container', this); // as singleton of the class
|
|
52
|
-
|
|
53
|
-
// pin itself for nested functions
|
|
54
|
-
const me = this;
|
|
55
|
-
|
|
56
|
-
/**
|
|
57
|
-
* Internal function to get/create object|function|class|module by given `id`.
|
|
58
|
-
*
|
|
59
|
-
* @param {string} mainId main object ID (singleton, module, new object, default export singleton)
|
|
60
|
-
* @param {string[]} uplineDeps dependencies registry to prevent circular loop.
|
|
61
|
-
* @returns {Promise<*>}
|
|
62
|
-
*/
|
|
63
|
-
async function getObject(mainId, uplineDeps) {
|
|
64
|
-
|
|
65
|
-
// FUNCS
|
|
66
|
-
/**
|
|
67
|
-
* Add 'spec' proxy as fnConstruct argument and create new object and all deps.
|
|
68
|
-
*
|
|
69
|
-
* @param {Function|Object} fnConstruct
|
|
70
|
-
* @returns {Promise<*>} created object
|
|
71
|
-
* @private
|
|
72
|
-
*/
|
|
73
|
-
function _useFactory(fnConstruct) {
|
|
74
|
-
// This promise will be resolved after all dependencies in spec proxy will be created.
|
|
75
|
-
return new Promise(function (resolve, reject) {
|
|
76
|
-
// MAIN
|
|
77
|
-
const constructorType = typeof fnConstruct;
|
|
78
|
-
if (constructorType === 'object') {
|
|
79
|
-
// `constructor` is an object, clone this fnConstruct and return cloned object
|
|
80
|
-
const objClone = Object.assign({}, fnConstruct);
|
|
81
|
-
resolve(objClone);
|
|
82
|
-
} else if (constructorType === 'function') {
|
|
83
|
-
// `constructor` is simple function or class
|
|
84
|
-
|
|
85
|
-
/**
|
|
86
|
-
* Create new function to resolve all deps and to create requested object.
|
|
87
|
-
* This function will be called from spec proxy for every failed dependency.
|
|
88
|
-
*/
|
|
89
|
-
const fnCreate = function () {
|
|
90
|
-
try {
|
|
91
|
-
// https://stackoverflow.com/a/29094018/4073821
|
|
92
|
-
const proto = Object.getOwnPropertyDescriptor(fnConstruct, 'prototype');
|
|
93
|
-
const isClass = proto && !proto.writable;
|
|
94
|
-
const instNew = (isClass) ? new fnConstruct(spec) : fnConstruct(spec);
|
|
95
|
-
// code line below will be inaccessible until all deps will be created in `spec`
|
|
96
|
-
// SpecProxy.EXCEPTION_TO_STEALTH will be thrown for every missed dep in `spec`
|
|
97
|
-
if (instNew instanceof Promise) {
|
|
98
|
-
instNew
|
|
99
|
-
.then((asyncInst) => {
|
|
100
|
-
resolve(asyncInst)
|
|
101
|
-
}
|
|
102
|
-
).catch((e) => {
|
|
103
|
-
// SpecProxy rejects `_useFactory` promise on any error
|
|
104
|
-
if (e === SpecProxy.EXCEPTION_TO_STEALTH) {
|
|
105
|
-
// stealth constructor exceptions to prevent execution interrupt on missed dependency
|
|
106
|
-
} else {
|
|
107
|
-
throw e;
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
);
|
|
111
|
-
} else {
|
|
112
|
-
resolve(instNew);
|
|
113
|
-
}
|
|
114
|
-
} catch (e) {
|
|
115
|
-
// SpecProxy rejects `_useFactory` promise on any error
|
|
116
|
-
if (e === SpecProxy.EXCEPTION_TO_STEALTH) {
|
|
117
|
-
// stealth constructor exceptions to prevent execution interrupt on missed dependency
|
|
118
|
-
} else {
|
|
119
|
-
throw e;
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
};
|
|
123
|
-
// create spec proxy to analyze dependencies of the constructing object in current scope
|
|
124
|
-
const spec = new SpecProxy(mainId, uplineDeps, singletons, fnCreate, getObject, reject);
|
|
125
|
-
// try to create object and start chain of deps resolving in SpecProxy
|
|
126
|
-
fnCreate();
|
|
127
|
-
} else {
|
|
128
|
-
throw new Error(`Unexpected type of factory function for '${mainId}'.`);
|
|
129
|
-
}
|
|
130
|
-
// `resolve` for this promise is called from fnCreate
|
|
131
|
-
// (fnCreate is recalled from spec proxy on every dep failure)
|
|
132
|
-
});
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
/**
|
|
136
|
-
* Lookup for requested dependency in internal storages or create new one if dependency constructor
|
|
137
|
-
* is available.
|
|
138
|
-
* @param {TeqFw_Di_Shared_IdParser_Dto} parsed
|
|
139
|
-
* @returns {*}
|
|
140
|
-
*/
|
|
141
|
-
async function getFromStorages(parsed) {
|
|
142
|
-
let result;
|
|
143
|
-
// get required instance from own registries or load sources and create new one
|
|
144
|
-
if ((parsed.typeTarget === ParsedId.TYPE_TARGET_SINGLETON) && (singletons.get(parsed.mapKey) !== undefined)) {
|
|
145
|
-
// singleton was created before, just return it
|
|
146
|
-
result = singletons.get(parsed.mapKey);
|
|
147
|
-
} else if ((parsed.typeTarget === ParsedId.TYPE_TARGET_FACTORY) && (factories.get(parsed.mapKey) !== undefined)) {
|
|
148
|
-
// factory with required `id` was loaded before, create & return new object
|
|
149
|
-
const factory = factories.get(parsed.mapKey);
|
|
150
|
-
result = await _useFactory(factory);
|
|
151
|
-
}
|
|
152
|
-
return result;
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
/**
|
|
156
|
-
* Replace original ES6 module name (interface) with it's alternative (implementation).
|
|
157
|
-
* @param {string} orig
|
|
158
|
-
* @return {string}
|
|
159
|
-
*/
|
|
160
|
-
function checkReplacements(orig) {
|
|
161
|
-
while (replacements[orig]) orig = replacements[orig];
|
|
162
|
-
return orig;
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
// MAIN
|
|
166
|
-
let result;
|
|
167
|
-
/** @type {TeqFw_Di_Shared_IdParser_Dto} */
|
|
168
|
-
const parsed = $parser.parse(mainId);
|
|
169
|
-
parsed.nameModule = checkReplacements(parsed.nameModule);
|
|
170
|
-
// try to find requested dependency in local storages
|
|
171
|
-
if (!parsed.isProxy) result = await getFromStorages(parsed);
|
|
172
|
-
// if not found then try to load sources and create new one
|
|
173
|
-
if (result === undefined) {
|
|
174
|
-
// Sources for requested dependency are not imported or not set manually before.
|
|
175
|
-
// Get ES6 module from loader.
|
|
176
|
-
const moduleId = (parsed.typeId === ParsedId.TYPE_ID_FILEPATH) ?
|
|
177
|
-
parsed.namePackage + '!' + parsed.nameModule :
|
|
178
|
-
parsed.nameModule;
|
|
179
|
-
const module = await moduleLoader.getModule(moduleId);
|
|
180
|
-
if (parsed.typeTarget === ParsedId.TYPE_TARGET_MODULE) {
|
|
181
|
-
// result as ES6 module
|
|
182
|
-
result = module;
|
|
183
|
-
} else if (parsed.typeTarget === ParsedId.TYPE_TARGET_EXPORT) {
|
|
184
|
-
// result as ES6 module export
|
|
185
|
-
result = module[parsed.nameExport];
|
|
186
|
-
} else {
|
|
187
|
-
// create new object (singleton or instance) using factory
|
|
188
|
-
if (parsed.isProxy) {
|
|
189
|
-
// we need to create proxy to resolve deps for the object later
|
|
190
|
-
const depId = parsed.orig.replace(/@/gi, '$');
|
|
191
|
-
result = new Proxy({dep: undefined, depId}, {
|
|
192
|
-
get: async function (base, name) {
|
|
193
|
-
if (name === 'create') base.dep = await me.get(base.depId);
|
|
194
|
-
return base.dep;
|
|
195
|
-
}
|
|
196
|
-
});
|
|
197
|
-
} else {
|
|
198
|
-
// we need use module export as factory for new object or singleton for the first time
|
|
199
|
-
const factory = module[parsed.nameExport];
|
|
200
|
-
factories.set(parsed.mapKey, factory);
|
|
201
|
-
const object = await _useFactory(factory);
|
|
202
|
-
// save singleton object in container storage
|
|
203
|
-
if (parsed.typeTarget === ParsedId.TYPE_TARGET_SINGLETON) singletons.set(parsed.mapKey, object);
|
|
204
|
-
result = object;
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
return result;
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
/**
|
|
212
|
-
* Add replacement for ES6 modules.
|
|
213
|
-
*
|
|
214
|
-
* @param {string} orig ('Vnd_Plug_Interface')
|
|
215
|
-
* @param {string} alter ('Vnd_Plug_Impl')
|
|
216
|
-
*/
|
|
217
|
-
this.addModuleReplacement = function (orig, alter) {
|
|
218
|
-
replacements[orig] = alter;
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
/**
|
|
222
|
-
*
|
|
223
|
-
* @param {string} namespace
|
|
224
|
-
* @param {string} path
|
|
225
|
-
* @param {boolean} [is_absolute]
|
|
226
|
-
* @param {string} [ext]
|
|
227
|
-
*/
|
|
228
|
-
this.addSourceMapping = function (namespace, path, is_absolute = false, ext = 'mjs') {
|
|
229
|
-
let parsed = $parser.parseLogicalNsId(namespace);
|
|
230
|
-
if (!parsed) parsed = $parser.parseFilepathId(namespace);
|
|
231
|
-
if (
|
|
232
|
-
(parsed.typeTarget !== ParsedId.TYPE_TARGET_MODULE) &&
|
|
233
|
-
(parsed.typeTarget !== ParsedId.TYPE_TARGET_PACKAGE)
|
|
234
|
-
)
|
|
235
|
-
throw new Error('Namespace cannot contain \'$\' symbol.');
|
|
236
|
-
const details = new DAutoload();
|
|
237
|
-
Object.assign(details, {ns: namespace, path, ext, isAbsolute: is_absolute});
|
|
238
|
-
resolver.addNamespaceRoot(details);
|
|
239
|
-
};
|
|
240
|
-
|
|
241
|
-
/**
|
|
242
|
-
* Delete stored instance or import result (factory function or object) by `id` (if exist).
|
|
243
|
-
*
|
|
244
|
-
* @param {string} depId
|
|
245
|
-
*/
|
|
246
|
-
this.delete = function (depId) {
|
|
247
|
-
const parsed = $parser.parse(depId);
|
|
248
|
-
if (parsed.typeTarget === ParsedId.TYPE_TARGET_SINGLETON) {
|
|
249
|
-
singletons.delete(parsed.mapKey);
|
|
250
|
-
} else if (parsed.typeTarget === ParsedId.TYPE_TARGET_FACTORY) {
|
|
251
|
-
factories.delete(parsed.mapKey);
|
|
252
|
-
} else {
|
|
253
|
-
const errMsg = `Dependency ID is not manually inserted factory or singleton: ${depId}. `
|
|
254
|
-
+ 'See \'https://github.com/teqfw/di/blob/master/docs/identifiers.md\'.';
|
|
255
|
-
throw new Error(errMsg);
|
|
256
|
-
}
|
|
257
|
-
};
|
|
258
|
-
|
|
259
|
-
/**
|
|
260
|
-
* Get/create object|function|class|module by dependency ID (wrapper for internal function).
|
|
261
|
-
*
|
|
262
|
-
* @param {string} depId 'namedDep', 'Vendor_Module', 'New_Object_From_Default$', 'Singleton_From_Default$$'
|
|
263
|
-
* @param {String} context ID of the main object for whom container retrieves the dependency (TODO)
|
|
264
|
-
* @returns {Promise<*>}
|
|
265
|
-
*
|
|
266
|
-
* TODO: we can use context to get significant info from requester (requester depId or prepared deps
|
|
267
|
-
* TODO: /bootstrap path, for example/).
|
|
268
|
-
*/
|
|
269
|
-
this.get = async function (depId, context = null) {
|
|
270
|
-
return await getObject(depId, []);
|
|
271
|
-
};
|
|
272
|
-
|
|
273
|
-
/**
|
|
274
|
-
* @returns {TeqFw_Di_Shared_Resolver}
|
|
275
|
-
*/
|
|
276
|
-
this.getNsResolver = function () {
|
|
277
|
-
return resolver;
|
|
278
|
-
};
|
|
279
|
-
/**
|
|
280
|
-
* Check existence of created instance or imported data in container.
|
|
281
|
-
*
|
|
282
|
-
* @param {string} depId
|
|
283
|
-
* @returns {boolean}
|
|
284
|
-
*/
|
|
285
|
-
this.has = function (depId) {
|
|
286
|
-
const parsed = $parser.parse(depId);
|
|
287
|
-
if (parsed.typeTarget === ParsedId.TYPE_TARGET_SINGLETON) {
|
|
288
|
-
return singletons.has(parsed.mapKey);
|
|
289
|
-
} else if (parsed.typeTarget === ParsedId.TYPE_TARGET_FACTORY) {
|
|
290
|
-
return factories.has(parsed.mapKey);
|
|
291
|
-
} else {
|
|
292
|
-
const errMsg = `Dependency ID is not manually inserted factory or singleton: ${depId}. `
|
|
293
|
-
+ 'See \'https://github.com/teqfw/di/blob/master/docs/identifiers.md\'.';
|
|
294
|
-
throw new Error(errMsg);
|
|
295
|
-
}
|
|
296
|
-
};
|
|
297
|
-
|
|
298
|
-
/**
|
|
299
|
-
* Get list of contained dependencies (created instances and loaded modules).
|
|
300
|
-
* @returns {{constructors: string[], singletons: string[], modules: string[]}}
|
|
301
|
-
*/
|
|
302
|
-
this.list = function () {
|
|
303
|
-
const sItems = Array.from(singletons.keys()).sort();
|
|
304
|
-
const fItems = Array.from(factories.keys()).sort();
|
|
305
|
-
return {singletons: sItems, constructors: fItems};
|
|
306
|
-
};
|
|
307
|
-
|
|
308
|
-
/**
|
|
309
|
-
* Place object into the container. Replace existing instance with the same ID.
|
|
310
|
-
*
|
|
311
|
-
* 'object' should correlate with 'depId':
|
|
312
|
-
* - singleton ('namedDep'): any object will be stored as singleton;
|
|
313
|
-
* - ES module ('Vendor_Module'): will be stored as ES module;
|
|
314
|
-
* - constructor ('New_Object_From_Default$'): will be stored as object constructor with key 'New_Object_From_Default';
|
|
315
|
-
* - instance ('Singleton_From_Default$$'): will be stored as singleton with key 'Singleton_From_Default';
|
|
316
|
-
*
|
|
317
|
-
* @param {string} depId 'namedDep', 'Vendor_Module', 'New_Object_From_Default$', 'Singleton_From_Default$$'.
|
|
318
|
-
* @param {Object} object
|
|
319
|
-
*/
|
|
320
|
-
this.set = function (depId, object) {
|
|
321
|
-
const parsed = $parser.parse(depId);
|
|
322
|
-
if (parsed.typeTarget === ParsedId.TYPE_TARGET_SINGLETON) {
|
|
323
|
-
singletons.set(parsed.mapKey, object);
|
|
324
|
-
} else if (parsed.typeTarget === ParsedId.TYPE_TARGET_FACTORY) {
|
|
325
|
-
factories.set(parsed.mapKey, object);
|
|
326
|
-
} else {
|
|
327
|
-
const errMsg = `Dependency ID is not valid for factory or singleton: ${depId}. `
|
|
328
|
-
+ 'See \'https://github.com/teqfw/di/blob/master/docs/identifiers.md\'.';
|
|
329
|
-
throw new Error(errMsg);
|
|
330
|
-
}
|
|
331
|
-
};
|
|
332
|
-
}
|
|
333
|
-
}
|
|
@@ -1,96 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Data transfer object to store parsing results of identifiers for imports and injects:
|
|
3
|
-
*
|
|
4
|
-
* Manually inserted into dependency injection container:
|
|
5
|
-
* - 'namedSingleton': singleton object stored in container (dependency injection);
|
|
6
|
-
* - 'namedFactory$$': create new object using stored factory (dependency injection);
|
|
7
|
-
*
|
|
8
|
-
* Filepath based identifiers for dynamic imports and dependency injection:
|
|
9
|
-
* - '@vendor/package!module': load and get ES module using Filepath Namespaces (dynamic import);
|
|
10
|
-
* - '@vendor/package!module#': load ES module and get default export (dynamic import);
|
|
11
|
-
* - '@vendor/package!module$': load ES module, create new object from default export and use as singleton;
|
|
12
|
-
* - '@vendor/package!module$$': load ES module and use default export as factory for new objects;
|
|
13
|
-
* - '@vendor/package!module#export': load ES module and get named export;
|
|
14
|
-
* - '@vendor/package!module#export$': load ES module, create new object from named export and use as singleton;
|
|
15
|
-
* - '@vendor/package!module#export$$': load ES module and use named export as factory for new objects;
|
|
16
|
-
|
|
17
|
-
* Logical namespaces identifiers for dependency injection and dynamic imports:
|
|
18
|
-
* - 'Ns_Module': load and get ES module using Logical Namespaces (dynamic import);
|
|
19
|
-
* - 'Ns_Module#': load ES module and get default export (dynamic import);
|
|
20
|
-
* - 'Ns_Module$': load ES module, create new object from default export and use as singleton;
|
|
21
|
-
* - 'Ns_Module$$': load ES module and use default export as factory for new objects;
|
|
22
|
-
* - 'Ns_Module#export': load ES module and get named export;
|
|
23
|
-
* - 'Ns_Module#export$': load ES module, create new object from named export and use as singleton;
|
|
24
|
-
* - 'Ns_Module#export$$': load ES module and use named export as factory for new objects;
|
|
25
|
-
*
|
|
26
|
-
* @see https://github.com/teqfw/di/blob/master/docs/identifiers.md
|
|
27
|
-
*/
|
|
28
|
-
// MODULE'S CLASSES
|
|
29
|
-
/**
|
|
30
|
-
* Structure to store parsing results of identifiers for imports and injects.
|
|
31
|
-
*/
|
|
32
|
-
export default class TeqFw_Di_Shared_IdParser_Dto {
|
|
33
|
-
/**
|
|
34
|
-
* 'true' if dependency should be a proxy.
|
|
35
|
-
* @type {boolean}
|
|
36
|
-
*/
|
|
37
|
-
isProxy;
|
|
38
|
-
/**
|
|
39
|
-
* Key to map object in container's store (singletons, constructors, modules) - original id w/o '$' chars.
|
|
40
|
-
* @type {string}
|
|
41
|
-
*/
|
|
42
|
-
mapKey;
|
|
43
|
-
/**
|
|
44
|
-
* Name of an export in the ES6-module (export default fn() {}).
|
|
45
|
-
* @type {string}
|
|
46
|
-
*/
|
|
47
|
-
nameExport;
|
|
48
|
-
/**
|
|
49
|
-
* Module name: 'module', 'Ns_Module'.
|
|
50
|
-
* @type {string}
|
|
51
|
-
*/
|
|
52
|
-
nameModule;
|
|
53
|
-
/**
|
|
54
|
-
* Package name: '@vendor/package'.
|
|
55
|
-
* @type {string}
|
|
56
|
-
*/
|
|
57
|
-
namePackage;
|
|
58
|
-
/**
|
|
59
|
-
* Original identifier: namedSingleton, namedConstructor$$, @vendor/package!module#export$$, Ns_Module#export$$.
|
|
60
|
-
* @type {string}
|
|
61
|
-
*/
|
|
62
|
-
orig;
|
|
63
|
-
/**
|
|
64
|
-
* type of ID (manual, filepath based, logical namespace; see TYPE_ID_...).
|
|
65
|
-
* @type {symbol}
|
|
66
|
-
*/
|
|
67
|
-
typeId;
|
|
68
|
-
/**
|
|
69
|
-
* Type of target object identified by ID (package, module, export, factory, singleton).
|
|
70
|
-
* @type {symbol}
|
|
71
|
-
*/
|
|
72
|
-
typeTarget;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
// ES2015 (ES6) 'static' is not compatible with Safari, so add as class props
|
|
76
|
-
/** @type {symbol} marker for filepath based IDs (@vendor/package!module#exportName$$) */
|
|
77
|
-
TeqFw_Di_Shared_IdParser_Dto.TYPE_ID_FILEPATH = Symbol('TYPE_ID_FILEPATH');
|
|
78
|
-
/** @type {symbol} marker for logical namespace IDs (Ns_Module#exportName$$) */
|
|
79
|
-
TeqFw_Di_Shared_IdParser_Dto.TYPE_ID_LOGICAL = Symbol('TYPE_ID_LOGICAL');
|
|
80
|
-
/** @type {symbol} marker for IDs for manually inserted objects into DI container (singleton, factory$$) */
|
|
81
|
-
TeqFw_Di_Shared_IdParser_Dto.TYPE_ID_MANUAL = Symbol('TYPE_ID_MANUAL');
|
|
82
|
-
/** @type {symbol} marker for export targets (@vendor/package!module#exportName) */
|
|
83
|
-
TeqFw_Di_Shared_IdParser_Dto.TYPE_TARGET_EXPORT = Symbol('TYPE_TARGET_EXPORT');
|
|
84
|
-
/** @type {symbol} marker for factory targets (@vendor/package!module#exportName$$) */
|
|
85
|
-
TeqFw_Di_Shared_IdParser_Dto.TYPE_TARGET_FACTORY = Symbol('TYPE_TARGET_FACTORY');
|
|
86
|
-
/** @type {symbol} marker for module targets (@vendor/package!module) */
|
|
87
|
-
TeqFw_Di_Shared_IdParser_Dto.TYPE_TARGET_MODULE = Symbol('TYPE_TARGET_MODULE');
|
|
88
|
-
/** @type {symbol} marker for package targets (@vendor/package! - RESERVED, NOT USED) */
|
|
89
|
-
TeqFw_Di_Shared_IdParser_Dto.TYPE_TARGET_PACKAGE = Symbol('TYPE_TARGET_PACKAGE');
|
|
90
|
-
/** @type {symbol} marker for singleton targets (@vendor/package!module#exportName$) */
|
|
91
|
-
TeqFw_Di_Shared_IdParser_Dto.TYPE_TARGET_SINGLETON = Symbol('TYPE_TARGET_SINGLETON');
|
|
92
|
-
|
|
93
|
-
// This DTO does not contain Factory class because it is used internally.
|
|
94
|
-
|
|
95
|
-
// freeze class to deny attributes changes
|
|
96
|
-
Object.freeze(TeqFw_Di_Shared_IdParser_Dto);
|
package/src/Shared/IdParser.mjs
DELETED
|
@@ -1,187 +0,0 @@
|
|
|
1
|
-
// MODULE'S IMPORT
|
|
2
|
-
import ParsedId from './IdParser/Dto.mjs';
|
|
3
|
-
|
|
4
|
-
// MODULE'S VARS
|
|
5
|
-
/** @type {string} default export keyword */
|
|
6
|
-
const DEF_EXP = 'default';
|
|
7
|
-
/** @type {string} logical namespace export mark (Ns_Mod.export) */
|
|
8
|
-
const EXP = '.';
|
|
9
|
-
/** @type {string} filesystem export mark (@vendor/package!module#export$$) and old logical export mark */
|
|
10
|
-
const EXP_OLD = '#';
|
|
11
|
-
/** @type {RegExp} expression for filepath based IDs (@vendor/package!module#export$$) */
|
|
12
|
-
const FILEPATH_ID = /^((([a-z@])([A-Za-z0-9_\-/@]*))(!([A-Za-z0-9_\-/@]*)?((#)?([A-Za-z0-9_]*)(\${1,2})?)?)?)$/;
|
|
13
|
-
/** @type {string} filesystem module mark (@vendor/package!module#export$$) */
|
|
14
|
-
const FSM = '!';
|
|
15
|
-
/** @type {string} new instance mark (Ns_Mod.export$$) */
|
|
16
|
-
const INST = '$$';
|
|
17
|
-
/** @type {RegExp} expression for logical namespace IDs (Ns_Module#export$$) */
|
|
18
|
-
const LOGICAL_NS_ID = /^((([A-Z])[A-Za-z0-9_]*)((#|.)?([A-Za-z0-9_]*)(\${1,2}|@{1,2})?)?)$/;
|
|
19
|
-
/** @type {RegExp} expression for objects that manually added to DI container (singleton, namedFactory$$) */
|
|
20
|
-
const MANUAL_DI_ID = /^((([a-z])[A-Za-z0-9_]*)(\$\$)?)$/;
|
|
21
|
-
/** @type {string} new instance proxy mark (Ns_Mod.export@@) */
|
|
22
|
-
const P_INST = '@@';
|
|
23
|
-
/** @type {string} singleton proxy mark (Ns_Mod.export@) */
|
|
24
|
-
const P_SNGLT = '@';
|
|
25
|
-
/** @type {string} singleton mark (Ns_Mod.export$) */
|
|
26
|
-
const SNGLT = '$';
|
|
27
|
-
|
|
28
|
-
// MODULE'S CLASSES
|
|
29
|
-
/**
|
|
30
|
-
* Dependency identifiers parser.
|
|
31
|
-
*/
|
|
32
|
-
export default class TeqFw_Di_Shared_IdParser {
|
|
33
|
-
/**
|
|
34
|
-
* Validate dependency identifier, parse and return parts of the identifier.
|
|
35
|
-
*
|
|
36
|
-
* @param {string} id Dependency identifier to validate and parse.
|
|
37
|
-
* @returns {TeqFw_Di_Shared_IdParser_Dto} Parsed data for given ID.
|
|
38
|
-
* @throws {Error} if `id` is not valid.
|
|
39
|
-
*/
|
|
40
|
-
parse(id) {
|
|
41
|
-
let result = this.parseManualDiId(id);
|
|
42
|
-
if (!result) result = this.parseLogicalNsId(id);
|
|
43
|
-
if (!result) result = this.parseFilepathId(id);
|
|
44
|
-
if (!result)
|
|
45
|
-
throw new Error(`Invalid identifier: '${id}'. See 'https://github.com/teqfw/di/blob/master/docs/identifiers.md'.`);
|
|
46
|
-
return result;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
/**
|
|
50
|
-
* Parse filepath based identifiers (@vendor/package!module#exportedFactory$$).
|
|
51
|
-
* @param {string} id
|
|
52
|
-
* @returns {null|TeqFw_Di_Shared_IdParser_Dto}
|
|
53
|
-
*/
|
|
54
|
-
parseFilepathId(id) {
|
|
55
|
-
let result = null;
|
|
56
|
-
const parts = FILEPATH_ID.exec(id);
|
|
57
|
-
if (parts) {
|
|
58
|
-
result = new ParsedId();
|
|
59
|
-
result.orig = id;
|
|
60
|
-
result.typeId = ParsedId.TYPE_ID_FILEPATH;
|
|
61
|
-
result.namePackage = parts[2];
|
|
62
|
-
if (!parts[6]) {
|
|
63
|
-
result.typeTarget = ParsedId.TYPE_TARGET_PACKAGE;
|
|
64
|
-
} else {
|
|
65
|
-
result.nameModule = parts[6];
|
|
66
|
-
result.mapKey = parts[1];
|
|
67
|
-
result.typeTarget = ParsedId.TYPE_TARGET_MODULE;
|
|
68
|
-
if (parts[8] === EXP_OLD) {
|
|
69
|
-
result.nameExport = DEF_EXP;
|
|
70
|
-
result.typeTarget = ParsedId.TYPE_TARGET_EXPORT;
|
|
71
|
-
result.mapKey = undefined;
|
|
72
|
-
}
|
|
73
|
-
if (parts[9]) {
|
|
74
|
-
result.nameExport = parts[9];
|
|
75
|
-
result.typeTarget = ParsedId.TYPE_TARGET_EXPORT;
|
|
76
|
-
result.mapKey = undefined;
|
|
77
|
-
}
|
|
78
|
-
if (parts[10]) {
|
|
79
|
-
if (parts[10] === INST) {
|
|
80
|
-
result.typeTarget = ParsedId.TYPE_TARGET_FACTORY;
|
|
81
|
-
} else if (parts[10] === SNGLT) {
|
|
82
|
-
result.typeTarget = ParsedId.TYPE_TARGET_SINGLETON;
|
|
83
|
-
}
|
|
84
|
-
if (result.nameExport === undefined) {
|
|
85
|
-
result.nameExport = DEF_EXP;
|
|
86
|
-
result.mapKey = result.namePackage + FSM + result.nameModule;
|
|
87
|
-
} else {
|
|
88
|
-
result.mapKey = result.namePackage + FSM + result.nameModule + EXP_OLD + result.nameExport;
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
return result;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
/**
|
|
97
|
-
* Parse logical namespaces identifiers (Ns_Module#exportedFactory$$).
|
|
98
|
-
* @param {string} id
|
|
99
|
-
* @returns {null|TeqFw_Di_Shared_IdParser_Dto}
|
|
100
|
-
*/
|
|
101
|
-
parseLogicalNsId(id) {
|
|
102
|
-
let result = null;
|
|
103
|
-
const parts = LOGICAL_NS_ID.exec(id);
|
|
104
|
-
if (parts) {
|
|
105
|
-
result = new ParsedId();
|
|
106
|
-
result.orig = id;
|
|
107
|
-
result.typeId = ParsedId.TYPE_ID_LOGICAL;
|
|
108
|
-
result.nameModule = parts[2];
|
|
109
|
-
result.isProxy = false;
|
|
110
|
-
result.mapKey = result.nameModule; // init mapKey with module's name
|
|
111
|
-
result.typeTarget = ParsedId.TYPE_TARGET_MODULE;
|
|
112
|
-
// Ns_Module.name$$[@@] - named instance [proxy]
|
|
113
|
-
if (
|
|
114
|
-
((parts[5] === EXP) || (parts[5] === EXP_OLD))
|
|
115
|
-
&& ((parts[7] === INST) || (parts[7] === P_INST))
|
|
116
|
-
) {
|
|
117
|
-
result.isProxy = (parts[7] === P_INST);
|
|
118
|
-
result.nameExport = parts[6];
|
|
119
|
-
result.typeTarget = ParsedId.TYPE_TARGET_FACTORY;
|
|
120
|
-
result.mapKey = result.nameModule + EXP + result.nameExport;
|
|
121
|
-
}
|
|
122
|
-
// Ns_Module.name$[@] - named singleton [proxy]
|
|
123
|
-
else if (
|
|
124
|
-
((parts[5] === EXP) || (parts[5] === EXP_OLD))
|
|
125
|
-
&& ((parts[7] === SNGLT) || (parts[7] === P_SNGLT))
|
|
126
|
-
) {
|
|
127
|
-
result.isProxy = (parts[7] === P_SNGLT);
|
|
128
|
-
result.nameExport = parts[6];
|
|
129
|
-
result.typeTarget = ParsedId.TYPE_TARGET_SINGLETON;
|
|
130
|
-
result.mapKey = result.nameModule + EXP + result.nameExport;
|
|
131
|
-
}
|
|
132
|
-
// Ns_Module.name - named export
|
|
133
|
-
else if (
|
|
134
|
-
((parts[5] === EXP) || (parts[5] === EXP_OLD))
|
|
135
|
-
&& ((parts[6] !== undefined) && (parts[6] !== ''))
|
|
136
|
-
) {
|
|
137
|
-
result.nameExport = parts[6];
|
|
138
|
-
result.typeTarget = ParsedId.TYPE_TARGET_EXPORT;
|
|
139
|
-
result.mapKey = undefined;
|
|
140
|
-
}
|
|
141
|
-
// Ns_Module$$[@@]- default instance [proxy]
|
|
142
|
-
else if ((parts[4] === INST) || (parts[4] === P_INST)) {
|
|
143
|
-
result.isProxy = (parts[4] === P_INST);
|
|
144
|
-
result.nameExport = DEF_EXP;
|
|
145
|
-
result.typeTarget = ParsedId.TYPE_TARGET_FACTORY;
|
|
146
|
-
}
|
|
147
|
-
// Ns_Module$[@] - default singleton [proxy]
|
|
148
|
-
else if ((parts[4] === SNGLT) || (parts[4] === P_SNGLT)) {
|
|
149
|
-
result.isProxy = (parts[4] === P_SNGLT);
|
|
150
|
-
result.nameExport = DEF_EXP;
|
|
151
|
-
result.typeTarget = ParsedId.TYPE_TARGET_SINGLETON;
|
|
152
|
-
}
|
|
153
|
-
// Ns_Module#[.] - default export
|
|
154
|
-
else if (
|
|
155
|
-
((parts[5] === EXP) || (parts[5] === EXP_OLD))
|
|
156
|
-
&& (parts[7] === undefined)
|
|
157
|
-
) {
|
|
158
|
-
result.nameExport = DEF_EXP;
|
|
159
|
-
result.typeTarget = ParsedId.TYPE_TARGET_EXPORT;
|
|
160
|
-
result.mapKey = undefined;
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
return result;
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
/**
|
|
167
|
-
* Parse manually inserted identifiers (singleton, factory$$).
|
|
168
|
-
* @param {string} id
|
|
169
|
-
* @returns {null|TeqFw_Di_Shared_IdParser_Dto}
|
|
170
|
-
*/
|
|
171
|
-
parseManualDiId(id) {
|
|
172
|
-
let result = null;
|
|
173
|
-
const parts = MANUAL_DI_ID.exec(id);
|
|
174
|
-
if (parts) {
|
|
175
|
-
result = new ParsedId();
|
|
176
|
-
result.orig = id;
|
|
177
|
-
result.typeId = ParsedId.TYPE_ID_MANUAL;
|
|
178
|
-
result.mapKey = parts[2];
|
|
179
|
-
if (parts[4] === '$$') {
|
|
180
|
-
result.typeTarget = ParsedId.TYPE_TARGET_FACTORY;
|
|
181
|
-
} else {
|
|
182
|
-
result.typeTarget = ParsedId.TYPE_TARGET_SINGLETON;
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
return result;
|
|
186
|
-
}
|
|
187
|
-
}
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
// MODULE'S CLASSES
|
|
2
|
-
/**
|
|
3
|
-
* Load ES6 module sources and save it to internal registry.
|
|
4
|
-
*/
|
|
5
|
-
export default class TeqFw_Di_Shared_ModuleLoader {
|
|
6
|
-
/** @type {TeqFw_Di_Shared_Resolver} */
|
|
7
|
-
resolver
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* @param {TeqFw_Di_Shared_Resolver} resolver
|
|
11
|
-
*/
|
|
12
|
-
constructor(resolver) {
|
|
13
|
-
this.resolver = resolver;
|
|
14
|
-
/**
|
|
15
|
-
* Storage for loaded modules.
|
|
16
|
-
* Module name ('Vendor_Project_Module') is a key in the map.
|
|
17
|
-
*/
|
|
18
|
-
const _modules = new Map();
|
|
19
|
-
|
|
20
|
-
this.getModule = async function (moduleId) {
|
|
21
|
-
let result, sourceFile;
|
|
22
|
-
if (_modules[moduleId]) {
|
|
23
|
-
result = _modules[moduleId];
|
|
24
|
-
} else {
|
|
25
|
-
try {
|
|
26
|
-
sourceFile = this.resolver.resolveModuleId(moduleId);
|
|
27
|
-
result = await import(sourceFile);
|
|
28
|
-
_modules.set(moduleId, result);
|
|
29
|
-
} catch (e) {
|
|
30
|
-
let msg = `Cannot load source file '${sourceFile}' (module id: ${moduleId}).`;
|
|
31
|
-
if (e.message) msg = `${msg} Error: ${e.message}`;
|
|
32
|
-
throw new Error(msg);
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
return result;
|
|
36
|
-
};
|
|
37
|
-
}
|
|
38
|
-
}
|
package/src/Shared/README.md
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
This code can be used in browsers and nodejs apps.
|