@speedkit/cli 4.21.0 → 4.22.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,362 +0,0 @@
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
- }
14
-
15
- const lazyLoadList = [
16
- // TODO: adjust/replace this exemplary list
17
-
18
- // Content page's stage image:
19
- {
20
- ...lazyLoadDefaultConfig,
21
- elementSelectors: [
22
- '.image-with-text-overlay__banner',
23
- // e.g. <example-url>
24
- ],
25
- limit: 1
26
- },
27
-
28
- // PLP product images:
29
- {
30
- ...lazyLoadDefaultConfig,
31
- elementSelectors: [
32
- '.product-image__wrapper',
33
- // e.g. <example-url>
34
- ],
35
- limit: 4
36
- },
37
-
38
- // PDP hero image:
39
- {
40
- ...lazyLoadDefaultConfig,
41
- elementSelectors: [
42
- '.product-gallery__image',
43
- // e.g. <example-url>
44
- ],
45
- limit: 1
46
- },
47
- ];
48
-
49
- {{/if}}
50
- /**
51
- * @type {DomDocumentHandlerConfig}
52
- */
53
- const config = {
54
- rejectHTML({db, document, variation, url, headers}) {
55
- /* Use to configure guards to prevent e.g. error and captcha pages from being cached */
56
- {{#if isPlentymarkets}}
57
- // we don't want to cache CSR pages
58
- if (!html.includes('data-server-rendered="true"')) {
59
- return `CSR Page delivered on url ${url}`;
60
- }
61
- return false;
62
- {{else}}
63
- return false;
64
- {{/if}}
65
- },
66
- blacklistHTML({db, document, variation, url, headers}) {
67
- /* Use to configure guards to black-list e.g. specific page types */
68
- {{#if useScrapingBee}}
69
-
70
- // dynamically blacklist not 200 status codes from ScrapingBee
71
- const spbStatusCode = headers["Spb-initial-status-code"];
72
- if (spbStatusCode && spbStatusCode !== "200") {
73
- return `initial status code unequal to 200 received from scrapingbee: ${url} ${spbStatusCode}`;
74
- }
75
-
76
- {{/if}}
77
- return false;
78
- },
79
- shouldTransform({db, document, variation, url, headers}) {
80
- return true;
81
- },
82
- shouldDelayDom({db, document, variation, url, headers}) {
83
- {{#if isPlentymarkets}}
84
- return false; // Plentymarkets specific
85
- {{else}}
86
- return true;
87
- {{/if}}
88
- },
89
- async preTransform({db, document, variation, url, headers}) {
90
- // do nothing
91
- },
92
- async postTransform({db, document, variation, url, headers}) {
93
- {{#if includeServiceWorker}}
94
- injectServiceWorkerScript(document, '{{includeServiceWorker}}');
95
-
96
- {{/if}}
97
- {{#if isShopify}}
98
- // Shopify specific: inject scripts which set css variables{{#if addSSR}} (normally set on the html element, but in SSR mode they have to be set on sk-html inside the shadow DOM){{/if}}
99
- injectCSSVariableScripts(document);
100
- {{/if}}
101
- },
102
- {{#if isPlentymarkets}}
103
- stripComments: false, // Plentymarkets specific
104
- {{/if}}
105
- {{#if isShopify}}
106
- ignoreAssets: ["accelerated-checkout-backwards-compat.css"],
107
- {{else}}
108
- ignoreAssets: [],
109
- {{/if}}
110
- {{#if activateCfRocketLoaderPlugin}}
111
- handleRocketLoader: true,
112
- {{/if}}
113
- {{#if handleUsercentrics}}
114
- handleUsercentrics: true,
115
- {{/if}}
116
- {{#if isShopify}}
117
- delayScriptPath: "/apps/speed-kit/speed-kit-dom-ready.js", // Shopify specific
118
- {{else}}
119
- delayScriptPath: "/speed-kit-dom-ready.js",
120
- {{/if}}
121
- executeDeploymentDetection: true,
122
-
123
- // Advanced configuration example:
124
- /*
125
- executeDeploymentDetection: {
126
- // Execute deployment detection scoped to locale subroutes
127
- scope: true,
128
-
129
- // Domains with unhashed assets to validate via content hash comparison
130
- compareContentHashes: ['<domain>'],
131
- }
132
- */
133
-
134
- {{#if removeLazyLoading}}
135
- lazyLoadList,
136
- {{/if}}
137
- {{#if addSSR}}
138
-
139
- // SSR specific:
140
- preRender: {
141
- // Wraps pre-rendered content in a shadow DOM and runs the standard SSR
142
- // setup (style preloads, dynamic styles, declarative shadow DOM polyfill,
143
- // sk-html/sk-body wrapping, render trigger). Pre-rendered fragments are
144
- // appended to `sk-shadow-app-root > template` by default.
145
- defaultShadowRoot: true,
146
- {{#if addSSRInnerShadowDomSupport}}
147
- // Overrides `customElements.define` so the static SSR DOM is upgraded
148
- // without invoking custom element constructors (required when the
149
- // customer ships its own shadow DOMs / web components).
150
- innerShadowDOMSupport: true,
151
- {{/if}}
152
- elements: [
153
- // append all extracted styles to all pre-rendered elements (as they are capsuled in shadow doms)
154
- // TODO check if all necessary styles are extracted by this rule
155
- {
156
- selector: [
157
- "head > style:not([media='print'])",
158
- "head > link[rel='stylesheet'][href]:not([media='print'])",
159
- "body > style:not([media='print'])",
160
- "body > link[rel='stylesheet'][href]:not([media='print'])",
161
- ].join(","),
162
- },
163
- // this will be the main element to extract from the rendered DOM
164
- // TODO change selector to the main element of your page
165
- { selector: "body > #app" },
166
- ],
167
- guards: [
168
- {
169
- whenPresent: 'body > #app',
170
- waitFor: [
171
- // TODO: adjust/replace this exemplary list
172
-
173
- // PLP wait for first image to be loaded
174
- // e.g. <example-url>
175
- '.products-list-page__products-list .products-list__tiles__element:first-child img[src]',
176
- // or the no results disclaimer appears:
177
- '.products-list-page__products-list .no-results-found',
178
- ],
179
- {{#if addSSRInnerShadowDomSupport}}
180
- waitForCallback: waitForCallback.toString(),
181
- {{/if}}
182
- },
183
- ],
184
- rules: {
185
- url: [
186
- // Asset Sub-Paths on Main Domains:
187
- // TODO: identify and declare specific asset sub-paths instead of putting entire domains on the allowlist
188
- // (e.g. www.domain.de/media/css instead of www.domain.de)
189
- "{{production.host}}",
190
- {{#if staging.host}}
191
- "{{staging.host}}",
192
- {{/if}}
193
-
194
- // Additional Asset Domains:
195
- // '<domain>',
196
- ],
197
- // dynamicUrl: [
198
- // // Domains requested via POST:
199
- // '<domain>',
200
- // ],
201
- resourceType: [
202
- // only resource types listed here will be fetched
203
- "xhr",
204
- "fetch",
205
- "font",
206
- "script",
207
- "document",
208
- "stylesheet",
209
- "image",
210
- ],
211
- },
212
- },
213
- {{/if}}
214
- }
215
-
216
- const documentHandler = new DomDocumentHandler({
217
- ...config,
218
- app: APP,
219
- dynamicFetcher: DYNAMIC_FETCHER,
220
- dynamicFetcherConfig: DYNAMIC_FETCHER_CONFIG,
221
- customStyles: CUSTOM_STYLES
222
- })
223
-
224
- export const post = async (db, req, res) => {
225
- await documentHandler.transformAndSend(db, req, res);
226
- }
227
- {{#if includeServiceWorker}}
228
-
229
- /**
230
- * Injects a script into the document to override the service worker registration.
231
- *
232
- * @param {Document} document - The document object where the script will be injected.
233
- * @param {string} searchedSwPath - The path of the service worker to be overridden.
234
- */
235
- function injectServiceWorkerScript(document, searchedSwPath) {
236
- const swScript = document.createElement("script");
237
- swScript.setAttribute("id", "speed-kit-service-worker");
238
- swScript.setAttribute("type", "text/javascript");
239
- swScript.innerHTML = `
240
- var serviceWorkerRegister = ServiceWorkerContainer.prototype.register;
241
- ServiceWorkerContainer.prototype.register = function (swPath, options) {
242
- if (swPath.indexOf('${searchedSwPath}') === 0 && SpeedKit.group === 'A') {
243
- return navigator.serviceWorker.getRegistration();
244
- }
245
- return serviceWorkerRegister.call(this, swPath, options);
246
- };`;
247
-
248
- document.querySelector("head").appendChild(swScript);
249
- }
250
- {{/if}}
251
- {{#if addSSRInnerShadowDomSupport}}
252
-
253
- //================================================================================
254
- // Puppeteer Callback
255
- //================================================================================
256
-
257
- /**
258
- * Waits for the callback when custom elements are defined on the document.
259
- *
260
- * @return {Promise} - A promise that resolves when all custom elements are defined, or if a timeout of 5 seconds occurs.
261
- */
262
- async function waitForCallback() {
263
- const undefinedElements = document.querySelectorAll(":not(:defined)");
264
- // need to be defined here as they are sent to the lambda function
265
- const customElementNames = [
266
- // TODO add correct custom element names (find them by executing document.querySelectorAll(":not(:defined)"))
267
- ];
268
-
269
- const promises = [...undefinedElements]
270
- .filter((element) => customElementNames.some((name) => element.localName.includes(name)))
271
- .map((element) => {
272
- // eslint-disable-next-line no-undef
273
- return customElements.whenDefined(element.localName);
274
- });
275
-
276
- return await Promise.race([
277
- Promise.all(promises),
278
- new Promise((resolve) => {
279
- setTimeout(resolve, 5000);
280
- }),
281
- ]);
282
- }
283
- {{/if}}
284
- {{#if isShopify}}
285
-
286
- /**
287
- * Injects scripts {{#if addSSR}}into sk-html within each shadow root that set CSS custom
288
- * properties (e.g. --window-height, --announcement-bar-height, --header-height)
289
- * on sk-html instead of the document's html element.{{else}}that set CSS custom properties
290
- * (e.g. --window-height, --announcement-bar-height, --header-height) on the html element.{{/if}}
291
- *
292
- * @param document
293
- */
294
- function injectCSSVariableScripts(document) {
295
- {{#if addSSR}}
296
- const customShadowRoots = document.querySelectorAll("sk-shadow-app-root > template");
297
- for (const customShadowRoot of customShadowRoots) {
298
- const skHtml = customShadowRoot.querySelector("sk-html");
299
- if (!skHtml) continue;
300
-
301
- const script = document.createElement("script");
302
- script.textContent = `(${setCSSVariablesOnSkHtml.toString()})();`;
303
- // insert script before #main so styles are applied earlier
304
- const main = skHtml.querySelector("#main");
305
- if (main) {
306
- main.before(script);
307
- } else {
308
- skHtml.appendChild(script);
309
- }
310
- }
311
- {{else}}
312
- const script = document.createElement("script");
313
- script.textContent = `(${setCSSVariablesOnHtml.toString()})();`;
314
- // insert script before #main so styles are applied earlier
315
- const main = document.querySelector("#main");
316
- if (!main) return;
317
- main.before(script);
318
- {{/if}}
319
- }
320
-
321
- /**
322
- * Client-side script that sets CSS custom properties on {{#if addSSR}}sk-html{{else}}the html element{{/if}}
323
- * (e.g. --window-height, --announcement-bar-height, --header-height).
324
- * Injected via .toString() into the document.
325
- *
326
- * TODO: double check whether the set CSS variables are being used by the theme
327
- * and that the queried selectors are correct for this Shopify store.
328
- */
329
- /* eslint-disable no-undef -- client-side function serialised via .toString() */
330
- function {{#if addSSR}}setCSSVariablesOnSkHtml{{else}}setCSSVariablesOnHtml{{/if}}() {
331
- {{#if addSSR}}
332
- const host = document.querySelector("sk-shadow-app-root");
333
- const root = host?.shadowRoot;
334
- if (!root) return;
335
- const skHtml = root.querySelector("sk-html");
336
- if (!skHtml) return;
337
- {{else}}
338
- const html = document.querySelector("html");
339
- if (!html) return;
340
- {{/if}}
341
-
342
- requestAnimationFrame(() => {
343
- const viewportHeight = window.visualViewport ? window.visualViewport.height : document.documentElement.clientHeight;
344
- {{#if addSSR}}skHtml{{else}}html{{/if}}.style.setProperty("--window-height", viewportHeight + "px");
345
- });
346
-
347
- const announcementBar = {{#if addSSR}}root{{else}}document{{/if}}.querySelector('[id^="shopify-section"][id$="announcement-bar"]');
348
- if (announcementBar) {
349
- {{#if addSSR}}skHtml{{else}}html{{/if}}.style.setProperty("--announcement-bar-height", announcementBar.clientHeight + "px");
350
- }
351
-
352
- const headerElement = {{#if addSSR}}root{{else}}document{{/if}}.querySelector('[id^="shopify-section"][id$="header"]');
353
- if (headerElement) {
354
- const headerHeight = headerElement.clientHeight;
355
- const headerHeightWithoutBottomNav = headerElement.querySelector(".header__wrapper")?.clientHeight;
356
- {{#if addSSR}}skHtml{{else}}html{{/if}}.style.setProperty("--header-height", headerHeight + "px");
357
- if (headerHeightWithoutBottomNav != null) {
358
- {{#if addSSR}}skHtml{{else}}html{{/if}}.style.setProperty("--header-height-without-bottom-nav", headerHeightWithoutBottomNav + "px");
359
- }
360
- }
361
- }
362
- {{/if}}
@@ -1,163 +0,0 @@
1
- (function() {
2
- /**
3
- * @type {DynamicBlockConfig}
4
- */
5
- window.dynamicBlockConfig = {
6
- dynamicScriptExecution: true,
7
- {{#if addDeviceDetection}}
8
- {{#unless addSSR}}
9
- detectDevice: (doc) => {
10
- // Only if you need a DOM marker; for width checks the SK config's detectDevice suffices.
11
- // TODO: set a marker present only in the mobile variation. Read it from `doc`.
12
- return doc.querySelector('<mobile-specific-element>') ? "mobile" : "desktop";
13
- },
14
- {{/unless}}
15
- {{#if addSSR}}
16
- // Mirror config_SpeedKit.js detectDevice — keep the breakpoint identical.
17
- detectDevice: (doc) => {
18
- let width = document.documentElement.clientWidth;
19
- if (!width) return null;
20
- return width <= 820 ? "mobile" : "desktop";
21
- },
22
- {{/if}}
23
- {{/if}}
24
- blocks: [
25
- // TODO: adjust/replace this exemplary list
26
- {
27
- selector: "body",
28
- {{#unless addSSR}}
29
- detectChanges: [
30
- // Main Headline:
31
- {
32
- find: "h1",
33
- compare: ["text"],
34
- },
35
-
36
- // Top Banner:
37
- {
38
- find: ".header .top-banner",
39
- compare: ["text"],
40
- },
41
-
42
- // First-Level Items of Header/Main Navigation:
43
- {
44
- find: ".header .main-navigation .first-level-item",
45
- compare: ["text"],
46
- },
47
-
48
- // Stage Teaser:
49
- // e.g. <example-url>
50
- {
51
- find: ".stage-teaser source",
52
- compare: ["srcset"],
53
- },
54
-
55
- // PLP Items:
56
- // e.g. <example-url>
57
- {
58
- find: ".product-grid .product img",
59
- limit: 9, // only compare the first 9 products
60
- compare: ["src"],
61
- },
62
-
63
- // PDP Hero Image:
64
- // e.g. <example-url>
65
- {
66
- find: ".product-detail .slide.main img",
67
- compare: ["src"],
68
- },
69
- ],
70
- {{/unless}}
71
- },
72
- {{#if isShopify}}
73
-
74
- // Shopify specific:
75
- {
76
- selector: "#shopify-section-announcement-bar",
77
- {{#unless addSSR}}
78
- merge: false, // TODO: remove if body is not the main block
79
- {{/unless}}
80
- detectChanges: [{
81
- find: ".announcement-bar--wrapper",
82
- compare: ["text"],
83
- }],
84
- },
85
- // Cover all the other high-level Shopify sections:
86
- // TODO: activate if body is not the main block
87
- // {
88
- // selector: "body > .shopify-section[id]",
89
- // idAttribute: "id",
90
- // },
91
- {{/if}}
92
- {{#if isPlentymarkets}}
93
-
94
- // Plentymarkets specific:
95
- // cover JSON with site contents
96
- {
97
- selector: 'script[type="application/json"]',
98
- merge: false,
99
- detectChanges: [{compare: ['text']}],
100
- },
101
- {{/if}}
102
-
103
- // default entries
104
- { selector: 'link[rel="canonical"]'},
105
- { selector: 'link[rel="alternate"][hreflang]', idAttribute: "hreflang", removeIfMissing: true, appendTo: "head"},
106
- { selector: "meta[name]", idAttribute: "name"},
107
- { selector: 'meta[property]', idAttribute: "property"},
108
- { selector: 'script[type="application/ld+json"]'},
109
- ],
110
- {{#if addSSR}}
111
-
112
- // SSR specific:
113
- prerendered: [{ selector: "body > sk-shadow-app-root" }],
114
- preRenderedRootConfig: [
115
- // TODO: adjust/replace this exemplary list
116
- {
117
- selector: "sk-body, body",
118
- detectChanges: [
119
- // Main Headline:
120
- {
121
- find: "h1",
122
- compare: ["text"],
123
- },
124
-
125
- // Top Banner:
126
- {
127
- find: ".header .top-banner",
128
- compare: ["text"],
129
- },
130
-
131
- // First-Level Items of Header/Main Navigation:
132
- {
133
- find: ".header .main-navigation .first-level-item",
134
- compare: ["text"],
135
- },
136
-
137
- // Stage Teaser:
138
- // e.g. <example-url>
139
- {
140
- find: ".stage-teaser source",
141
- compare: ["srcset"],
142
- },
143
-
144
- // PLP Items:
145
- // e.g. <example-url>
146
- {
147
- find: ".product-grid .product img",
148
- limit: 9, // only compare the first 9 products
149
- compare: ["src"],
150
- },
151
-
152
- // PDP Hero Image:
153
- // e.g. <example-url>
154
- {
155
- find: ".product-detail .slide.main img",
156
- compare: ["src"],
157
- },
158
- ],
159
- },
160
- ],
161
- {{/if}}
162
- };
163
- })();
@@ -1,137 +0,0 @@
1
- @keyframes speed-kit-loading-dots {
2
- 0% {
3
- box-shadow:
4
- 8px 0 #c8c8cd,
5
- 20px 0 #dddde0,
6
- 32px 0 #ededee;
7
- }
8
- 33% {
9
- box-shadow:
10
- 8px 0 #ededee,
11
- 20px 0 #c8c8cd,
12
- 32px 0 #dddde0;
13
- }
14
- 66% {
15
- box-shadow:
16
- 8px 0 #dddde0,
17
- 20px 0 #ededee,
18
- 32px 0 #c8c8cd;
19
- }
20
- }
21
-
22
- @keyframes speed-kit-skeleton {
23
- to {
24
- background-position: calc(100% + 400px) 0;
25
- }
26
- }
27
- {{#if addSSR}}
28
-
29
- /* SSR specific - container positioning until prerendering is ready */
30
- html.speed-kit-pre-rendered:not(.sk-prerender-ready) {
31
- /* ensure full width for pre-rendered container */
32
- sk-shadow-app-root {
33
- display: block;
34
- }
35
-
36
- /* hide other body elements and prevent them from being considered LCP candidates */
37
- body > *:not(sk-shadow-app-root) {
38
- position: absolute !important;
39
- pointer-events: none !important;
40
- opacity: 0 !important;
41
- z-index: -1 !important;
42
-
43
- /* don't use display: none as it would corrupt hydrateability */
44
- /* don't use visibility: hidden as it would corrupt innerText() */
45
- }
46
- }
47
-
48
- /*
49
- SSR specific:
50
- remember to use sk-html/sk-body as additional selectors if you reference html/body to also cover the SSR case
51
- */
52
- {{/if}}
53
- {{#if withGoogleOptimize}}
54
-
55
- /* Google Optimize Flicker Fix: */
56
- {{#if addSSR}}
57
- sk-html > sk-body,
58
- {{/if}}
59
- html > body {
60
- opacity: 1 !important;
61
- }
62
- {{/if}}
63
-
64
- .speed-kit-dynamic-loading {
65
- .dummy-price-class,
66
- .dummy-availability-class {
67
- visibility: hidden;
68
- }
69
-
70
- .dummy-price-class {
71
- position: relative;
72
-
73
- &::after {
74
- content: "";
75
- width: 8px;
76
- height: 8px;
77
- display: block;
78
- position: absolute;
79
- top: calc(50% - 4px);
80
- border-radius: 50%;
81
- box-shadow:
82
- 8px 0 #c8c8cd,
83
- 20px 0 #dddde0,
84
- 32px 0 #ededee;
85
- will-change: box-shadow;
86
- animation: speed-kit-loading-dots 1.5s infinite both;
87
- visibility: visible;
88
- z-index: 1;
89
-
90
- /* --- align left --- */
91
- left: -8px;
92
-
93
- /* --- align right --- */
94
- /* left: auto; */
95
- /* right: 32px; */
96
-
97
- /* --- align center --- */
98
- /* left: calc(50% - 24px); */
99
- }
100
- }
101
-
102
- /* Skeleton loading style: */
103
- .dummy-add2cart-class,
104
- .dummy-skeleton-container-class {
105
- background-color: #ededed;
106
- background-image: linear-gradient(90deg, rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, 0.8) 50%, rgba(255, 255, 255, 0) 100%);
107
- background-repeat: no-repeat;
108
- background-size: 200px 100%;
109
- background-position: calc(-1 * 200px) 0;
110
- animation: speed-kit-skeleton 1.5s 500ms infinite; /* works best with a container width of ~300-500 px */
111
- will-change: background-position;
112
- text-indent: -9999px;
113
-
114
- > * {
115
- visibility: hidden;
116
- }
117
- }
118
- .dummy-skeleton-container-class {
119
- /* chunk skeleton background into smaller boxes: */
120
- mask-size:
121
- 100% 98px,
122
- 100% 108px,
123
- 100% 98px;
124
- mask-position:
125
- 0 0,
126
- 163px 123px,
127
- 0 259px;
128
- mask-image: linear-gradient(#000, #000), linear-gradient(#000, #000), linear-gradient(#000, #000);
129
- mask-repeat: no-repeat;
130
- }
131
-
132
- /* Disable Add2Cart: */
133
- .dummy-add2cart-class {
134
- pointer-events: none !important;
135
- filter: grayscale(1) contrast(0.333) brightness(1.5) !important; /* = grayscale + blending 50% with white */
136
- }
137
- }