@teqfw/di 0.20.0 → 0.21.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 +236 -73
- package/RELEASE.md +12 -1
- package/dist/di.cjs.js +50 -50
- package/dist/di.esm.js +50 -50
- package/package.json +15 -7
- package/src/Api/Container/Parser/Chunk.js +23 -0
- package/src/Api/Container/Parser.js +28 -0
- package/src/Api/Container/PostProcessor/Chunk.js +17 -0
- package/src/Api/Container/PostProcessor.js +25 -0
- package/src/Api/Container/PreProcessor/Chunk.js +17 -0
- package/src/Api/Container/PreProcessor.js +23 -0
- package/src/Api/Container.js +61 -0
- package/src/{SpecAnalyser.js → Container/A/Composer/A/SpecParser.js} +11 -7
- package/src/Container/A/Composer.js +70 -0
- package/src/Container/A/Parser/Chunk/Def.js +70 -0
- package/src/Container/Parser.js +48 -0
- package/src/Container/PostProcessor.js +32 -0
- package/src/Container/PreProcessor.js +34 -0
- package/src/{Resolver.js → Container/Resolver.js} +4 -3
- package/src/Container.js +62 -55
- package/src/Defs.js +5 -1
- package/src/{Api/ObjectKey.js → DepId.js} +2 -1
- package/teqfw.json +9 -0
- package/src/Composer.js +0 -68
- package/src/DepId/Parser.mjs +0 -68
- package/src/Parser/Def.js +0 -63
- package/src/Parser/Old.js +0 -108
- package/src/Parser.js +0 -65
- package/src/PreProcessor/Replace.js +0 -48
- package/src/PreProcessor.js +0 -45
- package/src/Spec/Parser.mjs +0 -101
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The composer creates requested objects. It uses the container to create dependencies.
|
|
3
|
+
*
|
|
4
|
+
* @namespace TeqFw_Di_Container_A_Composer
|
|
5
|
+
*/
|
|
6
|
+
import Defs from '../../Defs.js';
|
|
7
|
+
import specParser from './Composer/A/SpecParser.js';
|
|
8
|
+
|
|
9
|
+
export default class TeqFw_Di_Container_A_Composer {
|
|
10
|
+
|
|
11
|
+
constructor() {
|
|
12
|
+
// VARS
|
|
13
|
+
let _debug = false;
|
|
14
|
+
|
|
15
|
+
// FUNCS
|
|
16
|
+
function log(msg) {
|
|
17
|
+
if (_debug) console.log(msg);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// INSTANCE METHODS
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Returns or creates and returns the requested object.
|
|
24
|
+
*
|
|
25
|
+
* @param {TeqFw_Di_DepId} depId
|
|
26
|
+
* @param {Object} module - imported es6 module
|
|
27
|
+
* @param {string[]} stack - array of the parent objects IDs to prevent dependency loop
|
|
28
|
+
* @param {TeqFw_Di_Container} container - to create dependencies for requested object
|
|
29
|
+
* @return {Promise<*>}
|
|
30
|
+
*/
|
|
31
|
+
this.create = async function (depId, module, stack, container) {
|
|
32
|
+
if (stack.includes(depId.value))
|
|
33
|
+
throw new Error(`Circular dependency for '${depId.value}'. Parents are: ${JSON.stringify(stack)}`);
|
|
34
|
+
if (depId.exportName) {
|
|
35
|
+
// use export from the es6-module
|
|
36
|
+
const stackNew = [...stack, depId.value];
|
|
37
|
+
const {[depId.exportName]: exp} = module;
|
|
38
|
+
if (depId.composition === Defs.COMPOSE_FACTORY) {
|
|
39
|
+
if (typeof exp === 'function') {
|
|
40
|
+
// create deps for factory function
|
|
41
|
+
const deps = specParser(exp);
|
|
42
|
+
if (deps.length) log(`Deps for object '${depId.value}' are: ${JSON.stringify(deps)}`);
|
|
43
|
+
const spec = {};
|
|
44
|
+
for (const dep of deps)
|
|
45
|
+
spec[dep] = await container.compose(dep, stackNew);
|
|
46
|
+
// create a new object with the factory function
|
|
47
|
+
const res = (Defs.isClass(exp)) ? new exp(spec) : exp(spec);
|
|
48
|
+
if (res instanceof Promise)
|
|
49
|
+
return await res;
|
|
50
|
+
else
|
|
51
|
+
return res;
|
|
52
|
+
} else
|
|
53
|
+
// just clone the export
|
|
54
|
+
return Object.assign({}, exp);
|
|
55
|
+
} else
|
|
56
|
+
// just return the export (w/o factory function)
|
|
57
|
+
return exp;
|
|
58
|
+
} else {
|
|
59
|
+
// TODO: this is almost useless option
|
|
60
|
+
return module;
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
this.setDebug = function (data) {
|
|
65
|
+
_debug = data;
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
// MAIN
|
|
69
|
+
}
|
|
70
|
+
};
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Default parser for object keys in format:
|
|
3
|
+
* - Vnd_Pkg_Prj_Mod$FA
|
|
4
|
+
*
|
|
5
|
+
* @namespace TeqFw_Di_Container_A_Parser_Chunk_Def
|
|
6
|
+
*/
|
|
7
|
+
import Dto from '../../../../DepId.js';
|
|
8
|
+
import Defs from '../../../../Defs.js';
|
|
9
|
+
|
|
10
|
+
// VARS
|
|
11
|
+
/** @type {RegExp} expression for default object key (Ns_Module[.|#]export$[F|A][S|I]) */
|
|
12
|
+
const REGEXP = /^((([A-Z])[A-Za-z0-9_]*)((#|\.)?([A-Za-z0-9]*)((\$)([F|A])?([S|I])?)?)?)$/;
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* @implements TeqFw_Di_Api_Container_Parser_Chunk
|
|
16
|
+
*/
|
|
17
|
+
export default class TeqFw_Di_Container_A_Parser_Chunk_Def {
|
|
18
|
+
|
|
19
|
+
canParse(depId) {
|
|
20
|
+
// default parser always trys to parse the depId
|
|
21
|
+
return true;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
parse(objectKey) {
|
|
25
|
+
const res = new Dto();
|
|
26
|
+
res.value = objectKey;
|
|
27
|
+
const parts = REGEXP.exec(objectKey);
|
|
28
|
+
if (parts) {
|
|
29
|
+
res.moduleName = parts[2];
|
|
30
|
+
if (parts[5] === '.') {
|
|
31
|
+
// App_Service.export...
|
|
32
|
+
if (parts[8] === '$') {
|
|
33
|
+
// App_Service.export$...
|
|
34
|
+
res.composition = Defs.COMPOSE_FACTORY;
|
|
35
|
+
res.exportName = parts[6];
|
|
36
|
+
res.life = (parts[10] === Defs.LIFE_INSTANCE)
|
|
37
|
+
? Defs.LIFE_INSTANCE : Defs.LIFE_SINGLETON;
|
|
38
|
+
} else {
|
|
39
|
+
res.composition = ((parts[8] === undefined) || (parts[8] === Defs.COMPOSE_AS_IS))
|
|
40
|
+
? Defs.COMPOSE_AS_IS : Defs.COMPOSE_FACTORY;
|
|
41
|
+
res.exportName = parts[6];
|
|
42
|
+
res.life = ((parts[8] === undefined) || (parts[10] === Defs.LIFE_SINGLETON))
|
|
43
|
+
? Defs.LIFE_SINGLETON : Defs.LIFE_INSTANCE;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
} else if (parts[8] === '$') {
|
|
48
|
+
// App_Logger$FS
|
|
49
|
+
res.composition = ((parts[9] === undefined) || (parts[9] === Defs.COMPOSE_FACTORY))
|
|
50
|
+
? Defs.COMPOSE_FACTORY : Defs.COMPOSE_AS_IS;
|
|
51
|
+
res.exportName = 'default';
|
|
52
|
+
if (parts[10]) {
|
|
53
|
+
res.life = (parts[10] === Defs.LIFE_SINGLETON) ? Defs.LIFE_SINGLETON : Defs.LIFE_INSTANCE;
|
|
54
|
+
} else {
|
|
55
|
+
res.life = (res.composition === Defs.COMPOSE_FACTORY) ? Defs.LIFE_SINGLETON : Defs.LIFE_INSTANCE;
|
|
56
|
+
}
|
|
57
|
+
} else {
|
|
58
|
+
// App_Service
|
|
59
|
+
res.composition = Defs.COMPOSE_AS_IS;
|
|
60
|
+
res.exportName = 'default';
|
|
61
|
+
res.life = Defs.LIFE_SINGLETON;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// we should always use singletons for as-is exports
|
|
66
|
+
if ((res.composition === Defs.COMPOSE_AS_IS) && (res.life === Defs.LIFE_INSTANCE))
|
|
67
|
+
throw new Error(`Export is not a function and should be used as a singleton only: '${res.value}'.`);
|
|
68
|
+
return res;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The parser for the runtime dependency ID contains multiple chunks. Each npm package can have its own format for
|
|
3
|
+
* a `depId`. The parser calls the chunks one by one to parse the string ID as a structure and returns the first result.
|
|
4
|
+
* If none of the chunks processed the `depId`, the parser calls the default chunk.
|
|
5
|
+
*/
|
|
6
|
+
import DefChunk from './A/Parser/Chunk/Def.js';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* @implements TeqFw_Di_Api_Container_Parser
|
|
10
|
+
*/
|
|
11
|
+
export default class TeqFw_Di_Container_Parser {
|
|
12
|
+
constructor() {
|
|
13
|
+
// VARS
|
|
14
|
+
/**
|
|
15
|
+
* The default chunk to parse the depId if no other chunks have parsed this depId.
|
|
16
|
+
*
|
|
17
|
+
* @type {TeqFw_Di_Api_Container_Parser_Chunk}
|
|
18
|
+
*/
|
|
19
|
+
let _defaultChunk = new DefChunk();
|
|
20
|
+
/**
|
|
21
|
+
* The array of the chunks to parse dependency IDs.
|
|
22
|
+
* @type {TeqFw_Di_Api_Container_Parser_Chunk[]}
|
|
23
|
+
*/
|
|
24
|
+
const _chunks = [];
|
|
25
|
+
|
|
26
|
+
// INSTANCE METHODS
|
|
27
|
+
|
|
28
|
+
this.addChunk = function (chunk) {
|
|
29
|
+
_chunks.push(chunk);
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
this.parse = function (depId) {
|
|
33
|
+
let res;
|
|
34
|
+
for (const one of _chunks)
|
|
35
|
+
if (one.canParse(depId)) {
|
|
36
|
+
res = one.parse(depId);
|
|
37
|
+
break;
|
|
38
|
+
}
|
|
39
|
+
if (!res)
|
|
40
|
+
res = _defaultChunk?.parse(depId);
|
|
41
|
+
return res;
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
this.setDefaultChunk = function (chunk) {
|
|
45
|
+
_defaultChunk = chunk;
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
};
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The post-processor handles the result object after composition and before returning.
|
|
3
|
+
*
|
|
4
|
+
* @implements TeqFw_Di_Api_Container_PostProcessor
|
|
5
|
+
*/
|
|
6
|
+
export default class TeqFw_Di_Container_PostProcessor {
|
|
7
|
+
|
|
8
|
+
constructor() {
|
|
9
|
+
// VARS
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* The array of the chunks to modify dependency IDs.
|
|
13
|
+
* @type {TeqFw_Di_Api_Container_PostProcessor_Chunk[]}
|
|
14
|
+
*/
|
|
15
|
+
const _chunks = [];
|
|
16
|
+
|
|
17
|
+
// INSTANCE METHODS
|
|
18
|
+
|
|
19
|
+
this.addChunk = function (chunk) {
|
|
20
|
+
_chunks.push(chunk);
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
this.modify = async function (obj, depId, stack) {
|
|
24
|
+
let res = obj;
|
|
25
|
+
for (const one of _chunks) {
|
|
26
|
+
res = one.modify(res, depId, stack);
|
|
27
|
+
if (res instanceof Promise) res = await res;
|
|
28
|
+
}
|
|
29
|
+
return res;
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
};
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The preprocessor handles object keys after the parsing but before creating any objects.
|
|
3
|
+
* A replacement rules can be implemented here.
|
|
4
|
+
* Every handler is a function with 2 arguments:
|
|
5
|
+
* - objectKey: current key after processing with other handlers;
|
|
6
|
+
* - originalKey: the key before any processing;
|
|
7
|
+
*
|
|
8
|
+
* @implements TeqFw_Di_Api_Container_PreProcessor
|
|
9
|
+
*/
|
|
10
|
+
export default class TeqFw_Di_Container_PreProcessor {
|
|
11
|
+
|
|
12
|
+
constructor() {
|
|
13
|
+
// VARS
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* The array of the chunks to modify dependency IDs.
|
|
17
|
+
* @type {TeqFw_Di_Api_Container_PreProcessor_Chunk[]}
|
|
18
|
+
*/
|
|
19
|
+
const _chunks = [];
|
|
20
|
+
|
|
21
|
+
// INSTANCE METHODS
|
|
22
|
+
|
|
23
|
+
this.addChunk = function (chunk) {
|
|
24
|
+
_chunks.push(chunk);
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
this.modify = function (depId, stack) {
|
|
28
|
+
let res = depId;
|
|
29
|
+
for (const one of _chunks)
|
|
30
|
+
res = one.modify(res, depId, stack);
|
|
31
|
+
return res;
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
};
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* - every package has sources with the same extensions (*.js, *.mjs, *.es6, ...)
|
|
8
8
|
* - namespaces can be nested (App_Web_ => ./@app/web/..., App_Web_Api_ => ./@app/web_api/...)
|
|
9
9
|
*/
|
|
10
|
-
import Defs from '
|
|
10
|
+
import Defs from '../Defs.js';
|
|
11
11
|
|
|
12
12
|
// VARS
|
|
13
13
|
const KEY_EXT = 'ext';
|
|
@@ -21,7 +21,7 @@ const KEY_PATH = 'root';
|
|
|
21
21
|
const NSS = '_';
|
|
22
22
|
|
|
23
23
|
// MAIN
|
|
24
|
-
export default class
|
|
24
|
+
export default class TeqFw_Di_Container_Resolver {
|
|
25
25
|
|
|
26
26
|
constructor() {
|
|
27
27
|
// VARS
|
|
@@ -55,7 +55,8 @@ export default class TeqFw_Di_Resolver {
|
|
|
55
55
|
}
|
|
56
56
|
}
|
|
57
57
|
if (root && ext) {
|
|
58
|
-
|
|
58
|
+
let tail = moduleName.replace(ns, '');
|
|
59
|
+
if (tail.indexOf(NSS) === 0) tail = tail.replace(NSS, '');
|
|
59
60
|
const file = tail.replaceAll(NSS, _ps);
|
|
60
61
|
return `${root}${_ps}${file}.${ext}`;
|
|
61
62
|
} else return moduleName;
|
package/src/Container.js
CHANGED
|
@@ -1,19 +1,18 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* The Object Container (composition root).
|
|
3
|
+
* @namespace TeqFw_Di_Container
|
|
3
4
|
*/
|
|
4
|
-
import Composer from './Composer.js';
|
|
5
|
+
import Composer from './Container/A/Composer.js';
|
|
5
6
|
import Defs from './Defs.js';
|
|
6
|
-
import Parser from './Parser.js';
|
|
7
|
-
import PreProcessor from './PreProcessor.js';
|
|
8
|
-
import
|
|
9
|
-
import Resolver from './Resolver.js';
|
|
10
|
-
|
|
11
|
-
// VARS
|
|
7
|
+
import Parser from './Container/Parser.js';
|
|
8
|
+
import PreProcessor from './Container/PreProcessor.js';
|
|
9
|
+
import PostProcessor from './Container/PostProcessor.js';
|
|
10
|
+
import Resolver from './Container/Resolver.js';
|
|
12
11
|
|
|
13
12
|
// FUNCS
|
|
14
13
|
/**
|
|
15
14
|
* ID to store singletons in the internal registry.
|
|
16
|
-
* @param {
|
|
15
|
+
* @param {TeqFw_Di_DepId} key
|
|
17
16
|
* @return {string}
|
|
18
17
|
*/
|
|
19
18
|
function getSingletonId(key) {
|
|
@@ -21,6 +20,9 @@ function getSingletonId(key) {
|
|
|
21
20
|
}
|
|
22
21
|
|
|
23
22
|
// MAIN
|
|
23
|
+
/**
|
|
24
|
+
* @implements TeqFw_Di_Api_Container
|
|
25
|
+
*/
|
|
24
26
|
export default class TeqFw_Di_Container {
|
|
25
27
|
|
|
26
28
|
constructor() {
|
|
@@ -29,13 +31,14 @@ export default class TeqFw_Di_Container {
|
|
|
29
31
|
let _debug = false;
|
|
30
32
|
let _parser = new Parser();
|
|
31
33
|
let _preProcessor = new PreProcessor();
|
|
32
|
-
|
|
34
|
+
let _postProcessor = new PostProcessor();
|
|
33
35
|
|
|
34
36
|
/**
|
|
35
|
-
* Registry for loaded es6 modules.
|
|
36
|
-
*
|
|
37
|
+
* Registry for paths for loaded es6 modules.
|
|
38
|
+
*
|
|
39
|
+
* @type {Object<string, string>}
|
|
37
40
|
*/
|
|
38
|
-
const
|
|
41
|
+
const _regPaths = {};
|
|
39
42
|
/**
|
|
40
43
|
* Registry to store singletons.
|
|
41
44
|
* @type {Object<string, *>}
|
|
@@ -44,31 +47,38 @@ export default class TeqFw_Di_Container {
|
|
|
44
47
|
let _resolver = new Resolver();
|
|
45
48
|
|
|
46
49
|
// FUNCS
|
|
47
|
-
function error() {
|
|
48
|
-
console.error(...arguments);
|
|
49
|
-
}
|
|
50
50
|
|
|
51
51
|
function log() {
|
|
52
52
|
if (_debug) console.log(...arguments);
|
|
53
53
|
}
|
|
54
54
|
|
|
55
|
-
|
|
56
55
|
// INSTANCE METHODS
|
|
57
56
|
|
|
58
|
-
this.get = async function (
|
|
59
|
-
|
|
57
|
+
this.get = async function (runtimeDepId) {
|
|
58
|
+
return this.compose(runtimeDepId, []);
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* This method is 'private' for the npm package. It is used in the Composer only.
|
|
63
|
+
*
|
|
64
|
+
* @param {string} depId runtime dependency ID
|
|
65
|
+
* @param {string[]} stack set of the depId to detect circular dependencies
|
|
66
|
+
* @return {Promise<*>}
|
|
67
|
+
*/
|
|
68
|
+
this.compose = async function (depId, stack = []) {
|
|
69
|
+
log(`Object '${depId}' is requested.`);
|
|
60
70
|
// return container itself if requested
|
|
61
71
|
if (
|
|
62
|
-
(
|
|
63
|
-
(
|
|
72
|
+
(depId === Defs.KEY_CONTAINER) ||
|
|
73
|
+
(depId === Defs.KEY_CONTAINER_NS)
|
|
64
74
|
) {
|
|
65
75
|
log(`Container itself is returned.`);
|
|
66
76
|
return _regSingles[Defs.KEY_CONTAINER];
|
|
67
77
|
}
|
|
68
78
|
// parse the `objectKey` and get the structured DTO
|
|
69
|
-
const parsed = _parser.parse(
|
|
79
|
+
const parsed = _parser.parse(depId);
|
|
70
80
|
// modify original key according to some rules (replacements, etc.)
|
|
71
|
-
const key = _preProcessor.
|
|
81
|
+
const key = _preProcessor.modify(parsed, stack);
|
|
72
82
|
// return existing singleton
|
|
73
83
|
if (key.life === Defs.LIFE_SINGLETON) {
|
|
74
84
|
const singleId = getSingletonId(key);
|
|
@@ -77,52 +87,47 @@ export default class TeqFw_Di_Container {
|
|
|
77
87
|
return _regSingles[singleId];
|
|
78
88
|
}
|
|
79
89
|
}
|
|
80
|
-
//
|
|
81
|
-
if (!
|
|
82
|
-
log(`ES6 module '${key.moduleName}' is not
|
|
90
|
+
// resolve path to es6 module if not resolved before
|
|
91
|
+
if (!_regPaths[key.moduleName]) {
|
|
92
|
+
log(`ES6 module '${key.moduleName}' is not resolved yet`);
|
|
83
93
|
// convert module name to the path to es6-module file with a sources
|
|
84
|
-
|
|
85
|
-
try {
|
|
86
|
-
_regModules[key.moduleName] = await import(path);
|
|
87
|
-
log(`ES6 module '${key.moduleName}' is loaded from '${path}'.`);
|
|
88
|
-
} catch (e) {
|
|
89
|
-
console.error(
|
|
90
|
-
e?.message,
|
|
91
|
-
`Object key: "${objectKey}".`,
|
|
92
|
-
`Path: "${path}".`,
|
|
93
|
-
`Stack: ${JSON.stringify(stack)}`
|
|
94
|
-
);
|
|
95
|
-
throw e;
|
|
96
|
-
}
|
|
97
|
-
|
|
94
|
+
_regPaths[key.moduleName] = _resolver.resolve(key.moduleName);
|
|
98
95
|
}
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
}
|
|
111
|
-
|
|
96
|
+
|
|
97
|
+
// load es6 module
|
|
98
|
+
let module;
|
|
99
|
+
const path = _regPaths[key.moduleName];
|
|
100
|
+
try {
|
|
101
|
+
module = await import(path);
|
|
102
|
+
log(`ES6 module '${key.moduleName}' is loaded from '${path}'.`);
|
|
103
|
+
} catch (e) {
|
|
104
|
+
console.error(
|
|
105
|
+
e?.message,
|
|
106
|
+
`Object key: "${depId}".`,
|
|
107
|
+
`Path: "${path}".`,
|
|
108
|
+
`Stack: ${JSON.stringify(stack)}`
|
|
109
|
+
);
|
|
110
|
+
throw e;
|
|
112
111
|
}
|
|
112
|
+
// create object using the composer then modify it in post-processor
|
|
113
|
+
let res = await _composer.create(key, module, stack, this);
|
|
114
|
+
res = await _postProcessor.modify(res, key, stack);
|
|
115
|
+
log(`Object '${depId}' is created.`);
|
|
113
116
|
|
|
117
|
+
// save singletons
|
|
114
118
|
if (key.life === Defs.LIFE_SINGLETON) {
|
|
115
119
|
const singleId = getSingletonId(key);
|
|
116
120
|
_regSingles[singleId] = res;
|
|
117
|
-
log(`Object '${
|
|
121
|
+
log(`Object '${depId}' is saved as singleton.`);
|
|
118
122
|
}
|
|
119
123
|
return res;
|
|
120
124
|
};
|
|
121
125
|
|
|
122
126
|
this.getParser = () => _parser;
|
|
123
127
|
|
|
124
|
-
this.getPreProcessor = () => _preProcessor
|
|
125
|
-
|
|
128
|
+
this.getPreProcessor = () => _preProcessor;
|
|
129
|
+
|
|
130
|
+
this.getPostProcessor = () => _postProcessor;
|
|
126
131
|
this.getResolver = () => _resolver;
|
|
127
132
|
|
|
128
133
|
this.setDebug = function (data) {
|
|
@@ -134,6 +139,8 @@ export default class TeqFw_Di_Container {
|
|
|
134
139
|
|
|
135
140
|
this.setPreProcessor = (data) => _preProcessor = data;
|
|
136
141
|
|
|
142
|
+
this.setPostProcessor = (data) => _postProcessor = data;
|
|
143
|
+
|
|
137
144
|
this.setResolver = (data) => _resolver = data;
|
|
138
145
|
|
|
139
146
|
// MAIN
|
package/src/Defs.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Hardcoded constants for the package.
|
|
2
|
+
* Hardcoded constants and useful utilities for the package.
|
|
3
|
+
* @namespace TeqFw_Di_Defs
|
|
3
4
|
*/
|
|
4
5
|
export default {
|
|
5
6
|
COMPOSE_AS_IS: 'A',
|
|
@@ -9,6 +10,9 @@ export default {
|
|
|
9
10
|
KEY_CONTAINER_NS: 'TeqFw_Di_Container$',
|
|
10
11
|
LIFE_INSTANCE: 'I',
|
|
11
12
|
LIFE_SINGLETON: 'S',
|
|
13
|
+
/**
|
|
14
|
+
* @deprecated use TeqFw_Core_Shared_Defaults.DI_WRAP_PROXY
|
|
15
|
+
*/
|
|
12
16
|
WRAP_PROXY: 'proxy',
|
|
13
17
|
|
|
14
18
|
/**
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* This is a DTO that represents the structure of an ID for a runtime dependency.
|
|
3
|
+
* @namespace TeqFw_Di_DepId
|
|
3
4
|
*/
|
|
4
|
-
export default class
|
|
5
|
+
export default class TeqFw_Di_DepId {
|
|
5
6
|
/**
|
|
6
7
|
* The name of an export of the module.
|
|
7
8
|
* @type {string}
|
package/teqfw.json
ADDED
package/src/Composer.js
DELETED
|
@@ -1,68 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
*
|
|
3
|
-
*/
|
|
4
|
-
import Defs from './Defs.js';
|
|
5
|
-
import specAnalyser from './SpecAnalyser.js';
|
|
6
|
-
|
|
7
|
-
// FUNCS
|
|
8
|
-
|
|
9
|
-
// MAIN
|
|
10
|
-
export default class TeqFw_Di_Composer {
|
|
11
|
-
|
|
12
|
-
constructor() {
|
|
13
|
-
// VARS
|
|
14
|
-
let _debug = false;
|
|
15
|
-
|
|
16
|
-
// FUNCS
|
|
17
|
-
function log(msg) {
|
|
18
|
-
if (_debug) console.log(msg);
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
// INSTANCE METHODS
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
*
|
|
25
|
-
* @param {TeqFw_Di_Api_ObjectKey} key
|
|
26
|
-
* @param {Object} module
|
|
27
|
-
* @param {string[]} stack array of the parent objects to prevent dependency loop
|
|
28
|
-
* @param {TeqFw_Di_Container} container
|
|
29
|
-
* @return {Promise<*>}
|
|
30
|
-
*/
|
|
31
|
-
this.create = async function (key, module, stack, container) {
|
|
32
|
-
if (stack.includes(key.value))
|
|
33
|
-
throw new Error(`Circular dependency for '${key.value}'. Parents are: ${JSON.stringify(stack)}`);
|
|
34
|
-
if (key.exportName) {
|
|
35
|
-
// use export from the es6-module
|
|
36
|
-
const stackNew = [...stack, key.value];
|
|
37
|
-
const {[key.exportName]: exp} = module;
|
|
38
|
-
if (key.composition === Defs.COMPOSE_FACTORY) {
|
|
39
|
-
if (typeof exp === 'function') {
|
|
40
|
-
// create deps for factory function
|
|
41
|
-
const deps = specAnalyser(exp);
|
|
42
|
-
if (deps.length) log(`Deps for object '${key.value}' are: ${JSON.stringify(deps)}`);
|
|
43
|
-
const spec = {};
|
|
44
|
-
for (const dep of deps)
|
|
45
|
-
spec[dep] = await container.get(dep, stackNew);
|
|
46
|
-
// create a new object with the factory function
|
|
47
|
-
const res = (Defs.isClass(exp)) ? new exp(spec) : exp(spec);
|
|
48
|
-
if (res instanceof Promise)
|
|
49
|
-
return await res;
|
|
50
|
-
else
|
|
51
|
-
return res;
|
|
52
|
-
} else
|
|
53
|
-
// just clone the export
|
|
54
|
-
return Object.assign({}, exp);
|
|
55
|
-
} else
|
|
56
|
-
return exp;
|
|
57
|
-
} else {
|
|
58
|
-
return module;
|
|
59
|
-
}
|
|
60
|
-
};
|
|
61
|
-
|
|
62
|
-
this.setDebug = function (data) {
|
|
63
|
-
_debug = data;
|
|
64
|
-
};
|
|
65
|
-
|
|
66
|
-
// MAIN
|
|
67
|
-
}
|
|
68
|
-
};
|
package/src/DepId/Parser.mjs
DELETED
|
@@ -1,68 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* This is default parser that converts dependency ID to the `depId` DTO.
|
|
3
|
-
* @namespace ObjectKey.Parser
|
|
4
|
-
*/
|
|
5
|
-
// IMPORTS
|
|
6
|
-
import {ObjectKey} from '../Api/ObjectKey.mjs';
|
|
7
|
-
|
|
8
|
-
// VARS
|
|
9
|
-
/** @type {RegExp} expression for depId (Ns_Module.export$$#adapter) */
|
|
10
|
-
const TMPL = /^(([A-Z])[A-Za-z0-9_]*)((.)([A-Za-z0-9_]*)?((\$|\$\$)?((#)([A-Za-z0-9_]*)?)?)?)?$/;
|
|
11
|
-
|
|
12
|
-
// FUNCS
|
|
13
|
-
/**
|
|
14
|
-
* @param {string} depId
|
|
15
|
-
* @return {Api.ObjectKey}
|
|
16
|
-
*/
|
|
17
|
-
export default function (depId) {
|
|
18
|
-
let res;
|
|
19
|
-
const parts = TMPL.exec(depId);
|
|
20
|
-
if (parts) {
|
|
21
|
-
const nameMod = parts[1];
|
|
22
|
-
const sepMod = parts[4]; // after-module separator
|
|
23
|
-
const sepExp = parts[6]; // after-export separator
|
|
24
|
-
const sepLife = parts[9]; // after-life separator
|
|
25
|
-
|
|
26
|
-
res = new ObjectKey();
|
|
27
|
-
// always presents
|
|
28
|
-
res.nameModule = nameMod;
|
|
29
|
-
if (sepLife === '#') {
|
|
30
|
-
// the longest form of the ID (Mod.exp$#adp)
|
|
31
|
-
res.adapter = parts[10] ?? 'default';
|
|
32
|
-
res.isExport = true;
|
|
33
|
-
res.isFactory = parts[7] !== undefined;
|
|
34
|
-
if (parts[7] === '$') res.isSingleton = true;
|
|
35
|
-
else if (parts[7] === '$$') res.isSingleton = false;
|
|
36
|
-
res.nameExport = parts[5];
|
|
37
|
-
} else if (sepMod === '#') {
|
|
38
|
-
// Mod#adp
|
|
39
|
-
res.adapter = parts[5] ?? 'default';
|
|
40
|
-
res.isExport = true;
|
|
41
|
-
res.isFactory = false;
|
|
42
|
-
res.nameExport = 'default';
|
|
43
|
-
} else if ((sepExp === '$') || (sepExp === '$$')) {
|
|
44
|
-
if (sepMod === '.') {
|
|
45
|
-
// Mod.exp$$
|
|
46
|
-
res.isExport = true;
|
|
47
|
-
res.isFactory = true;
|
|
48
|
-
if (parts[7] === '$') res.isSingleton = true;
|
|
49
|
-
else if (parts[7] === '$$') res.isSingleton = false;
|
|
50
|
-
res.nameExport = parts[5];
|
|
51
|
-
} else if (sepMod === '$') {
|
|
52
|
-
// Mod$$
|
|
53
|
-
res.isExport = true;
|
|
54
|
-
res.isFactory = true;
|
|
55
|
-
if (parts[3] === '$') res.isSingleton = true;
|
|
56
|
-
else if (parts[3] === '$$') res.isSingleton = false;
|
|
57
|
-
res.nameExport = 'default';
|
|
58
|
-
}
|
|
59
|
-
} else {
|
|
60
|
-
// Mod.exp
|
|
61
|
-
res.isFactory = false;
|
|
62
|
-
res.isExport = (sepMod === '.');
|
|
63
|
-
// define the name of the export
|
|
64
|
-
if (res.isExport) res.nameExport = parts[5] ?? 'default';
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
return res;
|
|
68
|
-
}
|