-
Notifications
You must be signed in to change notification settings - Fork 0
/
locator.esm.js
158 lines (137 loc) · 5.01 KB
/
locator.esm.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
'use strict';
export default class Locator {
/**
* @param {config} config
* @param {{}} locatableConfig
* @param {string} relativePathModifierToRoot eg '../../' if the locator is one folder deep from the root
*/
constructor(config, locatableConfig, relativePathModifierToRoot) {
this._config = config;
this._locatableConfig = locatableConfig;
this._relativePathModifierToRoot = relativePathModifierToRoot || '../../';
this._locatable = {};
this._reservedCharacters = ['%', '@', '~'];
}
/**
* @param {string} name
* @returns {object}
*/
async get(name) {
//there is already an instance
if (this._locatable[name] !== undefined) {
return this._locatable[name];
}
//there is no locatable config, throw an error
if (this._locatableConfig[name] === undefined) {
throw new Error('Could not retrieve an instance for ' + name);
}
//it's an alias, return the referenced object
if (typeof this._locatableConfig[name] === 'string') {
this._locatable[name] = await this.get(this._locatableConfig[name]);
return this._locatable[name];
}
//create an instance based on the configuration
this._locatable[name] = this._createInstance(this._locatableConfig[name]);
return this._locatable[name];
}
/**
* @param {string} requirable
* @returns {object}
* @private
*/
async _import(requirable) {
const originalRequirable = requirable;
if (requirable[0] === '.' && requirable[1] === '/') {
requirable = this._relativePathModifierToRoot + requirable.substring(2);
}
if (requirable.indexOf('[') === -1) {
return (await import(requirable)).default;
}
const splittedRequirable = requirable.split('[');
requirable = splittedRequirable[0];
let result = await import(requirable)
let determineIfDefaultShouldBeUsed = true
splittedRequirable.shift();
while(splittedRequirable.length > 0) {
const subRequirable = splittedRequirable.shift().replace(']', '');
if (determineIfDefaultShouldBeUsed && result.default !== undefined && result[subRequirable] === undefined) {
determineIfDefaultShouldBeUsed = false
result = result.default
}
if (result[subRequirable] === undefined) {
throw new Error('Could not require ' + originalRequirable);
}
result = result[subRequirable];
}
return result;
}
/**
* @param {{Array}|{string}} locatableConfig
* @return {object}
* @private
*/
async _createInstance(locatableConfig) {
//the config is a factory method, call and return it
if (typeof locatableConfig === 'function') {
return await locatableConfig(this, this._config);
}
const c = await this._import(locatableConfig[0]);
//if the second argument is a function, use it as factory method
if (typeof locatableConfig[1] === 'function') {
return await locatableConfig[1](this, c, this._config);
}
//if c is not constructable, return c
if (c.prototype === undefined || c.prototype.constructor === undefined) {
return c;
}
const dependencyConfigs = locatableConfig[1] || [];
const dependencies = [];
for (const dependencyConfig of dependencyConfigs) {
dependencies.push(await this._getDependency(dependencyConfig));
}
return new c(...dependencies);
}
/**
* @param {string|*} dependencyConfig
* @returns {object|*}
* @private
*/
async _getDependency(dependencyConfig) {
if (typeof dependencyConfig !== 'string') {
return dependencyConfig;
}
const firstChar = dependencyConfig[0];
const secondChar = dependencyConfig[1];
if (this._dependencyConfigIsEscaped(firstChar, secondChar)) {
return dependencyConfig.substring(1);
}
//config parameter
if (firstChar === '%') {
const configName = dependencyConfig.substring(1, dependencyConfig.length - 1);
return this._config.get(configName);
}
//another service
if (firstChar === '@') {
const name = dependencyConfig.substring(1);
return await this.get(name);
}
//require
if (firstChar === '~') {
const requirable = dependencyConfig.substring(1);
return await this._import(requirable);
}
return dependencyConfig;
}
/**
* @param {string} firstChar
* @param {string} secondChar
* @returns {boolean}
* @private
*/
_dependencyConfigIsEscaped(firstChar, secondChar) {
if (this._reservedCharacters.indexOf(firstChar) === -1) {
return false;
}
return firstChar === secondChar;
}
}