@merkur/integration 0.37.0 → 0.38.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/lib/index.cjs +308 -116
- package/lib/index.es9.cjs +224 -84
- package/lib/index.es9.mjs +222 -85
- package/lib/index.js +308 -116
- package/lib/index.mjs +306 -117
- package/lib/index.umd.js +1 -1
- package/package.json +3 -3
- package/server/__tests__/indexSpec.js +272 -30
- package/server/index.js +25 -3
- package/server/__tests__/__snapshots__/indexSpec.js.snap +0 -19
package/lib/index.es9.cjs
CHANGED
|
@@ -36,87 +36,138 @@ const exported = {
|
|
|
36
36
|
isES13Supported,
|
|
37
37
|
test
|
|
38
38
|
};
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
39
|
+
const isLoadedSymbol = Symbol.for('isLoaded');
|
|
40
|
+
const loadingPromiseSymbol = Symbol.for('loadingPromise');
|
|
41
|
+
function _attachElementToAsset(asset, element) {
|
|
42
|
+
return {
|
|
43
|
+
...asset,
|
|
44
|
+
element
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
function _handleAssetError({
|
|
48
|
+
asset,
|
|
49
|
+
message = `Error loading asset ${asset.source}.`
|
|
50
|
+
}) {
|
|
51
|
+
if (asset.optional) {
|
|
52
|
+
console.warn(message);
|
|
53
|
+
return _attachElementToAsset(asset, null);
|
|
54
|
+
}
|
|
55
|
+
const error = new Error(message);
|
|
56
|
+
error.asset = asset;
|
|
57
|
+
throw error;
|
|
58
|
+
}
|
|
59
|
+
function _addListenersToAssetElement(asset, element, resolve, reject) {
|
|
60
|
+
element.addEventListener('load', () => {
|
|
61
|
+
resolve(_attachElementToAsset(asset, element));
|
|
62
|
+
element[isLoadedSymbol] = true;
|
|
63
|
+
delete element[loadingPromiseSymbol];
|
|
64
|
+
});
|
|
65
|
+
element.addEventListener('error', () => {
|
|
66
|
+
if (element.parentNode) {
|
|
67
|
+
element.remove();
|
|
49
68
|
}
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
asset.optional ? resolve(error) : reject(error);
|
|
57
|
-
};
|
|
58
|
-
script.src = asset.source;
|
|
59
|
-
const {
|
|
60
|
-
attr
|
|
61
|
-
} = asset;
|
|
62
|
-
if (attr && Object.keys(attr).length) {
|
|
63
|
-
for (const name in attr) {
|
|
64
|
-
const value = attr[name];
|
|
65
|
-
if (typeof value === 'boolean') {
|
|
66
|
-
if (value) {
|
|
67
|
-
script.setAttribute(name, '');
|
|
68
|
-
} else {
|
|
69
|
-
script.removeAttribute(name);
|
|
70
|
-
}
|
|
71
|
-
} else {
|
|
72
|
-
script.setAttribute(name, value);
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
} else {
|
|
77
|
-
script.text = asset.source;
|
|
78
|
-
resolve();
|
|
69
|
+
try {
|
|
70
|
+
resolve(_handleAssetError({
|
|
71
|
+
asset
|
|
72
|
+
}));
|
|
73
|
+
} catch (error) {
|
|
74
|
+
reject(error);
|
|
79
75
|
}
|
|
80
|
-
root.appendChild(script);
|
|
81
76
|
});
|
|
82
77
|
}
|
|
83
78
|
function _loadStyle(asset, root) {
|
|
84
|
-
|
|
79
|
+
if (asset.type === 'inlineStyle') {
|
|
80
|
+
const style = document.createElement('style');
|
|
81
|
+
style.innerHTML = asset.source;
|
|
82
|
+
root.appendChild(style);
|
|
83
|
+
return _attachElementToAsset(asset, style);
|
|
84
|
+
}
|
|
85
|
+
const link = document.createElement('link');
|
|
86
|
+
link[loadingPromiseSymbol] = new Promise((resolve, reject) => {
|
|
87
|
+
_addListenersToAssetElement(asset, link, resolve, reject);
|
|
88
|
+
link.rel = 'stylesheet';
|
|
89
|
+
link.href = asset.source;
|
|
90
|
+
root.appendChild(link);
|
|
91
|
+
});
|
|
92
|
+
return link[loadingPromiseSymbol];
|
|
93
|
+
}
|
|
94
|
+
async function loadStyleAssets(assets, root = document.head) {
|
|
95
|
+
const styleElements = Array.from(root.querySelectorAll('style'));
|
|
96
|
+
return Promise.all(assets.map(asset => {
|
|
97
|
+
if (!['stylesheet', 'inlineStyle'].includes(asset.type) || !asset.source) {
|
|
98
|
+
return _attachElementToAsset(asset, null);
|
|
99
|
+
}
|
|
85
100
|
if (asset.type === 'stylesheet') {
|
|
86
|
-
const link =
|
|
87
|
-
link
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
const style = document.createElement('style');
|
|
94
|
-
style.innerHTML = asset.source;
|
|
95
|
-
root.appendChild(style);
|
|
96
|
-
resolve();
|
|
101
|
+
const link = root.querySelector(`link[href='${asset.source}']`);
|
|
102
|
+
if (link) {
|
|
103
|
+
if (link[loadingPromiseSymbol]) {
|
|
104
|
+
return link[loadingPromiseSymbol];
|
|
105
|
+
}
|
|
106
|
+
return _attachElementToAsset(asset, link);
|
|
107
|
+
}
|
|
97
108
|
}
|
|
98
|
-
|
|
109
|
+
if (asset.type === 'inlineStyle') {
|
|
110
|
+
const inlineStyle = styleElements.find(element => element.innerHTML === asset.source);
|
|
111
|
+
if (inlineStyle) {
|
|
112
|
+
return _attachElementToAsset(asset, inlineStyle);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
return _loadStyle(asset, root);
|
|
116
|
+
}));
|
|
99
117
|
}
|
|
100
|
-
function
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
118
|
+
function _findScriptElement(scriptElements, asset) {
|
|
119
|
+
if (asset.type === 'json') {
|
|
120
|
+
return scriptElements.find(element => element.dataset.src === asset.source);
|
|
121
|
+
}
|
|
122
|
+
if (!['script', 'inlineScript', 'inlineJson'].includes(asset.type)) {
|
|
123
|
+
return null;
|
|
124
|
+
}
|
|
125
|
+
const attributeKey = asset.type === 'script' ? 'src' : 'textContent';
|
|
126
|
+
const source = asset.type === 'inlineJson' ? JSON.stringify(asset.source) : asset.source;
|
|
127
|
+
return scriptElements.find(element => element[attributeKey] === source) || null;
|
|
128
|
+
}
|
|
129
|
+
function _loadScript(asset, root) {
|
|
130
|
+
const script = document.createElement('script');
|
|
131
|
+
if (asset.type === 'inlineScript') {
|
|
132
|
+
script.textContent = asset.source;
|
|
133
|
+
root.appendChild(script);
|
|
134
|
+
return _attachElementToAsset(asset, script);
|
|
135
|
+
}
|
|
136
|
+
script[loadingPromiseSymbol] = new Promise((resolve, reject) => {
|
|
137
|
+
script.defer = true;
|
|
138
|
+
_addListenersToAssetElement(asset, script, resolve, reject);
|
|
139
|
+
script.src = asset.source;
|
|
140
|
+
const {
|
|
141
|
+
attr
|
|
142
|
+
} = asset;
|
|
143
|
+
if (attr && Object.keys(attr).length) {
|
|
144
|
+
for (const name in attr) {
|
|
145
|
+
const value = attr[name];
|
|
146
|
+
if (typeof value === 'boolean') {
|
|
147
|
+
if (value) {
|
|
148
|
+
script.setAttribute(name, '');
|
|
149
|
+
} else {
|
|
150
|
+
script.removeAttribute(name);
|
|
151
|
+
}
|
|
152
|
+
} else {
|
|
153
|
+
script.setAttribute(name, value);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
105
156
|
}
|
|
106
|
-
|
|
107
|
-
}
|
|
108
|
-
return
|
|
157
|
+
root.appendChild(script);
|
|
158
|
+
});
|
|
159
|
+
return script[loadingPromiseSymbol];
|
|
109
160
|
}
|
|
110
161
|
async function loadScriptAssets(assets, root = document.head) {
|
|
111
|
-
const scriptElements = root.querySelectorAll('script');
|
|
112
|
-
|
|
162
|
+
const scriptElements = Array.from(root.querySelectorAll('script'));
|
|
163
|
+
return Promise.all(assets.map(asset => {
|
|
164
|
+
if (!['script', 'inlineScript'].includes(asset.type) || !asset.source) {
|
|
165
|
+
return _attachElementToAsset(asset, null);
|
|
166
|
+
}
|
|
113
167
|
const {
|
|
114
168
|
source
|
|
115
169
|
} = asset;
|
|
116
170
|
const _asset = Object.assign({}, asset);
|
|
117
|
-
if (_asset.type !== 'script' && _asset.type !== 'inlineScript') {
|
|
118
|
-
return scripts;
|
|
119
|
-
}
|
|
120
171
|
if (source === Object(source)) {
|
|
121
172
|
if (source.es13 && exported.isES13Supported()) {
|
|
122
173
|
_asset.source = source.es13;
|
|
@@ -128,33 +179,122 @@ async function loadScriptAssets(assets, root = document.head) {
|
|
|
128
179
|
_asset.source = null;
|
|
129
180
|
}
|
|
130
181
|
if (!_asset.source) {
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
182
|
+
return _handleAssetError({
|
|
183
|
+
asset: _asset,
|
|
184
|
+
message: `Asset '${_asset.name}' is missing ES variant and could not be loaded.`
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
if (_asset.test && exported.test(_asset.test)) {
|
|
189
|
+
return _attachElementToAsset(_asset, _findScriptElement(scriptElements, _asset));
|
|
190
|
+
}
|
|
191
|
+
const script = _findScriptElement(scriptElements, _asset);
|
|
192
|
+
if (script && _asset.type === 'script') {
|
|
193
|
+
if (script[loadingPromiseSymbol]) {
|
|
194
|
+
return script[loadingPromiseSymbol];
|
|
195
|
+
}
|
|
196
|
+
if (script[isLoadedSymbol]) {
|
|
197
|
+
return _attachElementToAsset(_asset, script);
|
|
198
|
+
}
|
|
199
|
+
return new Promise((resolve, reject) => _addListenersToAssetElement(_asset, script, resolve, reject));
|
|
200
|
+
} else if (script && _asset.type === 'inlineScript') {
|
|
201
|
+
return _attachElementToAsset(_asset, script);
|
|
202
|
+
}
|
|
203
|
+
return _loadScript(_asset, root);
|
|
204
|
+
}));
|
|
205
|
+
}
|
|
206
|
+
async function _fetchData(source) {
|
|
207
|
+
const response = await fetch(source);
|
|
208
|
+
if (!response.ok) {
|
|
209
|
+
throw new Error(`Failed to fetch from '${source}' with status ${response.status} ${response.statusText}.`);
|
|
210
|
+
}
|
|
211
|
+
return response.text();
|
|
212
|
+
}
|
|
213
|
+
function _removeElementAfterTimeout(element, timeout) {
|
|
214
|
+
if (timeout) {
|
|
215
|
+
setTimeout(() => {
|
|
216
|
+
if (element.parentNode) {
|
|
217
|
+
element.remove();
|
|
218
|
+
}
|
|
219
|
+
}, timeout);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
function _loadJsonAsset(asset, root) {
|
|
223
|
+
const script = document.createElement('script');
|
|
224
|
+
script.type = 'application/json';
|
|
225
|
+
if (asset.type === 'inlineJson') {
|
|
226
|
+
script.textContent = JSON.stringify(asset.source);
|
|
227
|
+
root.appendChild(script);
|
|
228
|
+
_removeElementAfterTimeout(script, asset.ttl);
|
|
229
|
+
return _attachElementToAsset(asset, script);
|
|
230
|
+
}
|
|
231
|
+
script[loadingPromiseSymbol] = new Promise((resolve, reject) => {
|
|
232
|
+
script.dataset.src = asset.source;
|
|
233
|
+
root.appendChild(script);
|
|
234
|
+
(async () => {
|
|
235
|
+
try {
|
|
236
|
+
const textContent = await _fetchData(asset.source);
|
|
237
|
+
script.textContent = textContent;
|
|
238
|
+
delete script[loadingPromiseSymbol];
|
|
239
|
+
_removeElementAfterTimeout(script, asset.ttl);
|
|
240
|
+
resolve(_attachElementToAsset(asset, script));
|
|
241
|
+
} catch (error) {
|
|
242
|
+
script.remove();
|
|
243
|
+
try {
|
|
244
|
+
resolve(_handleAssetError({
|
|
245
|
+
asset,
|
|
246
|
+
message: `Error loading JSON asset '${asset.name}': ${error.message}`
|
|
247
|
+
}));
|
|
248
|
+
} catch (error) {
|
|
249
|
+
reject(error);
|
|
136
250
|
}
|
|
137
|
-
console.warn(message);
|
|
138
|
-
return scripts;
|
|
139
251
|
}
|
|
252
|
+
})();
|
|
253
|
+
});
|
|
254
|
+
return script[loadingPromiseSymbol];
|
|
255
|
+
}
|
|
256
|
+
async function loadJsonAssets(assets, root = document.head) {
|
|
257
|
+
const scriptElements = Array.from(root.querySelectorAll('script[type="application/json"]'));
|
|
258
|
+
return Promise.all(assets.map(asset => {
|
|
259
|
+
if (!['json', 'inlineJson'].includes(asset.type) || !asset.source) {
|
|
260
|
+
return _attachElementToAsset(asset, null);
|
|
140
261
|
}
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
262
|
+
const script = _findScriptElement(scriptElements, asset);
|
|
263
|
+
if (script) {
|
|
264
|
+
if (script[loadingPromiseSymbol]) {
|
|
265
|
+
return script[loadingPromiseSymbol];
|
|
266
|
+
}
|
|
267
|
+
if (script.textContent) {
|
|
268
|
+
return _attachElementToAsset(asset, script);
|
|
144
269
|
}
|
|
145
|
-
return
|
|
146
|
-
|
|
147
|
-
|
|
270
|
+
return _handleAssetError({
|
|
271
|
+
asset,
|
|
272
|
+
message: `JSON asset '${asset.name}' is missing textContent and could not be loaded.`
|
|
273
|
+
});
|
|
148
274
|
}
|
|
149
|
-
|
|
150
|
-
|
|
275
|
+
return _loadJsonAsset(asset, root);
|
|
276
|
+
}));
|
|
277
|
+
}
|
|
278
|
+
function _mergeResults(results) {
|
|
279
|
+
return results.reduce((acc, results) => {
|
|
280
|
+
results.forEach((result, index) => {
|
|
281
|
+
if (!acc[index]) {
|
|
282
|
+
acc[index] = result;
|
|
283
|
+
} else if (result.element) {
|
|
284
|
+
acc[index] = result;
|
|
285
|
+
}
|
|
286
|
+
});
|
|
287
|
+
return acc;
|
|
151
288
|
}, []);
|
|
152
|
-
return Promise.all(scriptsToRender.map(asset => _loadScript(asset, root)));
|
|
153
289
|
}
|
|
154
|
-
function loadAssets(assets, root) {
|
|
155
|
-
|
|
290
|
+
async function loadAssets(assets, root) {
|
|
291
|
+
const results = await Promise.all([loadScriptAssets(assets, root), loadStyleAssets(assets, root), loadJsonAssets(assets, root)]);
|
|
292
|
+
return _mergeResults(results);
|
|
156
293
|
}
|
|
294
|
+
exports.isLoadedSymbol = isLoadedSymbol;
|
|
157
295
|
exports.loadAssets = loadAssets;
|
|
296
|
+
exports.loadJsonAssets = loadJsonAssets;
|
|
158
297
|
exports.loadScriptAssets = loadScriptAssets;
|
|
159
298
|
exports.loadStyleAssets = loadStyleAssets;
|
|
299
|
+
exports.loadingPromiseSymbol = loadingPromiseSymbol;
|
|
160
300
|
exports.testScript = exported;
|
package/lib/index.es9.mjs
CHANGED
|
@@ -34,87 +34,138 @@ const exported = {
|
|
|
34
34
|
isES13Supported,
|
|
35
35
|
test
|
|
36
36
|
};
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
37
|
+
const isLoadedSymbol = Symbol.for('isLoaded');
|
|
38
|
+
const loadingPromiseSymbol = Symbol.for('loadingPromise');
|
|
39
|
+
function _attachElementToAsset(asset, element) {
|
|
40
|
+
return {
|
|
41
|
+
...asset,
|
|
42
|
+
element
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
function _handleAssetError({
|
|
46
|
+
asset,
|
|
47
|
+
message = `Error loading asset ${asset.source}.`
|
|
48
|
+
}) {
|
|
49
|
+
if (asset.optional) {
|
|
50
|
+
console.warn(message);
|
|
51
|
+
return _attachElementToAsset(asset, null);
|
|
52
|
+
}
|
|
53
|
+
const error = new Error(message);
|
|
54
|
+
error.asset = asset;
|
|
55
|
+
throw error;
|
|
56
|
+
}
|
|
57
|
+
function _addListenersToAssetElement(asset, element, resolve, reject) {
|
|
58
|
+
element.addEventListener('load', () => {
|
|
59
|
+
resolve(_attachElementToAsset(asset, element));
|
|
60
|
+
element[isLoadedSymbol] = true;
|
|
61
|
+
delete element[loadingPromiseSymbol];
|
|
62
|
+
});
|
|
63
|
+
element.addEventListener('error', () => {
|
|
64
|
+
if (element.parentNode) {
|
|
65
|
+
element.remove();
|
|
47
66
|
}
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
asset.optional ? resolve(error) : reject(error);
|
|
55
|
-
};
|
|
56
|
-
script.src = asset.source;
|
|
57
|
-
const {
|
|
58
|
-
attr
|
|
59
|
-
} = asset;
|
|
60
|
-
if (attr && Object.keys(attr).length) {
|
|
61
|
-
for (const name in attr) {
|
|
62
|
-
const value = attr[name];
|
|
63
|
-
if (typeof value === 'boolean') {
|
|
64
|
-
if (value) {
|
|
65
|
-
script.setAttribute(name, '');
|
|
66
|
-
} else {
|
|
67
|
-
script.removeAttribute(name);
|
|
68
|
-
}
|
|
69
|
-
} else {
|
|
70
|
-
script.setAttribute(name, value);
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
} else {
|
|
75
|
-
script.text = asset.source;
|
|
76
|
-
resolve();
|
|
67
|
+
try {
|
|
68
|
+
resolve(_handleAssetError({
|
|
69
|
+
asset
|
|
70
|
+
}));
|
|
71
|
+
} catch (error) {
|
|
72
|
+
reject(error);
|
|
77
73
|
}
|
|
78
|
-
root.appendChild(script);
|
|
79
74
|
});
|
|
80
75
|
}
|
|
81
76
|
function _loadStyle(asset, root) {
|
|
82
|
-
|
|
77
|
+
if (asset.type === 'inlineStyle') {
|
|
78
|
+
const style = document.createElement('style');
|
|
79
|
+
style.innerHTML = asset.source;
|
|
80
|
+
root.appendChild(style);
|
|
81
|
+
return _attachElementToAsset(asset, style);
|
|
82
|
+
}
|
|
83
|
+
const link = document.createElement('link');
|
|
84
|
+
link[loadingPromiseSymbol] = new Promise((resolve, reject) => {
|
|
85
|
+
_addListenersToAssetElement(asset, link, resolve, reject);
|
|
86
|
+
link.rel = 'stylesheet';
|
|
87
|
+
link.href = asset.source;
|
|
88
|
+
root.appendChild(link);
|
|
89
|
+
});
|
|
90
|
+
return link[loadingPromiseSymbol];
|
|
91
|
+
}
|
|
92
|
+
async function loadStyleAssets(assets, root = document.head) {
|
|
93
|
+
const styleElements = Array.from(root.querySelectorAll('style'));
|
|
94
|
+
return Promise.all(assets.map(asset => {
|
|
95
|
+
if (!['stylesheet', 'inlineStyle'].includes(asset.type) || !asset.source) {
|
|
96
|
+
return _attachElementToAsset(asset, null);
|
|
97
|
+
}
|
|
83
98
|
if (asset.type === 'stylesheet') {
|
|
84
|
-
const link =
|
|
85
|
-
link
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
const style = document.createElement('style');
|
|
92
|
-
style.innerHTML = asset.source;
|
|
93
|
-
root.appendChild(style);
|
|
94
|
-
resolve();
|
|
99
|
+
const link = root.querySelector(`link[href='${asset.source}']`);
|
|
100
|
+
if (link) {
|
|
101
|
+
if (link[loadingPromiseSymbol]) {
|
|
102
|
+
return link[loadingPromiseSymbol];
|
|
103
|
+
}
|
|
104
|
+
return _attachElementToAsset(asset, link);
|
|
105
|
+
}
|
|
95
106
|
}
|
|
96
|
-
|
|
107
|
+
if (asset.type === 'inlineStyle') {
|
|
108
|
+
const inlineStyle = styleElements.find(element => element.innerHTML === asset.source);
|
|
109
|
+
if (inlineStyle) {
|
|
110
|
+
return _attachElementToAsset(asset, inlineStyle);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
return _loadStyle(asset, root);
|
|
114
|
+
}));
|
|
97
115
|
}
|
|
98
|
-
function
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
116
|
+
function _findScriptElement(scriptElements, asset) {
|
|
117
|
+
if (asset.type === 'json') {
|
|
118
|
+
return scriptElements.find(element => element.dataset.src === asset.source);
|
|
119
|
+
}
|
|
120
|
+
if (!['script', 'inlineScript', 'inlineJson'].includes(asset.type)) {
|
|
121
|
+
return null;
|
|
122
|
+
}
|
|
123
|
+
const attributeKey = asset.type === 'script' ? 'src' : 'textContent';
|
|
124
|
+
const source = asset.type === 'inlineJson' ? JSON.stringify(asset.source) : asset.source;
|
|
125
|
+
return scriptElements.find(element => element[attributeKey] === source) || null;
|
|
126
|
+
}
|
|
127
|
+
function _loadScript(asset, root) {
|
|
128
|
+
const script = document.createElement('script');
|
|
129
|
+
if (asset.type === 'inlineScript') {
|
|
130
|
+
script.textContent = asset.source;
|
|
131
|
+
root.appendChild(script);
|
|
132
|
+
return _attachElementToAsset(asset, script);
|
|
133
|
+
}
|
|
134
|
+
script[loadingPromiseSymbol] = new Promise((resolve, reject) => {
|
|
135
|
+
script.defer = true;
|
|
136
|
+
_addListenersToAssetElement(asset, script, resolve, reject);
|
|
137
|
+
script.src = asset.source;
|
|
138
|
+
const {
|
|
139
|
+
attr
|
|
140
|
+
} = asset;
|
|
141
|
+
if (attr && Object.keys(attr).length) {
|
|
142
|
+
for (const name in attr) {
|
|
143
|
+
const value = attr[name];
|
|
144
|
+
if (typeof value === 'boolean') {
|
|
145
|
+
if (value) {
|
|
146
|
+
script.setAttribute(name, '');
|
|
147
|
+
} else {
|
|
148
|
+
script.removeAttribute(name);
|
|
149
|
+
}
|
|
150
|
+
} else {
|
|
151
|
+
script.setAttribute(name, value);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
103
154
|
}
|
|
104
|
-
|
|
105
|
-
}
|
|
106
|
-
return
|
|
155
|
+
root.appendChild(script);
|
|
156
|
+
});
|
|
157
|
+
return script[loadingPromiseSymbol];
|
|
107
158
|
}
|
|
108
159
|
async function loadScriptAssets(assets, root = document.head) {
|
|
109
|
-
const scriptElements = root.querySelectorAll('script');
|
|
110
|
-
|
|
160
|
+
const scriptElements = Array.from(root.querySelectorAll('script'));
|
|
161
|
+
return Promise.all(assets.map(asset => {
|
|
162
|
+
if (!['script', 'inlineScript'].includes(asset.type) || !asset.source) {
|
|
163
|
+
return _attachElementToAsset(asset, null);
|
|
164
|
+
}
|
|
111
165
|
const {
|
|
112
166
|
source
|
|
113
167
|
} = asset;
|
|
114
168
|
const _asset = Object.assign({}, asset);
|
|
115
|
-
if (_asset.type !== 'script' && _asset.type !== 'inlineScript') {
|
|
116
|
-
return scripts;
|
|
117
|
-
}
|
|
118
169
|
if (source === Object(source)) {
|
|
119
170
|
if (source.es13 && exported.isES13Supported()) {
|
|
120
171
|
_asset.source = source.es13;
|
|
@@ -126,30 +177,116 @@ async function loadScriptAssets(assets, root = document.head) {
|
|
|
126
177
|
_asset.source = null;
|
|
127
178
|
}
|
|
128
179
|
if (!_asset.source) {
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
180
|
+
return _handleAssetError({
|
|
181
|
+
asset: _asset,
|
|
182
|
+
message: `Asset '${_asset.name}' is missing ES variant and could not be loaded.`
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
if (_asset.test && exported.test(_asset.test)) {
|
|
187
|
+
return _attachElementToAsset(_asset, _findScriptElement(scriptElements, _asset));
|
|
188
|
+
}
|
|
189
|
+
const script = _findScriptElement(scriptElements, _asset);
|
|
190
|
+
if (script && _asset.type === 'script') {
|
|
191
|
+
if (script[loadingPromiseSymbol]) {
|
|
192
|
+
return script[loadingPromiseSymbol];
|
|
193
|
+
}
|
|
194
|
+
if (script[isLoadedSymbol]) {
|
|
195
|
+
return _attachElementToAsset(_asset, script);
|
|
196
|
+
}
|
|
197
|
+
return new Promise((resolve, reject) => _addListenersToAssetElement(_asset, script, resolve, reject));
|
|
198
|
+
} else if (script && _asset.type === 'inlineScript') {
|
|
199
|
+
return _attachElementToAsset(_asset, script);
|
|
200
|
+
}
|
|
201
|
+
return _loadScript(_asset, root);
|
|
202
|
+
}));
|
|
203
|
+
}
|
|
204
|
+
async function _fetchData(source) {
|
|
205
|
+
const response = await fetch(source);
|
|
206
|
+
if (!response.ok) {
|
|
207
|
+
throw new Error(`Failed to fetch from '${source}' with status ${response.status} ${response.statusText}.`);
|
|
208
|
+
}
|
|
209
|
+
return response.text();
|
|
210
|
+
}
|
|
211
|
+
function _removeElementAfterTimeout(element, timeout) {
|
|
212
|
+
if (timeout) {
|
|
213
|
+
setTimeout(() => {
|
|
214
|
+
if (element.parentNode) {
|
|
215
|
+
element.remove();
|
|
216
|
+
}
|
|
217
|
+
}, timeout);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
function _loadJsonAsset(asset, root) {
|
|
221
|
+
const script = document.createElement('script');
|
|
222
|
+
script.type = 'application/json';
|
|
223
|
+
if (asset.type === 'inlineJson') {
|
|
224
|
+
script.textContent = JSON.stringify(asset.source);
|
|
225
|
+
root.appendChild(script);
|
|
226
|
+
_removeElementAfterTimeout(script, asset.ttl);
|
|
227
|
+
return _attachElementToAsset(asset, script);
|
|
228
|
+
}
|
|
229
|
+
script[loadingPromiseSymbol] = new Promise((resolve, reject) => {
|
|
230
|
+
script.dataset.src = asset.source;
|
|
231
|
+
root.appendChild(script);
|
|
232
|
+
(async () => {
|
|
233
|
+
try {
|
|
234
|
+
const textContent = await _fetchData(asset.source);
|
|
235
|
+
script.textContent = textContent;
|
|
236
|
+
delete script[loadingPromiseSymbol];
|
|
237
|
+
_removeElementAfterTimeout(script, asset.ttl);
|
|
238
|
+
resolve(_attachElementToAsset(asset, script));
|
|
239
|
+
} catch (error) {
|
|
240
|
+
script.remove();
|
|
241
|
+
try {
|
|
242
|
+
resolve(_handleAssetError({
|
|
243
|
+
asset,
|
|
244
|
+
message: `Error loading JSON asset '${asset.name}': ${error.message}`
|
|
245
|
+
}));
|
|
246
|
+
} catch (error) {
|
|
247
|
+
reject(error);
|
|
134
248
|
}
|
|
135
|
-
console.warn(message);
|
|
136
|
-
return scripts;
|
|
137
249
|
}
|
|
250
|
+
})();
|
|
251
|
+
});
|
|
252
|
+
return script[loadingPromiseSymbol];
|
|
253
|
+
}
|
|
254
|
+
async function loadJsonAssets(assets, root = document.head) {
|
|
255
|
+
const scriptElements = Array.from(root.querySelectorAll('script[type="application/json"]'));
|
|
256
|
+
return Promise.all(assets.map(asset => {
|
|
257
|
+
if (!['json', 'inlineJson'].includes(asset.type) || !asset.source) {
|
|
258
|
+
return _attachElementToAsset(asset, null);
|
|
138
259
|
}
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
260
|
+
const script = _findScriptElement(scriptElements, asset);
|
|
261
|
+
if (script) {
|
|
262
|
+
if (script[loadingPromiseSymbol]) {
|
|
263
|
+
return script[loadingPromiseSymbol];
|
|
264
|
+
}
|
|
265
|
+
if (script.textContent) {
|
|
266
|
+
return _attachElementToAsset(asset, script);
|
|
142
267
|
}
|
|
143
|
-
return
|
|
144
|
-
|
|
145
|
-
|
|
268
|
+
return _handleAssetError({
|
|
269
|
+
asset,
|
|
270
|
+
message: `JSON asset '${asset.name}' is missing textContent and could not be loaded.`
|
|
271
|
+
});
|
|
146
272
|
}
|
|
147
|
-
|
|
148
|
-
|
|
273
|
+
return _loadJsonAsset(asset, root);
|
|
274
|
+
}));
|
|
275
|
+
}
|
|
276
|
+
function _mergeResults(results) {
|
|
277
|
+
return results.reduce((acc, results) => {
|
|
278
|
+
results.forEach((result, index) => {
|
|
279
|
+
if (!acc[index]) {
|
|
280
|
+
acc[index] = result;
|
|
281
|
+
} else if (result.element) {
|
|
282
|
+
acc[index] = result;
|
|
283
|
+
}
|
|
284
|
+
});
|
|
285
|
+
return acc;
|
|
149
286
|
}, []);
|
|
150
|
-
return Promise.all(scriptsToRender.map(asset => _loadScript(asset, root)));
|
|
151
287
|
}
|
|
152
|
-
function loadAssets(assets, root) {
|
|
153
|
-
|
|
288
|
+
async function loadAssets(assets, root) {
|
|
289
|
+
const results = await Promise.all([loadScriptAssets(assets, root), loadStyleAssets(assets, root), loadJsonAssets(assets, root)]);
|
|
290
|
+
return _mergeResults(results);
|
|
154
291
|
}
|
|
155
|
-
export { loadAssets, loadScriptAssets, loadStyleAssets, exported as testScript };
|
|
292
|
+
export { isLoadedSymbol, loadAssets, loadJsonAssets, loadScriptAssets, loadStyleAssets, loadingPromiseSymbol, exported as testScript };
|