@speedkit/cli 2.66.0-alpha.1 → 2.67.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,10 +1,37 @@
1
- # [2.66.0-alpha.1](https://gitlab.orestes.info/baqend/speed-kit-cli/compare/v2.65.0...v2.66.0-alpha.1) (2024-11-01)
1
+ # [2.67.0](https://gitlab.orestes.info/baqend/speed-kit-cli/compare/v2.66.0...v2.67.0) (2024-12-10)
2
2
 
3
3
 
4
4
  ### Features
5
5
 
6
- * allow prerelease from alpha and beta branch ([365e480](https://gitlab.orestes.info/baqend/speed-kit-cli/commit/365e480347a9efadc764a1aa6c907908259338d6))
7
- * embed devtools ([def9d97](https://gitlab.orestes.info/baqend/speed-kit-cli/commit/def9d97f686b905e6db8d9db2df3510493bd00e2))
6
+ * **customer-config:** add currency verification todo ([96d09df](https://gitlab.orestes.info/baqend/speed-kit-cli/commit/96d09df4610a7b2ebe2ceeabef5a00bf0cbeab38))
7
+
8
+ # [2.66.0](https://gitlab.orestes.info/baqend/speed-kit-cli/compare/v2.65.3...v2.66.0) (2024-12-05)
9
+
10
+
11
+ ### Features
12
+
13
+ * **wizard:** add hint which lazy-removal option to use based on use-case ([daba081](https://gitlab.orestes.info/baqend/speed-kit-cli/commit/daba0817180dc8d63630d84fc1a634067c0967ec))
14
+
15
+ ## [2.65.3](https://gitlab.orestes.info/baqend/speed-kit-cli/compare/v2.65.2...v2.65.3) (2024-11-13)
16
+
17
+
18
+ ### Bug Fixes
19
+
20
+ * **onboarding:** live update styles ([648c145](https://gitlab.orestes.info/baqend/speed-kit-cli/commit/648c14557b1d82235e93f609cdf46d443f7972db))
21
+
22
+ ## [2.65.2](https://gitlab.orestes.info/baqend/speed-kit-cli/compare/v2.65.1...v2.65.2) (2024-11-12)
23
+
24
+
25
+ ### Bug Fixes
26
+
27
+ * **speed-kit-cli:** missing opening tag in force install ([dbee840](https://gitlab.orestes.info/baqend/speed-kit-cli/commit/dbee840057c2ebbd00d032bf482297592025a818))
28
+
29
+ ## [2.65.1](https://gitlab.orestes.info/baqend/speed-kit-cli/compare/v2.65.0...v2.65.1) (2024-11-08)
30
+
31
+
32
+ ### Bug Fixes
33
+
34
+ * **onboarding:** remove reference to defaultLocalDevtools ([c2c3128](https://gitlab.orestes.info/baqend/speed-kit-cli/commit/c2c3128069169488a2cc5e1da85ea77676f55e8e))
8
35
 
9
36
  # [2.65.0](https://gitlab.orestes.info/baqend/speed-kit-cli/compare/v2.64.0...v2.65.0) (2024-11-01)
10
37
 
package/README.md CHANGED
@@ -21,7 +21,7 @@ $ npm install -g @speedkit/cli
21
21
  $ sk COMMAND
22
22
  running command...
23
23
  $ sk (--version)
24
- @speedkit/cli/2.66.0-alpha.1 linux-x64 node-v20.18.0
24
+ @speedkit/cli/2.67.0 linux-x64 node-v20.18.1
25
25
  $ sk --help [COMMAND]
26
26
  USAGE
27
27
  $ sk COMMAND
@@ -353,7 +353,7 @@ DESCRIPTION
353
353
  Display help for sk.
354
354
  ```
355
355
 
356
- _See code: [@oclif/plugin-help](https://github.com/oclif/plugin-help/blob/v6.2.3/src/commands/help.ts)_
356
+ _See code: [@oclif/plugin-help](https://github.com/oclif/plugin-help/blob/v6.2.16/src/commands/help.ts)_
357
357
 
358
358
  ## `sk login [APP]`
359
359
 
@@ -2,6 +2,9 @@
2
2
  {{#if removeLazyLoading}}
3
3
  const lazyLoadDefaultConfig = {
4
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
5
8
  {find: 'data-src', set: 'src'},
6
9
  {find: 'data-srcset', set: 'srcset'},
7
10
  ],
@@ -1,43 +1,9 @@
1
1
  (function() {
2
- {{#if addPreRendering}}
3
- {{#unless supportShadowDomInPreRendering}}
4
- const PRERENDER_TIMEOUT = 30_000; // 30s
5
- const PRERENDER_CHECK_INTERVAL = 300; // 300ms
6
- const preRenderedRootConfig = [
7
- // TODO: adjust/replace this exemplary list
8
- {
9
- selector: "body > #app, #speed-kit-pre-rendered-app",
10
- detectChanges: [
11
- {
12
- // header area
13
- find: "#page-root > div > .aem-container > div:nth-of-type(1)",
14
- detectChanges: [
15
- // main categories
16
- {
17
- find: 'button[id^="menu-button"] p',
18
- compare: ["text"],
19
- },
20
- // logo
21
- {
22
- find: "img[src]",
23
- compare: ["src"],
24
- },
25
- ],
26
- },
27
- ],
28
- },
29
- ];
30
-
31
- {{/unless}}
32
- {{/if}}
33
2
  /**
34
3
  * @type {DynamicBlockConfig}
35
4
  */
36
5
  window.dynamicBlockConfig = {
37
- {{#if isShopify}}
38
- dynamicScriptExecution: true, // Shopify specific
39
- // => as some async external scripts are injected more or less randomly into the html, destroying the order of local & remote scripts
40
- {{/if}}
6
+ dynamicScriptExecution: true,
41
7
  {{#if addPreRendering}}
42
8
  assetChangeDetection: {
43
9
  ignore: [
@@ -62,31 +28,30 @@
62
28
  {{/unless}}
63
29
  {{/if}}
64
30
  // TODO: adjust/replace this exemplary list
65
-
66
31
  {
67
32
  {{#if addPreRendering}}
68
- selector: '#main',
33
+ selector: ".prerendered-top-level-element",
69
34
  {{else}}
70
- selector: 'body',
35
+ selector: "body",
71
36
  {{/if}}
72
37
  detectChanges: [
73
38
  // Main Headline:
74
39
  {
75
- find: 'h1',
76
- compare: ['text'],
40
+ find: "h1",
41
+ compare: ["text"],
77
42
  },
78
43
 
79
44
  // Home:
80
45
  {
81
- find: '.stage-teaser source',
82
- compare: ['srcset'],
46
+ find: ".stage-teaser source",
47
+ compare: ["srcset"],
83
48
  },
84
49
 
85
50
  // PLP:
86
51
  {
87
- find: '.product-grid .product img',
52
+ find: ".product-grid .product img",
88
53
  limit: 9, // only compare the first 9 products
89
- compare: ['src'],
54
+ compare: ["src"],
90
55
  },
91
56
  // ...
92
57
 
@@ -95,7 +60,6 @@
95
60
  ],
96
61
  },
97
62
  {{#if isShopify}}
98
-
99
63
  // Shopify specific:
100
64
  {
101
65
  selector: "#shopify-section-announcement-bar",
@@ -135,11 +99,11 @@
135
99
 
136
100
  // Workaround for Cloudflare's Rocket Loader (e.g. needed for Shopify)
137
101
  {
138
- selector: 'script[data-cf-settings]',
102
+ selector: "script[data-cf-settings]",
139
103
  mergeFunction: function (localBlock, remoteBlock) {
140
- let cFHash = remoteBlock.getAttribute('data-cf-settings');
141
- localBlock.setAttribute('data-cf-settings', cFHash);
142
- let deferScripts = document.querySelectorAll('script[src][defer]');
104
+ let cFHash = remoteBlock.getAttribute("data-cf-settings");
105
+ localBlock.setAttribute("data-cf-settings", cFHash);
106
+ let deferScripts = document.querySelectorAll("script[src][defer]");
143
107
  deferScripts.forEach((deferScript) => {
144
108
  let type = deferScript.type;
145
109
  type = type.replace(/[a-f0-9]{24}/, cFHash.split('-')[0]);
@@ -150,327 +114,36 @@
150
114
  {{/if}}
151
115
 
152
116
  // default entries
153
- {selector: 'link[rel="canonical"]'},
154
- {selector: 'meta[name]', idAttribute: 'name'},
155
- {selector: 'meta[property]', idAttribute: 'property'},
156
- {selector: 'script[type="application/ld+json"]'},
117
+ { selector: 'link[rel="canonical"]'},
118
+ { selector: "meta[name]", idAttribute: "name"},
119
+ { selector: 'meta[property]', idAttribute: "property"},
120
+ { selector: 'script[type="application/ld+json"]'},
157
121
  ],
158
- };
159
-
160
- //================================================================================
161
- // Utility Functions
162
- //================================================================================
163
-
164
- {{#if addPreRendering}}
165
- {{#if supportShadowDomInPreRendering}}
166
- const preRenderedRootConfig = [
167
- // TODO: adjust/replace this exemplary list
168
- // TODO: accessing elements within a shadow-dom, nested detectChanges is needed on that level
169
- {
170
- selector: ".sk-container",
171
- remote: "body > #app",
122
+ {{#if addPreRendering}}
123
+ preRenderedRootConfig: [
124
+ // TODO: adjust/replace this exemplary list
125
+ {
126
+ selector: ".prerendered-top-level-element",
172
127
  detectChanges: [
173
128
  {
174
- find: "tef-carousel",
129
+ // header area
130
+ find: "#page-root > div > .aem-container > div:nth-of-type(1)",
175
131
  detectChanges: [
132
+ // main categories
133
+ {
134
+ find: 'button[id^="menu-button"] p',
135
+ compare: ["text"],
136
+ },
137
+ // logo
176
138
  {
177
- find: ".slide tef-stage-teaser",
178
- detectChanges: [
179
- {
180
- find: '[slot="visual"] source',
181
- compare: ["srcset"],
182
- },
183
- {
184
- find: '[slot="overline"]',
185
- compare: ["text"],
186
- },
187
- ],
139
+ find: "img[src]",
140
+ compare: ["src"],
188
141
  },
189
142
  ],
190
143
  },
191
- ],
192
- },
193
- ];
194
-
195
- document.addEventListener("dynamic-fetcher-data-ready", function () {
196
- setTimeout(() => {
197
- registerRemoteRenderObserver();
198
- }, 500);
199
- });
200
-
201
- function registerRemoteRenderObserver() {
202
- window?.performance?.mark("sk-prerender-observe-start");
203
-
204
- let promises = [];
205
- for (const preRenderedRoot of preRenderedRootConfig) {
206
- const remoteSelector = preRenderedRoot.remote;
207
- const remoteRootElement = document.querySelector(remoteSelector);
208
- if (!remoteRootElement) {
209
- continue;
210
- }
211
- promises = [...promises, ...collectWaitForEvents(remoteRootElement)];
212
- }
213
- removePlaceholderWhenResolved(promises);
214
- }
215
-
216
- function collectWaitForEvents(remoteRootElement) {
217
- const undefinedElements =
218
- remoteRootElement.querySelectorAll(":not(:defined)");
219
- return [...undefinedElements]
220
- .filter((element) => element.localName.startsWith("{{shadowDomCustomElementPrefix}}"))
221
- .map((element) => {
222
- return customElements.whenDefined(element.localName);
223
- });
224
- }
225
-
226
- /**
227
- * checks for all webComponents to be ready before switching to origin
228
- *
229
- * @param remoteRootElement
230
- */
231
- function removePlaceholderWhenResolved(promises) {
232
- const preRenderedPlaceHolders = document.querySelectorAll(
233
- ".speed-kit-shadow-app-root",
234
- );
235
- Promise.race([
236
- Promise.all(promises),
237
- new Promise((resolve) => {
238
- setTimeout(resolve, 5000);
239
- }),
240
- ])
241
- .catch((error) => {})
242
- .then(() => {
243
- try {
244
- detectChanges();
245
- } catch (error) {}
246
- preRenderedPlaceHolders.forEach((localRootElement) =>
247
- localRootElement.remove(),
248
- );
249
- dispatchReadyEvent(window.dynamicBlockConfig.preRenderWaitFor);
250
- });
251
- }
252
- {{else}}
253
- function handlePreRenderBlock(localBlock, remoteBlock) {
254
- // handle preRendering by inserting pre-rendered #app before initial #app
255
- if (
256
- window.dynamicBlockConfig.preRenderWaitFor &&
257
- window.dynamicBlockConfig.preRenderWaitFor.length > 0
258
- ) {
259
- localBlock.classList.add("speed-kit-shadow-app-root");
260
- localBlock.before(remoteBlock);
261
- return;
262
- }
263
- SpeedKit.replaceBlock(localBlock, remoteBlock);
264
- }
265
-
266
- document.addEventListener("dynamic-fetcher-data-ready", () => {
267
- function registerRemoteRenderObserver() {
268
- window?.performance?.mark("sk-prerender-observe-start");
269
-
270
- for (const preRenderedRoot of preRenderedRootConfig) {
271
- const selector = preRenderedRoot.selector
272
- .split(",")
273
- .map((selector) => {
274
- return selector + ".speed-kit-pre-rendered-block-added";
275
- })
276
- .join(",");
277
-
278
- const localRootElement = document.querySelector(selector);
279
-
280
- if (!localRootElement) {
281
- continue;
282
- }
283
- removePlaceholder(localRootElement);
284
- }
285
- }
286
- registerRemoteRenderObserver();
287
- });
288
-
289
- /**
290
- * checks for all webComponents to be ready before switching to origin
291
- *
292
- * @param localRootElement
293
- */
294
- function removePlaceholder(localRootElement) {
295
- let waitFor = [];
296
- for (let selector of window.dynamicBlockConfig.preRenderWaitFor) {
297
- waitFor.push(
298
- querySelectorWithRetry(
299
- document,
300
- selector,
301
- PRERENDER_CHECK_INTERVAL,
302
- Math.floor(PRERENDER_TIMEOUT / PRERENDER_CHECK_INTERVAL),
303
- ),
304
- );
305
- }
306
-
307
- const promises = [...waitFor];
308
-
309
- Promise.race([
310
- Promise.all(promises),
311
- new Promise((resolve) => {
312
- setTimeout(resolve, PRERENDER_TIMEOUT);
313
- }),
314
- ])
315
- .catch((error) => {})
316
- .then(() => {
317
- try {
318
- detectChanges(
319
- localRootElement,
320
- window.dynamicBlockConfig.preRenderWaitFor,
321
- );
322
- } catch (error) {}
323
- localRootElement.remove();
324
- dispatchReadyEvent(window.dynamicBlockConfig.preRenderWaitFor);
325
- });
326
- }
327
-
328
- async function sleep(interval = 100) {
329
- return new Promise((resolve) => setTimeout(resolve, interval));
330
- }
331
-
332
- /**
333
- * retry searching for a given selector in the actual app root
334
- * @param root
335
- * @param selector
336
- * @param retryInterval
337
- * @param maxAttempts
338
- * @returns {Promise<*>}
339
- */
340
- async function querySelectorWithRetry(
341
- root,
342
- selector,
343
- retryInterval = 100,
344
- maxAttempts = 50,
345
- ) {
346
- for (let i = 0; i < maxAttempts; i++) {
347
- const element = root.querySelector(selector);
348
- if (element) {
349
- return element;
350
- }
351
- await sleep(retryInterval);
352
- }
353
- }
354
- {{/if}}
355
- /**
356
- * dispatch prerender ready event after shadow app root was removed
357
- */
358
- function dispatchReadyEvent(preRenderWaitForElement) {
359
- window?.performance?.mark("sk-prerender-observe-end");
360
- document.dispatchEvent(
361
- new CustomEvent("speed-kit-prerender-ready", {
362
- detail: { element: preRenderWaitForElement },
363
- }),
364
- );
365
- document.querySelector("html").classList.add("sk-prerender-ready");
366
- }
367
-
368
- function detectChanges() {
369
- window?.performance?.mark("sk-prerender-change-detection-start");
370
-
371
- for (const config of preRenderedRootConfig) {
372
- const localRootElement = document.querySelector(
373
- `${config.selector}.speed-kit-pre-rendered-block-added`,
374
- );
375
- const remoteRootElement = document.querySelector(
376
- `${config.selector}:not(.speed-kit-pre-rendered-block-added)`,
377
- );
378
-
379
- if (!localRootElement || !remoteRootElement) {
380
- continue;
381
- }
382
- if (config.detectChanges && config.detectChanges.length > 0) {
383
- for (const detect of config.detectChanges) {
384
- detectChangesInBlock(localRootElement, remoteRootElement, detect);
385
- }
386
- }
387
- }
388
- window?.performance?.mark("sk-prerender-change-detection-end");
389
- }
390
-
391
- function detectChangesInBlock(
392
- parentLocalElement,
393
- parentRemoteElement,
394
- config,
395
- ) {
396
- const localRootElement = parentLocalElement.querySelector(config.find);
397
- const remoteRootElement = parentRemoteElement.querySelector(config.find);
398
-
399
- if (!localRootElement) {
400
- return;
401
- }
402
-
403
- if (!remoteRootElement) {
404
- sendRevalidation(config.find, "remote block not found");
405
- }
406
-
407
- // on next subElement search for changes
408
- if (config.detectChanges && config.detectChanges.length > 0) {
409
- for (const detect of config.detectChanges) {
410
- detectChangesInBlock(localRootElement, remoteRootElement, detect);
411
- }
412
- return;
413
- }
414
-
415
- if (!config.compare || config.compare.length === 0) {
416
- return;
417
- }
418
-
419
- for (const compare of config.compare) {
420
- if (
421
- compare === "text" &&
422
- localRootElement.innerHTML !== remoteRootElement.innerHTML
423
- ) {
424
- sendRevalidation(config.find, "content diffs");
425
- continue;
426
- }
427
-
428
- if (
429
- localRootElement.hasAttribute(compare) &&
430
- remoteRootElement.hasAttribute(compare) &&
431
- localRootElement.getAttribute(compare) !==
432
- remoteRootElement.getAttribute(compare)
433
- ) {
434
- sendRevalidation(config.find, `${compare} diffs`);
435
- }
436
- }
437
- }
438
-
439
- /**
440
- * @param selector {string}
441
- * @param reason {string}
442
- */
443
- function sendRevalidation(selector, reason) {
444
- let baqendAssetUrl = location.href;
445
- let userId = window.SpeedKit.userId || "";
446
- let sessionId = window.SpeedKit.sessionId || "";
447
- let navigateId = window.SpeedKit.navigateId;
448
-
449
- let detectedChanges = {
450
- url: baqendAssetUrl,
451
- triggeredBy: { [selector]: [reason] },
452
- };
453
-
454
- let beaconUrl =
455
- "https://" +
456
- window.dynamicBlockConfig.appName +
457
- ".app.baqend.com/v1/rum/pi";
458
- let data = {
459
- piId: navigateId,
460
- userId: userId,
461
- sessionId: sessionId,
462
- changeDetection: detectedChanges,
463
- debug: true,
464
- };
465
-
466
- if (navigator.sendBeacon) {
467
- navigator.sendBeacon(beaconUrl, JSON.stringify(data));
468
- return;
469
- }
470
-
471
- let xhr = new XMLHttpRequest();
472
- xhr.open("POST", beaconUrl, true);
473
- xhr.send(JSON.stringify(data));
474
- }
475
- {{/if}}
144
+ ],
145
+ },
146
+ ],
147
+ {{/if}}
148
+ };
476
149
  })();
@@ -26,7 +26,10 @@ import { PredictivePreloading } from 'predictive-preloading/predictivePreloading
26
26
  new HistoryApiPlugin(), // Soft Navigation Tracking using History API
27
27
  {{/if}}
28
28
  {{#if useGATracking}}
29
- new GAEcommerceTrackingPlugin({ defaultCurrency: "EUR" }),
29
+ new GAEcommerceTrackingPlugin({
30
+ // TODO: verify default currency
31
+ defaultCurrency: "EUR"
32
+ }),
30
33
  {{/if}}
31
34
 
32
35
  {{#if addPreRendering}}
@@ -221,6 +224,7 @@ import { PredictivePreloading } from 'predictive-preloading/predictivePreloading
221
224
  // for (var i = 0; i < ecommerce.add.products.length; i++) {
222
225
  // aTCProductsArray.push({
223
226
  // id: ecommerce.add.products[i].id ? ecommerce.add.products[i].id : location.href,
227
+ // // TODO: verify default currency
224
228
  // currencyCode: currencyCode ? currencyCode : 'EUR',
225
229
  // price: Number(ecommerce.add.products[i].price),
226
230
  // quantity: ecommerce.add.products[i].quantity ? ecommerce.add.products[i].quantity : 1,
@@ -251,6 +255,7 @@ import { PredictivePreloading } from 'predictive-preloading/predictivePreloading
251
255
  // var orderId = purchase.actionField.id;
252
256
  // var orderValue = String(purchase.actionField.revenue).replace(',', '.');
253
257
  // return {
258
+ // // TODO: verify currency
254
259
  // id: String(orderId), currencyCode: 'EUR', revenue: Number(orderValue),
255
260
  // };
256
261
  // },
@@ -6,6 +6,5 @@ export declare class ExtensionValidator {
6
6
  private cli;
7
7
  constructor(cliConfig: UserCliConfig, cliBasePath: string, cli: CliServiceInterface);
8
8
  getValidBrowserExtensionPaths(): Promise<string[]>;
9
- private loadEmbeddedDevtoolsExtension;
10
9
  private getManifestConfig;
11
10
  }
@@ -8,7 +8,6 @@ const cli_1 = require("../../../cli");
8
8
  const safe_1 = require("../../../../helpers/safe");
9
9
  const node_fs_1 = require("node:fs");
10
10
  const browser_extension_error_1 = require("../../error/browser-extension-error");
11
- const SPEED_KIT_EXTENSION_NAME = "Speed Kit DevTools Extension";
12
11
  class ExtensionValidator {
13
12
  cliConfig;
14
13
  cliBasePath;
@@ -21,7 +20,6 @@ class ExtensionValidator {
21
20
  async getValidBrowserExtensionPaths() {
22
21
  const validExtensions = [];
23
22
  const validPaths = [];
24
- let addIncludedExtension = true;
25
23
  this.cli.startAction("resolve browserExtensions");
26
24
  const browserExtensions = this.cliConfig.chromeExtensionPaths
27
25
  .split(",")
@@ -34,9 +32,6 @@ class ExtensionValidator {
34
32
  if (!manifestConfig) {
35
33
  continue;
36
34
  }
37
- if (manifestConfig.name.includes(SPEED_KIT_EXTENSION_NAME)) {
38
- addIncludedExtension = false;
39
- }
40
35
  validExtensions.push({
41
36
  path: cleanedExtensionPath,
42
37
  name: manifestConfig.name,
@@ -44,29 +39,12 @@ class ExtensionValidator {
44
39
  });
45
40
  validPaths.push(cleanedExtensionPath);
46
41
  }
47
- if (addIncludedExtension) {
48
- await this.loadEmbeddedDevtoolsExtension(validExtensions, validPaths);
49
- }
50
42
  this.cli.endAction(cli_1.CliActionStatus.COMPLETED);
51
43
  if (validExtensions.length > 0) {
52
44
  this.cli.table(validExtensions, { name: {}, version: {}, path: {} });
53
45
  }
54
46
  return validPaths;
55
47
  }
56
- async loadEmbeddedDevtoolsExtension(validExtensions, validPaths) {
57
- const defaultDevelopmentToolsExtensionPath = node_path_1.default.resolve(this.cliBasePath, "dist", "devtools");
58
- const manifestConfigResult = await (0, safe_1.safe)(this.getManifestConfig(defaultDevelopmentToolsExtensionPath));
59
- if (manifestConfigResult.success !== true) {
60
- this.cli.writeError("could not find manifestFile for embedded extension");
61
- return;
62
- }
63
- validExtensions.push({
64
- path: defaultDevelopmentToolsExtensionPath,
65
- name: manifestConfigResult.data.name + "[embedded]",
66
- version: manifestConfigResult.data.version,
67
- });
68
- validPaths.push(defaultDevelopmentToolsExtensionPath);
69
- }
70
48
  async getManifestConfig(cleanedExtensionPath) {
71
49
  const manifestPath = node_path_1.default.resolve(cleanedExtensionPath, "manifest.json");
72
50
  if (!(0, node_fs_1.existsSync)(manifestPath)) {
@@ -113,9 +113,19 @@ class CustomerDomainDocumentResponse {
113
113
  }
114
114
  rewriteInstallResource(text) {
115
115
  text = text.replace(onboarding_model_1.SpeedKitInstallRegex, "");
116
- return text.replace(/<\s*head\b[^>]*>/, (head) => {
117
- return `${head}\n<script src="${this.customerConfig.installPath}" ${this.customerConfig.installParams}></script>`;
118
- });
116
+ const matchOpeningHead = /<\s*head\b[^>]*>/;
117
+ const matchClosingHead = /<\/\s*head\s*>/;
118
+ if (matchOpeningHead.test(text)) {
119
+ return text.replace(matchOpeningHead, (head) => {
120
+ return `${head}\n<script src="${this.customerConfig.installPath}" ${this.customerConfig.installParams}></script>`;
121
+ });
122
+ }
123
+ if (matchClosingHead.test(text)) {
124
+ return text.replace(matchClosingHead, (head) => {
125
+ return `<script src="${this.customerConfig.installPath}" ${this.customerConfig.installParams}></script>${head}\n`;
126
+ });
127
+ }
128
+ return text;
119
129
  }
120
130
  rewriteHtmlToLocalConfig(html) {
121
131
  const DF_CONFIG_PATTERN = /(<script[^>]* id="speed-kit-df-config"[^>]*>)([\S\s]*?)(<\/script>)/g;
@@ -15,14 +15,89 @@ export declare class FileWatcher {
15
15
  private browserContext;
16
16
  private cache;
17
17
  constructor(cli: CliService, customerConfig: CustomerConfig, files: FileListInterface, documentHandler: DocumentHandlerServer, browserContext: BrowserContext, cache: Cache);
18
+ /**
19
+ * will perform various actions based on the changed file
20
+ *
21
+ * @param page
22
+ * @param serviceWorkerReinstallCallback
23
+ */
18
24
  addCallbackOnFileChange(page: Page, serviceWorkerReinstallCallback: (file: ConfigFileInterface) => Promise<void>): void;
25
+ /**
26
+ * changes on these files will trigger
27
+ * - a rebuild of the serviceWorker
28
+ * - a rebuild of the installResource
29
+ * - currentService worker will be unregistered
30
+ * - onNextNavigate the new serviceWorker will be installed
31
+ *
32
+ * @param page
33
+ * @param installResource
34
+ * @param serviceWorkerReinstallCallback
35
+ * @private
36
+ */
19
37
  private handleServiceWorkerDependencies;
38
+ /**
39
+ * changes on these files will trigger
40
+ * - rebuild of the local documentHandler
41
+ * - clearCache in browser
42
+ * - clearCache in local documentHandler
43
+ *
44
+ * @param page
45
+ * @param installResource
46
+ * @private
47
+ */
20
48
  private handleGenericConfigFiles;
49
+ /**
50
+ * changes on these file will trigger:
51
+ * - rebuild of documentHandler
52
+ * - cacheClear of local documentHandler
53
+ * - cacheClear in browser
54
+ * - live exchange of the styles in the currently opened page
55
+ *
56
+ * @param page
57
+ * @private
58
+ */
21
59
  private handleDynamicStyles;
60
+ /**
61
+ * fileWatcherCallback that will reinstall the serviceWorker
62
+ *
63
+ * @param file
64
+ * @param page
65
+ * @param installResource
66
+ * @param serviceWorkerReinstallCallback
67
+ * @private
68
+ */
22
69
  private onChangeServiceWorkerCallback;
23
- private updateStyles;
70
+ /**
71
+ * fileWatcherCallback that will rebuild the local documentHandler
72
+ *
73
+ * @param page
74
+ * @param installResource
75
+ * @param file
76
+ * @private
77
+ */
24
78
  private onChangeConfigFileCallback;
79
+ /**
80
+ * fileWatcherCallback that will reinstall the serviceWorker
81
+ *
82
+ * @param file
83
+ * @param page
84
+ * @private
85
+ */
25
86
  private onChangeDynamicStyleCallback;
26
- private clearPageFunction;
27
- private clearServiceWorker;
87
+ /**
88
+ * clear speed-kit-caches in browser without interrupting the current workflow on error
89
+ *
90
+ * @param page
91
+ * @private
92
+ */
93
+ private safeClearPageBrowsersCacheStorage;
94
+ /**
95
+ * clear speed-kit-caches in browser
96
+ *
97
+ * @param page
98
+ * @private
99
+ */
100
+ private clearPageBrowsersCacheStorage;
101
+ private unregisterServiceWorker;
102
+ private updateStylesInlineFunction;
28
103
  }
@@ -4,6 +4,7 @@ exports.FileWatcher = void 0;
4
4
  const safe_1 = require("../../../helpers/safe");
5
5
  const integration_api_1 = require("../../integration-api");
6
6
  const files_1 = require("../../../models/files");
7
+ const race_1 = require("../../../helpers/race");
7
8
  /**
8
9
  * Add different callbacks that will be executed if a local file is changed.
9
10
  */
@@ -22,12 +23,30 @@ class FileWatcher {
22
23
  this.browserContext = browserContext;
23
24
  this.cache = cache;
24
25
  }
26
+ /**
27
+ * will perform various actions based on the changed file
28
+ *
29
+ * @param page
30
+ * @param serviceWorkerReinstallCallback
31
+ */
25
32
  addCallbackOnFileChange(page, serviceWorkerReinstallCallback) {
26
33
  const installResource = this.files.getByName(files_1.INTEGRATION_FILES.BUILD.INSTALL);
27
34
  this.handleServiceWorkerDependencies(page, installResource, serviceWorkerReinstallCallback);
28
35
  this.handleGenericConfigFiles(page, installResource);
29
36
  this.handleDynamicStyles(page);
30
37
  }
38
+ /**
39
+ * changes on these files will trigger
40
+ * - a rebuild of the serviceWorker
41
+ * - a rebuild of the installResource
42
+ * - currentService worker will be unregistered
43
+ * - onNextNavigate the new serviceWorker will be installed
44
+ *
45
+ * @param page
46
+ * @param installResource
47
+ * @param serviceWorkerReinstallCallback
48
+ * @private
49
+ */
31
50
  handleServiceWorkerDependencies(page, installResource, serviceWorkerReinstallCallback) {
32
51
  // add watcher to serviceWorker related files
33
52
  const serviceWorkerDependencies = [
@@ -52,6 +71,16 @@ class FileWatcher {
52
71
  });
53
72
  }
54
73
  }
74
+ /**
75
+ * changes on these files will trigger
76
+ * - rebuild of the local documentHandler
77
+ * - clearCache in browser
78
+ * - clearCache in local documentHandler
79
+ *
80
+ * @param page
81
+ * @param installResource
82
+ * @private
83
+ */
55
84
  handleGenericConfigFiles(page, installResource) {
56
85
  // add watcher to configFiles
57
86
  const generalConfigFiles = [
@@ -76,6 +105,16 @@ class FileWatcher {
76
105
  });
77
106
  }
78
107
  }
108
+ /**
109
+ * changes on these file will trigger:
110
+ * - rebuild of documentHandler
111
+ * - cacheClear of local documentHandler
112
+ * - cacheClear in browser
113
+ * - live exchange of the styles in the currently opened page
114
+ *
115
+ * @param page
116
+ * @private
117
+ */
79
118
  handleDynamicStyles(page) {
80
119
  if (!this.files.hasFile(files_1.INTEGRATION_FILES.CONFIG.DYNAMIC_STYLES)) {
81
120
  return;
@@ -90,58 +129,94 @@ class FileWatcher {
90
129
  }
91
130
  });
92
131
  }
132
+ /**
133
+ * fileWatcherCallback that will reinstall the serviceWorker
134
+ *
135
+ * @param file
136
+ * @param page
137
+ * @param installResource
138
+ * @param serviceWorkerReinstallCallback
139
+ * @private
140
+ */
93
141
  async onChangeServiceWorkerCallback(file, page, installResource, serviceWorkerReinstallCallback) {
94
142
  this.cli.writeWarning(`changed file: ${file.name} (reinstall serviceWorker)`);
95
143
  // clear speed-kit-service-worker-caches and uninstall serviceWorker
96
- await this.clearPageFunction(page);
97
- await this.clearServiceWorker(page);
144
+ await this.safeClearPageBrowsersCacheStorage(page);
145
+ await this.unregisterServiceWorker(page);
98
146
  // rebuild installResource
99
147
  await installResource.buildContent();
100
148
  // tell puppeteer to reinstall serviceworker on next navigate
101
149
  await serviceWorkerReinstallCallback(file);
102
150
  }
103
- updateStyles(styles) {
104
- const styleElement = document.querySelector("#speed-kit-df-styles");
105
- if (styleElement) {
106
- styleElement.textContent = decodeURI(styles);
107
- }
108
- }
151
+ /**
152
+ * fileWatcherCallback that will rebuild the local documentHandler
153
+ *
154
+ * @param page
155
+ * @param installResource
156
+ * @param file
157
+ * @private
158
+ */
109
159
  async onChangeConfigFileCallback(page, installResource, file) {
110
- await this.clearPageFunction(page);
160
+ this.cli.writeWarning(`changed file: ${file.name}`);
161
+ await this.safeClearPageBrowsersCacheStorage(page);
111
162
  await this.documentHandler.buildDocumentHandler();
112
163
  this.cache.clear();
113
164
  await installResource.buildContent();
114
- this.cli.writeWarning(`changed file: ${file.name}`);
115
165
  }
166
+ /**
167
+ * fileWatcherCallback that will reinstall the serviceWorker
168
+ *
169
+ * @param file
170
+ * @param page
171
+ * @private
172
+ */
116
173
  async onChangeDynamicStyleCallback(file, page) {
117
- this.cli.writeWarning(`changed file: ${file.name} (update styles on page)`);
174
+ this.cli.writeWarning(`changed file: ${file.name}`);
175
+ await this.documentHandler.buildDocumentHandler();
176
+ this.cache.clear();
118
177
  const escapedStyles = encodeURI(file.getContent());
119
- const evaluateResult = await (0, safe_1.safe)(page.evaluate(`(function ${this.updateStyles.toString()})("${escapedStyles}");`));
120
- if (evaluateResult.success === false) {
178
+ const evaluateResult = await (0, safe_1.safe)((0, race_1.race)(Promise.all([
179
+ page.evaluate(this.updateStylesInlineFunction, escapedStyles),
180
+ this.safeClearPageBrowsersCacheStorage(page),
181
+ ]), 200));
182
+ if (evaluateResult.success === false &&
183
+ !evaluateResult.error.includes("timeout")) {
121
184
  this.cli.writeError(evaluateResult.error);
122
- console.log(evaluateResult.errorObj);
185
+ console.error(evaluateResult.errorObj);
123
186
  }
124
- const clearPageResult = await (0, safe_1.safe)(this.clearPageFunction(page));
125
- if (clearPageResult.success === false) {
126
- this.cli.writeError(clearPageResult.error);
187
+ }
188
+ /**
189
+ * clear speed-kit-caches in browser without interrupting the current workflow on error
190
+ *
191
+ * @param page
192
+ * @private
193
+ */
194
+ async safeClearPageBrowsersCacheStorage(page) {
195
+ const result = await (0, safe_1.safe)(this.clearPageBrowsersCacheStorage(page));
196
+ if (result.success === false) {
197
+ this.cli.writeError(result.error);
127
198
  }
128
- await this.documentHandler.buildDocumentHandler();
129
- this.cache.clear();
130
199
  }
131
- async clearPageFunction(page) {
200
+ /**
201
+ * clear speed-kit-caches in browser
202
+ *
203
+ * @param page
204
+ * @private
205
+ */
206
+ async clearPageBrowsersCacheStorage(page) {
132
207
  const client = await page.target().createCDPSession();
133
208
  const cacheResponse = await client.send("CacheStorage.requestCacheNames", {
134
209
  securityOrigin: this.browserContext.domain,
135
210
  });
136
211
  for (const cache of cacheResponse.caches) {
137
212
  if (cache.cacheName.includes("baqend")) {
138
- await client.send("CacheStorage.deleteCache", {
213
+ await (0, safe_1.safe)((0, race_1.race)(client.send("CacheStorage.deleteCache", {
139
214
  cacheId: cache.cacheId,
140
- });
215
+ }), 20));
141
216
  }
142
217
  }
143
218
  }
144
- async clearServiceWorker(page) {
219
+ async unregisterServiceWorker(page) {
145
220
  const client = await page.target().createCDPSession();
146
221
  const currentOrigin = new URL(page.url()).origin;
147
222
  await client.send("ServiceWorker.enable");
@@ -149,5 +224,18 @@ class FileWatcher {
149
224
  scopeURL: currentOrigin,
150
225
  });
151
226
  }
227
+ updateStylesInlineFunction(styles) {
228
+ try {
229
+ const styleElement = document.querySelector("#speed-kit-df-styles");
230
+ if (styleElement) {
231
+ styleElement.textContent = decodeURI(styles);
232
+ styleElement.setAttribute("last-onboarding-live-update", new Date().toLocaleTimeString());
233
+ }
234
+ }
235
+ catch (error) {
236
+ console.error(error);
237
+ }
238
+ return true;
239
+ }
152
240
  }
153
241
  exports.FileWatcher = FileWatcher;
@@ -33,7 +33,7 @@ class Cache {
33
33
  }
34
34
  }
35
35
  clear() {
36
- this.cli.writeWarning("clear request-caches");
36
+ this.cli.writeSuccess("clear cached requests from local documentHandler");
37
37
  this.inMemoryCache.clear();
38
38
  }
39
39
  }
@@ -712,5 +712,5 @@
712
712
  ]
713
713
  }
714
714
  },
715
- "version": "2.66.0-alpha.1"
715
+ "version": "2.67.0"
716
716
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@speedkit/cli",
3
3
  "description": "Speed Kit CLI",
4
- "version": "2.66.0-alpha.1",
4
+ "version": "2.67.0",
5
5
  "author": {
6
6
  "name": "Baqend.com",
7
7
  "email": "info@baqend.com"
@@ -66,27 +66,27 @@
66
66
  "type": "commonjs",
67
67
  "dependencies": {
68
68
  "@aws-sdk/client-athena": "^3.529",
69
- "@inquirer/prompts": "^3.0",
69
+ "@inquirer/prompts": "^3.3.2",
70
70
  "@oclif/core": "^3.23",
71
71
  "@oclif/plugin-autocomplete": "^3.0",
72
- "@oclif/plugin-help": "^6.0",
73
- "@oclif/plugin-not-found": "^3.0",
74
- "@oclif/plugin-warn-if-update-available": "^3.0",
72
+ "@oclif/plugin-help": "^6.2.16",
73
+ "@oclif/plugin-not-found": "^3.2.25",
74
+ "@oclif/plugin-warn-if-update-available": "^3.1.21",
75
75
  "axios": "^1.6",
76
76
  "baqend": "^3.4",
77
- "chalk": "^4",
77
+ "chalk": "^4.1.2",
78
78
  "clipboardy-cjs": "^3.0.0",
79
79
  "css-select": "5.1.0",
80
- "deepmerge": "^4.2.2",
80
+ "deepmerge": "^4.3.1",
81
81
  "diff": "^5.1.0",
82
82
  "dom-serializer": "2.0.0",
83
83
  "domhandler": "5.0.3",
84
84
  "domutils": "^3.1",
85
- "dotenv": "^16.4",
85
+ "dotenv": "^16.4.5",
86
86
  "extract-zip": "^2.0.1",
87
87
  "fs-extra": "^11.2.0",
88
88
  "handlebars": "^4.7.8",
89
- "htmlparser2": "8.0",
89
+ "htmlparser2": "8.0.2",
90
90
  "json2csv": "^6.0.0-alpha.2",
91
91
  "node-fetch": "^2.7.0",
92
92
  "parse5": "7.1",
@@ -101,8 +101,8 @@
101
101
  "ts-node": "^10.9.2"
102
102
  },
103
103
  "devDependencies": {
104
- "@commitlint/cli": "^17.7.1",
105
- "@commitlint/config-conventional": "^17.7.0",
104
+ "@commitlint/cli": "^17.8.1",
105
+ "@commitlint/config-conventional": "^17.8.1",
106
106
  "@istanbuljs/nyc-config-typescript": "^1.0.2",
107
107
  "@oclif/prettier-config": "^0.2.1",
108
108
  "@oclif/test": "^3",
@@ -115,18 +115,18 @@
115
115
  "@typescript-eslint/eslint-plugin": "^7.2.0",
116
116
  "chai": "^4.4.1",
117
117
  "copyfiles": "^2.4.1",
118
- "eslint": "^8.57",
118
+ "eslint": "^8.57.1",
119
119
  "eslint-config-prettier": "^9.1",
120
120
  "eslint-plugin-prettier": "^5.1",
121
121
  "eslint-plugin-unicorn": "^51.0.1",
122
- "globby": "^11",
122
+ "globby": "^11.1.0",
123
123
  "husky": "^8.0.3",
124
124
  "lint-staged": "^14.0.1",
125
125
  "memfs": "^4.8.0",
126
126
  "mocha": "^10.1.0",
127
127
  "mocha-junit-reporter": "^2.2.1",
128
128
  "mock-fs": "^5.2.0",
129
- "nock": "^13.5",
129
+ "nock": "^13.5.5",
130
130
  "nyc": "^15.1.0",
131
131
  "oclif": "^4.8.8",
132
132
  "shx": "^0.3.4",