@merkur/integration 0.38.0 → 0.40.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 CHANGED
@@ -1,6 +1,6 @@
1
1
  <p align="center">
2
2
  <a href="https://merkur.js.org/docs/getting-started" title="Getting started">
3
- <img src="https://raw.githubusercontent.com/mjancarik/merkur/master/images/merkur-illustration.png" width="100px" height="100px" alt="Merkur illustration"/>
3
+ <img src="https://raw.githubusercontent.com/mjancarik/merkur/master/images/merkur-logo.png" width="100px" height="100px" alt="Merkur illustration"/>
4
4
  </a>
5
5
  </p>
6
6
 
package/lib/index.cjs CHANGED
@@ -3,86 +3,68 @@
3
3
  let _isES9Supported;
4
4
  let _isES11Supported;
5
5
  let _isES13Supported;
6
-
6
+ let _isES15Supported;
7
7
  function isES9Supported() {
8
8
  if (_isES9Supported === undefined) {
9
- _isES9Supported =
10
- exported.test(
11
- 'return (() => { const o = { t: 1 }; return { ...o }; })() && (async () => ({}))()',
12
- ) && !!Object.values;
9
+ _isES9Supported = exported.test('return (() => { const o = { t: 1 }; return { ...o }; })() && (async () => ({}))()') && !!Object.values;
13
10
  }
14
-
15
11
  return _isES9Supported;
16
12
  }
17
-
18
13
  function isES11Supported() {
19
14
  if (_isES11Supported === undefined) {
20
- _isES11Supported =
21
- exported.test(
22
- 'return (() => { const o = { t: { q: true } }; return o?.t?.q && (o?.a?.q ?? true); })()',
23
- ) &&
24
- exported.test('return typeof Promise.allSettled === "function"') &&
25
- exported.test('return typeof globalThis !== "undefined"') &&
26
- exported.test('return typeof 9007199254740991n === "bigint"');
15
+ _isES11Supported = exported.test('return (() => { const o = { t: { q: true } }; return o?.t?.q && (o?.a?.q ?? true); })()') && exported.test('return typeof Promise.allSettled === "function"') && exported.test('return typeof globalThis !== "undefined"') && exported.test('return typeof 9007199254740991n === "bigint"');
27
16
  }
28
-
29
17
  return _isES11Supported;
30
18
  }
31
-
32
19
  function isES13Supported() {
33
20
  if (_isES13Supported === undefined) {
34
- _isES13Supported =
35
- exported.test('return [1,1].findLast(e => e === 1)') &&
36
- exported.test('return Object.hasOwn({a:1}, "a")');
21
+ _isES13Supported = exported.test('return [1,1].findLast(e => e === 1)') && exported.test('return Object.hasOwn({a:1}, "a")');
37
22
  }
38
-
39
23
  return _isES13Supported;
40
24
  }
41
-
25
+ function isES15Supported() {
26
+ if (_isES15Supported === undefined) {
27
+ _isES15Supported = exported.test('return typeof Promise.withResolvers === "function"') && exported.test('return typeof Object.groupBy === "function"');
28
+ }
29
+ return _isES15Supported;
30
+ }
42
31
  function test(snippet) {
43
32
  try {
44
33
  const fn = new Function(snippet);
45
34
  const result = fn();
46
-
47
35
  return !!result;
48
36
  } catch (e) {
49
37
  return false;
50
38
  }
51
39
  }
52
-
53
40
  const exported = {
54
41
  isES9Supported,
55
42
  isES11Supported,
56
43
  isES13Supported,
57
- test,
44
+ isES15Supported,
45
+ test
58
46
  };
59
47
 
60
48
  const isLoadedSymbol = Symbol.for('isLoaded');
61
49
  const loadingPromiseSymbol = Symbol.for('loadingPromise');
62
-
63
50
  function _attachElementToAsset(asset, element) {
64
51
  return {
65
52
  ...asset,
66
- element,
53
+ element
67
54
  };
68
55
  }
