@shoper/cli 0.7.1-1 → 0.8.1-10

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.
@@ -18,7 +18,8 @@ export class Browser {
18
18
  this.#onInfo?.(`Launching browser...`);
19
19
  this.#browser = await puppeteer.launch({
20
20
  headless: false,
21
- defaultViewport: null
21
+ defaultViewport: null,
22
+ args: process.platform === 'linux' ? ['--no-sandbox'] : []
22
23
  });
23
24
  this.#browser.on('disconnected', () => {
24
25
  this.#onDisconnected?.('Browser disconnected');
@@ -43,6 +44,7 @@ export class Browser {
43
44
  const id = tabId ?? `tab-${uuid()}`;
44
45
  this.#onInfo?.(`Opening new tab ${id}: ${url}`);
45
46
  const page = await this.#browser.newPage();
47
+ await page.setCacheEnabled(false);
46
48
  await page.goto(url, { waitUntil: 'networkidle2' });
47
49
  this.#tabs.set(id, { page, url });
48
50
  this.#onInfo?.(`Tab ${id} opened successfully`);
@@ -68,6 +70,10 @@ export class Browser {
68
70
  throw error;
69
71
  }
70
72
  }
73
+ async refreshAll() {
74
+ const openTabs = this.getOpenTabs();
75
+ await Promise.allSettled(openTabs.map((tabId) => this.refresh(tabId)));
76
+ }
71
77
  async closeTab(tabId) {
72
78
  const tab = this.#tabs.get(tabId);
73
79
  if (!tab) {
@@ -21,8 +21,9 @@ import { LOGGER_API_NAME } from '../../../cli/utilities/features/logger/logger_c
21
21
  import { Watch } from './watch.js';
22
22
  import { Flags } from '@oclif/core';
23
23
  import { ThemeError } from '../ui/theme_error.js';
24
+ import { retro } from 'gradient-string';
24
25
  export class ThemeWatchCommand extends BaseThemeCommand {
25
- static summary = 'Watch local theme files for changes and update your store preview in real time.';
26
+ static summary = `Watch local theme files for changes and update your store preview in real time. ${retro('[BETA]')}`;
26
27
  static description = `Continuously monitors your local theme files for any changes and uploads them automatically to your store. Your store preview will refresh in real time to reflect updates immediately.\n\nYou must run this command from a specific theme directory (ID not needed).`;
27
28
  static examples = [
28
29
  {
@@ -9,7 +9,8 @@ export class ThemeActionsUtils {
9
9
  static getFilesGlobsThatMatchesAction({ filesStructure, actionValue, actionType }) {
10
10
  return Object.entries(filesStructure).reduce((acc, [filePath, fileStructureItem]) => {
11
11
  if (fileStructureItem._links?.[actionType] && this._doesActionValueMatch(fileStructureItem._links[actionType], actionValue)) {
12
- return [...acc, toUnixPath(filePath)];
12
+ const normalizedPath = this._normalizeFileGlob(toUnixPath(filePath));
13
+ return [...acc, normalizedPath];
13
14
  }
14
15
  return acc;
15
16
  }, []);
@@ -104,6 +105,12 @@ export class ThemeActionsUtils {
104
105
  //
105
106
  // return fileRecords;
106
107
  // }
108
+ static _normalizeFileGlob(fileGlob) {
109
+ if (fileGlob.endsWith('/')) {
110
+ return `${fileGlob}${THEME_PUSH_WILDCARD_GLOBS_FOR_FILES}`;
111
+ }
112
+ return fileGlob;
113
+ }
107
114
  static _doesActionValueMatch(currentActionValue, valuesToMatch) {
108
115
  if (typeof valuesToMatch === 'string') {
109
116
  return currentActionValue === valuesToMatch || valuesToMatch === THEME_ALL_ACTIONS_NAME;
@@ -2,6 +2,7 @@ import globs from 'fast-glob';
2
2
  import { AppError } from '../../../../cli/utilities/features/logger/logs/app_error.js';
3
3
  import { toUnixPath } from '../../../../utils/path_utils.js';
4
4
  import { ThemeFilesUtils } from '../utils/files/theme_files_utils.js';
5
+ import { THEME_PUSH_WILDCARD_GLOBS_FOR_FILES } from './service/theme_push_service_constants.js';
5
6
  export class ThemePushUtils {
6
7
  static async getAllFilesThatAreSendToRemote(themeDir) {
7
8
  const filesStructure = await ThemeFilesUtils.getThemeFilesStructure(themeDir);
@@ -12,7 +13,7 @@ export class ThemePushUtils {
12
13
  });
13
14
  //need unix styles globs
14
15
  const filesToArchive = Object.keys(filesStructure)
15
- .map((path) => toUnixPath(path))
16
+ .map((path) => ThemePushUtils._normalizeFileGlob(toUnixPath(path)))
16
17
  .filter((path) => path !== '*');
17
18
  //need unix styles globs
18
19
  filesToArchive.push('styles/src/**/*');
@@ -22,4 +23,10 @@ export class ThemePushUtils {
22
23
  cwd: themeDir
23
24
  }).then((files) => files.sort());
24
25
  }
26
+ static _normalizeFileGlob(fileGlob) {
27
+ if (fileGlob.endsWith('/')) {
28
+ return `${fileGlob}${THEME_PUSH_WILDCARD_GLOBS_FOR_FILES}`;
29
+ }
30
+ return fileGlob;
31
+ }
25
32
  }
@@ -2,7 +2,7 @@ import { AppLog } from '../../../../../cli/utilities/features/logger/logs/app_lo
2
2
  import chokidar from 'chokidar';
3
3
  import { APP_LOGS_TYPES } from '../../../../../cli/utilities/features/logger/logs/app_logs_constants.js';
4
4
  import { relative } from '../../../../../utils/path_utils.js';
5
- import { SHOP_BROWSER_TAB_ID, THEME_BATCH_DELAY_MS } from '../theme_watch_constants.js';
5
+ import { SHOP_BROWSER_TAB_ID, STORE_FRONT_BROWSER_TAB_ID, THEME_BATCH_DELAY_MS } from '../theme_watch_constants.js';
6
6
  import { FILE_STATES } from '../../../../../utils/fs/fs_constants.js';
7
7
  export class ThemeWatchService {
8
8
  #themePushApi;
@@ -14,6 +14,7 @@ export class ThemeWatchService {
14
14
  #changedFiles = new Set();
15
15
  #watcher = null;
16
16
  #urlForWatchedTheme = null;
17
+ #storeFrontUrl = null;
17
18
  constructor({ themePushApi, browserClient }) {
18
19
  this.#themePushApi = themePushApi;
19
20
  this.#browserApi = new browserClient({
@@ -26,9 +27,11 @@ export class ThemeWatchService {
26
27
  this.#onMessage = onMessage || null;
27
28
  const themePath = executionContext.themeRootDir;
28
29
  this.#urlForWatchedTheme = `${credentials.shopUrl}/admin/configSkins/skin-preview/id/${executionContext.themeId}`;
30
+ this.#storeFrontUrl = credentials.shopUrl;
29
31
  if (openBrowser) {
30
32
  await this.#browserApi.launch();
31
33
  await this.#browserApi.openTab(this.#urlForWatchedTheme, SHOP_BROWSER_TAB_ID);
34
+ await this.#browserApi.openTab(this.#storeFrontUrl, STORE_FRONT_BROWSER_TAB_ID);
32
35
  }
33
36
  this._onInfoMessage(`Watching for changes in: ${themePath}`);
34
37
  this.#watcher = chokidar.watch(themePath, {
@@ -46,7 +49,7 @@ export class ThemeWatchService {
46
49
  const fileState = await themeChecksums.getFileState(relativePath);
47
50
  if (fileState == FILE_STATES.unchanged)
48
51
  return;
49
- this._onInfoMessage(`Detected change in: ${relativePath} ${fileState}`);
52
+ this._onInfoMessage(`Detected change in: ${relativePath}`);
50
53
  this.#changedFiles.add(relativePath);
51
54
  if (this.#batchTimeout)
52
55
  this._clearQueuedBatch();
@@ -82,21 +85,30 @@ export class ThemeWatchService {
82
85
  .on('ready', () => this._onInfoMessage('Initial scan complete. Ready for changes.'));
83
86
  }
84
87
  async ensureBrowserTabOpen() {
85
- if (!this.#urlForWatchedTheme)
88
+ if (!this.#urlForWatchedTheme || !this.#storeFrontUrl)
86
89
  return;
87
90
  if (!this.#browserApi.isBrowserOpen()) {
88
91
  this._onInfoMessage(`Browser is not open, launching...`);
89
92
  await this.#browserApi.launch();
90
93
  await this.#browserApi.openTab(this.#urlForWatchedTheme, SHOP_BROWSER_TAB_ID);
91
- this._onInfoMessage(`Browser opened with new tab`);
94
+ await this.#browserApi.openTab(this.#storeFrontUrl, STORE_FRONT_BROWSER_TAB_ID);
95
+ this._onInfoMessage(`Browser opened with new tabs`);
92
96
  return;
93
97
  }
98
+ let reopened = false;
94
99
  if (!this.#browserApi.isTabOpen(SHOP_BROWSER_TAB_ID)) {
95
- this._onInfoMessage(`Tab was closed, reopening...`);
100
+ this._onInfoMessage(`Admin preview tab was closed, reopening...`);
96
101
  await this.#browserApi.openTab(this.#urlForWatchedTheme, SHOP_BROWSER_TAB_ID);
97
- return;
102
+ reopened = true;
103
+ }
104
+ if (!this.#browserApi.isTabOpen(STORE_FRONT_BROWSER_TAB_ID)) {
105
+ this._onInfoMessage(`Store front tab was closed, reopening...`);
106
+ await this.#browserApi.openTab(this.#storeFrontUrl, STORE_FRONT_BROWSER_TAB_ID);
107
+ reopened = true;
108
+ }
109
+ if (!reopened) {
110
+ this._onInfoMessage(`Browser and tabs already open`);
98
111
  }
99
- this._onInfoMessage(`Browser and tab already open`);
100
112
  }
101
113
  async stopWatching() {
102
114
  this._onInfoMessage('Shutting down...');
@@ -105,6 +117,7 @@ export class ThemeWatchService {
105
117
  await this.#watcher?.close();
106
118
  await this.#browserApi.close();
107
119
  this.#urlForWatchedTheme = null;
120
+ this.#storeFrontUrl = null;
108
121
  }
109
122
  _clearQueuedBatch() {
110
123
  if (this.#batchTimeout) {
@@ -112,13 +125,14 @@ export class ThemeWatchService {
112
125
  this.#batchTimeout = null;
113
126
  }
114
127
  }
115
- async _refreshBrowserTab() {
116
- if (!SHOP_BROWSER_TAB_ID || !this.#browserApi.isTabOpen(SHOP_BROWSER_TAB_ID))
128
+ async _refreshBrowserTabs() {
129
+ const openTabs = this.#browserApi.getOpenTabs();
130
+ if (openTabs.length === 0)
117
131
  return;
118
132
  try {
119
- this._onInfoMessage(`Refreshing browser tab...`);
120
- await this.#browserApi.refresh(SHOP_BROWSER_TAB_ID);
121
- this._onInfoMessage(`Browser tab refreshed`);
133
+ this._onInfoMessage(`Refreshing browser tabs...`);
134
+ await this.#browserApi.refreshAll();
135
+ this._onInfoMessage(`Browser tabs refreshed`);
122
136
  }
123
137
  catch (error) {
124
138
  this._onErrorMessage(`Error refreshing browser:`, error);
@@ -132,7 +146,7 @@ export class ThemeWatchService {
132
146
  this._onInfoMessage(`Pushing theme...`);
133
147
  await this.#themePushApi.partialPush(params);
134
148
  this._onSuccessMessage(`Theme pushed successfully`);
135
- await this._refreshBrowserTab();
149
+ await this._refreshBrowserTabs();
136
150
  if (this.#pendingPush) {
137
151
  this._onInfoMessage(`Executing queued push...`);
138
152
  this.#isPushing = false;
@@ -2,3 +2,4 @@ export const THEME_WATCH_FEATURE_NAME = 'ThemeWatch';
2
2
  export const THEME_WATCH_API_NAME = 'ThemeWatchApi';
3
3
  export const THEME_BATCH_DELAY_MS = 750;
4
4
  export const SHOP_BROWSER_TAB_ID = 'shop-page';
5
+ export const STORE_FRONT_BROWSER_TAB_ID = 'store-front';
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@shoper/cli",
3
3
  "packageManager": "yarn@3.2.0",
4
4
  "sideEffects": false,
5
- "version": "0.7.1-1",
5
+ "version": "0.8.1-10",
6
6
  "description": "CLI tool for Shoper",
7
7
  "author": "Joanna Firek",
8
8
  "license": "MIT",
@@ -79,7 +79,8 @@
79
79
  "yauzl": "3.2.0",
80
80
  "figlet": "1.9.4",
81
81
  "yazl": "3.3.1",
82
- "puppeteer": "24.31.0"
82
+ "puppeteer": "24.31.0",
83
+ "gradient-string": "3.0.0"
83
84
  },
84
85
  "devDependencies": {
85
86
  "@babel/core": "7.27.1",