@teqfw/di 0.12.1 → 0.20.1
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 +11 -1
- 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 +14 -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 +65 -0
- package/src/Spec/Parser.mjs +101 -0
- package/src/SpecAnalyser.js +82 -0
- package/teqfw.json +2 -1
- 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 -74
- package/src/Shared/SpecProxy.mjs +0 -110
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
// MODULE'S IMPORT
|
|
2
|
-
import IdParser from '../IdParser.mjs';
|
|
3
|
-
|
|
4
|
-
// MODULE'S VARS
|
|
5
|
-
const $parser = new IdParser();
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* Map codebase file path namespaces to files/URLs.
|
|
9
|
-
*/
|
|
10
|
-
export default class TeqFw_Di_Shared_Resolver_FilepathNs {
|
|
11
|
-
/** @type {Object.<string, TeqFw_Di_Shared_Api_Dto_Plugin_Desc_Autoload>} */
|
|
12
|
-
packages = {}
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* Register 'package to sources root' mapping.
|
|
16
|
-
*
|
|
17
|
-
* @param {TeqFw_Di_Shared_Api_Dto_Plugin_Desc_Autoload} details namespace resolving details
|
|
18
|
-
*/
|
|
19
|
-
addNamespaceRoot(details) {
|
|
20
|
-
this.packages[details.ns] = details;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* List all registered packages with resolving details.
|
|
25
|
-
*
|
|
26
|
-
* @returns {Object.<string, TeqFw_Di_Shared_Api_Dto_Plugin_Desc_Autoload>}
|
|
27
|
-
*/
|
|
28
|
-
list() {
|
|
29
|
-
return this.packages;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* Resolve module id to module source path.
|
|
34
|
-
* @param {string} moduleId '@vendor/package!path/to/module'
|
|
35
|
-
* @returns {string} './@vendor/package/dist/path/to/module.mjs'
|
|
36
|
-
*/
|
|
37
|
-
resolveModuleId(moduleId) {
|
|
38
|
-
let result;
|
|
39
|
-
/** @type {TeqFw_Di_Shared_IdParser_Dto} */
|
|
40
|
-
const parsed = $parser.parse(moduleId);
|
|
41
|
-
const pkg = parsed.namePackage;
|
|
42
|
-
if (this.packages[pkg]) {
|
|
43
|
-
/** @type {TeqFw_Di_Shared_Api_Dto_Plugin_Desc_Autoload} */
|
|
44
|
-
const details = this.packages[pkg];
|
|
45
|
-
result = `${details.path}/${parsed.nameModule}.${details.ext}`;
|
|
46
|
-
} else {
|
|
47
|
-
throw new Error(`Cannot resolve path for id '${moduleId}'.`);
|
|
48
|
-
}
|
|
49
|
-
return result;
|
|
50
|
-
}
|
|
51
|
-
}
|
|
@@ -1,136 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Tree-like structure of namespaces registry entry.
|
|
3
|
-
*
|
|
4
|
-
* @typedef {Object<string, NamespaceDetails|TeqFw_Di_Shared_Api_Dto_Plugin_Desc_Autoload>} NamespaceDetails
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
// MODULE'S VARS
|
|
8
|
-
/**
|
|
9
|
-
* Namespace parts separator.
|
|
10
|
-
*
|
|
11
|
-
* @type {string}
|
|
12
|
-
*/
|
|
13
|
-
const NSS = '_';
|
|
14
|
-
/**
|
|
15
|
-
* Key to save sources data in namespaces registry.
|
|
16
|
-
*
|
|
17
|
-
* @type {string}
|
|
18
|
-
*/
|
|
19
|
-
const KEY_DATA = '.data';
|
|
20
|
-
|
|
21
|
-
// MODULE'S CLASSES
|
|
22
|
-
/**
|
|
23
|
-
* Map codebase logical namespaces to files/URLs.
|
|
24
|
-
*/
|
|
25
|
-
export default class TeqFw_Di_Shared_Resolver_LogicalNs {
|
|
26
|
-
/**
|
|
27
|
-
* Registry for logical namespaces. Tree-like structure to save root paths (relative or absolute) to sources
|
|
28
|
-
* by namespaces.
|
|
29
|
-
*
|
|
30
|
-
* TeqFw_Prj_App => ./path/to/app/files
|
|
31
|
-
* TeqFw_Prj_App_Mod => ./another/path/to/mod/files
|
|
32
|
-
* TeqFw_Prj_App_Mod_Rewrite => ./rewrite/path/to/part/of/mod/files
|
|
33
|
-
*
|
|
34
|
-
* @type {Object<string, NamespaceDetails>}
|
|
35
|
-
* @private
|
|
36
|
-
*/
|
|
37
|
-
namespaces = {};
|
|
38
|
-
|
|
39
|
-
/**
|
|
40
|
-
* Register sources path mapping details for namespace.
|
|
41
|
-
*
|
|
42
|
-
* @param {TeqFw_Di_Shared_Api_Dto_Plugin_Desc_Autoload} details namespace resolving details
|
|
43
|
-
*/
|
|
44
|
-
addNamespaceRoot(details) {
|
|
45
|
-
const spaces = details.ns.split(NSS);
|
|
46
|
-
let pointer = this.namespaces;
|
|
47
|
-
for (const one of spaces) {
|
|
48
|
-
if (!pointer[one]) {
|
|
49
|
-
pointer[one] = {};
|
|
50
|
-
pointer = pointer[one];
|
|
51
|
-
} else {
|
|
52
|
-
pointer = pointer[one];
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
// add source folder to the namespaces map
|
|
56
|
-
pointer[KEY_DATA] = details;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
/**
|
|
60
|
-
* List all namespaces with resolving details.
|
|
61
|
-
*
|
|
62
|
-
* @returns {Object.<string, TeqFw_Di_Shared_Api_Dto_Plugin_Desc_Autoload>}
|
|
63
|
-
*/
|
|
64
|
-
list() {
|
|
65
|
-
const result = {};
|
|
66
|
-
|
|
67
|
-
/**
|
|
68
|
-
* Scan one level of mapping tree and save mapping data into results (if found) and/or dive deeper.
|
|
69
|
-
|
|
70
|
-
* @param {string} curPath
|
|
71
|
-
* @param level
|
|
72
|
-
*/
|
|
73
|
-
function scanLevel(curPath, level) {
|
|
74
|
-
for (const key of Object.keys(level)) {
|
|
75
|
-
if (key === KEY_DATA) {
|
|
76
|
-
result[curPath] = level[KEY_DATA];
|
|
77
|
-
} else {
|
|
78
|
-
const subPath = (curPath) ? curPath + NSS + key : key;
|
|
79
|
-
const sub = level[key];
|
|
80
|
-
scanLevel(subPath, sub);
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
scanLevel('', this.namespaces);
|
|
86
|
-
return result;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
/**
|
|
90
|
-
* Resolve path to module's source using `moduleId`:
|
|
91
|
-
* - Vendor_Project_Module => './relative/path/to/vendor/Project/Module.mjs'
|
|
92
|
-
* - Vendor_Project_Module => '/absolute/path/to/project/Module.js'
|
|
93
|
-
* - Vendor_Project_Module => 'https://vendor.com/lib/Project/Module.js'
|
|
94
|
-
*
|
|
95
|
-
* @param {string} moduleId
|
|
96
|
-
* @returns {*}
|
|
97
|
-
*/
|
|
98
|
-
resolveModuleId(moduleId) {
|
|
99
|
-
let result;
|
|
100
|
-
const parts = moduleId.split(NSS);
|
|
101
|
-
let nsExplored = ''; // explored part of the object's full name
|
|
102
|
-
let pointer = this.namespaces;
|
|
103
|
-
for (const part of parts) {
|
|
104
|
-
if (pointer[part]) {
|
|
105
|
-
pointer = pointer[part];
|
|
106
|
-
if (pointer[KEY_DATA]) {
|
|
107
|
-
// compose path to root module of the current namespace (`index.[js|mjs]`)
|
|
108
|
-
/** @type {TeqFw_Di_Shared_Api_Dto_Plugin_Desc_Autoload} */
|
|
109
|
-
const entry = pointer[KEY_DATA];
|
|
110
|
-
// compose path to NS default root
|
|
111
|
-
result = `${entry.path}/../index.${entry.ext}`;
|
|
112
|
-
if (!entry.isAbsolute) result = `./${result}`;
|
|
113
|
-
}
|
|
114
|
-
} else {
|
|
115
|
-
// compose path to requested module starting from namespace root
|
|
116
|
-
if (pointer[KEY_DATA]) {
|
|
117
|
-
/** @type {TeqFw_Di_Shared_Api_Dto_Plugin_Desc_Autoload} */
|
|
118
|
-
const entry = pointer[KEY_DATA];
|
|
119
|
-
const nsModule = nsExplored.substring(1);
|
|
120
|
-
const nsObject = moduleId.substring(nsModule.length + 1);
|
|
121
|
-
const pathObject = nsObject.replace(new RegExp(NSS, 'g'), '/') + '.' + entry.ext;
|
|
122
|
-
result = `${entry.path}/${pathObject}`;
|
|
123
|
-
if (!entry.isAbsolute) result = `./${result}`;
|
|
124
|
-
break;
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
nsExplored += NSS + part;
|
|
128
|
-
}
|
|
129
|
-
if (result === undefined) throw new Error(`Cannot resolve path for id '${moduleId}'.`);
|
|
130
|
-
// strip '././' from result
|
|
131
|
-
result = result.replace('././', './');
|
|
132
|
-
return result;
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
}
|
|
136
|
-
|
package/src/Shared/Resolver.mjs
DELETED
|
@@ -1,74 +0,0 @@
|
|
|
1
|
-
// MODULE'S IMPORT
|
|
2
|
-
import FilepathNs from './Resolver/FilepathNs.mjs';
|
|
3
|
-
import LogicalNs from './Resolver/LogicalNs.mjs';
|
|
4
|
-
|
|
5
|
-
// MODULE'S VARS
|
|
6
|
-
/** @type {RegExp} expression for logical namespace IDs w/o dep. injection fraction (Ns_Module) */
|
|
7
|
-
const LOGICAL_NS = /^((([A-Z])[A-Za-z0-9_]*)?)$/;
|
|
8
|
-
|
|
9
|
-
// MODULE'S CLASSES
|
|
10
|
-
/**
|
|
11
|
-
* Map codebase namespaces to files/URLs.
|
|
12
|
-
*/
|
|
13
|
-
export default class TeqFw_Di_Shared_Resolver {
|
|
14
|
-
/** @type {TeqFw_Di_Shared_Resolver_LogicalNs} */
|
|
15
|
-
logicalNs = new LogicalNs();
|
|
16
|
-
/** @type {TeqFw_Di_Shared_Resolver_FilepathNs} */
|
|
17
|
-
filepathNs = new FilepathNs();
|
|
18
|
-
// TODO: this is a hotfix
|
|
19
|
-
/** @type {boolean} */
|
|
20
|
-
isWindows = false;
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* Registry sources path mapping details for namespace.
|
|
24
|
-
*
|
|
25
|
-
* @param {TeqFw_Di_Shared_Api_Dto_Plugin_Desc_Autoload} details namespace resolving details
|
|
26
|
-
*/
|
|
27
|
-
addNamespaceRoot(details) {
|
|
28
|
-
const parsed = LOGICAL_NS.exec(details.ns);
|
|
29
|
-
if (parsed) {
|
|
30
|
-
// this is logical namespace
|
|
31
|
-
this.logicalNs.addNamespaceRoot(details);
|
|
32
|
-
} else {
|
|
33
|
-
// this is file path based namespace
|
|
34
|
-
this.filepathNs.addNamespaceRoot(details);
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
* Resolve path to module's source using `moduleId`:
|
|
40
|
-
* - '@vendor/package!path/to/module' => './@vendor/package/dist/path/to/module.mjs'
|
|
41
|
-
* - 'Vendor_Project_Module' => 'https://vendor.com/lib/Project/Module.js'
|
|
42
|
-
*
|
|
43
|
-
* @param {string} moduleId
|
|
44
|
-
* @returns {string}
|
|
45
|
-
*/
|
|
46
|
-
resolveModuleId(moduleId) {
|
|
47
|
-
let result;
|
|
48
|
-
const parsed = LOGICAL_NS.exec(moduleId);
|
|
49
|
-
if (parsed) {
|
|
50
|
-
// this is logical namespace
|
|
51
|
-
result = this.logicalNs.resolveModuleId(moduleId);
|
|
52
|
-
} else {
|
|
53
|
-
// this is file path based namespace
|
|
54
|
-
result = this.filepathNs.resolveModuleId(moduleId);
|
|
55
|
-
}
|
|
56
|
-
// TODO: tmp fix for windows
|
|
57
|
-
if (this.isWindows) {
|
|
58
|
-
result = 'file://' + result.replace(/\//g, '\\');
|
|
59
|
-
}
|
|
60
|
-
return result;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
/**
|
|
64
|
-
* List all namespaces with resolving details.
|
|
65
|
-
*
|
|
66
|
-
* @returns {{filepathNs: Object<string, TeqFw_Di_Shared_Api_Dto_Plugin_Desc_Autoload>, logicalNs: Object<string, TeqFw_Di_Shared_Api_Dto_Plugin_Desc_Autoload>}}
|
|
67
|
-
*/
|
|
68
|
-
list() {
|
|
69
|
-
const filepathNs = this.filepathNs.list();
|
|
70
|
-
const logicalNs = this.logicalNs.list();
|
|
71
|
-
return {filepathNs, logicalNs};
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
}
|
package/src/Shared/SpecProxy.mjs
DELETED
|
@@ -1,110 +0,0 @@
|
|
|
1
|
-
// MODULE'S IMPORT
|
|
2
|
-
import IdParser from './IdParser.mjs';
|
|
3
|
-
import ParsedId from './IdParser/Dto.mjs';
|
|
4
|
-
|
|
5
|
-
// MODULE'S VARS
|
|
6
|
-
const $parser = new IdParser();
|
|
7
|
-
|
|
8
|
-
// MODULE'S CLASSES
|
|
9
|
-
/**
|
|
10
|
-
* Proxy object for constructors specification ('spec' argument in constructor) to analyze dependencies and to collect
|
|
11
|
-
* required dependencies. This proxy adds constructed instances into container's `_instances` object.
|
|
12
|
-
*
|
|
13
|
-
* This code is too much coupled to `TeqFw_Di_Shared_Container` and extracted to separate class just for decreasing
|
|
14
|
-
* of nesting levels.
|
|
15
|
-
*
|
|
16
|
-
* @class
|
|
17
|
-
*/
|
|
18
|
-
export default class TeqFw_Di_Shared_SpecProxy {
|
|
19
|
-
/**
|
|
20
|
-
* @param {string} mainId ID of the constructing object ('Vendor_Module$', 'Vendor_Module$$', 'dbCfg').
|
|
21
|
-
* @param {string[]} uplineDeps All incomplete dependencies in current construction process
|
|
22
|
-
* (to prevent circular dependencies).
|
|
23
|
-
* @param {Map} containerSingletons Container level registry with created singletons (ids: 'dbCfg', 'Module$$').
|
|
24
|
-
* @param {Function} fnCreate constructing process level registry to save functions that
|
|
25
|
-
* construct main object & nested dependencies.
|
|
26
|
-
* @param {Function} fnGetObject `TeqFw_Di_Shared_Container.getObject` function to get/create required dependencies.
|
|
27
|
-
* construction process on some error (import error or circular dependency, for example).
|
|
28
|
-
* @param {Function} fnRejectUseFactory 'reject' function from 'TeqFw_Di_Shared_Container.getObject._useFactory' result.
|
|
29
|
-
* @returns {{}} Proxy object to resolve dependencies as `constructor(spec)`.
|
|
30
|
-
*/
|
|
31
|
-
constructor(
|
|
32
|
-
mainId,
|
|
33
|
-
uplineDeps,
|
|
34
|
-
containerSingletons,
|
|
35
|
-
fnCreate,
|
|
36
|
-
fnGetObject,
|
|
37
|
-
fnRejectUseFactory
|
|
38
|
-
) {
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* Resolved dependencies for currently constructing object (with `mainId`).
|
|
42
|
-
*
|
|
43
|
-
* @type {Object<string, Object>}
|
|
44
|
-
*/
|
|
45
|
-
const deps = {};
|
|
46
|
-
|
|
47
|
-
return new Proxy({}, {
|
|
48
|
-
get(target, prop) {
|
|
49
|
-
// convert property name of the `spec` object in `constructor(spec)` to dependency id string.
|
|
50
|
-
const depId = String(prop);
|
|
51
|
-
if (deps[depId]) {
|
|
52
|
-
// use dependency from local registry (it is possible on second, third, ... usage
|
|
53
|
-
// of the `constructor` after exception on un-existing dependency)
|
|
54
|
-
return deps[depId];
|
|
55
|
-
} else {
|
|
56
|
-
// we have no dependency in the local cache yet
|
|
57
|
-
// look up dependency in container's registry
|
|
58
|
-
const parsed = $parser.parse(depId);
|
|
59
|
-
if (
|
|
60
|
-
(parsed.typeTarget === ParsedId.TYPE_TARGET_SINGLETON) &&
|
|
61
|
-
containerSingletons.has(parsed.mapKey)
|
|
62
|
-
) {
|
|
63
|
-
// requested dependency is an instance and is created before
|
|
64
|
-
// save dependency to local registry & return
|
|
65
|
-
deps[depId] = containerSingletons.get(parsed.mapKey);
|
|
66
|
-
return deps[depId];
|
|
67
|
-
} else if (
|
|
68
|
-
(parsed.typeId === ParsedId.TYPE_ID_MANUAL) &&
|
|
69
|
-
(parsed.typeTarget === ParsedId.TYPE_TARGET_SINGLETON) &&
|
|
70
|
-
!containerSingletons.has(parsed.mapKey)
|
|
71
|
-
) {
|
|
72
|
-
throw new Error(`There is no '${parsed.mapKey}' singleton in the container.`);
|
|
73
|
-
} else {
|
|
74
|
-
// check stack of incomplete dependencies
|
|
75
|
-
if (parsed.nameModule) { // don't process manually inserted singletons
|
|
76
|
-
if (uplineDeps.includes(parsed.nameModule)) {
|
|
77
|
-
// `dep_id` is already requested to be created, so we report it as 'main'
|
|
78
|
-
const err = new Error(`Circular dependencies (main: ${depId}; dep: ${mainId})`);
|
|
79
|
-
fnRejectUseFactory(err); // reject async _useFactory
|
|
80
|
-
throw err; // break sync object's constructor
|
|
81
|
-
}
|
|
82
|
-
// ... and register new one
|
|
83
|
-
uplineDeps.push(parsed.nameModule);
|
|
84
|
-
}
|
|
85
|
-
// create new required dependency for this object
|
|
86
|
-
fnGetObject(depId, uplineDeps).then((obj) => {
|
|
87
|
-
if (obj === undefined) throw new Error(`Cannot resolve dependency '${depId}'.`);
|
|
88
|
-
// save created `dep_id` instance to local dependencies registry
|
|
89
|
-
deps[depId] = obj;
|
|
90
|
-
// remove created dependency from circular registry
|
|
91
|
-
uplineDeps.splice(uplineDeps.indexOf(parsed.nameModule), 1);
|
|
92
|
-
// re-call main object construction function
|
|
93
|
-
fnCreate();
|
|
94
|
-
}).catch(err => {
|
|
95
|
-
// re-throw error from promise
|
|
96
|
-
fnRejectUseFactory(err); // reject async _useFactory
|
|
97
|
-
});
|
|
98
|
-
}
|
|
99
|
-
// interrupt construction process until new dependency will be created
|
|
100
|
-
// and new construction process will be started (see try-catch block in `fnCreate`)
|
|
101
|
-
throw TeqFw_Di_Shared_SpecProxy.EXCEPTION_TO_STEALTH;
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
});
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
// static properties (compatible with Safari "< 14.1", "iOS < 14.5" form)
|
|
109
|
-
/** Marker for construction exceptions that should be stolen. */
|
|
110
|
-
TeqFw_Di_Shared_SpecProxy.EXCEPTION_TO_STEALTH = Symbol('exception_to_stealth')
|