69
-
70
56
  function _handleAssetError({
71
57
  asset,
72
- message = `Error loading asset ${asset.source}.`,
58
+ message = `Error loading asset ${asset.source}.`
73
59
  }) {
74
60
  if (asset.optional) {
75
61
  console.warn(message);
76
-
77
62
  return _attachElementToAsset(asset, null);
78
63
  }
79
-
80
64
  const error = new Error(message);
81
65
  error.asset = asset;
82
-
83
66
  throw error;
84
67
  }
85
-
86
68
  function _addListenersToAssetElement(asset, element, resolve, reject) {
87
69
  element.addEventListener('load', () => {
88
70
  resolve(_attachElementToAsset(asset, element));
@@ -93,116 +75,94 @@ function _addListenersToAssetElement(asset, element, resolve, reject) {
93
75
  if (element.parentNode) {
94
76
  element.remove();
95
77
  }
96
-
97
78
  try {
98
- resolve(_handleAssetError({ asset }));
79
+ resolve(_handleAssetError({
80
+ asset
81
+ }));
99
82
  } catch (error) {
100
83
  reject(error);
101
84
  }
102
85
  });
103
86
  }
104
-
105
87
  function _loadStyle(asset, root) {
106
88
  if (asset.type === 'inlineStyle') {
107
89
  const style = document.createElement('style');
90
+ style.dataset.merkurAssetName = asset.name;
108
91
  style.innerHTML = asset.source;
109
92
  root.appendChild(style);
110
-
111
93
  return _attachElementToAsset(asset, style);
112
94
  }
113
-
114
95
  const link = document.createElement('link');
115
-
116
96
  link[loadingPromiseSymbol] = new Promise((resolve, reject) => {
117
97
  _addListenersToAssetElement(asset, link, resolve, reject);
118
98
  link.rel = 'stylesheet';
99
+ link.dataset.merkurAssetName = asset.name;
119
100
  link.href = asset.source;
120
-
121
101
  root.appendChild(link);
122
102
  });
123
-
124
103
  return link[loadingPromiseSymbol];
125
104
  }
126
-
127
105
  async function loadStyleAssets(assets, root = document.head) {
128
106
  const styleElements = Array.from(root.querySelectorAll('style'));
129
-
130
- return Promise.all(
131
- assets.map((asset) => {
132
- if (
133
- !['stylesheet', 'inlineStyle'].includes(asset.type) ||
134
- !asset.source
135
- ) {
136
- return _attachElementToAsset(asset, null);
137
- }
138
-
139
- if (asset.type === 'stylesheet') {
140
- const link = root.querySelector(`link[href='${asset.source}']`);
141
-
142
- if (link) {
143
- if (link[loadingPromiseSymbol]) {
144
- return link[loadingPromiseSymbol];
145
- }
146
-
147
- return _attachElementToAsset(asset, link);
107
+ return Promise.all(assets.map(asset => {
108
+ if (!['stylesheet', 'inlineStyle'].includes(asset.type) || !asset.source) {
109
+ return _attachElementToAsset(asset, null);
110
+ }
111
+ if (asset.type === 'stylesheet') {
112
+ const link = root.querySelector(`link[href='${asset.source}']`);
113
+ if (link) {
114
+ if (link[loadingPromiseSymbol]) {
115
+ return link[loadingPromiseSymbol];
148
116
  }
117
+ return _attachElementToAsset(asset, link);
149
118
  }
150
-
151
- if (asset.type === 'inlineStyle') {
152
- const inlineStyle = styleElements.find(
153
- (element) => element.innerHTML === asset.source,
154
- );
155
-
156
- if (inlineStyle) {
157
- return _attachElementToAsset(asset, inlineStyle);
158
- }
119
+ }
120
+ if (asset.type === 'inlineStyle') {
121
+ const inlineStyle = styleElements.find(element => element.innerHTML === asset.source);
122
+ if (inlineStyle) {
123
+ return _attachElementToAsset(asset, inlineStyle);
159
124
  }
160
-
161
- return _loadStyle(asset, root);
162
- }),
163
- );
125
+ }
126
+ return _loadStyle(asset, root);
127
+ }));
164
128
  }
165
-
166
129
  function _findScriptElement(scriptElements, asset) {
167
130
  if (asset.type === 'json') {
168
- return scriptElements.find(
169
- (element) => element.dataset.src === asset.source,
170
- );
131
+ return scriptElements.find(element => element.dataset.src === asset.source);
171
132
  }
172
-
173
133
  if (!['script', 'inlineScript', 'inlineJson'].includes(asset.type)) {
174
134
  return null;
175
135
  }
176
-
177
136
  const attributeKey = asset.type === 'script' ? 'src' : 'textContent';
178
- const source =
179
- asset.type === 'inlineJson' ? JSON.stringify(asset.source) : asset.source;
180
-
181
- return (
182
- scriptElements.find((element) => element[attributeKey] === source) || null
183
- );
137
+ const source = asset.type === 'inlineJson' ? JSON.stringify(asset.source) : asset.source;
138
+ return scriptElements.find(element => element[attributeKey] === source) || null;
184
139
  }
185
-
186
140
  function _loadScript(asset, root) {
187
141
  const script = document.createElement('script');
142
+ script.dataset.merkurAssetName = asset.name;
188
143
 
144
+ // Set script type to module if specified
145
+ if (asset.module) {
146
+ script.type = 'module';
147
+ }
189
148
  if (asset.type === 'inlineScript') {
190
149
  script.textContent = asset.source;
191
150
  root.appendChild(script);
192
-
193
151
  return _attachElementToAsset(asset, script);
194
152
  }
195
-
196
153
  script[loadingPromiseSymbol] = new Promise((resolve, reject) => {
197
- script.defer = true;
154
+ // Don't set defer for module scripts as it can interfere with module loading
155
+ if (!asset.module) {
156
+ script.defer = true;
157
+ }
198
158
  _addListenersToAssetElement(asset, script, resolve, reject);
199
159
  script.src = asset.source;
200
-
201
- const { attr } = asset;
160
+ const {
161
+ attr
162
+ } = asset;
202
163
  if (attr && Object.keys(attr).length) {
203
164
  for (const name in attr) {
204
165
  const value = attr[name];
205
-
206
166
  if (typeof value === 'boolean') {
207
167
  if (value) {
208
168
  script.setAttribute(name, '');
@@ -214,86 +174,73 @@ function _loadScript(asset, root) {
214
174
  }
215
175
  }
216
176
  }
217
-
218
177
  root.appendChild(script);
219
178
  });
220
-
221
179
  return script[loadingPromiseSymbol];
222
180
  }
223
-
224
181
  async function loadScriptAssets(assets, root = document.head) {
225
182
  const scriptElements = Array.from(root.querySelectorAll('script'));
183
+ return Promise.all(assets.map(asset => {
184
+ if (!['script', 'inlineScript'].includes(asset.type) || !asset.source) {
185
+ return _attachElementToAsset(asset, null);
186
+ }
226
187
 
227
- return Promise.all(
228
- assets.map((asset) => {
229
- if (!['script', 'inlineScript'].includes(asset.type) || !asset.source) {
230
- return _attachElementToAsset(asset, null);
231
- }
232
-
233
- const { source } = asset;
234
- const _asset = Object.assign({}, asset);
235
-
236
- if (source === Object(source)) {
237
- if (source.es13 && exported.isES13Supported()) {
238
- _asset.source = source.es13;
239
- } else if (source.es11 && exported.isES11Supported()) {
240
- _asset.source = source.es11;
241
- } else if (source.es9 && exported.isES9Supported()) {
242
- _asset.source = source.es9;
243
- } else {
244
- _asset.source = null;
245
- }
246
-
247
- if (!_asset.source) {
248
- return _handleAssetError({
249
- asset: _asset,
250
- message: `Asset '${_asset.name}' is missing ES variant and could not be loaded.`,
251
- });
188
+ // Module scripts should not use defer attribute as it can cause issues
189
+ if (asset.module && asset.attr && asset.attr.defer !== false) {
190
+ asset = {
191
+ ...asset,
192
+ attr: {
193
+ ...asset.attr,
194
+ defer: false
252
195
  }
196
+ };
197
+ }
198
+ const {
199
+ source
200
+ } = asset;
201
+ const _asset = Object.assign({}, asset);
202
+ if (source === Object(source)) {
203
+ if (source.es13 && exported.isES13Supported()) {
204
+ _asset.source = source.es13;
205
+ } else if (source.es11 && exported.isES11Supported()) {
206
+ _asset.source = source.es11;
207
+ } else if (source.es9 && exported.isES9Supported()) {
208
+ _asset.source = source.es9;
209
+ } else {
210
+ _asset.source = null;
253
211
  }
254
-
255
- if (_asset.test && exported.test(_asset.test)) {
256
- return _attachElementToAsset(
257
- _asset,
258
- _findScriptElement(scriptElements, _asset),
259
- );
212
+ if (!_asset.source) {
213
+ return _handleAssetError({
214
+ asset: _asset,
215
+ message: `Asset '${_asset.name}' is missing ES variant and could not be loaded.`
216
+ });
260
217
  }
261
-
262
- const script = _findScriptElement(scriptElements, _asset);
263
-
264
- if (script && _asset.type === 'script') {
265
- if (script[loadingPromiseSymbol]) {
266
- return script[loadingPromiseSymbol];
267
- }
268
-
269
- if (script[isLoadedSymbol]) {
270
- return _attachElementToAsset(_asset, script);
271
- }
272
-
273
- return new Promise((resolve, reject) =>
274
- _addListenersToAssetElement(_asset, script, resolve, reject),
275
- );
276
- } else if (script && _asset.type === 'inlineScript') {
218
+ }
219
+ if (_asset.test && exported.test(_asset.test)) {
220
+ return _attachElementToAsset(_asset, _findScriptElement(scriptElements, _asset));
221
+ }
222
+ const script = _findScriptElement(scriptElements, _asset);
223
+ if (script && _asset.type === 'script') {
224
+ if (script[loadingPromiseSymbol]) {
225
+ return script[loadingPromiseSymbol];
226
+ }
227
+ if (script[isLoadedSymbol]) {
277
228
  return _attachElementToAsset(_asset, script);
278
229
  }
279
-
280
- return _loadScript(_asset, root);
281
- }),
282
- );
230
+ return new Promise((resolve, reject) => _addListenersToAssetElement(_asset, script, resolve, reject));
231
+ } else if (script && _asset.type === 'inlineScript') {
232
+ return _attachElementToAsset(_asset, script);
233
+ }
234
+ return _loadScript(_asset, root);
235
+ }));
283
236
  }
284
-
285
237
  async function _fetchData(source) {
286
238
  const response = await fetch(source);
287
-
288
239
  if (!response.ok) {
289
- throw new Error(
290
- `Failed to fetch from '${source}' with status ${response.status} ${response.statusText}.`,
291
- );
240
+ throw new Error(`Failed to fetch from '${source}' with status ${response.status} ${response.statusText}.`);
292
241
  }
293
-
294
242
  return response.text();
295
243
  }
296
-
297
244
  function _removeElementAfterTimeout(element, timeout) {
298
245
  if (timeout) {
299
246
  setTimeout(() => {
@@ -303,23 +250,19 @@ function _removeElementAfterTimeout(element, timeout) {
303
250
  }, timeout);
304
251
  }
305
252
  }
306
-
307
253
  function _loadJsonAsset(asset, root) {
308
254
  const script = document.createElement('script');
255
+ script.dataset.merkurAssetName = asset.name;
309
256
  script.type = 'application/json';
310
-
311
257
  if (asset.type === 'inlineJson') {
312
258
  script.textContent = JSON.stringify(asset.source);
313
259
  root.appendChild(script);
314
260
  _removeElementAfterTimeout(script, asset.ttl);
315
-
316
261
  return _attachElementToAsset(asset, script);
317
262
  }
318
-
319
263
  script[loadingPromiseSymbol] = new Promise((resolve, reject) => {
320
264
  script.dataset.src = asset.source;
321
265
  root.appendChild(script);
322
-
323
266
  (async () => {
324
267
  try {
325
268
  const textContent = await _fetchData(asset.source);
@@ -329,57 +272,41 @@ function _loadJsonAsset(asset, root) {
329
272
  resolve(_attachElementToAsset(asset, script));
330
273
  } catch (error) {
331
274
  script.remove();
332
-
333
275
  try {
334
- resolve(
335
- _handleAssetError({
336
- asset,
337
- message: `Error loading JSON asset '${asset.name}': ${error.message}`,
338
- }),
339
- );
276
+ resolve(_handleAssetError({
277
+ asset,
278
+ message: `Error loading JSON asset '${asset.name}': ${error.message}`
279
+ }));
340
280
  } catch (error) {
341
281
  reject(error);
342
282
  }
343
283
  }
344
284
  })();
345
285
  });
346
-
347
286
  return script[loadingPromiseSymbol];
348
287
  }
349
-
350
288
  async function loadJsonAssets(assets, root = document.head) {
351
- const scriptElements = Array.from(
352
- root.querySelectorAll('script[type="application/json"]'),
353
- );
354
-
355
- return Promise.all(
356
- assets.map((asset) => {
357
- if (!['json', 'inlineJson'].includes(asset.type) || !asset.source) {
358
- return _attachElementToAsset(asset, null);
289
+ const scriptElements = Array.from(root.querySelectorAll('script[type="application/json"]'));
290
+ return Promise.all(assets.map(asset => {
291
+ if (!['json', 'inlineJson'].includes(asset.type) || !asset.source) {
292
+ return _attachElementToAsset(asset, null);
293
+ }
294
+ const script = _findScriptElement(scriptElements, asset);
295
+ if (script) {
296
+ if (script[loadingPromiseSymbol]) {
297
+ return script[loadingPromiseSymbol];
359
298
  }
360
-
361
- const script = _findScriptElement(scriptElements, asset);
362
-
363
- if (script) {
364
- if (script[loadingPromiseSymbol]) {
365
- return script[loadingPromiseSymbol];
366
- }
367
-
368
- if (script.textContent) {
369
- return _attachElementToAsset(asset, script);
370
- }
371
-
372
- return _handleAssetError({
373
- asset,
374
- message: `JSON asset '${asset.name}' is missing textContent and could not be loaded.`,
375
- });
299
+ if (script.textContent) {
300
+ return _attachElementToAsset(asset, script);
376
301
  }
377
-
378
- return _loadJsonAsset(asset, root);
379
- }),
380
- );
302
+ return _handleAssetError({
303
+ asset,
304
+ message: `JSON asset '${asset.name}' is missing textContent and could not be loaded.`
305
+ });
306
+ }
307
+ return _loadJsonAsset(asset, root);
308
+ }));
381
309
  }
382
-
383
310
  function _mergeResults(results) {
384
311
  return results.reduce((acc, results) => {
385
312
  results.forEach((result, index) => {
@@ -389,18 +316,11 @@ function _mergeResults(results) {
389
316
  acc[index] = result;
390
317
  }
391
318
  });
392
-
393
319
  return acc;
394
320
  }, []);
395
321
  }
396
-
397
322
  async function loadAssets(assets, root) {
398
- const results = await Promise.all([
399
- loadScriptAssets(assets, root),
400
- loadStyleAssets(assets, root),
401
- loadJsonAssets(assets, root),
402
- ]);
403
-
323
+ const results = await Promise.all([loadScriptAssets(assets, root), loadStyleAssets(assets, root), loadJsonAssets(assets, root)]);
404
324
  return _mergeResults(results);
405
325
  }
406
326
 
package/lib/index.es9.cjs CHANGED
@@ -3,6 +3,7 @@
3
3
  let _isES9Supported;
4
4
  let _isES11Supported;
5
5
  let _isES13Supported;
6
+ let _isES15Supported;
6
7
  function isES9Supported() {
7
8
  if (_isES9Supported === undefined) {
8
9
  _isES9Supported = exported.test('return (() => { const o = { t: 1 }; return { ...o }; })() && (async () => ({}))()') && !!Object.values;
@@ -21,6 +22,12 @@ function isES13Supported() {
21
22
  }
22
23
  return _isES13Supported;
23
24
  }
25
+ function isES15Supported() {
26
+ if (_isES15Supported === undefined) {
27
+ _isES15Supported = exported.test('return typeof Promise.withResolvers === "function"') && exported.test('return typeof Object.groupBy === "function"');
28
+ }
29
+ return _isES15Supported;
30
+ }
24
31
  function test(snippet) {
25
32
  try {
26
33
  const fn = new Function(snippet);
@@ -34,6 +41,7 @@ const exported = {
34
41
  isES9Supported,
35
42
  isES11Supported,
36
43
  isES13Supported,
44
+ isES15Supported,
37
45
  test
38
46
  };
39
47
  const isLoadedSymbol = Symbol.for('isLoaded');
@@ -78,6 +86,7 @@ function _addListenersToAssetElement(asset, element, resolve, reject) {
78
86
  function _loadStyle(asset, root) {
79
87
  if (asset.type === 'inlineStyle') {
80
88
  const style = document.createElement('style');
89
+ style.dataset.merkurAssetName = asset.name;
81
90
  style.innerHTML = asset.source;
82
91
  root.appendChild(style);
83
92
  return _attachElementToAsset(asset, style);
@@ -86,6 +95,7 @@ function _loadStyle(asset, root) {
86
95
  link[loadingPromiseSymbol] = new Promise((resolve, reject) => {
87
96
  _addListenersToAssetElement(asset, link, resolve, reject);
88
97
  link.rel = 'stylesheet';
98
+ link.dataset.merkurAssetName = asset.name;
89
99
  link.href = asset.source;
90
100
  root.appendChild(link);
91
101
  });
@@ -128,13 +138,22 @@ function _findScriptElement(scriptElements, asset) {
128
138
  }
129
139
  function _loadScript(asset, root) {
130
140
  const script = document.createElement('script');
141
+ script.dataset.merkurAssetName = asset.name;
142
+
143
+ // Set script type to module if specified
144
+ if (asset.module) {
145
+ script.type = 'module';
146
+ }
131
147
  if (asset.type === 'inlineScript') {
132
148
  script.textContent = asset.source;
133
149
  root.appendChild(script);
134
150
  return _attachElementToAsset(asset, script);
135
151
  }
136
152
  script[loadingPromiseSymbol] = new Promise((resolve, reject) => {
137
- script.defer = true;
153
+ // Don't set defer for module scripts as it can interfere with module loading
154
+ if (!asset.module) {
155
+ script.defer = true;
156
+ }
138
157
  _addListenersToAssetElement(asset, script, resolve, reject);
139
158
  script.src = asset.source;
140
159
  const {
@@ -164,6 +183,17 @@ async function loadScriptAssets(assets, root = document.head) {
164
183
  if (!['script', 'inlineScript'].includes(asset.type) || !asset.source) {
165
184
  return _attachElementToAsset(asset, null);
166
185
  }
186
+
187
+ // Module scripts should not use defer attribute as it can cause issues
188
+ if (asset.module && asset.attr && asset.attr.defer !== false) {
189
+ asset = {
190
+ ...asset,
191
+ attr: {
192
+ ...asset.attr,
193
+ defer: false
194
+ }
195
+ };
196
+ }
167
197
  const {
168
198
  source
169
199
  } = asset;
@@ -221,6 +251,7 @@ function _removeElementAfterTimeout(element, timeout) {
221
251
  }
222
252
  function _loadJsonAsset(asset, root) {
223
253
  const script = document.createElement('script');
254
+ script.dataset.merkurAssetName = asset.name;
224
255
  script.type = 'application/json';
225
256
  if (asset.type === 'inlineJson') {
226
257
  script.textContent = JSON.stringify(asset.source);