@speedkit/cli 4.21.0 → 4.22.1
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 +22 -0
- package/README.md +7 -2
- package/dist/commands/generate-customer-config.d.ts +2 -0
- package/dist/commands/generate-customer-config.js +10 -2
- package/dist/services/bundler/bundle-service.d.ts +12 -0
- package/dist/services/bundler/bundle-service.js +28 -1
- package/dist/services/bundler/bundle-service.spec.d.ts +1 -0
- package/dist/services/bundler/bundle-service.spec.js +54 -0
- package/dist/services/customer-config/customer-config-service-context.d.ts +9 -1
- package/dist/services/customer-config/customer-config-service-context.js +11 -1
- package/dist/services/customer-config/customer-config-service-model.d.ts +6 -0
- package/dist/services/customer-config/customer-config-service-model.js +36 -0
- package/dist/services/customer-config/customer-config-service.d.ts +8 -3
- package/dist/services/customer-config/customer-config-service.js +71 -25
- package/dist/services/customer-config/template-source.d.ts +33 -0
- package/dist/services/customer-config/template-source.js +49 -0
- package/dist/services/integration-api/integration-api-model.js +3 -1
- package/oclif.manifest.json +20 -1
- package/package.json +8 -1
- package/dist/services/customer-config/templates/config_SpeedKit.js.hbs +0 -548
- package/dist/services/customer-config/templates/config_customer.json.hbs +0 -38
- package/dist/services/customer-config/templates/config_documentHandler.js.hbs +0 -362
- package/dist/services/customer-config/templates/config_dynamicBlocks.es6.hbs +0 -163
- package/dist/services/customer-config/templates/config_dynamicStyles.css.hbs +0 -137
- package/dist/services/customer-config/templates/config_loadHandler.js.hbs +0 -391
- package/dist/services/customer-config/templates/downtimeDetection.js.hbs +0 -209
|
@@ -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
|
-
}
|