@speedkit/cli 2.33.0 → 2.35.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/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,17 @@
|
|
|
1
|
+
# [2.35.0](https://gitlab.orestes.info/baqend/speed-kit-cli/compare/v2.34.0...v2.35.0) (2024-06-21)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Features
|
|
5
|
+
|
|
6
|
+
* **customer-config:** add ignore assets ([8774d78](https://gitlab.orestes.info/baqend/speed-kit-cli/commit/8774d78d042ee80937c13cdd38acff84ca5b7e4f))
|
|
7
|
+
|
|
8
|
+
# [2.34.0](https://gitlab.orestes.info/baqend/speed-kit-cli/compare/v2.33.0...v2.34.0) (2024-06-21)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Features
|
|
12
|
+
|
|
13
|
+
* **config-wizard:** update prerendering template ([7803e34](https://gitlab.orestes.info/baqend/speed-kit-cli/commit/7803e34a2eb129e9e43badebb0d17414e1e076c8))
|
|
14
|
+
|
|
1
15
|
# [2.33.0](https://gitlab.orestes.info/baqend/speed-kit-cli/compare/v2.32.0...v2.33.0) (2024-06-19)
|
|
2
16
|
|
|
3
17
|
|
package/README.md
CHANGED
|
@@ -50,6 +50,12 @@ const config = {
|
|
|
50
50
|
},
|
|
51
51
|
blacklistHTML({db, {{#if addPreRendering}}document{{else}}html{{/if}}, variation, url, headers}) {
|
|
52
52
|
/* Use to configure guards to black-list e.g. specific page types */
|
|
53
|
+
{{#if addPreRendering}}
|
|
54
|
+
// dynamically blacklist unsupported variations as they are not covered in lambda
|
|
55
|
+
if (variation.toUpperCase() === "TABLET") {
|
|
56
|
+
return `Tablet viewport is unsupported: ${url}`;
|
|
57
|
+
}
|
|
58
|
+
{{/if}}
|
|
53
59
|
return false;
|
|
54
60
|
},
|
|
55
61
|
shouldTransform({db, {{#if addPreRendering}}document{{else}}html{{/if}}, variation, url, headers}) {
|
|
@@ -66,7 +72,7 @@ const config = {
|
|
|
66
72
|
{{#unless addPreRendering}}
|
|
67
73
|
return html;
|
|
68
74
|
{{else}}
|
|
69
|
-
|
|
75
|
+
generateMainShadowRoot(document);
|
|
70
76
|
{{/unless}}
|
|
71
77
|
},
|
|
72
78
|
postTransform({db, {{#if addPreRendering}}document{{else}}html{{/if}}, variation, url, headers}) {
|
|
@@ -76,17 +82,32 @@ const config = {
|
|
|
76
82
|
{{/if}}
|
|
77
83
|
return html;
|
|
78
84
|
{{else}}
|
|
79
|
-
|
|
85
|
+
createPreloadsForInnerStyleLinks(document);
|
|
86
|
+
removeInnerScriptNodes(document);
|
|
87
|
+
assignStylesToShadowDom(document);
|
|
88
|
+
addDeclarativeShadowDOMPolyfill(document);
|
|
89
|
+
addPolyFillAfterMainPreRenderTemplate(document);
|
|
80
90
|
{{/unless}}
|
|
91
|
+
{{#if supportShadowDomInPreRendering}}
|
|
92
|
+
overrideCustomElementDefine(document);
|
|
93
|
+
walkPreRenderedElements(document);
|
|
94
|
+
{{/if}}
|
|
81
95
|
},
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
96
|
+
{{#if isPlentymarkets}}
|
|
97
|
+
stripComments: false, // Plentymarkets specific
|
|
98
|
+
{{/if}}
|
|
99
|
+
{{#if addPreRendering}}
|
|
100
|
+
ignoreAssets: [
|
|
101
|
+
// keep aligned with dynamicBlockConfig.assetChangeDetection.ignoreAssets
|
|
102
|
+
],
|
|
103
|
+
{{else}}
|
|
104
|
+
ignoreAssets: [],
|
|
105
|
+
{{/if}}
|
|
106
|
+
{{#if isShopify}}
|
|
107
|
+
delayScriptPath: "/apps/speed-kit/speed-kit-dom-ready.js", // Shopify specific
|
|
108
|
+
{{else}}
|
|
109
|
+
delayScriptPath: "/speed-kit-dom-ready.js",
|
|
110
|
+
{{/if}}
|
|
90
111
|
executeDeploymentDetection: true,
|
|
91
112
|
{{#unless addPreRendering}}
|
|
92
113
|
{{#if removeLazyLoading}}
|
|
@@ -94,19 +115,30 @@ const config = {
|
|
|
94
115
|
{{/if}}
|
|
95
116
|
{{else}}
|
|
96
117
|
preRender: {
|
|
97
|
-
lambdaUrl: '
|
|
118
|
+
lambdaUrl: '<generate-new-lambda-function-and-paste-url-endpoint-here>',
|
|
98
119
|
elements: [
|
|
99
|
-
// app shell e.g. vue often uses #app
|
|
100
120
|
// this will be the main element to extract from the rendered DOM
|
|
101
|
-
{selector:
|
|
121
|
+
{ selector: "body > #app", appendTo: ".sk-container template" },
|
|
122
|
+
// append all extracted styles to all pre-rendered elements (as they are capsuled in shadow doms)
|
|
123
|
+
{
|
|
124
|
+
selector: 'head > link[href*="css"]',
|
|
125
|
+
appendTo: ".sk-container template",
|
|
126
|
+
},
|
|
127
|
+
{
|
|
128
|
+
selector: "head > style",
|
|
129
|
+
appendTo: ".sk-container template",
|
|
130
|
+
},
|
|
102
131
|
],
|
|
103
132
|
guards: [
|
|
104
|
-
{
|
|
105
|
-
whenPresent: '
|
|
133
|
+
{
|
|
134
|
+
whenPresent: 'body > #app',
|
|
106
135
|
waitFor: [
|
|
107
136
|
// e.g. wait for first 4 images to be loaded
|
|
108
137
|
'.products-list-page__products-list .products-list__tiles__element:nth-child(-n+4) img[src=""]',
|
|
109
138
|
],
|
|
139
|
+
{{#if supportShadowDomInPreRendering}}
|
|
140
|
+
waitForCallback: waitForCallback.toString(),
|
|
141
|
+
{{/if}}
|
|
110
142
|
},
|
|
111
143
|
],
|
|
112
144
|
rules: {
|
|
@@ -116,11 +148,13 @@ const config = {
|
|
|
116
148
|
],
|
|
117
149
|
resourceType: [
|
|
118
150
|
// only resource types listed here will be fetched
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
151
|
+
"xhr",
|
|
152
|
+
"fetch",
|
|
153
|
+
"font",
|
|
154
|
+
"script",
|
|
155
|
+
"document",
|
|
156
|
+
"stylesheet",
|
|
157
|
+
"image",
|
|
124
158
|
],
|
|
125
159
|
},
|
|
126
160
|
},
|
|
@@ -165,3 +199,303 @@ export const post = async (db, req, res) => {
|
|
|
165
199
|
}
|
|
166
200
|
{{/if}}
|
|
167
201
|
{{/unless}}
|
|
202
|
+
|
|
203
|
+
{{#if addPreRendering}}
|
|
204
|
+
/**
|
|
205
|
+
* Generates the main shadow root for the given DOM.
|
|
206
|
+
* This is the parent element of the extracted pre-rendered content.
|
|
207
|
+
* @param document
|
|
208
|
+
*/
|
|
209
|
+
function generateMainShadowRoot(document) {
|
|
210
|
+
const customContainer = document.createElement("div");
|
|
211
|
+
customContainer.classList.add("sk-container");
|
|
212
|
+
customContainer.classList.add("speed-kit-shadow-app-root");
|
|
213
|
+
const template = document.createElement("template");
|
|
214
|
+
template.setAttribute("shadowrootmode", "open");
|
|
215
|
+
customContainer.appendChild(template);
|
|
216
|
+
|
|
217
|
+
document.querySelector("body").prepend(customContainer);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Creates preloads for inner style links in the given document.
|
|
222
|
+
*
|
|
223
|
+
* @param {Document} document - The document object to search for inner style links.
|
|
224
|
+
*
|
|
225
|
+
* @return {void}
|
|
226
|
+
*/
|
|
227
|
+
function createPreloadsForInnerStyleLinks(document) {
|
|
228
|
+
const innerStyleLinks = document.querySelectorAll(
|
|
229
|
+
'.speed-kit-pre-rendered-block-added[rel="stylesheet"][href]',
|
|
230
|
+
);
|
|
231
|
+
const head = document.querySelector("head");
|
|
232
|
+
innerStyleLinks.forEach((styleLink) => {
|
|
233
|
+
const link = document.createElement("link");
|
|
234
|
+
link.setAttribute("rel", "preload");
|
|
235
|
+
link.setAttribute("as", "stylesheet");
|
|
236
|
+
link.setAttribute("href", styleLink.getAttribute("href"));
|
|
237
|
+
head.append(link);
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* Removes all inner script nodes from the given document.
|
|
243
|
+
*
|
|
244
|
+
* @param {Document} document - The document object from which to remove script nodes.
|
|
245
|
+
*/
|
|
246
|
+
function removeInnerScriptNodes(document) {
|
|
247
|
+
// remove all innerShadowDom script, as they would be executed again
|
|
248
|
+
Array.from(
|
|
249
|
+
document.querySelectorAll(".speed-kit-pre-rendered-block-added script"),
|
|
250
|
+
).forEach((scriptNode) => scriptNode.remove());
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* Assigns styles to the Shadow DOM of specified document.
|
|
255
|
+
*
|
|
256
|
+
* @param {Document} document - The document object to which styles are to be assigned.
|
|
257
|
+
* @throws {Abort} Throws an error if `skStyles` is not found.
|
|
258
|
+
*/
|
|
259
|
+
function assignStylesToShadowDom(document) {
|
|
260
|
+
const skStyles = document.querySelector('[id="speed-kit-df-styles"]');
|
|
261
|
+
|
|
262
|
+
if (!skStyles) {
|
|
263
|
+
throw new Abort("skStyles not found");
|
|
264
|
+
}
|
|
265
|
+
const clonedStyles = document.createElement("style");
|
|
266
|
+
clonedStyles.classList.add("speed-kit-df-styles");
|
|
267
|
+
clonedStyles.textContent = skStyles.textContent;
|
|
268
|
+
|
|
269
|
+
const customShadowElements = getCustomShadowElements(document);
|
|
270
|
+
customShadowElements.forEach((shadowElement) => {
|
|
271
|
+
shadowElement.prepend(clonedStyles);
|
|
272
|
+
});
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
/**
|
|
276
|
+
* Helper function to select all template elements.
|
|
277
|
+
* @param document
|
|
278
|
+
* @returns {*}
|
|
279
|
+
*/
|
|
280
|
+
function getCustomShadowElements(document) {
|
|
281
|
+
return document.querySelectorAll(".sk-container > template");
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
* Adds the declarative shadow dom polyfill to the head of the document.
|
|
286
|
+
* @param document
|
|
287
|
+
*/
|
|
288
|
+
function addDeclarativeShadowDOMPolyfill(document) {
|
|
289
|
+
const head = document.querySelector("head");
|
|
290
|
+
if (head) {
|
|
291
|
+
const script = document.createElement("script");
|
|
292
|
+
script.textContent = SKShadowRootsPolyfill.toString();
|
|
293
|
+
head.append(script);
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
/**
|
|
298
|
+
* Polyfills the creation of Shadow Roots for browsers that do not support Shadow DOM.
|
|
299
|
+
* If the browser supports Shadow DOM, this method does nothing.
|
|
300
|
+
* https://developer.chrome.com/docs/css-ui/declarative-shadow-dom#polyfill
|
|
301
|
+
*
|
|
302
|
+
* @param {HTMLTemplateElement} template - The template element to be transformed into a Shadow Root.
|
|
303
|
+
*
|
|
304
|
+
* @constructor
|
|
305
|
+
*/
|
|
306
|
+
function SKShadowRootsPolyfill(template) {
|
|
307
|
+
// If the browser supports Shadow DOM, do nothing, as template is already transformed to a shadowRoot.
|
|
308
|
+
if (!template) {
|
|
309
|
+
return;
|
|
310
|
+
}
|
|
311
|
+
const mode = template.getAttribute("shadowrootmode");
|
|
312
|
+
const shadowRoot = template.parentNode.attachShadow({ mode });
|
|
313
|
+
shadowRoot.appendChild(template.content);
|
|
314
|
+
template.remove();
|
|
315
|
+
shadowRoot
|
|
316
|
+
.querySelectorAll("template[shadowrootmode]")
|
|
317
|
+
.forEach((template) => {
|
|
318
|
+
SKShadowRootsPolyfill(template);
|
|
319
|
+
});
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
/**
|
|
323
|
+
* Calls the polyfill for shadowRoots after the main pre-rendered template.
|
|
324
|
+
* @param document
|
|
325
|
+
*/
|
|
326
|
+
function addPolyFillAfterMainPreRenderTemplate(document) {
|
|
327
|
+
let topLevelPreRenderElements = document.querySelectorAll(
|
|
328
|
+
".speed-kit-pre-rendered-block-added",
|
|
329
|
+
);
|
|
330
|
+
topLevelPreRenderElements.forEach((element) => {
|
|
331
|
+
let topTemplate = element.querySelector('template[shadowrootmode="open"]');
|
|
332
|
+
if (!topTemplate) {
|
|
333
|
+
return;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
// polyfill for shadowRoots
|
|
337
|
+
let polyfillScript = document.createElement("script");
|
|
338
|
+
polyfillScript.textContent = `SKShadowRootsPolyfill(document?.currentScript?.previousElementSibling)`;
|
|
339
|
+
topTemplate.after(polyfillScript);
|
|
340
|
+
});
|
|
341
|
+
}
|
|
342
|
+
{{#if supportShadowDomInPreRendering}}
|
|
343
|
+
//================================================================================
|
|
344
|
+
// PuppeteerCallback
|
|
345
|
+
//================================================================================
|
|
346
|
+
/**
|
|
347
|
+
* Waits for the callback when custom elements are defined on the document.
|
|
348
|
+
*
|
|
349
|
+
* @return {Promise} - A promise that resolves when all custom elements are defined, or if a timeout of 5 seconds occurs.
|
|
350
|
+
*/
|
|
351
|
+
async function waitForCallback() {
|
|
352
|
+
const undefinedElements = document.querySelectorAll(":not(:defined)");
|
|
353
|
+
|
|
354
|
+
const promises = [...undefinedElements]
|
|
355
|
+
.filter((button) => button.localName.startsWith("{{shadowDomCustomElementPrefix}}"))
|
|
356
|
+
.map((button) => {
|
|
357
|
+
// eslint-disable-next-line no-undef
|
|
358
|
+
return customElements.whenDefined(button.localName);
|
|
359
|
+
});
|
|
360
|
+
|
|
361
|
+
return await Promise.race([
|
|
362
|
+
Promise.all(promises),
|
|
363
|
+
new Promise((resolve) => {
|
|
364
|
+
setTimeout(resolve, 5000);
|
|
365
|
+
}),
|
|
366
|
+
]);
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
//================================================================================
|
|
370
|
+
// INLINE SCRIPTS
|
|
371
|
+
//================================================================================
|
|
372
|
+
|
|
373
|
+
/**
|
|
374
|
+
* Overrides the window.customElements.define method to add custom code before calling the original function.
|
|
375
|
+
*/
|
|
376
|
+
function overrideDefine() {
|
|
377
|
+
// Store the original function
|
|
378
|
+
// eslint-disable-next-line no-undef
|
|
379
|
+
let originalDefine = window.customElements.define;
|
|
380
|
+
|
|
381
|
+
// Create new function
|
|
382
|
+
// eslint-disable-next-line no-undef
|
|
383
|
+
window.customElements.define = function (e, o, options) {
|
|
384
|
+
// Execute your custom code
|
|
385
|
+
// Create a class for the element
|
|
386
|
+
let skProxy = class extends o {
|
|
387
|
+
constructor() {
|
|
388
|
+
super();
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
connectedCallback() {
|
|
392
|
+
if (this.hasAttribute("data-sk-static")) {
|
|
393
|
+
return;
|
|
394
|
+
}
|
|
395
|
+
// normal functionality
|
|
396
|
+
super.connectedCallback();
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
disconnectedCallback() {
|
|
400
|
+
// for sk-shadow-root don't do anything
|
|
401
|
+
if (this.hasAttribute("data-sk-static")) {
|
|
402
|
+
return;
|
|
403
|
+
}
|
|
404
|
+
// normal functionality
|
|
405
|
+
super.disconnectedCallback();
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
adoptedCallback() {
|
|
409
|
+
// for sk-shadow-root don't do anything
|
|
410
|
+
if (this.hasAttribute("data-sk-static")) {
|
|
411
|
+
return;
|
|
412
|
+
}
|
|
413
|
+
// normal functionality
|
|
414
|
+
super.adoptedCallback();
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
attributeChangedCallback(name, oldValue, newValue) {
|
|
418
|
+
// for sk-shadow-root don't do anything
|
|
419
|
+
if (this.hasAttribute("data-sk-static")) {
|
|
420
|
+
return;
|
|
421
|
+
}
|
|
422
|
+
// normal functionality
|
|
423
|
+
super.attributeChangedCallback(name, oldValue, newValue);
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
attachShadow(mode) {
|
|
427
|
+
if (this.hasAttribute("data-sk-static")) {
|
|
428
|
+
return null;
|
|
429
|
+
}
|
|
430
|
+
return super.attachShadow(mode);
|
|
431
|
+
}
|
|
432
|
+
};
|
|
433
|
+
|
|
434
|
+
// Call the original function with modified arguments
|
|
435
|
+
// eslint-disable-next-line no-undef
|
|
436
|
+
originalDefine.call(window.customElements, e, skProxy, options);
|
|
437
|
+
};
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
/**
|
|
441
|
+
* Overrides the custom element definition in the given document.
|
|
442
|
+
*
|
|
443
|
+
* @param {Document} document - The document object where the custom element is defined.
|
|
444
|
+
*
|
|
445
|
+
* @return {void} - This method does not return anything.
|
|
446
|
+
*/
|
|
447
|
+
function overrideCustomElementDefine(document) {
|
|
448
|
+
const script = document.createElement("script");
|
|
449
|
+
script.setAttribute("type", "text/javascript");
|
|
450
|
+
script.classList.add("sk-shadowroot-extension");
|
|
451
|
+
script.textContent = `(${overrideDefine.toString()})()`;
|
|
452
|
+
document.querySelector("head").appendChild(script);
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
/**
|
|
456
|
+
* Walks through pre-rendered elements and performs specific actions on each element.
|
|
457
|
+
*
|
|
458
|
+
* @param {Document} document - The HTML document object.
|
|
459
|
+
*/
|
|
460
|
+
function walkPreRenderedElements(document) {
|
|
461
|
+
const customShadowElements = getCustomShadowElements(document);
|
|
462
|
+
walkElements(customShadowElements, (element) => {
|
|
463
|
+
addStaticContextForPrerenderedElements(element);
|
|
464
|
+
// TODO extend if shadow-doms should be modified, as access to the shadow dom is not possible via querySelectors from the outside
|
|
465
|
+
});
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
/**
|
|
469
|
+
* Recursively walks through a tree of elements and applies a callback function to each element.
|
|
470
|
+
*
|
|
471
|
+
* @param {Array} elements - The array of elements to walk through. Each element should be an object with a 'children' property.
|
|
472
|
+
* @param {Function} callback - The callback function to apply to each element. It should take a single argument, which will be the current element.
|
|
473
|
+
*
|
|
474
|
+
* @return {undefined}
|
|
475
|
+
*/
|
|
476
|
+
function walkElements(elements, callback) {
|
|
477
|
+
elements.forEach((element) => {
|
|
478
|
+
callback(element);
|
|
479
|
+
if (element.children && element.children.length > 0) {
|
|
480
|
+
walkElements(element.children, callback);
|
|
481
|
+
}
|
|
482
|
+
});
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
/**
|
|
486
|
+
* Adds static context for prerendered elements.
|
|
487
|
+
*
|
|
488
|
+
* @param {Element} element The element to add static context for.
|
|
489
|
+
*/
|
|
490
|
+
function addStaticContextForPrerenderedElements(element) {
|
|
491
|
+
if (
|
|
492
|
+
"name" in element &&
|
|
493
|
+
element.name !== undefined &&
|
|
494
|
+
element.name.startsWith("{{shadowDomCustomElementPrefix}}")
|
|
495
|
+
) {
|
|
496
|
+
element.setAttribute("data-sk-static", "1");
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
{{else}}
|
|
500
|
+
{{/if}}
|
|
501
|
+
{{/if}}
|
|
@@ -37,6 +37,14 @@
|
|
|
37
37
|
dynamicScriptExecution: true, // Shopify specific
|
|
38
38
|
// => as some async external scripts are injected more or less randomly into the html, destroying the order of local & remote scripts
|
|
39
39
|
{{/if}}
|
|
40
|
+
{{#if addPreRendering}}
|
|
41
|
+
assetChangeDetection: {
|
|
42
|
+
ignore: [
|
|
43
|
+
// keep aligned with DocumentHandler's config.ignoreAssets
|
|
44
|
+
],
|
|
45
|
+
},
|
|
46
|
+
{{/if}}
|
|
47
|
+
|
|
40
48
|
blocks: [
|
|
41
49
|
// TODO: adjust/replace this exemplary list
|
|
42
50
|
{{#if addPreRendering}}
|
package/oclif.manifest.json
CHANGED