@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
|
@@ -0,0 +1,45 @@
|
|
|
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
|
+
export default class TeqFw_Di_PreProcessor {
|
|
9
|
+
|
|
10
|
+
constructor() {
|
|
11
|
+
// VARS
|
|
12
|
+
/**
|
|
13
|
+
* The array of handlers in the dependency order (from the basic (di) up to the app).
|
|
14
|
+
* @type {Array<function(TeqFw_Di_Api_ObjectKey, TeqFw_Di_Api_ObjectKey):TeqFw_Di_Api_ObjectKey>}
|
|
15
|
+
*/
|
|
16
|
+
const _handlers = [];
|
|
17
|
+
|
|
18
|
+
// INSTANCE METHODS
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
*
|
|
22
|
+
* @param {function(TeqFw_Di_Api_ObjectKey, TeqFw_Di_Api_ObjectKey):TeqFw_Di_Api_ObjectKey} hndl
|
|
23
|
+
*/
|
|
24
|
+
this.addHandler = function (hndl) {
|
|
25
|
+
_handlers.push(hndl);
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Get all pre-processing handlers.
|
|
30
|
+
* @return {Array<function(TeqFw_Di_Api_ObjectKey, TeqFw_Di_Api_ObjectKey): TeqFw_Di_Api_ObjectKey>}
|
|
31
|
+
*/
|
|
32
|
+
this.getHandlers = () => _handlers;
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* @param {TeqFw_Di_Api_ObjectKey} objectKey
|
|
36
|
+
* @return {TeqFw_Di_Api_ObjectKey}
|
|
37
|
+
*/
|
|
38
|
+
this.process = function (objectKey) {
|
|
39
|
+
let res = objectKey;
|
|
40
|
+
for (const one of _handlers)
|
|
41
|
+
res = one(res, objectKey);
|
|
42
|
+
return res;
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
};
|
package/src/Resolver.js
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Resolver should convert ES6 module name into the path to the sources (file path or URL).
|
|
3
|
+
*
|
|
4
|
+
* This is a base resolver that considers that:
|
|
5
|
+
* - module name is Zend1-compatible ('Vendor_Package_Module')
|
|
6
|
+
* - every namespace is bound to some real path ('Vendor_Package_' => '.../node_modules/@vendor/package/src/...)
|
|
7
|
+
* - every package has sources with the same extensions (*.js, *.mjs, *.es6, ...)
|
|
8
|
+
* - namespaces can be nested (App_Web_ => ./@app/web/..., App_Web_Api_ => ./@app/web_api/...)
|
|
9
|
+
*/
|
|
10
|
+
import Defs from './Defs.js';
|
|
11
|
+
|
|
12
|
+
// VARS
|
|
13
|
+
const KEY_EXT = 'ext';
|
|
14
|
+
const KEY_NS = 'ns';
|
|
15
|
+
const KEY_PATH = 'root';
|
|
16
|
+
/**
|
|
17
|
+
* Namespace parts separator.
|
|
18
|
+
*
|
|
19
|
+
* @type {string}
|
|
20
|
+
*/
|
|
21
|
+
const NSS = '_';
|
|
22
|
+
|
|
23
|
+
// MAIN
|
|
24
|
+
export default class TeqFw_Di_Resolver {
|
|
25
|
+
|
|
26
|
+
constructor() {
|
|
27
|
+
// VARS
|
|
28
|
+
const _regNs = {};
|
|
29
|
+
let _namespaces = [];
|
|
30
|
+
let _ps = '/'; // web & unix path separator
|
|
31
|
+
|
|
32
|
+
// INSTANCE METHODS
|
|
33
|
+
|
|
34
|
+
this.addNamespaceRoot = function (ns, path, ext) {
|
|
35
|
+
_regNs[ns] = {
|
|
36
|
+
[KEY_EXT]: ext ?? Defs.EXT,
|
|
37
|
+
[KEY_NS]: ns,
|
|
38
|
+
[KEY_PATH]: path,
|
|
39
|
+
};
|
|
40
|
+
_namespaces = Object.keys(_regNs).sort((a, b) => b.localeCompare(a));
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Convert the module name to the path of the source files .
|
|
45
|
+
* @param {string} moduleName 'Vendor_Package_Module'
|
|
46
|
+
* @return {string} '/home/user/app/node_modules/@vendor/package/src/Module.js'
|
|
47
|
+
*/
|
|
48
|
+
this.resolve = function (moduleName) {
|
|
49
|
+
let root, ext, ns;
|
|
50
|
+
for (ns of _namespaces) {
|
|
51
|
+
if (moduleName.startsWith(ns)) {
|
|
52
|
+
root = _regNs[ns][KEY_PATH];
|
|
53
|
+
ext = _regNs[ns][KEY_EXT];
|
|
54
|
+
break;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
if (root && ext) {
|
|
58
|
+
let tail = moduleName.replace(ns, '');
|
|
59
|
+
if (tail.indexOf(NSS) === 0) tail = tail.replace(NSS, '');
|
|
60
|
+
const file = tail.replaceAll(NSS, _ps);
|
|
61
|
+
return `${root}${_ps}${file}.${ext}`;
|
|
62
|
+
} else return moduleName;
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
};
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This is default parser that converts dependencies specification into array of depIds.
|
|
3
|
+
* @namespace Spec.Parser
|
|
4
|
+
*/
|
|
5
|
+
// IMPORTS
|
|
6
|
+
|
|
7
|
+
// VARS
|
|
8
|
+
const FN_ARW = /^.*\((\{[^}]*}|[^)]*)\)\s* =>.*$/s;
|
|
9
|
+
const FN_CLASS = /class.*constructor.*\((.*)\).*/s;
|
|
10
|
+
const FN_REG = /^function.*\((\{[^}]*}|[^)]*)\)\s*\{[^}]*}$/s;
|
|
11
|
+
|
|
12
|
+
// FUNCS
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Extract spec arguments from the class constructor '({...}=>{})
|
|
16
|
+
* @param {string} spec
|
|
17
|
+
* @return {null|string}
|
|
18
|
+
*/
|
|
19
|
+
function specClass(spec) {
|
|
20
|
+
const parts = FN_CLASS.exec(spec);
|
|
21
|
+
if (parts?.[1]) {
|
|
22
|
+
return parts[1].replace('{', '')
|
|
23
|
+
.replace('}', '')
|
|
24
|
+
.replace(/\n/g, ' ');
|
|
25
|
+
}
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Extract spec arguments from the arrow '({...}=>{})
|
|
31
|
+
* @param {string} spec
|
|
32
|
+
* @return {null|string}
|
|
33
|
+
*/
|
|
34
|
+
function specFnArrow(spec) {
|
|
35
|
+
const parts = FN_ARW.exec(spec);
|
|
36
|
+
if (parts?.[1]) {
|
|
37
|
+
return parts[1].replace('{', '')
|
|
38
|
+
.replace('}', '')
|
|
39
|
+
.replace(/\n/g, ' ');
|
|
40
|
+
}
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Extract spec arguments from the regular 'function({...})
|
|
46
|
+
* @param {string} spec
|
|
47
|
+
* @return {null|string}
|
|
48
|
+
*/
|
|
49
|
+
function specFnRegular(spec) {
|
|
50
|
+
const parts = FN_REG.exec(spec);
|
|
51
|
+
if (parts?.[1]) {
|
|
52
|
+
return parts[1].replace('{', '')
|
|
53
|
+
.replace('}', '')
|
|
54
|
+
.replace(/\n/g, ' ');
|
|
55
|
+
}
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function argsComplex(args) {
|
|
60
|
+
const res = [];
|
|
61
|
+
/** @type {string[]} */
|
|
62
|
+
const all = args.split(',');
|
|
63
|
+
for (const one of all) {
|
|
64
|
+
let arg = one.trim();
|
|
65
|
+
if (arg.includes('[')) {
|
|
66
|
+
// ['Vendor_App_Mod.arg1$$#adp']: arg1,
|
|
67
|
+
const norm = arg.replaceAll('\'', '')
|
|
68
|
+
.replaceAll('"', '')
|
|
69
|
+
.replace('[', '')
|
|
70
|
+
.replace(']', '');
|
|
71
|
+
const parts = norm.split(':');
|
|
72
|
+
res.push(parts[0]);
|
|
73
|
+
} else if (arg.includes(':')) {
|
|
74
|
+
// Vendor_App_Mod$$: arg1,
|
|
75
|
+
const parts = arg.split(':');
|
|
76
|
+
res.push(parts[0]);
|
|
77
|
+
} else if (arg.length) {
|
|
78
|
+
// Vendor_App_Mod$$
|
|
79
|
+
res.push(arg);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
return res;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* @param {Object} factory
|
|
87
|
+
* @return {string[]}
|
|
88
|
+
*/
|
|
89
|
+
export default function (factory) {
|
|
90
|
+
const res = [];
|
|
91
|
+
if (typeof factory === 'function') {
|
|
92
|
+
const def = factory.toString();
|
|
93
|
+
let spec = specFnRegular(def);
|
|
94
|
+
if (spec) return argsComplex(spec);
|
|
95
|
+
spec = specFnArrow(def);
|
|
96
|
+
if (spec) return argsComplex(spec);
|
|
97
|
+
spec = specClass(def);
|
|
98
|
+
if (spec) return argsComplex(spec);
|
|
99
|
+
}
|
|
100
|
+
return res;
|
|
101
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This function analyzes specification of dependencies extracted from the text definition of the function itself.
|
|
3
|
+
*/
|
|
4
|
+
import Defs from './Defs.js';
|
|
5
|
+
|
|
6
|
+
// VARS
|
|
7
|
+
const FUNC = /function\s*\w*\s*\(\s*\{([^\}]*)\}/s;
|
|
8
|
+
const CLASS = /constructor\s*\(\s*\{([^\}]*)\}/s;
|
|
9
|
+
|
|
10
|
+
// FUNCS
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Internal function to analyze extracted parameters.
|
|
14
|
+
*
|
|
15
|
+
* @param {string} params
|
|
16
|
+
* @return {string[]}
|
|
17
|
+
* @private
|
|
18
|
+
*/
|
|
19
|
+
function _analyze(params) {
|
|
20
|
+
const res = [];
|
|
21
|
+
// create wrapper for arguments and collect dependencies using Proxy
|
|
22
|
+
try {
|
|
23
|
+
const fn = new Function(`{${params}}`, 'return');
|
|
24
|
+
const spec = new Proxy({}, {
|
|
25
|
+
get: (target, prop) => res.push(prop),
|
|
26
|
+
});
|
|
27
|
+
// run wrapper and return dependencies
|
|
28
|
+
fn(spec);
|
|
29
|
+
} catch (e) {
|
|
30
|
+
const msg = `Cannot analyze the deps specification:${params}\n`
|
|
31
|
+
+ `\nPlease, be sure that spec does not contain extra ')' in a comments.`
|
|
32
|
+
+ `\n\nError: ${e}`;
|
|
33
|
+
throw new Error(msg);
|
|
34
|
+
}
|
|
35
|
+
return res;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* @param {Function|Object} exp
|
|
40
|
+
* @return {string[]}
|
|
41
|
+
*/
|
|
42
|
+
function _analyzeClass(exp) {
|
|
43
|
+
const res = [];
|
|
44
|
+
// extract arguments from constructor
|
|
45
|
+
const def = exp.toString();
|
|
46
|
+
const parts = CLASS.exec(def);
|
|
47
|
+
if (parts) {
|
|
48
|
+
res.push(..._analyze(parts[1]));
|
|
49
|
+
} // else: constructor does not have arguments
|
|
50
|
+
return res;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* @param {Function|Object} exp
|
|
55
|
+
* @return {string[]}
|
|
56
|
+
*/
|
|
57
|
+
function _analyzeFunc(exp) {
|
|
58
|
+
const res = [];
|
|
59
|
+
// extract arguments from factory function
|
|
60
|
+
const def = exp.toString();
|
|
61
|
+
const parts = FUNC.exec(def);
|
|
62
|
+
if (parts) {
|
|
63
|
+
res.push(..._analyze(parts[1]));
|
|
64
|
+
} // else: constructor does not have arguments
|
|
65
|
+
return res;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// MAIN
|
|
69
|
+
/**
|
|
70
|
+
* @param {Function|Object} exp
|
|
71
|
+
* @return {string[]}
|
|
72
|
+
*/
|
|
73
|
+
export default function (exp) {
|
|
74
|
+
if (typeof exp === 'function') {
|
|
75
|
+
if (Defs.isClass(exp)) {
|
|
76
|
+
return _analyzeClass(exp);
|
|
77
|
+
} else {
|
|
78
|
+
return _analyzeFunc(exp);
|
|
79
|
+
}
|
|
80
|
+
} else
|
|
81
|
+
return [];
|
|
82
|
+
}
|
package/teqfw.json
CHANGED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
// const path = require('path');
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
|
|
4
|
+
const __dirname = path.dirname(new URL(import.meta.url).pathname);
|
|
5
|
+
|
|
6
|
+
export default {
|
|
7
|
+
entry: {
|
|
8
|
+
esm: {import: './index.mjs', filename: 'di.esm.js'},
|
|
9
|
+
cjs: {import: './index.cjs', filename: 'di.cjs.js'},
|
|
10
|
+
},
|
|
11
|
+
output: {
|
|
12
|
+
path: path.resolve(__dirname, 'dist'),
|
|
13
|
+
},
|
|
14
|
+
mode: 'development',
|
|
15
|
+
};
|
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* DTO to represent plugin descriptor (teqfw.json) structure
|
|
3
|
-
* that is related to 'di' node:
|
|
4
|
-
*/
|
|
5
|
-
// MODULE'S VARS
|
|
6
|
-
const NS = 'TeqFw_Di_Back_Api_Dto_Plugin_Desc';
|
|
7
|
-
|
|
8
|
-
// MODULE'S CLASSES
|
|
9
|
-
export default class TeqFw_Di_Back_Api_Dto_Plugin_Desc {
|
|
10
|
-
/** @type {TeqFw_Di_Shared_Api_Dto_Plugin_Desc_Autoload} */
|
|
11
|
-
autoload;
|
|
12
|
-
/**
|
|
13
|
-
* Replacements for IDs:
|
|
14
|
-
* - {'Interface_Name': 'Impl_Name'}: replace es6-module 'Interface_Name' with 'Impl_Name' on injection;
|
|
15
|
-
* - {'front': {'Interface_Name': 'Impl_Name'}}: do the same for 'front' area only (should be implemented outside this plugin)
|
|
16
|
-
* @type {Object<string, string>|Object<string, Object<string, string>>}
|
|
17
|
-
*/
|
|
18
|
-
replace;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
// attributes names to use as aliases in queries to object props
|
|
22
|
-
TeqFw_Di_Back_Api_Dto_Plugin_Desc.AUTOLOAD = 'autoload';
|
|
23
|
-
TeqFw_Di_Back_Api_Dto_Plugin_Desc.REPLACE = 'replace';
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* Factory to create new DTO instances.
|
|
27
|
-
* @memberOf TeqFw_Di_Back_Api_Dto_Plugin_Desc
|
|
28
|
-
*/
|
|
29
|
-
export class Factory {
|
|
30
|
-
static namespace = NS;
|
|
31
|
-
|
|
32
|
-
constructor(spec) {
|
|
33
|
-
/** @type {TeqFw_Di_Shared_Api_Dto_Plugin_Desc_Autoload.Factory} */
|
|
34
|
-
const fAutoload = spec['TeqFw_Di_Shared_Api_Dto_Plugin_Desc_Autoload.Factory$'];
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
* @param {*} data
|
|
38
|
-
* @return {TeqFw_Di_Back_Api_Dto_Plugin_Desc}
|
|
39
|
-
*/
|
|
40
|
-
this.create = function (data = null) {
|
|
41
|
-
|
|
42
|
-
// FUNCS
|
|
43
|
-
function parseReplace(data) {
|
|
44
|
-
const res = {};
|
|
45
|
-
if (typeof data === 'object')
|
|
46
|
-
for (const ns of Object.keys(data)) {
|
|
47
|
-
const node = data[ns];
|
|
48
|
-
if (typeof node === 'string') {
|
|
49
|
-
res[ns] = data[ns]; // {"interface": "impl"}
|
|
50
|
-
} else if (typeof node === 'object') {
|
|
51
|
-
res[ns] = {}; // {"interface": {"area": "impl"}}
|
|
52
|
-
for (const area of Object.keys(node))
|
|
53
|
-
if (typeof node[area] === 'string')
|
|
54
|
-
res[ns][area] = node[area];
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
return res;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
// MAIN
|
|
61
|
-
const res = new TeqFw_Di_Back_Api_Dto_Plugin_Desc();
|
|
62
|
-
res.autoload = fAutoload.create(data?.autoload);
|
|
63
|
-
res.replace = parseReplace(data?.replace);
|
|
64
|
-
return res;
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
// freeze DTO class to deny attributes changes and pin namespace
|
|
70
|
-
Object.freeze(TeqFw_Di_Back_Api_Dto_Plugin_Desc);
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
// MODULE'S CLASSES
|
|
2
|
-
/**
|
|
3
|
-
* Data structure for TeqFW plugins scanner (@see TeqFw_Di_Back_Plugin_Scanner).
|
|
4
|
-
*/
|
|
5
|
-
export default class TeqFw_Di_Back_Api_Dto_Scanned {
|
|
6
|
-
/** @type {Object} 'package.json' data */
|
|
7
|
-
package;
|
|
8
|
-
/** @type {String} absolute path to the root of the plugin package */
|
|
9
|
-
path;
|
|
10
|
-
/** @type {Object} 'teqfw.json' data */
|
|
11
|
-
teqfw;
|
|
12
|
-
}
|
package/src/Back/Api/README.md
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
Data structures and functionality available for consumer's backend code (published API for external nodejs programs).
|
package/src/Back/Defaults.mjs
DELETED
|
@@ -1,154 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Scanner to lookup for Teq-plugins in given folder and 'node_modules' subfolder and to compose array with
|
|
3
|
-
* 'TeqFw_Di_Shared_Api_Dto_Plugin_Desc_Autoload' data.
|
|
4
|
-
*/
|
|
5
|
-
// MODULE'S IMPORT
|
|
6
|
-
import ScanData from '../Api/Dto/Scanned.mjs';
|
|
7
|
-
import {existsSync, readdirSync, readFileSync, statSync} from 'fs';
|
|
8
|
-
import {join} from 'path';
|
|
9
|
-
|
|
10
|
-
// MODULE'S VARS
|
|
11
|
-
const PACKAGE = 'package.json';
|
|
12
|
-
const TEQFW = 'teqfw.json';
|
|
13
|
-
|
|
14
|
-
// MODULE'S CLASSES
|
|
15
|
-
export default class TeqFw_Di_Back_Plugin_Scanner {
|
|
16
|
-
|
|
17
|
-
constructor(spec) {
|
|
18
|
-
// DEPS
|
|
19
|
-
/** @type {TeqFw_Di_Back_Defaults} */
|
|
20
|
-
const DEF = spec['TeqFw_Di_Back_Defaults$'];
|
|
21
|
-
/** @type {TeqFw_Di_Back_Api_Dto_Plugin_Desc.Factory} */
|
|
22
|
-
const fDesc = spec['TeqFw_Di_Back_Api_Dto_Plugin_Desc.Factory$'];
|
|
23
|
-
/** @type {TeqFw_Di_Shared_Api_Dto_Plugin_Desc_Autoload.Factory} */
|
|
24
|
-
const fAutoload = spec['TeqFw_Di_Shared_Api_Dto_Plugin_Desc_Autoload.Factory$'];
|
|
25
|
-
|
|
26
|
-
// INSTANCE METHODS
|
|
27
|
-
|
|
28
|
-
this.getDescriptors = async function (path) {
|
|
29
|
-
const result = [];
|
|
30
|
-
const plugins = await this.scanFilesystem(path);
|
|
31
|
-
for (const [, one] of Object.entries(plugins)) {
|
|
32
|
-
if (one.teqfw?.[DEF.NAME]) {
|
|
33
|
-
/** @type {TeqFw_Di_Back_Api_Dto_Plugin_Desc} */
|
|
34
|
-
const desc = fDesc.create(one.teqfw[DEF.NAME]);
|
|
35
|
-
Object.freeze(desc);
|
|
36
|
-
result.push(desc);
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
return result;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
/**
|
|
43
|
-
* Scan given 'path' to get namespaces mapping for TeqFW plugins.
|
|
44
|
-
* @param {String} path
|
|
45
|
-
* @return {Promise<Object.<string, TeqFw_Di_Shared_Api_Dto_Plugin_Desc_Autoload>>}
|
|
46
|
-
*/
|
|
47
|
-
this.getNamespaces = async function (path) {
|
|
48
|
-
const result = {};
|
|
49
|
-
const plugins = await this.scanFilesystem(path);
|
|
50
|
-
for (const [path, one] of Object.entries(plugins)) {
|
|
51
|
-
if (one.teqfw?.[DEF.NAME]) {
|
|
52
|
-
/** @type {TeqFw_Di_Back_Api_Dto_Plugin_Desc} */
|
|
53
|
-
const desc = fDesc.create(one.teqfw[DEF.NAME]);
|
|
54
|
-
if (desc?.autoload?.ns) {
|
|
55
|
-
// make a copy, setup and freeze it
|
|
56
|
-
/** @type {TeqFw_Di_Shared_Api_Dto_Plugin_Desc_Autoload} */
|
|
57
|
-
const item = fAutoload.create(desc.autoload);
|
|
58
|
-
item.path = join(path, item.path);
|
|
59
|
-
Object.freeze(item);
|
|
60
|
-
result[item.ns] = item;
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
return result;
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
// DEFINE PROTO METHODS
|
|
69
|
-
|
|
70
|
-
/**
|
|
71
|
-
* Scan given 'path' to get JSON descriptors for TeqFW plugins ('teqfw.json')
|
|
72
|
-
* and for corresponded 'package.json' files.
|
|
73
|
-
*
|
|
74
|
-
* @param {String} path
|
|
75
|
-
* @return {Promise<Object.<string, TeqFw_Di_Back_Api_Dto_Scanned>>} Object-to-path map for found plugins
|
|
76
|
-
*/
|
|
77
|
-
async scanFilesystem(path) {
|
|
78
|
-
// FUNCS
|
|
79
|
-
function readData(path) {
|
|
80
|
-
// FUNCS
|
|
81
|
-
/**
|
|
82
|
-
* Check existence of JSON file, read content, parse JSON and return data.
|
|
83
|
-
*
|
|
84
|
-
* @param {String} filename
|
|
85
|
-
* @returns {Object|null}
|
|
86
|
-
*/
|
|
87
|
-
function readJson(filename) {
|
|
88
|
-
let result = null;
|
|
89
|
-
try {
|
|
90
|
-
const stat = statSync(filename);
|
|
91
|
-
if (stat.isFile()) {
|
|
92
|
-
const buffer = readFileSync(filename);
|
|
93
|
-
const content = buffer.toString();
|
|
94
|
-
const json = JSON.parse(content);
|
|
95
|
-
if (typeof json === 'object') {
|
|
96
|
-
result = json;
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
} catch (e) {
|
|
100
|
-
// stealth exception if JSON file does not exist.
|
|
101
|
-
if (e.code !== 'ENOENT' && e.code !== 'ENOTDIR') {
|
|
102
|
-
// re-throw other exceptions (wrong format or something else)
|
|
103
|
-
throw e;
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
return result;
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
// MAIN
|
|
110
|
-
let result = null;
|
|
111
|
-
const pathTeqfw = join(path, TEQFW);
|
|
112
|
-
const pathPkg = join(path, PACKAGE);
|
|
113
|
-
const dataTeqfw = readJson(pathTeqfw);
|
|
114
|
-
const dataPkg = readJson(pathPkg);
|
|
115
|
-
if ((dataTeqfw !== null) && (dataPkg !== null)) {
|
|
116
|
-
result = new ScanData();
|
|
117
|
-
result.teqfw = dataTeqfw;
|
|
118
|
-
result.package = dataPkg;
|
|
119
|
-
result.path = path;
|
|
120
|
-
}
|
|
121
|
-
return result;
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
// MAIN
|
|
125
|
-
const result = {};
|
|
126
|
-
// get scan data for root folder (application itself)
|
|
127
|
-
const dataRoot = readData(path);
|
|
128
|
-
if (dataRoot !== null) result[path] = dataRoot;
|
|
129
|
-
// scan 'node modules' packages for TeqFW plugins
|
|
130
|
-
const pathNodeMods = join(path, 'node_modules');
|
|
131
|
-
if (existsSync(pathNodeMods)) {
|
|
132
|
-
const packages = readdirSync(pathNodeMods);
|
|
133
|
-
for (const pack of packages) {
|
|
134
|
-
if (pack[0] === '@') {
|
|
135
|
-
// scan scope for nested packages
|
|
136
|
-
const pathScope = join(pathNodeMods, pack);
|
|
137
|
-
const scopedPackages = readdirSync(pathScope);
|
|
138
|
-
for (const sub of scopedPackages) {
|
|
139
|
-
const pathNested = join(pathScope, sub);
|
|
140
|
-
const dataNested = readData(pathNested);
|
|
141
|
-
if (dataNested !== null) result[pathNested] = dataNested;
|
|
142
|
-
}
|
|
143
|
-
} else {
|
|
144
|
-
// check package
|
|
145
|
-
const pathNested = join(pathNodeMods, pack);
|
|
146
|
-
const dataNested = readData(pathNested);
|
|
147
|
-
if (dataNested !== null) result[pathNested] = dataNested;
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
return result;
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
}
|
package/src/Back/README.md
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
This code can be used in nodejs apps only (contains nodejs imports).
|
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* DTO to represent plugin descriptor (teqfw.json) structure
|
|
3
|
-
* that is related to 'di/autoload' node:
|
|
4
|
-
*/
|
|
5
|
-
// MODULE'S VARS
|
|
6
|
-
const NS = 'TeqFw_Di_Shared_Api_Dto_Plugin_Desc_Autoload';
|
|
7
|
-
|
|
8
|
-
// MODULE'S CLASSES
|
|
9
|
-
export default class TeqFw_Di_Shared_Api_Dto_Plugin_Desc_Autoload {
|
|
10
|
-
/** @type {string} extension for files in the namespace */
|
|
11
|
-
ext;
|
|
12
|
-
/** @type {boolean} absolute or relative mapping is used in namespace */
|
|
13
|
-
isAbsolute;
|
|
14
|
-
/** @type {string} namespace ('Vnd_Project_Plugin') */
|
|
15
|
-
ns;
|
|
16
|
-
/** @type {string} path to the root of the namespace relative to npm package root ('./src') */
|
|
17
|
-
path;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
// attributes names to use as aliases in queries to object props
|
|
21
|
-
TeqFw_Di_Shared_Api_Dto_Plugin_Desc_Autoload.EXT = 'ext';
|
|
22
|
-
TeqFw_Di_Shared_Api_Dto_Plugin_Desc_Autoload.IS_ABSOLUTE = 'isAbsolute';
|
|
23
|
-
TeqFw_Di_Shared_Api_Dto_Plugin_Desc_Autoload.NS = 'ns';
|
|
24
|
-
TeqFw_Di_Shared_Api_Dto_Plugin_Desc_Autoload.PATH = 'path';
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* Factory to create new DTO instances.
|
|
28
|
-
* @memberOf TeqFw_Di_Shared_Api_Dto_Plugin_Desc_Autoload
|
|
29
|
-
*/
|
|
30
|
-
export class Factory {
|
|
31
|
-
static namespace = NS;
|
|
32
|
-
|
|
33
|
-
constructor() {
|
|
34
|
-
/**
|
|
35
|
-
* @param {TeqFw_Di_Shared_Api_Dto_Plugin_Desc_Autoload|null} data
|
|
36
|
-
* @return {TeqFw_Di_Shared_Api_Dto_Plugin_Desc_Autoload}
|
|
37
|
-
*/
|
|
38
|
-
this.create = function (data = null) {
|
|
39
|
-
const res = new TeqFw_Di_Shared_Api_Dto_Plugin_Desc_Autoload();
|
|
40
|
-
res.ext = data?.ext ?? 'mjs';
|
|
41
|
-
res.isAbsolute = data?.isAbsolute ?? false;
|
|
42
|
-
res.ns = data?.ns;
|
|
43
|
-
res.path = data?.path;
|
|
44
|
-
return res;
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
// freeze DTO class to deny attributes changes and pin namespace
|
|
50
|
-
Object.freeze(TeqFw_Di_Shared_Api_Dto_Plugin_Desc_Autoload);
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Interface for proxy factory to create objects using 'Vnd_Plugin_Single@' & 'Vnd_Plugin_Obj@@' dependency ID.
|
|
3
|
-
* @interface
|
|
4
|
-
*/
|
|
5
|
-
export default class TeqFw_Di_Shared_Api_IProxy {
|
|
6
|
-
/**
|
|
7
|
-
* Get target object asynchronously.
|
|
8
|
-
* @return {Promise<*>}
|
|
9
|
-
*/
|
|
10
|
-
get create() { }
|
|
11
|
-
}
|