@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.
- package/build/theme/class/browser/browser.js +7 -1
- package/build/theme/commands/watch/theme_watch_command.js +2 -1
- package/build/theme/features/theme/actions/theme_actions_utils.js +8 -1
- package/build/theme/features/theme/push/theme_push_utils.js +8 -1
- package/build/theme/features/theme/watch/service/theme_watch_service.js +27 -13
- package/build/theme/features/theme/watch/theme_watch_constants.js +1 -0
- package/package.json +3 -2
|
@@ -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 =
|
|
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
|
-
|
|
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}
|
|
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.
|
|
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(`
|
|
100
|
+
this._onInfoMessage(`Admin preview tab was closed, reopening...`);
|
|
96
101
|
await this.#browserApi.openTab(this.#urlForWatchedTheme, SHOP_BROWSER_TAB_ID);
|
|
97
|
-
|
|
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
|
|
116
|
-
|
|
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
|
|
120
|
-
await this.#browserApi.
|
|
121
|
-
this._onInfoMessage(`Browser
|
|
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.
|
|
149
|
+
await this._refreshBrowserTabs();
|
|
136
150
|
if (this.#pendingPush) {
|
|
137
151
|
this._onInfoMessage(`Executing queued push...`);
|
|
138
152
|
this.#isPushing = false;
|
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.
|
|
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",
|