@shoper/cli 0.6.4-2 → 0.7.1-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/build/cli/class/errors/http/http_errors_factory.js +1 -1
- package/build/cli/core/cli_setup.js +6 -6
- package/build/cli/index.js +1 -0
- package/build/cli/utilities/features/logger/logs/app_logs_constants.js +2 -1
- package/build/index.js +2 -2
- package/build/theme/class/archive/theme_archive.js +1 -45
- package/build/theme/class/browser/browser.js +108 -0
- package/build/theme/class/checksums/theme_checksums.js +50 -12
- package/build/theme/commands/pull/theme_pull_command.js +4 -3
- package/build/theme/commands/push/theme_push_command.js +17 -8
- package/build/theme/commands/theme_commands_constants.js +2 -1
- package/build/theme/commands/theme_verify_command.js +3 -3
- package/build/theme/commands/ui/theme_error.js +6 -6
- package/build/theme/commands/watch/theme_watch_command.js +88 -0
- package/build/theme/commands/watch/theme_watch_constants.js +21 -0
- package/build/theme/commands/watch/theme_watch_utils.js +32 -0
- package/build/theme/commands/watch/theme_watching_info.js +14 -0
- package/build/theme/commands/watch/watch.js +55 -0
- package/build/theme/features/theme/actions/theme_actions_constants.js +2 -1
- package/build/theme/features/theme/actions/theme_actions_utils.js +60 -5
- package/build/theme/features/theme/fetch/service/theme_fetch_service.js +2 -2
- package/build/theme/features/theme/init/service/theme_init_service.js +2 -2
- package/build/theme/features/theme/merge/service/theme_merge_service.js +2 -2
- package/build/theme/features/theme/push/api/theme_push_api.js +2 -2
- package/build/theme/features/theme/push/service/theme_push_service.js +93 -33
- package/build/theme/features/theme/push/service/theme_push_service_types.js +1 -0
- package/build/theme/features/theme/push/theme_push_utils.js +2 -2
- package/build/theme/features/theme/utils/archive/theme_archive_utils.js +26 -0
- package/build/theme/{class/archive/theme_archive_errors_factory.js → features/theme/utils/archive/theme_archive_utils_errors_factory.js} +1 -1
- package/build/theme/features/theme/utils/files/them_files_constants.js +1 -0
- package/build/theme/features/theme/utils/{files_structure/theme_files_structure_utils.js → files/theme_files_utils.js} +34 -23
- package/build/theme/features/theme/utils/files_structure/theme_file_structure_errors_factory.js +1 -9
- package/build/theme/features/theme/utils/hidden_directory/hidden_directory_utils.js +0 -1
- package/build/theme/features/theme/verify/verify/theme_verify_service.js +19 -12
- package/build/theme/features/theme/watch/api/theme_watch_api.js +19 -0
- package/build/theme/features/theme/watch/service/theme_watch_service.js +167 -0
- package/build/theme/features/theme/watch/theme_watch_constants.js +4 -0
- package/build/theme/features/theme/watch/theme_watch_initializer.js +22 -0
- package/build/theme/index.js +6 -2
- package/build/ui/command_input/command_input.js +25 -0
- package/build/ui/logs/log_entry.js +12 -0
- package/build/ui/logs/logs_constants.js +20 -0
- package/build/ui/logs/logs_list.js +18 -0
- package/build/ui/logs/use_logs.js +23 -0
- package/build/ui/ui_dump/ui_dump.js +9 -4
- package/build/utils/array_utils.js +3 -0
- package/build/utils/checksums/checksums_utils.js +0 -2
- package/build/utils/fs/fs_constants.js +6 -0
- package/build/utils/fs/fs_utils.js +1 -1
- package/package.json +9 -4
|
@@ -7,7 +7,6 @@ import { ThemeChecksums } from '../../../../class/checksums/theme_checksums.js';
|
|
|
7
7
|
export class HiddenDirectoryUtils {
|
|
8
8
|
static async ensureFilesInsideThemeMetaDataDirectoryUntouched(themeDirectory, logger) {
|
|
9
9
|
const themeMetadataPath = this.getThemeHiddenDirectoryPath(themeDirectory);
|
|
10
|
-
console.log('themeMetadataPath', themeMetadataPath);
|
|
11
10
|
const themeChecksums = new ThemeChecksums({
|
|
12
11
|
themeDir: themeDirectory,
|
|
13
12
|
loggerApi: logger
|
|
@@ -3,12 +3,12 @@ import { ThemePublishUtils } from '../../skinstore/theme_publish_utils.js';
|
|
|
3
3
|
import { ThemePushErrorsFactory } from '../../push/theme_push_errors_factory.js';
|
|
4
4
|
import { join } from '../../../../../utils/path_utils.js';
|
|
5
5
|
import { v4 as uuid } from 'uuid';
|
|
6
|
-
import {
|
|
6
|
+
import { ThemeArchiveUtils } from '../../utils/archive/theme_archive_utils.js';
|
|
7
|
+
import { ThemeActionsUtils } from '../../actions/theme_actions_utils.js';
|
|
8
|
+
import { ThemeFilesUtils } from '../../utils/files/theme_files_utils.js';
|
|
7
9
|
import { THEME_WILDCARD_ACTION_NAME } from '../../actions/service/theme_actions_service_constants.js';
|
|
8
10
|
import { THEME_ACTIONS_TYPES } from '../../actions/theme_actions_constants.js';
|
|
9
|
-
import {
|
|
10
|
-
import { ThemeActionsUtils } from '../../actions/theme_actions_utils.js';
|
|
11
|
-
import { ArrayUtils } from '@dreamcommerce/utilities';
|
|
11
|
+
import { FILE_STATES } from '../../../../../utils/fs/fs_constants.js';
|
|
12
12
|
export class ThemeVerifyService {
|
|
13
13
|
#loggerApi;
|
|
14
14
|
constructor({ loggerApi }) {
|
|
@@ -23,13 +23,15 @@ export class ThemeVerifyService {
|
|
|
23
23
|
throw ThemePushErrorsFactory.createErrorWhilePushingUnpublishedThemeWithSkinstoreData(credentials.shopUrl);
|
|
24
24
|
try {
|
|
25
25
|
this.#loggerApi.debug('Getting file records from action data.');
|
|
26
|
+
//TODO to do api?
|
|
26
27
|
const filesRecords = await ThemeActionsUtils.getFilesRecordsFromActionData({
|
|
27
28
|
themeRootDir,
|
|
28
29
|
themeAction: verifyAction,
|
|
29
|
-
filesStructure
|
|
30
|
+
filesStructure,
|
|
31
|
+
themeChecksums
|
|
30
32
|
});
|
|
31
33
|
this.#loggerApi.debug('Filtering for modified or not created files to verify.');
|
|
32
|
-
const filesToVerify =
|
|
34
|
+
const filesToVerify = filesRecords.filter(({ state }) => [FILE_STATES.modified, FILE_STATES.created].includes(state));
|
|
33
35
|
this.#loggerApi.debug('Files to verify determined.', { details: { count: filesToVerify.length } });
|
|
34
36
|
if (filesToVerify.length) {
|
|
35
37
|
this.#loggerApi.info(`Verifying ${filesToVerify.length} theme files.`);
|
|
@@ -46,11 +48,16 @@ export class ThemeVerifyService {
|
|
|
46
48
|
await this._createFilesList(themeRootDir, filesToVerify);
|
|
47
49
|
const themeArchivePath = join(tmpDir, `${uuid()}.zip`);
|
|
48
50
|
this.#loggerApi.info('Creating theme archive for verification.');
|
|
49
|
-
await
|
|
51
|
+
await ThemeArchiveUtils.create({
|
|
50
52
|
dist: themeArchivePath,
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
53
|
+
rootDir: themeRootDir,
|
|
54
|
+
logger: this.#loggerApi,
|
|
55
|
+
files: await ThemeActionsUtils.getFilesThatMatchesAction({
|
|
56
|
+
actionValue: THEME_WILDCARD_ACTION_NAME,
|
|
57
|
+
actionType: THEME_ACTIONS_TYPES.push,
|
|
58
|
+
filesStructure,
|
|
59
|
+
rootDir: themeRootDir
|
|
60
|
+
})
|
|
54
61
|
});
|
|
55
62
|
this.#loggerApi.info('Theme archive created.');
|
|
56
63
|
const { isSuccess, messages } = await themeFilesUploadApi.uploadArchive({
|
|
@@ -63,11 +70,11 @@ export class ThemeVerifyService {
|
|
|
63
70
|
}
|
|
64
71
|
finally {
|
|
65
72
|
this.#loggerApi.debug('Cleaning up files list.');
|
|
66
|
-
await
|
|
73
|
+
await ThemeFilesUtils.removeAFilesListFile(themeRootDir);
|
|
67
74
|
}
|
|
68
75
|
}
|
|
69
76
|
async _createFilesList(themeRootDir, filesRecords) {
|
|
70
77
|
this.#loggerApi.debug('Creating filesList.json for the archive.');
|
|
71
|
-
await
|
|
78
|
+
await ThemeFilesUtils.createAFilesListFile(themeRootDir, ThemeFilesUtils.mapFilesRecordsToFilesList(filesRecords));
|
|
72
79
|
}
|
|
73
80
|
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { FeatureApi } from '@dreamcommerce/star_core';
|
|
2
|
+
import { THEME_WATCH_API_NAME } from '../theme_watch_constants.js';
|
|
3
|
+
export class ThemeWatchApi extends FeatureApi {
|
|
4
|
+
moduleName = THEME_WATCH_API_NAME;
|
|
5
|
+
#service;
|
|
6
|
+
constructor(service) {
|
|
7
|
+
super();
|
|
8
|
+
this.#service = service;
|
|
9
|
+
}
|
|
10
|
+
async watchTheme(props) {
|
|
11
|
+
return this.#service.watchTheme(props);
|
|
12
|
+
}
|
|
13
|
+
stopWatching() {
|
|
14
|
+
return this.#service.stopWatching();
|
|
15
|
+
}
|
|
16
|
+
ensureBrowserTabOpen() {
|
|
17
|
+
return this.#service.ensureBrowserTabOpen();
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import { AppLog } from '../../../../../cli/utilities/features/logger/logs/app_log.js';
|
|
2
|
+
import chokidar from 'chokidar';
|
|
3
|
+
import { APP_LOGS_TYPES } from '../../../../../cli/utilities/features/logger/logs/app_logs_constants.js';
|
|
4
|
+
import { relative } from '../../../../../utils/path_utils.js';
|
|
5
|
+
import { SHOP_BROWSER_TAB_ID, THEME_BATCH_DELAY_MS } from '../theme_watch_constants.js';
|
|
6
|
+
import { FILE_STATES } from '../../../../../utils/fs/fs_constants.js';
|
|
7
|
+
export class ThemeWatchService {
|
|
8
|
+
#themePushApi;
|
|
9
|
+
#isPushing = false;
|
|
10
|
+
#pendingPush = false;
|
|
11
|
+
#browserApi;
|
|
12
|
+
#batchTimeout = null;
|
|
13
|
+
#onMessage = null;
|
|
14
|
+
#changedFiles = new Set();
|
|
15
|
+
#watcher = null;
|
|
16
|
+
#urlForWatchedTheme = null;
|
|
17
|
+
constructor({ themePushApi, browserClient }) {
|
|
18
|
+
this.#themePushApi = themePushApi;
|
|
19
|
+
this.#browserApi = new browserClient({
|
|
20
|
+
onDisconnected: () => {
|
|
21
|
+
this._onInfoMessage('Browser closed');
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
async watchTheme({ themeChecksums, executionContext, credentials, themeFilesUploadApi, filesStructure, action, openBrowser = false, onMessage }) {
|
|
26
|
+
this.#onMessage = onMessage || null;
|
|
27
|
+
const themePath = executionContext.themeRootDir;
|
|
28
|
+
this.#urlForWatchedTheme = `${credentials.shopUrl}/admin/configSkins/skin-preview/id/${executionContext.themeId}`;
|
|
29
|
+
if (openBrowser) {
|
|
30
|
+
await this.#browserApi.launch();
|
|
31
|
+
await this.#browserApi.openTab(this.#urlForWatchedTheme, SHOP_BROWSER_TAB_ID);
|
|
32
|
+
}
|
|
33
|
+
this._onInfoMessage(`Watching for changes in: ${themePath}`);
|
|
34
|
+
this.#watcher = chokidar.watch(themePath, {
|
|
35
|
+
ignored: /^(?:.*[/\\])?\..*$/,
|
|
36
|
+
persistent: true,
|
|
37
|
+
ignoreInitial: true,
|
|
38
|
+
awaitWriteFinish: {
|
|
39
|
+
stabilityThreshold: 100,
|
|
40
|
+
pollInterval: 50
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
const handleFileChange = async (filePath) => {
|
|
44
|
+
const relativePath = relative(themePath, filePath);
|
|
45
|
+
await themeChecksums.updateCurrentChecksums();
|
|
46
|
+
const fileState = await themeChecksums.getFileState(relativePath);
|
|
47
|
+
if (fileState == FILE_STATES.unchanged)
|
|
48
|
+
return;
|
|
49
|
+
this._onInfoMessage(`Detected change in: ${relativePath} ${fileState}`);
|
|
50
|
+
this.#changedFiles.add(relativePath);
|
|
51
|
+
if (this.#batchTimeout)
|
|
52
|
+
this._clearQueuedBatch();
|
|
53
|
+
this.#batchTimeout = setTimeout(async () => {
|
|
54
|
+
const fileCount = this.#changedFiles.size;
|
|
55
|
+
this._onInfoMessage(`Processing batch of ${fileCount} file(s)`);
|
|
56
|
+
this.#batchTimeout = null;
|
|
57
|
+
if (this.#isPushing) {
|
|
58
|
+
this._onInfoMessage(`Push in progress, queuing another push...`);
|
|
59
|
+
this.#pendingPush = true;
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
try {
|
|
63
|
+
await this._executePush({
|
|
64
|
+
credentials,
|
|
65
|
+
filesStructure,
|
|
66
|
+
action,
|
|
67
|
+
executionContext,
|
|
68
|
+
themeChecksums,
|
|
69
|
+
themeFilesUploadApi
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
catch (err) {
|
|
73
|
+
this.#onMessage?.(err);
|
|
74
|
+
}
|
|
75
|
+
}, THEME_BATCH_DELAY_MS);
|
|
76
|
+
};
|
|
77
|
+
this.#watcher
|
|
78
|
+
.on('add', handleFileChange)
|
|
79
|
+
.on('change', handleFileChange)
|
|
80
|
+
.on('unlink', handleFileChange)
|
|
81
|
+
.on('error', (error) => this._onErrorMessage(`Watcher error:`, error))
|
|
82
|
+
.on('ready', () => this._onInfoMessage('Initial scan complete. Ready for changes.'));
|
|
83
|
+
}
|
|
84
|
+
async ensureBrowserTabOpen() {
|
|
85
|
+
if (!this.#urlForWatchedTheme)
|
|
86
|
+
return;
|
|
87
|
+
if (!this.#browserApi.isBrowserOpen()) {
|
|
88
|
+
this._onInfoMessage(`Browser is not open, launching...`);
|
|
89
|
+
await this.#browserApi.launch();
|
|
90
|
+
await this.#browserApi.openTab(this.#urlForWatchedTheme, SHOP_BROWSER_TAB_ID);
|
|
91
|
+
this._onInfoMessage(`Browser opened with new tab`);
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
if (!this.#browserApi.isTabOpen(SHOP_BROWSER_TAB_ID)) {
|
|
95
|
+
this._onInfoMessage(`Tab was closed, reopening...`);
|
|
96
|
+
await this.#browserApi.openTab(this.#urlForWatchedTheme, SHOP_BROWSER_TAB_ID);
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
this._onInfoMessage(`Browser and tab already open`);
|
|
100
|
+
}
|
|
101
|
+
async stopWatching() {
|
|
102
|
+
this._onInfoMessage('Shutting down...');
|
|
103
|
+
if (this.#batchTimeout)
|
|
104
|
+
this._clearQueuedBatch();
|
|
105
|
+
await this.#watcher?.close();
|
|
106
|
+
await this.#browserApi.close();
|
|
107
|
+
this.#urlForWatchedTheme = null;
|
|
108
|
+
}
|
|
109
|
+
_clearQueuedBatch() {
|
|
110
|
+
if (this.#batchTimeout) {
|
|
111
|
+
clearTimeout(this.#batchTimeout);
|
|
112
|
+
this.#batchTimeout = null;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
async _refreshBrowserTab() {
|
|
116
|
+
if (!SHOP_BROWSER_TAB_ID || !this.#browserApi.isTabOpen(SHOP_BROWSER_TAB_ID))
|
|
117
|
+
return;
|
|
118
|
+
try {
|
|
119
|
+
this._onInfoMessage(`Refreshing browser tab...`);
|
|
120
|
+
await this.#browserApi.refresh(SHOP_BROWSER_TAB_ID);
|
|
121
|
+
this._onInfoMessage(`Browser tab refreshed`);
|
|
122
|
+
}
|
|
123
|
+
catch (error) {
|
|
124
|
+
this._onErrorMessage(`Error refreshing browser:`, error);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
async _executePush(params) {
|
|
128
|
+
this.#isPushing = true;
|
|
129
|
+
this.#pendingPush = false;
|
|
130
|
+
try {
|
|
131
|
+
this.#changedFiles.clear();
|
|
132
|
+
this._onInfoMessage(`Pushing theme...`);
|
|
133
|
+
await this.#themePushApi.partialPush(params);
|
|
134
|
+
this._onSuccessMessage(`Theme pushed successfully`);
|
|
135
|
+
await this._refreshBrowserTab();
|
|
136
|
+
if (this.#pendingPush) {
|
|
137
|
+
this._onInfoMessage(`Executing queued push...`);
|
|
138
|
+
this.#isPushing = false;
|
|
139
|
+
await this._executePush(params);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
finally {
|
|
143
|
+
this.#isPushing = false;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
_onInfoMessage(message, details) {
|
|
147
|
+
this.#onMessage?.(new AppLog({
|
|
148
|
+
level: APP_LOGS_TYPES.info,
|
|
149
|
+
message,
|
|
150
|
+
details
|
|
151
|
+
}));
|
|
152
|
+
}
|
|
153
|
+
_onErrorMessage(message, error) {
|
|
154
|
+
this.#onMessage?.(new AppLog({
|
|
155
|
+
level: APP_LOGS_TYPES.error,
|
|
156
|
+
message,
|
|
157
|
+
details: error
|
|
158
|
+
}));
|
|
159
|
+
}
|
|
160
|
+
_onSuccessMessage(message, details) {
|
|
161
|
+
this.#onMessage?.(new AppLog({
|
|
162
|
+
level: APP_LOGS_TYPES.success,
|
|
163
|
+
message,
|
|
164
|
+
details
|
|
165
|
+
}));
|
|
166
|
+
}
|
|
167
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { FEATURE_CORES_TYPES, SyncFeatureInitializer } from '@dreamcommerce/star_core';
|
|
2
|
+
import { THEME_WATCH_FEATURE_NAME } from './theme_watch_constants.js';
|
|
3
|
+
import { ThemeWatchService } from './service/theme_watch_service.js';
|
|
4
|
+
import { THEME_PUSH_API_NAME } from '../push/theme_push_constants.js';
|
|
5
|
+
import { ThemeWatchApi } from './api/theme_watch_api.js';
|
|
6
|
+
import { Browser } from '../../../class/browser/browser.js';
|
|
7
|
+
//TODO to pownna byc inicjalizacja w komponencie reaktowym/komendzie watch.tsx
|
|
8
|
+
export class ThemeWatchInitializer extends SyncFeatureInitializer {
|
|
9
|
+
static featureName = THEME_WATCH_FEATURE_NAME;
|
|
10
|
+
init() {
|
|
11
|
+
const themePushApi = this.getApiSync(THEME_PUSH_API_NAME);
|
|
12
|
+
const service = new ThemeWatchService({ themePushApi, browserClient: Browser });
|
|
13
|
+
return {
|
|
14
|
+
cores: [
|
|
15
|
+
{
|
|
16
|
+
type: FEATURE_CORES_TYPES.api,
|
|
17
|
+
instance: new ThemeWatchApi(service)
|
|
18
|
+
}
|
|
19
|
+
]
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
}
|
package/build/theme/index.js
CHANGED
|
@@ -15,6 +15,8 @@ import { ThemePushInitializer } from './features/theme/push/theme_push_initializ
|
|
|
15
15
|
import { ThemeVerifyInitializer } from './features/theme/verify/theme_verify_initializer.js';
|
|
16
16
|
import { ThemeDeleteInitializer } from './features/theme/delete/theme_delete_initalizer.js';
|
|
17
17
|
import { ThemeActionsInitializer } from './features/theme/actions/theme_actions_initializer.js';
|
|
18
|
+
import { ThemeWatchCommand } from './commands/watch/theme_watch_command.js';
|
|
19
|
+
import { ThemeWatchInitializer } from './features/theme/watch/theme_watch_initializer.js';
|
|
18
20
|
export const COMMANDS = {
|
|
19
21
|
[THEME_COMMANDS_NAME.list]: ThemeListCommand,
|
|
20
22
|
[THEME_COMMANDS_NAME.pull]: ThemePullCommand,
|
|
@@ -23,14 +25,16 @@ export const COMMANDS = {
|
|
|
23
25
|
[THEME_COMMANDS_NAME.verify]: ThemeVerifyCommand,
|
|
24
26
|
[THEME_COMMANDS_NAME.info]: ThemeInfoCommand,
|
|
25
27
|
[THEME_COMMANDS_NAME.delete]: ThemeDeleteCommand,
|
|
26
|
-
[THEME_COMMANDS_NAME.publish]: ThemePublishCommand
|
|
28
|
+
[THEME_COMMANDS_NAME.publish]: ThemePublishCommand,
|
|
29
|
+
[THEME_COMMANDS_NAME.watch]: ThemeWatchCommand
|
|
27
30
|
};
|
|
28
31
|
export const COMMAND_TO_FEATURES_MAP = {
|
|
29
32
|
[THEME_COMMANDS_NAME.pull]: [ThemeMergeInitializer, ThemeFetchInitializer],
|
|
30
33
|
[THEME_COMMANDS_NAME.init]: ThemeInitInitializer,
|
|
31
34
|
[THEME_COMMANDS_NAME.push]: [ThemeFetchInitializer, ThemePushInitializer],
|
|
32
35
|
[THEME_COMMANDS_NAME.verify]: ThemeVerifyInitializer,
|
|
33
|
-
[THEME_COMMANDS_NAME.delete]: ThemeDeleteInitializer
|
|
36
|
+
[THEME_COMMANDS_NAME.delete]: ThemeDeleteInitializer,
|
|
37
|
+
[THEME_COMMANDS_NAME.watch]: [ThemeFetchInitializer, ThemePushInitializer, ThemeWatchInitializer]
|
|
34
38
|
};
|
|
35
39
|
export const getThemeInitializersForCommand = (commandName) => {
|
|
36
40
|
if (!commandName)
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { Box } from '../box.js';
|
|
2
|
+
import { Text } from '../text.js';
|
|
3
|
+
import React, { useState } from 'react';
|
|
4
|
+
import TextInput from 'ink-text-input';
|
|
5
|
+
import { COMMAND_PREFIX } from '../../theme/commands/watch/theme_watch_constants.js';
|
|
6
|
+
export const CommandInput = ({ onSubmit, commands = [] }) => {
|
|
7
|
+
const [query, setQuery] = useState('');
|
|
8
|
+
const showSuggestions = query.startsWith(COMMAND_PREFIX);
|
|
9
|
+
const searchTerm = query.substring(1);
|
|
10
|
+
const filteredCommands = commands.filter((cmd) => cmd.key.startsWith(searchTerm));
|
|
11
|
+
const handleSubmit = (value) => {
|
|
12
|
+
onSubmit(value);
|
|
13
|
+
setQuery('');
|
|
14
|
+
};
|
|
15
|
+
return (React.createElement(Box, { flexDirection: "column" },
|
|
16
|
+
React.createElement(Box, { borderStyle: "round", borderColor: "blue", paddingX: 1, flexDirection: "row" },
|
|
17
|
+
React.createElement(Text, { color: "blue" }, '> '),
|
|
18
|
+
React.createElement(TextInput, { value: query, onChange: setQuery, onSubmit: handleSubmit, placeholder: `Type ${COMMAND_PREFIX} to see commands...` })),
|
|
19
|
+
showSuggestions ? (React.createElement(Box, { flexDirection: "column", marginTop: 1, marginLeft: 2 },
|
|
20
|
+
filteredCommands.map((cmd) => (React.createElement(Box, { key: cmd.key },
|
|
21
|
+
React.createElement(Box, { width: 15 },
|
|
22
|
+
React.createElement(Text, { color: "cyan", bold: true }, cmd.key)),
|
|
23
|
+
React.createElement(Text, { color: "gray" }, cmd.description)))),
|
|
24
|
+
filteredCommands.length === 0 && React.createElement(Text, { color: "red" }, "No matching commands found"))) : null));
|
|
25
|
+
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { CSS_TEXT_COLORS } from '../color_constants.js';
|
|
2
|
+
export const LOG_TYPES = {
|
|
3
|
+
info: 'info',
|
|
4
|
+
success: 'success',
|
|
5
|
+
error: 'error',
|
|
6
|
+
warning: 'warning',
|
|
7
|
+
custom: 'custom'
|
|
8
|
+
};
|
|
9
|
+
export const MAP_LOG_TYPE_TO_TEXT_COLOR = {
|
|
10
|
+
[LOG_TYPES.info]: CSS_TEXT_COLORS.info,
|
|
11
|
+
[LOG_TYPES.success]: CSS_TEXT_COLORS.success,
|
|
12
|
+
[LOG_TYPES.error]: CSS_TEXT_COLORS.danger,
|
|
13
|
+
[LOG_TYPES.warning]: CSS_TEXT_COLORS.warning
|
|
14
|
+
};
|
|
15
|
+
export const MAP_LOG_TYPE_TO_LABEL = {
|
|
16
|
+
[LOG_TYPES.info]: 'INFO',
|
|
17
|
+
[LOG_TYPES.success]: 'SUCCESS',
|
|
18
|
+
[LOG_TYPES.error]: 'ERROR',
|
|
19
|
+
[LOG_TYPES.warning]: 'WARNING'
|
|
20
|
+
};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { Box } from '../box.js';
|
|
2
|
+
import { Text } from '../text.js';
|
|
3
|
+
import React from 'react';
|
|
4
|
+
import { LOG_TYPES, MAP_LOG_TYPE_TO_LABEL, MAP_LOG_TYPE_TO_TEXT_COLOR } from './logs_constants.js';
|
|
5
|
+
export const LogsList = ({ logs }) => {
|
|
6
|
+
return (React.createElement(Box, { flexDirection: "column", marginBottom: 1, width: "100%" }, logs.map((log) => {
|
|
7
|
+
if (log.type === LOG_TYPES.custom)
|
|
8
|
+
return React.createElement(Box, { key: log.id }, log.content);
|
|
9
|
+
const logLabel = MAP_LOG_TYPE_TO_LABEL[log.type];
|
|
10
|
+
const logColor = MAP_LOG_TYPE_TO_TEXT_COLOR[log.type];
|
|
11
|
+
return typeof log.content === 'string' ? (React.createElement(Text, { key: log.id },
|
|
12
|
+
React.createElement(Text, { color: logColor },
|
|
13
|
+
"[",
|
|
14
|
+
logLabel,
|
|
15
|
+
"] "),
|
|
16
|
+
log.content)) : (log.content);
|
|
17
|
+
})));
|
|
18
|
+
};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { useState } from 'react';
|
|
2
|
+
import { LOG_TYPES } from './logs_constants.js';
|
|
3
|
+
import { LogEntry } from './log_entry.js';
|
|
4
|
+
import { v4 as uuid } from 'uuid';
|
|
5
|
+
export const useLogs = (maxLogs) => {
|
|
6
|
+
const [logs, setLogs] = useState([]);
|
|
7
|
+
const addLog = (content, type = LOG_TYPES.info) => {
|
|
8
|
+
setLogs((prev) => {
|
|
9
|
+
const newLogs = [
|
|
10
|
+
...prev,
|
|
11
|
+
new LogEntry({
|
|
12
|
+
id: uuid(),
|
|
13
|
+
type,
|
|
14
|
+
content,
|
|
15
|
+
timestamp: Date.now()
|
|
16
|
+
})
|
|
17
|
+
];
|
|
18
|
+
return newLogs.slice(-maxLogs);
|
|
19
|
+
});
|
|
20
|
+
};
|
|
21
|
+
const clearLogs = () => setLogs([]);
|
|
22
|
+
return { logs, addLog, clearLogs };
|
|
23
|
+
};
|
|
@@ -16,6 +16,7 @@ import { MessageBox } from '../message_box/message_box.js';
|
|
|
16
16
|
import { MESSAGE_BOX_VARIANTS } from '../message_box/message_box_constants.js';
|
|
17
17
|
import { TABLE_COMPONENT_DATA } from './ui_dump_constants.js';
|
|
18
18
|
import { Table } from '../table/table.js';
|
|
19
|
+
import { Text } from '../text.js';
|
|
19
20
|
export const UiDump = () => {
|
|
20
21
|
return (React.createElement(Box, { flexDirection: "column", gap: 1 },
|
|
21
22
|
React.createElement(UiComponentBox, { name: "Icons" },
|
|
@@ -41,13 +42,17 @@ export const UiDump = () => {
|
|
|
41
42
|
React.createElement(Flag, null, "--with-settings"),
|
|
42
43
|
"` flag.")),
|
|
43
44
|
React.createElement(UiComponentBox, { name: "Success Message Box" },
|
|
44
|
-
React.createElement(MessageBox, { header: "Theme created!", type: MESSAGE_BOX_VARIANTS.success },
|
|
45
|
+
React.createElement(MessageBox, { header: "Theme created!", type: MESSAGE_BOX_VARIANTS.success },
|
|
46
|
+
React.createElement(Text, null, "A copy of theme ID: 1 (\"Storefront\") has been created with the name \"Storefront Copy 2\" and ID: 5."))),
|
|
45
47
|
React.createElement(UiComponentBox, { name: "Info Message Box" },
|
|
46
|
-
React.createElement(MessageBox, { header: "Theme created!", type: MESSAGE_BOX_VARIANTS.info },
|
|
48
|
+
React.createElement(MessageBox, { header: "Theme created!", type: MESSAGE_BOX_VARIANTS.info },
|
|
49
|
+
React.createElement(Text, null, "A copy of theme ID: 1 (\"Storefront\") has been created with the name \"Storefront Copy 2\" and ID: 5."))),
|
|
47
50
|
React.createElement(UiComponentBox, { name: "Warnging Message Box" },
|
|
48
|
-
React.createElement(MessageBox, { header: "Theme created!", type: MESSAGE_BOX_VARIANTS.warning },
|
|
51
|
+
React.createElement(MessageBox, { header: "Theme created!", type: MESSAGE_BOX_VARIANTS.warning },
|
|
52
|
+
React.createElement(Text, null, "A copy of theme ID: 1 (\"Storefront\") has been created with the name \"Storefront Copy 2\" and ID: 5."))),
|
|
49
53
|
React.createElement(UiComponentBox, { name: "Error Message Box" },
|
|
50
|
-
React.createElement(MessageBox, { header: "Theme created!", type: MESSAGE_BOX_VARIANTS.error },
|
|
54
|
+
React.createElement(MessageBox, { header: "Theme created!", type: MESSAGE_BOX_VARIANTS.error },
|
|
55
|
+
React.createElement(Text, null, "A copy of theme ID: 1 (\"Storefront\") has been created with the name \"Storefront Copy 2\" and ID: 5."))),
|
|
51
56
|
React.createElement(UiComponentBox, { name: "Table" },
|
|
52
57
|
React.createElement(Table, { data: TABLE_COMPONENT_DATA }))));
|
|
53
58
|
};
|
|
@@ -12,8 +12,6 @@ export const computeFileChecksum = async (filePath, algorithm = 'md5') => {
|
|
|
12
12
|
const hash = createHash(algorithm);
|
|
13
13
|
const stream = createReadStream(filePath);
|
|
14
14
|
stream.on('data', (data) => {
|
|
15
|
-
console.log('data chunk received for hashing');
|
|
16
|
-
console.log('data', data);
|
|
17
15
|
hash.update(data);
|
|
18
16
|
});
|
|
19
17
|
stream.on('close', () => {
|
|
@@ -100,7 +100,7 @@ export const getAllFilesAndDirectoriesInside = async (path, options) => {
|
|
|
100
100
|
withFileTypes
|
|
101
101
|
});
|
|
102
102
|
};
|
|
103
|
-
export const getAllDirectoriesNamesInside = async (path, options) => {
|
|
103
|
+
export const getAllDirectoriesNamesInside = async (path, options = { recursive: false, hidden: false }) => {
|
|
104
104
|
const { recursive = true, hidden = true } = options;
|
|
105
105
|
const files = await fsPromises.readdir(path, {
|
|
106
106
|
recursive,
|
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.7.1-1",
|
|
6
6
|
"description": "CLI tool for Shoper",
|
|
7
7
|
"author": "Joanna Firek",
|
|
8
8
|
"license": "MIT",
|
|
@@ -54,6 +54,7 @@
|
|
|
54
54
|
"ink": "6.0.1",
|
|
55
55
|
"ink-link": "4.1.0",
|
|
56
56
|
"ink-gradient": "3.0.0",
|
|
57
|
+
"ink-text-input": "6.0.0",
|
|
57
58
|
"inquirer": "12.5.2",
|
|
58
59
|
"inquirer-select-line": "1.1.3",
|
|
59
60
|
"is-hidden-file": "1.1.2",
|
|
@@ -67,15 +68,18 @@
|
|
|
67
68
|
"pino-pretty": "13.1.2",
|
|
68
69
|
"react": "19.1.0",
|
|
69
70
|
"reflect-metadata": "0.2.2",
|
|
71
|
+
"chokidar": "4.0.3",
|
|
70
72
|
"rxjs": "7.8.2",
|
|
71
73
|
"semver": "7.7.1",
|
|
72
74
|
"strip-ansi": "7.1.0",
|
|
73
75
|
"tmp-promise": "3.0.3",
|
|
74
76
|
"uuid": "11.1.0",
|
|
77
|
+
"micromatch": "4.0.8",
|
|
75
78
|
"walk-sync": "3.0.0",
|
|
76
79
|
"yauzl": "3.2.0",
|
|
80
|
+
"figlet": "1.9.4",
|
|
77
81
|
"yazl": "3.3.1",
|
|
78
|
-
"
|
|
82
|
+
"puppeteer": "24.31.0"
|
|
79
83
|
},
|
|
80
84
|
"devDependencies": {
|
|
81
85
|
"@babel/core": "7.27.1",
|
|
@@ -86,14 +90,15 @@
|
|
|
86
90
|
"@types/fs-extra": "11.0.4",
|
|
87
91
|
"@types/jest": "29.5.14",
|
|
88
92
|
"@types/jsonwebtoken": "9.0.9",
|
|
89
|
-
"@types/klaw": "3.0.7",
|
|
90
|
-
"@types/lodash": "4.17.17",
|
|
91
93
|
"@types/node": "18.19.84",
|
|
92
94
|
"@types/react": "19.1.8",
|
|
93
95
|
"@types/semver": "7.7.0",
|
|
94
96
|
"@types/tmp": "0.2.6",
|
|
95
97
|
"@types/yauzl": "2.10.3",
|
|
96
98
|
"@types/yazl": "2.4.6",
|
|
99
|
+
"@types/klaw": "3.0.7",
|
|
100
|
+
"@types/lodash": "4.17.17",
|
|
101
|
+
"@types/micromatch": "4.0.9",
|
|
97
102
|
"@typescript-eslint/eslint-plugin": "8.29.1",
|
|
98
103
|
"babel-jest": "29.7.0",
|
|
99
104
|
"eslint": "9.39.1",
|