@speedkit/cli 2.32.0 → 2.34.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,18 @@
|
|
|
1
|
+
# [2.34.0](https://gitlab.orestes.info/baqend/speed-kit-cli/compare/v2.33.0...v2.34.0) (2024-06-21)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Features
|
|
5
|
+
|
|
6
|
+
* **config-wizard:** update prerendering template ([7803e34](https://gitlab.orestes.info/baqend/speed-kit-cli/commit/7803e34a2eb129e9e43badebb0d17414e1e076c8))
|
|
7
|
+
|
|
8
|
+
# [2.33.0](https://gitlab.orestes.info/baqend/speed-kit-cli/compare/v2.32.0...v2.33.0) (2024-06-19)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Features
|
|
12
|
+
|
|
13
|
+
* **customer-config:** extend default query parameter stripping ([f67a696](https://gitlab.orestes.info/baqend/speed-kit-cli/commit/f67a696d427115f3895b7093f7d5d7d3dcfa6b30))
|
|
14
|
+
* **customer-config:** extend default query parameter stripping ([12b643c](https://gitlab.orestes.info/baqend/speed-kit-cli/commit/12b643cbb1d09ed5f751136a03d92adf9d1226b4))
|
|
15
|
+
|
|
1
16
|
# [2.32.0](https://gitlab.orestes.info/baqend/speed-kit-cli/compare/v2.31.1...v2.32.0) (2024-06-14)
|
|
2
17
|
|
|
3
18
|
|
package/README.md
CHANGED
|
@@ -314,8 +314,8 @@
|
|
|
314
314
|
"_ga", // Google Analytics User ID (Universal Analytics)
|
|
315
315
|
"_gl", // Google Analytics User ID (GA 4+)
|
|
316
316
|
"_kx", // klaviyo
|
|
317
|
+
"ad_id",
|
|
317
318
|
"awc", // AWIN (former Zanox Affiliate Network)
|
|
318
|
-
"campaign",
|
|
319
319
|
"cjid",
|
|
320
320
|
"cto_pld",
|
|
321
321
|
"dclid", // another Click ID
|
|
@@ -323,11 +323,18 @@
|
|
|
323
323
|
"epik",
|
|
324
324
|
"fbclid", // Facebook Click ID
|
|
325
325
|
"gtm_debug", // Google Tag Manager
|
|
326
|
+
"kgclid",
|
|
326
327
|
"msclkid", // Microsoft Click ID
|
|
328
|
+
"rtbhc", // RTB House
|
|
329
|
+
"soluteclid", // Solute Click ID
|
|
327
330
|
"srsltid", // google search engine
|
|
331
|
+
"syclid",
|
|
332
|
+
"tduid", // Tradedoubler UID
|
|
333
|
+
"ttclid", // TikTok Click ID
|
|
328
334
|
"yclid", // Yandex Click ID
|
|
329
335
|
"zanpid", // AWIN (former Zanox Affiliate Network)
|
|
330
336
|
/^[gw]braid$/, // Google's iOS web-to-app campaign measurement
|
|
337
|
+
/^campaign(_id)?$/,
|
|
331
338
|
/^cq_/,
|
|
332
339
|
/^et_/,
|
|
333
340
|
/^gad(w|_source)?$/, // Google Ads
|
|
@@ -335,6 +342,7 @@
|
|
|
335
342
|
/^klar_/, // getklar.com
|
|
336
343
|
/^neocom_/, // Neocom.ai
|
|
337
344
|
/^sc_/, // e.g. Adobe
|
|
345
|
+
/^sv(1|_campaign_id)$/,
|
|
338
346
|
/^trc_/, // Tracify
|
|
339
347
|
/^uslk_/, // Userlike Chatbot
|
|
340
348
|
/^utm[_-]/, // Urchin Tracking Module
|
|
@@ -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,8 +82,16 @@ 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
96
|
{{#if isPlentymarkets}}
|
|
83
97
|
stripComments: false, // Plentymarkets specific
|
|
@@ -94,19 +108,30 @@ const config = {
|
|
|
94
108
|
{{/if}}
|
|
95
109
|
{{else}}
|
|
96
110
|
preRender: {
|
|
97
|
-
lambdaUrl: '
|
|
111
|
+
lambdaUrl: '<generate-new-lambda-function-and-paste-url-endpoint-here>',
|
|
98
112
|
elements: [
|
|
99
|
-
// app shell e.g. vue often uses #app
|
|
100
113
|
// this will be the main element to extract from the rendered DOM
|
|
101
|
-
{selector:
|
|
114
|
+
{ selector: "body > #app", appendTo: ".sk-container template" },
|
|
115
|
+
// append all extracted styles to all pre-rendered elements (as they are capsuled in shadow doms)
|
|
116
|
+
{
|
|
117
|
+
selector: 'head > link[href*="css"]',
|
|
118
|
+
appendTo: ".sk-container template",
|
|
119
|
+
},
|
|
120
|
+
{
|
|
121
|
+
selector: "head > style",
|
|
122
|
+
appendTo: ".sk-container template",
|
|
123
|
+
},
|
|
102
124
|
],
|
|
103
125
|
guards: [
|
|
104
|
-
{
|
|
105
|
-
whenPresent: '
|
|
126
|
+
{
|
|
127
|
+
whenPresent: 'body > #app',
|
|
106
128
|
waitFor: [
|
|
107
129
|
// e.g. wait for first 4 images to be loaded
|
|
108
130
|
'.products-list-page__products-list .products-list__tiles__element:nth-child(-n+4) img[src=""]',
|
|
109
131
|
],
|
|
132
|
+
{{#if supportShadowDomInPreRendering}}
|
|
133
|
+
waitForCallback: waitForCallback.toString(),
|
|
134
|
+
{{/if}}
|
|
110
135
|
},
|
|
111
136
|
],
|
|
112
137
|
rules: {
|
|
@@ -116,11 +141,13 @@ const config = {
|
|
|
116
141
|
],
|
|
117
142
|
resourceType: [
|
|
118
143
|
// only resource types listed here will be fetched
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
144
|
+
"xhr",
|
|
145
|
+
"fetch",
|
|
146
|
+
"font",
|
|
147
|
+
"script",
|
|
148
|
+
"document",
|
|
149
|
+
"stylesheet",
|
|
150
|
+
"image",
|
|
124
151
|
],
|
|
125
152
|
},
|
|
126
153
|
},
|
|
@@ -165,3 +192,303 @@ export const post = async (db, req, res) => {
|
|
|
165
192
|
}
|
|
166
193
|
{{/if}}
|
|
167
194
|
{{/unless}}
|
|
195
|
+
|
|
196
|
+
{{#if addPreRendering}}
|
|
197
|
+
/**
|
|
198
|
+
* Generates the main shadow root for the given DOM.
|
|
199
|
+
* This is the parent element of the extracted pre-rendered content.
|
|
200
|
+
* @param document
|
|
201
|
+
*/
|
|
202
|
+
function generateMainShadowRoot(document) {
|
|
203
|
+
const customContainer = document.createElement("div");
|
|
204
|
+
customContainer.classList.add("sk-container");
|
|
205
|
+
customContainer.classList.add("speed-kit-shadow-app-root");
|
|
206
|
+
const template = document.createElement("template");
|
|
207
|
+
template.setAttribute("shadowrootmode", "open");
|
|
208
|
+
customContainer.appendChild(template);
|
|
209
|
+
|
|
210
|
+
document.querySelector("body").prepend(customContainer);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Creates preloads for inner style links in the given document.
|
|
215
|
+
*
|
|
216
|
+
* @param {Document} document - The document object to search for inner style links.
|
|
217
|
+
*
|
|
218
|
+
* @return {void}
|
|
219
|
+
*/
|
|
220
|
+
function createPreloadsForInnerStyleLinks(document) {
|
|
221
|
+
const innerStyleLinks = document.querySelectorAll(
|
|
222
|
+
'.speed-kit-pre-rendered-block-added[rel="stylesheet"][href]',
|
|
223
|
+
);
|
|
224
|
+
const head = document.querySelector("head");
|
|
225
|
+
innerStyleLinks.forEach((styleLink) => {
|
|
226
|
+
const link = document.createElement("link");
|
|
227
|
+
link.setAttribute("rel", "preload");
|
|
228
|
+
link.setAttribute("as", "stylesheet");
|
|
229
|
+
link.setAttribute("href", styleLink.getAttribute("href"));
|
|
230
|
+
head.append(link);
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* Removes all inner script nodes from the given document.
|
|
236
|
+
*
|
|
237
|
+
* @param {Document} document - The document object from which to remove script nodes.
|
|
238
|
+
*/
|
|
239
|
+
function removeInnerScriptNodes(document) {
|
|
240
|
+
// remove all innerShadowDom script, as they would be executed again
|
|
241
|
+
Array.from(
|
|
242
|
+
document.querySelectorAll(".speed-kit-pre-rendered-block-added script"),
|
|
243
|
+
).forEach((scriptNode) => scriptNode.remove());
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* Assigns styles to the Shadow DOM of specified document.
|
|
248
|
+
*
|
|
249
|
+
* @param {Document} document - The document object to which styles are to be assigned.
|
|
250
|
+
* @throws {Abort} Throws an error if `skStyles` is not found.
|
|
251
|
+
*/
|
|
252
|
+
function assignStylesToShadowDom(document) {
|
|
253
|
+
const skStyles = document.querySelector('[id="speed-kit-df-styles"]');
|
|
254
|
+
|
|
255
|
+
if (!skStyles) {
|
|
256
|
+
throw new Abort("skStyles not found");
|
|
257
|
+
}
|
|
258
|
+
const clonedStyles = document.createElement("style");
|
|
259
|
+
clonedStyles.classList.add("speed-kit-df-styles");
|
|
260
|
+
clonedStyles.textContent = skStyles.textContent;
|
|
261
|
+
|
|
262
|
+
const customShadowElements = getCustomShadowElements(document);
|
|
263
|
+
customShadowElements.forEach((shadowElement) => {
|
|
264
|
+
shadowElement.prepend(clonedStyles);
|
|
265
|
+
});
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* Helper function to select all template elements.
|
|
270
|
+
* @param document
|
|
271
|
+
* @returns {*}
|
|
272
|
+
*/
|
|
273
|
+
function getCustomShadowElements(document) {
|
|
274
|
+
return document.querySelectorAll(".sk-container > template");
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* Adds the declarative shadow dom polyfill to the head of the document.
|
|
279
|
+
* @param document
|
|
280
|
+
*/
|
|
281
|
+
function addDeclarativeShadowDOMPolyfill(document) {
|
|
282
|
+
const head = document.querySelector("head");
|
|
283
|
+
if (head) {
|
|
284
|
+
const script = document.createElement("script");
|
|
285
|
+
script.textContent = SKShadowRootsPolyfill.toString();
|
|
286
|
+
head.append(script);
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
/**
|
|
291
|
+
* Polyfills the creation of Shadow Roots for browsers that do not support Shadow DOM.
|
|
292
|
+
* If the browser supports Shadow DOM, this method does nothing.
|
|
293
|
+
* https://developer.chrome.com/docs/css-ui/declarative-shadow-dom#polyfill
|
|
294
|
+
*
|
|
295
|
+
* @param {HTMLTemplateElement} template - The template element to be transformed into a Shadow Root.
|
|
296
|
+
*
|
|
297
|
+
* @constructor
|
|
298
|
+
*/
|
|
299
|
+
function SKShadowRootsPolyfill(template) {
|
|
300
|
+
// If the browser supports Shadow DOM, do nothing, as template is already transformed to a shadowRoot.
|
|
301
|
+
if (!template) {
|
|
302
|
+
return;
|
|
303
|
+
}
|
|
304
|
+
const mode = template.getAttribute("shadowrootmode");
|
|
305
|
+
const shadowRoot = template.parentNode.attachShadow({ mode });
|
|
306
|
+
shadowRoot.appendChild(template.content);
|
|
307
|
+
template.remove();
|
|
308
|
+
shadowRoot
|
|
309
|
+
.querySelectorAll("template[shadowrootmode]")
|
|
310
|
+
.forEach((template) => {
|
|
311
|
+
SKShadowRootsPolyfill(template);
|
|
312
|
+
});
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
/**
|
|
316
|
+
* Calls the polyfill for shadowRoots after the main pre-rendered template.
|
|
317
|
+
* @param document
|
|
318
|
+
*/
|
|
319
|
+
function addPolyFillAfterMainPreRenderTemplate(document) {
|
|
320
|
+
let topLevelPreRenderElements = document.querySelectorAll(
|
|
321
|
+
".speed-kit-pre-rendered-block-added",
|
|
322
|
+
);
|
|
323
|
+
topLevelPreRenderElements.forEach((element) => {
|
|
324
|
+
let topTemplate = element.querySelector('template[shadowrootmode="open"]');
|
|
325
|
+
if (!topTemplate) {
|
|
326
|
+
return;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
// polyfill for shadowRoots
|
|
330
|
+
let polyfillScript = document.createElement("script");
|
|
331
|
+
polyfillScript.textContent = `SKShadowRootsPolyfill(document?.currentScript?.previousElementSibling)`;
|
|
332
|
+
topTemplate.after(polyfillScript);
|
|
333
|
+
});
|
|
334
|
+
}
|
|
335
|
+
{{#if supportShadowDomInPreRendering}}
|
|
336
|
+
//================================================================================
|
|
337
|
+
// PuppeteerCallback
|
|
338
|
+
//================================================================================
|
|
339
|
+
/**
|
|
340
|
+
* Waits for the callback when custom elements are defined on the document.
|
|
341
|
+
*
|
|
342
|
+
* @return {Promise} - A promise that resolves when all custom elements are defined, or if a timeout of 5 seconds occurs.
|
|
343
|
+
*/
|
|
344
|
+
async function waitForCallback() {
|
|
345
|
+
const undefinedElements = document.querySelectorAll(":not(:defined)");
|
|
346
|
+
|
|
347
|
+
const promises = [...undefinedElements]
|
|
348
|
+
.filter((button) => button.localName.startsWith("{{shadowDomCustomElementPrefix}}"))
|
|
349
|
+
.map((button) => {
|
|
350
|
+
// eslint-disable-next-line no-undef
|
|
351
|
+
return customElements.whenDefined(button.localName);
|
|
352
|
+
});
|
|
353
|
+
|
|
354
|
+
return await Promise.race([
|
|
355
|
+
Promise.all(promises),
|
|
356
|
+
new Promise((resolve) => {
|
|
357
|
+
setTimeout(resolve, 5000);
|
|
358
|
+
}),
|
|
359
|
+
]);
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
//================================================================================
|
|
363
|
+
// INLINE SCRIPTS
|
|
364
|
+
//================================================================================
|
|
365
|
+
|
|
366
|
+
/**
|
|
367
|
+
* Overrides the window.customElements.define method to add custom code before calling the original function.
|
|
368
|
+
*/
|
|
369
|
+
function overrideDefine() {
|
|
370
|
+
// Store the original function
|
|
371
|
+
// eslint-disable-next-line no-undef
|
|
372
|
+
let originalDefine = window.customElements.define;
|
|
373
|
+
|
|
374
|
+
// Create new function
|
|
375
|
+
// eslint-disable-next-line no-undef
|
|
376
|
+
window.customElements.define = function (e, o, options) {
|
|
377
|
+
// Execute your custom code
|
|
378
|
+
// Create a class for the element
|
|
379
|
+
let skProxy = class extends o {
|
|
380
|
+
constructor() {
|
|
381
|
+
super();
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
connectedCallback() {
|
|
385
|
+
if (this.hasAttribute("data-sk-static")) {
|
|
386
|
+
return;
|
|
387
|
+
}
|
|
388
|
+
// normal functionality
|
|
389
|
+
super.connectedCallback();
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
disconnectedCallback() {
|
|
393
|
+
// for sk-shadow-root don't do anything
|
|
394
|
+
if (this.hasAttribute("data-sk-static")) {
|
|
395
|
+
return;
|
|
396
|
+
}
|
|
397
|
+
// normal functionality
|
|
398
|
+
super.disconnectedCallback();
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
adoptedCallback() {
|
|
402
|
+
// for sk-shadow-root don't do anything
|
|
403
|
+
if (this.hasAttribute("data-sk-static")) {
|
|
404
|
+
return;
|
|
405
|
+
}
|
|
406
|
+
// normal functionality
|
|
407
|
+
super.adoptedCallback();
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
attributeChangedCallback(name, oldValue, newValue) {
|
|
411
|
+
// for sk-shadow-root don't do anything
|
|
412
|
+
if (this.hasAttribute("data-sk-static")) {
|
|
413
|
+
return;
|
|
414
|
+
}
|
|
415
|
+
// normal functionality
|
|
416
|
+
super.attributeChangedCallback(name, oldValue, newValue);
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
attachShadow(mode) {
|
|
420
|
+
if (this.hasAttribute("data-sk-static")) {
|
|
421
|
+
return null;
|
|
422
|
+
}
|
|
423
|
+
return super.attachShadow(mode);
|
|
424
|
+
}
|
|
425
|
+
};
|
|
426
|
+
|
|
427
|
+
// Call the original function with modified arguments
|
|
428
|
+
// eslint-disable-next-line no-undef
|
|
429
|
+
originalDefine.call(window.customElements, e, skProxy, options);
|
|
430
|
+
};
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
/**
|
|
434
|
+
* Overrides the custom element definition in the given document.
|
|
435
|
+
*
|
|
436
|
+
* @param {Document} document - The document object where the custom element is defined.
|
|
437
|
+
*
|
|
438
|
+
* @return {void} - This method does not return anything.
|
|
439
|
+
*/
|
|
440
|
+
function overrideCustomElementDefine(document) {
|
|
441
|
+
const script = document.createElement("script");
|
|
442
|
+
script.setAttribute("type", "text/javascript");
|
|
443
|
+
script.classList.add("sk-shadowroot-extension");
|
|
444
|
+
script.textContent = `(${overrideDefine.toString()})()`;
|
|
445
|
+
document.querySelector("head").appendChild(script);
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
/**
|
|
449
|
+
* Walks through pre-rendered elements and performs specific actions on each element.
|
|
450
|
+
*
|
|
451
|
+
* @param {Document} document - The HTML document object.
|
|
452
|
+
*/
|
|
453
|
+
function walkPreRenderedElements(document) {
|
|
454
|
+
const customShadowElements = getCustomShadowElements(document);
|
|
455
|
+
walkElements(customShadowElements, (element) => {
|
|
456
|
+
addStaticContextForPrerenderedElements(element);
|
|
457
|
+
// TODO extend if shadow-doms should be modified, as access to the shadow dom is not possible via querySelectors from the outside
|
|
458
|
+
});
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
/**
|
|
462
|
+
* Recursively walks through a tree of elements and applies a callback function to each element.
|
|
463
|
+
*
|
|
464
|
+
* @param {Array} elements - The array of elements to walk through. Each element should be an object with a 'children' property.
|
|
465
|
+
* @param {Function} callback - The callback function to apply to each element. It should take a single argument, which will be the current element.
|
|
466
|
+
*
|
|
467
|
+
* @return {undefined}
|
|
468
|
+
*/
|
|
469
|
+
function walkElements(elements, callback) {
|
|
470
|
+
elements.forEach((element) => {
|
|
471
|
+
callback(element);
|
|
472
|
+
if (element.children && element.children.length > 0) {
|
|
473
|
+
walkElements(element.children, callback);
|
|
474
|
+
}
|
|
475
|
+
});
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
/**
|
|
479
|
+
* Adds static context for prerendered elements.
|
|
480
|
+
*
|
|
481
|
+
* @param {Element} element The element to add static context for.
|
|
482
|
+
*/
|
|
483
|
+
function addStaticContextForPrerenderedElements(element) {
|
|
484
|
+
if (
|
|
485
|
+
"name" in element &&
|
|
486
|
+
element.name !== undefined &&
|
|
487
|
+
element.name.startsWith("{{shadowDomCustomElementPrefix}}")
|
|
488
|
+
) {
|
|
489
|
+
element.setAttribute("data-sk-static", "1");
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
{{else}}
|
|
493
|
+
{{/if}}
|
|
494
|
+
{{/if}}
|
package/oclif.manifest.json
CHANGED