@shoper/cli 0.8.3-0 → 0.9.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/build/theme/class/browser/browser.js +6 -1
- package/build/theme/commands/push/theme_push_command.js +2 -7
- package/build/theme/features/theme/watch/service/theme_watch_service.js +26 -12
- package/build/theme/features/theme/watch/theme_watch_constants.js +1 -0
- package/build/theme/hooks/themes_actions/ensure_themes_actions_hook_constants.js +2 -1
- package/build/utils/zip/create_zip_utils.js +6 -6
- package/package.json +1 -1
- package/build/theme/commands/push/ui/theme_invalid_folders_error.js +0 -16
- package/build/theme/utils/push_validators/push_path_validator_utils.js +0 -16
- package/build/theme/utils/push_validators/push_validator_constants.js +0 -8
|
@@ -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');
|
|
@@ -69,6 +70,10 @@ export class Browser {
|
|
|
69
70
|
throw error;
|
|
70
71
|
}
|
|
71
72
|
}
|
|
73
|
+
async refreshAll() {
|
|
74
|
+
const openTabs = this.getOpenTabs();
|
|
75
|
+
await Promise.allSettled(openTabs.map((tabId) => this.refresh(tabId)));
|
|
76
|
+
}
|
|
72
77
|
async closeTab(tabId) {
|
|
73
78
|
const tab = this.#tabs.get(tabId);
|
|
74
79
|
if (!tab) {
|
|
@@ -25,8 +25,6 @@ import { ThemeError } from '../ui/theme_error.js';
|
|
|
25
25
|
import { mapToPermissionsTree } from '../../utils/directory_validator/directory_validator_utils.js';
|
|
26
26
|
import { mapChecksumToTree } from '../../../utils/checksums/checksums_utils.js';
|
|
27
27
|
import { ThemeUnpermittedActionsError } from './ui/theme_unpermitted_actions_error.js';
|
|
28
|
-
import { validateThemeFolderStructure } from '../../utils/push_validators/push_path_validator_utils.js';
|
|
29
|
-
import { ThemeInvalidFoldersError } from './ui/theme_invalid_folders_error.js';
|
|
30
28
|
import { LOGGER_API_NAME } from '../../../cli/utilities/features/logger/logger_constants.js';
|
|
31
29
|
export class ThemePushCommand extends BaseThemeCommand {
|
|
32
30
|
static summary = 'Uploads your local theme files to the store and overwrites the current version of the theme in your store.';
|
|
@@ -88,11 +86,8 @@ export class ThemePushCommand extends BaseThemeCommand {
|
|
|
88
86
|
permissions: mapToPermissionsTree(permissions),
|
|
89
87
|
rootDirectory: executionContext.themeRootDir
|
|
90
88
|
});
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
renderOnce(React.createElement(ThemeInvalidFoldersError, { invalidDirectories: folderStructureValidation.invalidDirectories }));
|
|
94
|
-
return;
|
|
95
|
-
}
|
|
89
|
+
//TODO jak wysyla folder z nazwa posiadajaco \ na windows, wychodzimy
|
|
90
|
+
//TODO validacja folderów przed pushem
|
|
96
91
|
if (!validationResult.isValid) {
|
|
97
92
|
renderOnce(React.createElement(ThemeUnpermittedActionsError, { unpermittedActions: validationResult.unpermittedActions }));
|
|
98
93
|
return;
|
|
@@ -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, {
|
|
@@ -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;
|
|
@@ -5,7 +5,7 @@ import { createWriteStream } from 'node:fs';
|
|
|
5
5
|
import { StreamReadError } from '../fs/errors/stream_read_error.js';
|
|
6
6
|
import { CreateZipError } from './errors/create_zip_error.js';
|
|
7
7
|
import { StreamWriteError } from '../fs/errors/stream_write_error.js';
|
|
8
|
-
import { join
|
|
8
|
+
import { join } from '../path_utils.js';
|
|
9
9
|
import { AppError } from '../../cli/utilities/features/logger/logs/app_error.js';
|
|
10
10
|
export const createZip = async ({ files, dist, baseDir = process.cwd(), logger }) => {
|
|
11
11
|
const zipfile = new yazl.ZipFile();
|
|
@@ -29,14 +29,14 @@ export const createZip = async ({ files, dist, baseDir = process.cwd(), logger }
|
|
|
29
29
|
code: 'FILE_NOT_FOUND',
|
|
30
30
|
details: { file, baseDir }
|
|
31
31
|
});
|
|
32
|
-
const zipEntryName = toUnixPath(file);
|
|
33
32
|
if (await isDirectory(fullPath)) {
|
|
34
|
-
logger.debug('Adding empty directory to zip', { details: { directory:
|
|
35
|
-
zipfile.addEmptyDirectory(
|
|
33
|
+
logger.debug('Adding empty directory to zip', { details: { directory: file } });
|
|
34
|
+
zipfile.addEmptyDirectory(file);
|
|
36
35
|
}
|
|
37
36
|
else {
|
|
38
|
-
logger.debug('Adding file to zip', { details: { file
|
|
39
|
-
zipfile.addFile(fullPath,
|
|
37
|
+
logger.debug('Adding file to zip', { details: { file, fullPath } });
|
|
38
|
+
zipfile.addFile(fullPath, file, {
|
|
39
|
+
//TODO params
|
|
40
40
|
compress: true
|
|
41
41
|
});
|
|
42
42
|
}
|
package/package.json
CHANGED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import { List } from '../../../../ui/list/list.js';
|
|
2
|
-
import { Error } from '../../../../ui/message_box/error.js';
|
|
3
|
-
import { Text } from '../../../../ui/text.js';
|
|
4
|
-
import React from 'react';
|
|
5
|
-
import { ALLOWED_THEME_TOP_LEVEL_DIRECTORIES } from '../../../utils/push_validators/push_validator_constants.js';
|
|
6
|
-
export const ThemeInvalidFoldersError = ({ invalidDirectories }) => {
|
|
7
|
-
const items = invalidDirectories.map((dir) => ({
|
|
8
|
-
content: dir
|
|
9
|
-
}));
|
|
10
|
-
return (React.createElement(Error, { header: "Invalid theme folder structure" },
|
|
11
|
-
React.createElement(Text, null, "The following directories are not allowed in the theme root:"),
|
|
12
|
-
React.createElement(List, { items: items }),
|
|
13
|
-
React.createElement(Text, null,
|
|
14
|
-
"Allowed top-level directories: ",
|
|
15
|
-
ALLOWED_THEME_TOP_LEVEL_DIRECTORIES.join(', '))));
|
|
16
|
-
};
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import { getAllDirectoriesNamesInside } from '../../../utils/fs/fs_utils.js';
|
|
2
|
-
import { ALLOWED_THEME_TOP_LEVEL_DIRECTORIES } from './push_validator_constants.js';
|
|
3
|
-
export const validateThemeFolderStructure = async (themeRootDir) => {
|
|
4
|
-
const directories = await getAllDirectoriesNamesInside(themeRootDir, { recursive: false, hidden: true });
|
|
5
|
-
const allowedSet = new Set(ALLOWED_THEME_TOP_LEVEL_DIRECTORIES);
|
|
6
|
-
const invalidDirectories = directories.filter((dir) => {
|
|
7
|
-
if (dir.startsWith('.') && !allowedSet.has(dir)) {
|
|
8
|
-
return false;
|
|
9
|
-
}
|
|
10
|
-
return !allowedSet.has(dir);
|
|
11
|
-
});
|
|
12
|
-
return {
|
|
13
|
-
isValid: invalidDirectories.length === 0,
|
|
14
|
-
invalidDirectories
|
|
15
|
-
};
|
|
16
|
-
};
|