@speedkit/cli 3.9.1 → 3.11.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.
@@ -1,53 +1,51 @@
1
- {{#unless addPreRendering}}
2
- {{#if removeLazyLoading}}
3
- const lazyLoadDefaultConfig = {
4
- attributes: [
5
- // TODO adjust based on if original attribute should be preserved or renamed
6
- // when using find and set the original attribute is preserved
7
- // when using find and rename the original attribute is renamed
8
- {find: 'data-src', set: 'src'},
9
- {find: 'data-srcset', set: 'srcset'},
10
- ],
11
- lazyClass: 'lazyload',
12
- lazyLoad: false,
13
- preload: true // preload lazy-removed images in head
14
- }
1
+ {{#if removeLazyLoading}}
2
+ const lazyLoadDefaultConfig = {
3
+ attributes: [
4
+ // TODO adjust based on if original attribute should be preserved or renamed
5
+ // when using find and set the original attribute is preserved
6
+ // when using find and rename the original attribute is renamed
7
+ {find: 'data-src', set: 'src'},
8
+ {find: 'data-srcset', set: 'srcset'},
9
+ ],
10
+ lazyClass: 'lazyload',
11
+ lazyLoad: false,
12
+ preload: true // preload lazy-removed images in head
13
+ }
15
14
 
16
- const lazyLoadList = [
17
- // TODO: adjust/replace this exemplary list
15
+ const lazyLoadList = [
16
+ // TODO: adjust/replace this exemplary list
18
17
 
19
- // Content page's stage image:
20
- {
21
- ...lazyLoadDefaultConfig,
22
- parentClasses: [
23
- 'image-with-text-overlay__banner',
24
- ],
25
- limit: 1
26
- },
18
+ // Content page's stage image:
19
+ {
20
+ ...lazyLoadDefaultConfig,
21
+ parentClasses: [
22
+ 'image-with-text-overlay__banner',
23
+ ],
24
+ limit: 1
25
+ },
27
26
 
28
- // PLP product images:
29
- {
30
- ...lazyLoadDefaultConfig,
31
- parentClasses: [
32
- 'product-image__wrapper',
33
- ],
34
- limit: 4
35
- },
27
+ // PLP product images:
28
+ {
29
+ ...lazyLoadDefaultConfig,
30
+ parentClasses: [
31
+ 'product-image__wrapper',
32
+ ],
33
+ limit: 4
34
+ },
36
35
 
37
- // PDP hero image:
38
- {
39
- ...lazyLoadDefaultConfig,
40
- parentClasses: [
41
- 'product-gallery__image',
42
- ],
43
- limit: 1
44
- },
45
- ];
36
+ // PDP hero image:
37
+ {
38
+ ...lazyLoadDefaultConfig,
39
+ parentClasses: [
40
+ 'product-gallery__image',
41
+ ],
42
+ limit: 1
43
+ },
44
+ ];
46
45
 
47
- {{/if}}
48
- {{/unless}}
46
+ {{/if}}
49
47
  const config = {
50
- rejectHTML({db, {{#if addPreRendering}}document{{else}}html{{/if}}, variation, url, headers}) {
48
+ rejectHTML({db, document, variation, url, headers}) {
51
49
  /* Use to configure guards to prevent e.g. error and captcha pages from being cached */
52
50
  {{#if isPlentymarkets}}
53
51
  // we don't want to cache CSR pages
@@ -59,59 +57,58 @@ const config = {
59
57
  return false;
60
58
  {{/if}}
61
59
  },
62
- blacklistHTML({db, {{#if addPreRendering}}document{{else}}html{{/if}}, variation, url, headers}) {
60
+ blacklistHTML({db, document, variation, url, headers}) {
63
61
  /* Use to configure guards to black-list e.g. specific page types */
64
- {{#if addPreRendering}}
65
- // dynamically blacklist unsupported variations as they are not covered in lambda
66
- if (variation.toUpperCase() === "TABLET") {
67
- return `Tablet viewport is unsupported: ${url}`;
68
- }
69
-
70
- {{/if}}
71
62
  {{#if useScrapingBee}}
72
- // dynamically blacklist not 200 status codes from ScrapingBee
73
- const spbStatusCode = headers["Spb-initial-status-code"];
74
- if (spbStatusCode && spbStatusCode !== "200") {
75
- return `initial status code unequal to 200 received from scrapingbee: ${url} ${spbStatusCode}`;
76
- }
63
+
64
+ // dynamically blacklist not 200 status codes from ScrapingBee
65
+ const spbStatusCode = headers["Spb-initial-status-code"];
66
+ if (spbStatusCode && spbStatusCode !== "200") {
67
+ return `initial status code unequal to 200 received from scrapingbee: ${url} ${spbStatusCode}`;
68
+ }
77
69
  {{/if}}
70
+
78
71
  return false;
79
72
  },
80
- shouldTransform({db, {{#if addPreRendering}}document{{else}}html{{/if}}, variation, url, headers}) {
73
+ shouldTransform({db, document, variation, url, headers}) {
81
74
  return true;
82
75
  },
83
- shouldDelayDom({db, {{#if addPreRendering}}document{{else}}html{{/if}}, variation, url, headers}) {
76
+ shouldDelayDom({db, document, variation, url, headers}) {
84
77
  {{#if isPlentymarkets}}
85
78
  return false; // Plentymarkets specific
86
79
  {{else}}
87
80
  return true;
88
81
  {{/if}}
89
82
  },
90
- async preTransform({db, {{#if addPreRendering}}document{{else}}html{{/if}}, variation, url, headers}) {
91
- {{#unless addPreRendering}}
92
- return html;
93
- {{else}}
83
+ async preTransform({db, document, variation, url, headers}) {
84
+ {{#if addPreRendering}}
85
+ // SSR specific:
94
86
  generateMainShadowRoot(document);
95
- {{/unless}}
96
- },
97
- async postTransform({db, {{#if addPreRendering}}document{{else}}html{{/if}}, variation, url, headers}) {
98
- {{#unless addPreRendering}}
99
- {{#if includeServiceWorker}}
100
- html = injectServiceWorkerScript(html, '{{includeServiceWorker}}');
101
- {{/if}}
102
- return html;
103
87
  {{else}}
88
+ // do nothing
89
+ {{/if}}
90
+ },
91
+ async postTransform({db, document, variation, url, headers}) {
92
+ {{#if includeServiceWorker}}
93
+ injectServiceWorkerScript(document, '{{includeServiceWorker}}');
94
+
95
+ {{/if}}
96
+ {{#if addSSR}}
97
+ // SSR specific:
104
98
  createPreloadsForInnerStyleLinks(document);
105
99
  removeInnerScriptNodes(document);
106
- assignStylesToShadowDom(document);
100
+ assignDynamicStylesToShadowDom(document);
107
101
  addDeclarativeShadowDOMPolyfill(document);
102
+ wrapPreRenderedElementsWithCustomRootTags(document);
108
103
  addPolyFillAfterMainPreRenderTemplate(document);
109
- hideCustomShadowDom(document);
110
- showCustomShadowDom(document);
111
- {{/unless}}
112
- {{#if supportShadowDomInPreRendering}}
113
- overrideCustomElementDefine(document);
114
- walkPreRenderedElements(document);
104
+ triggerShadowRendering(document);
105
+ {{#if addSSRInnerShadowDomSupport}}
106
+
107
+ overrideCustomElementDefine(document);
108
+ walkPreRenderedElements(document);
109
+ {{/if}}
110
+ {{else}}
111
+ // do nothing
115
112
  {{/if}}
116
113
  },
117
114
  {{#if isPlentymarkets}}
@@ -130,11 +127,12 @@ const config = {
130
127
  {{else}}
131
128
  executeDeploymentDetection: true,
132
129
  {{/if}}
133
- {{#unless addPreRendering}}
134
- {{#if removeLazyLoading}}
135
- lazyLoadList,
136
- {{/if}}
137
- {{else}}
130
+ {{#if removeLazyLoading}}
131
+ lazyLoadList,
132
+ {{/if}}
133
+ {{#if addSSR}}
134
+
135
+ // SSR specific:
138
136
  preRender: {
139
137
  lambdaUrl: '<generate-new-lambda-function-and-paste-url-endpoint-here>',
140
138
  elements: [
@@ -147,11 +145,11 @@ const config = {
147
145
  "body > style",
148
146
  "body > link[rel='stylesheet'][href]",
149
147
  ].join(","),
150
- appendTo: ".sk-container template",
148
+ appendTo: "sk-shadow-app-root > template",
151
149
  },
152
150
  // this will be the main element to extract from the rendered DOM
153
151
  // TODO change selector to the main element of your page
154
- { selector: "body > #app", appendTo: ".sk-container template" },
152
+ { selector: "body > #app", appendTo: "sk-shadow-app-root > template" },
155
153
  ],
156
154
  guards: [
157
155
  {
@@ -160,7 +158,7 @@ const config = {
160
158
  // e.g. wait for first 4 images to be loaded
161
159
  '.products-list-page__products-list .products-list__tiles__element:nth-child(-n+4) img[src=""]',
162
160
  ],
163
- {{#if supportShadowDomInPreRendering}}
161
+ {{#if addSSRInnerShadowDomSupport}}
164
162
  waitForCallback: waitForCallback.toString(),
165
163
  {{/if}}
166
164
  },
@@ -182,10 +180,10 @@ const config = {
182
180
  ],
183
181
  },
184
182
  },
185
- {{/unless}}
183
+ {{/if}}
186
184
  }
187
185
 
188
- const documentHandler = new {{#if addPreRendering}}DomDocumentHandler{{else}}DocumentHandler{{/if}}({
186
+ const documentHandler = new DomDocumentHandler({
189
187
  ...config,
190
188
  app: APP,
191
189
  dynamicFetcher: DYNAMIC_FETCHER,
@@ -196,44 +194,42 @@ const documentHandler = new {{#if addPreRendering}}DomDocumentHandler{{else}}Doc
196
194
  export const post = async (db, req, res) => {
197
195
  await documentHandler.transformAndSend(db, req, res);
198
196
  }
199
- {{#unless addPreRendering}}
200
- {{#if includeServiceWorker}}
201
-
202
- //================================================================================
203
- // Utility Functions
204
- //================================================================================
205
-
206
- function prepend(string, searchString, prependString) {
207
- const index = string.lastIndexOf(searchString);
208
- return string.slice(0, index) + prependString + string.slice(index);
209
- }
197
+ {{#if includeServiceWorker}}
210
198
 
211
- function injectServiceWorkerScript(html, searchedSwPath) {
212
- return prepend(html, '</head>', `
213
- <script class="speed-kit-service-worker">
214
- var serviceWorkerRegister = ServiceWorkerContainer.prototype.register;
215
- ServiceWorkerContainer.prototype.register = function (swPath, options) {
199
+ /**
200
+ * Injects a script into the document to override the service worker registration.
201
+ *
202
+ * @param {Document} document - The document object where the script will be injected.
203
+ * @param {string} searchedSwPath - The path of the service worker to be overridden.
204
+ */
205
+ function injectServiceWorkerScript(document, searchedSwPath) {
206
+ const swScript = document.createElement("script");
207
+ swScript.setAttribute("id", "speed-kit-service-worker");
208
+ swScript.type = 'text/javascript';
209
+ swScript.innerHTML = `
210
+ var serviceWorkerRegister = ServiceWorkerContainer.prototype.register;
211
+ ServiceWorkerContainer.prototype.register = function (swPath, options) {
216
212
  if (swPath.indexOf('${searchedSwPath}') === 0 && SpeedKit.group === 'A') {
217
- return navigator.serviceWorker.getRegistration();
213
+ return navigator.serviceWorker.getRegistration();
218
214
  }
219
215
  return serviceWorkerRegister.call(this, swPath, options);
220
- };
221
- </script>
222
- `);
223
- }
224
- {{/if}}
225
- {{/unless}}
216
+ };`;
217
+
218
+ document.head.appendChild(swScript);
219
+ }
220
+ {{/if}}
221
+ {{#if addSSR}}
226
222
 
227
- {{#if addPreRendering}}
228
223
  /**
229
224
  * Generates the main shadow root for the given DOM.
230
225
  * This is the parent element of the extracted pre-rendered content.
226
+ *
231
227
  * @param document
232
228
  */
233
229
  function generateMainShadowRoot(document) {
234
- const customContainer = document.createElement("div");
235
- customContainer.classList.add("sk-container");
236
- customContainer.classList.add("speed-kit-shadow-app-root");
230
+ const customContainer = document.createElement("sk-shadow-app-root");
231
+ customContainer.setAttribute("aria-hidden", "true"); // hide from screen readers
232
+
237
233
  const template = document.createElement("template");
238
234
  template.setAttribute("shadowrootmode", "open");
239
235
  customContainer.appendChild(template);
@@ -245,21 +241,22 @@ export const post = async (db, req, res) => {
245
241
  * Creates preloads for inner style links in the given document.
246
242
  *
247
243
  * @param {Document} document - The document object to search for inner style links.
248
- *
249
244
  * @return {void}
250
245
  */
251
246
  function createPreloadsForInnerStyleLinks(document) {
252
- const innerStyleLinks = document.querySelectorAll(
253
- '.speed-kit-pre-rendered-block-added[rel="stylesheet"][href]',
254
- );
255
- const head = document.querySelector("head");
256
- innerStyleLinks.forEach((styleLink) => {
257
- const link = document.createElement("link");
258
- link.setAttribute("rel", "preload");
259
- link.setAttribute("as", "stylesheet");
260
- link.setAttribute("href", styleLink.getAttribute("href"));
261
- head.append(link);
262
- });
247
+ const customShadowRoots = getCustomShadowElements(document);
248
+ for (const customShadowRoot of customShadowRoots) {
249
+ const innerStyleLinks = customShadowRoot.querySelectorAll('link[rel="stylesheet"][href]');
250
+
251
+ const head = document.querySelector("head");
252
+ innerStyleLinks.forEach((styleLink) => {
253
+ const link = document.createElement("link");
254
+ link.setAttribute("rel", "preload");
255
+ link.setAttribute("as", "stylesheet");
256
+ link.setAttribute("href", styleLink.getAttribute("href"));
257
+ head.append(link);
258
+ });
259
+ }
263
260
  }
264
261
 
265
262
  /**
@@ -269,25 +266,28 @@ export const post = async (db, req, res) => {
269
266
  */
270
267
  function removeInnerScriptNodes(document) {
271
268
  // remove all innerShadowDom script, as they would be executed again
272
- Array.from(
273
- document.querySelectorAll(".speed-kit-pre-rendered-block-added script"),
274
- ).forEach((scriptNode) => scriptNode.remove());
269
+ const customShadowRoots = getCustomShadowElements(document);
270
+ for (const customShadowRoot of customShadowRoots) {
271
+ Array.from(customShadowRoot.querySelectorAll("script")).forEach((scriptNode) => scriptNode.remove());
272
+ }
275
273
  }
276
274
 
277
275
  /**
278
- * Assigns styles to the Shadow DOM of specified document.
276
+ * Assigns dynamic styles to the Shadow DOM of specified document.
279
277
  *
280
278
  * @param {Document} document - The document object to which styles are to be assigned.
281
279
  * @throws {Abort} Throws an error if `skStyles` is not found.
282
280
  */
283
- function assignStylesToShadowDom(document) {
284
- const skStyles = document.querySelector('[id="speed-kit-df-styles"]');
281
+ function assignDynamicStylesToShadowDom(document) {
282
+ const skStyles = document.querySelector("#speed-kit-df-styles");
285
283
 
286
284
  if (!skStyles) {
287
- throw new Abort("skStyles not found");
285
+ throw new Abort("#speed-kit-df-styles not found");
288
286
  }
289
287
  const clonedStyles = document.createElement("style");
290
- clonedStyles.classList.add("speed-kit-df-styles");
288
+ for (const attr of skStyles.attributes) {
289
+ clonedStyles.setAttribute(attr.name, attr.value);
290
+ }
291
291
  clonedStyles.textContent = skStyles.textContent;
292
292
 
293
293
  const customShadowElements = getCustomShadowElements(document);
@@ -298,15 +298,17 @@ export const post = async (db, req, res) => {
298
298
 
299
299
  /**
300
300
  * Helper function to select all template elements.
301
+ *
301
302
  * @param document
302
303
  * @returns {*}
303
304
  */
304
305
  function getCustomShadowElements(document) {
305
- return document.querySelectorAll(".sk-container > template");
306
+ return document.querySelectorAll("sk-shadow-app-root > template");
306
307
  }
307
308
 
308
309
  /**
309
310
  * Adds the declarative shadow dom polyfill to the head of the document.
311
+ *
310
312
  * @param document
311
313
  */
312
314
  function addDeclarativeShadowDOMPolyfill(document) {
@@ -324,7 +326,6 @@ export const post = async (db, req, res) => {
324
326
  * https://developer.chrome.com/docs/css-ui/declarative-shadow-dom#polyfill
325
327
  *
326
328
  * @param {HTMLTemplateElement} template - The template element to be transformed into a Shadow Root.
327
- *
328
329
  * @constructor
329
330
  */
330
331
  function SKShadowRootsPolyfill(template) {
@@ -344,49 +345,92 @@ export const post = async (db, req, res) => {
344
345
  }
345
346
 
346
347
  /**
347
- * Calls the polyfill for shadowRoots after the main pre-rendered template.
348
+ * Wraps pre-rendered elements with custom sk-html and sk-body tags.
349
+ *
348
350
  * @param document
349
351
  */
350
- function addPolyFillAfterMainPreRenderTemplate(document) {
351
- let topLevelPreRenderElements = document.querySelectorAll(
352
- ".speed-kit-pre-rendered-block-added",
353
- );
354
- topLevelPreRenderElements.forEach((element) => {
355
- let topTemplate = element.querySelector('template[shadowrootmode="open"]');
356
- if (!topTemplate) {
357
- return;
352
+ function wrapPreRenderedElementsWithCustomRootTags(document) {
353
+ const customShadowRoots = getCustomShadowElements(document);
354
+
355
+ function createCustomElement(tagName, sourceSelector) {
356
+ const el = document.createElement(tagName);
357
+ const source = document.querySelector(sourceSelector);
358
+ if (source) {
359
+ for (const attr of source.attributes) {
360
+ el.setAttribute(attr.name, attr.value);
361
+ }
358
362
  }
363
+ return el;
364
+ }
359
365
 
360
- // polyfill for shadowRoots
361
- let polyfillScript = document.createElement("script");
362
- polyfillScript.textContent = `SKShadowRootsPolyfill(document?.currentScript?.previousElementSibling)`;
363
- topTemplate.after(polyfillScript);
364
- });
366
+ for (const customShadowRoot of customShadowRoots) {
367
+ const skHtml = createCustomElement("sk-html", "html");
368
+ const skBody = createCustomElement("sk-body", "body");
369
+
370
+ // move all children from the shadow root element into sk-body
371
+ while (customShadowRoot.firstChild) {
372
+ // remove class which is no longer needed
373
+ customShadowRoot.firstChild.classList.remove("speed-kit-pre-rendered-block-added");
374
+
375
+ // move element to sk-body
376
+ skBody.appendChild(customShadowRoot.firstChild);
377
+ }
378
+
379
+ // append sk-body inside sk-html
380
+ skHtml.appendChild(skBody);
381
+
382
+ // append sk-html into the shadow root element
383
+ customShadowRoot.appendChild(skHtml);
384
+ }
365
385
  }
366
386
 
367
- function hideCustomShadowDom(document) {
387
+ /**
388
+ * Calls the polyfill for shadowRoots after the main pre-rendered template.
389
+ *
390
+ * @param document
391
+ */
392
+ function addPolyFillAfterMainPreRenderTemplate(document) {
368
393
  const customShadowRoots = getCustomShadowElements(document);
394
+ for (const customShadowRoot of customShadowRoots) {
395
+ let topLevelPreRenderElements = customShadowRoot.querySelectorAll("sk-body > *");
396
+ topLevelPreRenderElements.forEach((element) => {
397
+ let topTemplate = element.querySelector('template[shadowrootmode="open"]');
398
+ if (!topTemplate) {
399
+ return;
400
+ }
369
401
 
370
- customShadowRoots.forEach((element) => {
371
- const style = document.createElement("style");
372
- style.textContent = `.speed-kit-pre-rendered-block-added{visibility:hidden;}`;
373
- element.prepend(style);
374
- });
402
+ // polyfill for shadowRoots
403
+ let polyfillScript = document.createElement("script");
404
+ polyfillScript.textContent = `SKShadowRootsPolyfill(document?.currentScript?.previousElementSibling)`;
405
+ topTemplate.after(polyfillScript);
406
+ });
407
+ }
375
408
  }
376
409
 
377
- function showCustomShadowDom(document) {
410
+ /**
411
+ * Forces rendering of elements within custom shadow DOMs by briefly toggling
412
+ * their visibility via injected style rules.
413
+ *
414
+ * @param document
415
+ */
416
+ function triggerShadowRendering(document) {
378
417
  const customShadowRoots = getCustomShadowElements(document);
379
-
380
- customShadowRoots.forEach((element) => {
381
- const style = document.createElement("style");
382
- style.textContent = `.speed-kit-pre-rendered-block-added{visibility:visible;}`;
383
- element.appendChild(style);
384
- });
418
+ for (const customShadowRoot of customShadowRoots) {
419
+ const hidingStyle = document.createElement("style");
420
+ hidingStyle.textContent = `sk-html{visibility:hidden;}`;
421
+ customShadowRoot.prepend(hidingStyle);
422
+
423
+ const visibilityStyle = document.createElement("style");
424
+ visibilityStyle.textContent = `sk-html{visibility:visible;}`;
425
+ customShadowRoot.appendChild(visibilityStyle);
426
+ }
385
427
  }
386
- {{#if supportShadowDomInPreRendering}}
428
+ {{#if addSSRInnerShadowDomSupport}}
429
+
387
430
  //================================================================================
388
- // PuppeteerCallback
431
+ // Puppeteer Callback
389
432
  //================================================================================
433
+
390
434
  /**
391
435
  * Waits for the callback when custom elements are defined on the document.
392
436
  *
@@ -465,7 +509,7 @@ export const post = async (db, req, res) => {
465
509
  return instance;
466
510
  };
467
511
 
468
- // create a dynamic proxy class which si overwriting all lifecycle hooks of the original class
512
+ // create a dynamic proxy class which is overwriting all lifecycle hooks of the original class
469
513
  skProxy.prototype = Object.create(o.prototype, {
470
514
  constructor: {
471
515
  value: skProxy,
@@ -495,7 +539,7 @@ export const post = async (db, req, res) => {
495
539
 
496
540
  Object.setPrototypeOf(skProxy, o);
497
541
 
498
- const skContainer = document.querySelector(".speed-kit-shadow-app-root");
542
+ const skContainer = document.querySelector("sk-shadow-app-root");
499
543
  let skContainerParentNode;
500
544
  let skContainerNextNode;
501
545
  if (skContainer) {
@@ -560,6 +604,5 @@ export const post = async (db, req, res) => {
560
604
  }
561
605
  });
562
606
  }
563
- {{else}}
564
607
  {{/if}}
565
608
  {{/if}}