@shoper/cli 0.6.4-3 → 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.
Files changed (50) hide show
  1. package/build/cli/class/errors/http/http_errors_factory.js +1 -1
  2. package/build/cli/core/cli_setup.js +6 -6
  3. package/build/cli/index.js +1 -0
  4. package/build/cli/utilities/features/logger/logs/app_logs_constants.js +2 -1
  5. package/build/index.js +2 -2
  6. package/build/theme/class/archive/theme_archive.js +1 -45
  7. package/build/theme/class/browser/browser.js +108 -0
  8. package/build/theme/class/checksums/theme_checksums.js +50 -12
  9. package/build/theme/commands/pull/theme_pull_command.js +4 -3
  10. package/build/theme/commands/push/theme_push_command.js +17 -8
  11. package/build/theme/commands/theme_commands_constants.js +2 -1
  12. package/build/theme/commands/theme_verify_command.js +3 -3
  13. package/build/theme/commands/ui/theme_error.js +6 -6
  14. package/build/theme/commands/watch/theme_watch_command.js +88 -0
  15. package/build/theme/commands/watch/theme_watch_constants.js +21 -0
  16. package/build/theme/commands/watch/theme_watch_utils.js +32 -0
  17. package/build/theme/commands/watch/theme_watching_info.js +14 -0
  18. package/build/theme/commands/watch/watch.js +55 -0
  19. package/build/theme/features/theme/actions/theme_actions_constants.js +2 -1
  20. package/build/theme/features/theme/actions/theme_actions_utils.js +60 -5
  21. package/build/theme/features/theme/fetch/service/theme_fetch_service.js +2 -2
  22. package/build/theme/features/theme/init/service/theme_init_service.js +2 -2
  23. package/build/theme/features/theme/merge/service/theme_merge_service.js +2 -2
  24. package/build/theme/features/theme/push/api/theme_push_api.js +2 -2
  25. package/build/theme/features/theme/push/service/theme_push_service.js +93 -33
  26. package/build/theme/features/theme/push/service/theme_push_service_types.js +1 -0
  27. package/build/theme/features/theme/push/theme_push_utils.js +2 -2
  28. package/build/theme/features/theme/utils/archive/theme_archive_utils.js +26 -0
  29. package/build/theme/{class/archive/theme_archive_errors_factory.js → features/theme/utils/archive/theme_archive_utils_errors_factory.js} +1 -1
  30. package/build/theme/features/theme/utils/files/them_files_constants.js +1 -0
  31. package/build/theme/features/theme/utils/{files_structure/theme_files_structure_utils.js → files/theme_files_utils.js} +34 -23
  32. package/build/theme/features/theme/utils/files_structure/theme_file_structure_errors_factory.js +1 -9
  33. package/build/theme/features/theme/utils/hidden_directory/hidden_directory_utils.js +0 -1
  34. package/build/theme/features/theme/verify/verify/theme_verify_service.js +19 -12
  35. package/build/theme/features/theme/watch/api/theme_watch_api.js +19 -0
  36. package/build/theme/features/theme/watch/service/theme_watch_service.js +167 -0
  37. package/build/theme/features/theme/watch/theme_watch_constants.js +4 -0
  38. package/build/theme/features/theme/watch/theme_watch_initializer.js +22 -0
  39. package/build/theme/index.js +6 -2
  40. package/build/ui/command_input/command_input.js +25 -0
  41. package/build/ui/logs/log_entry.js +12 -0
  42. package/build/ui/logs/logs_constants.js +20 -0
  43. package/build/ui/logs/logs_list.js +18 -0
  44. package/build/ui/logs/use_logs.js +23 -0
  45. package/build/ui/ui_dump/ui_dump.js +9 -4
  46. package/build/utils/array_utils.js +3 -0
  47. package/build/utils/checksums/checksums_utils.js +0 -4
  48. package/build/utils/fs/fs_constants.js +6 -0
  49. package/build/utils/fs/fs_utils.js +1 -1
  50. 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 { ThemeArchive } from '../../../../class/archive/theme_archive.js';
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 { ThemeFilesStructureUtils } from '../../utils/files_structure/theme_files_structure_utils.js';
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 = await ArrayUtils.asyncFilter(filesRecords, async ({ path }) => (await themeChecksums.hasThemeFileBeenModified(path)) || !(await themeChecksums.hasThemeFileBeenCreated(path)));
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 new ThemeArchive(themeRootDir).createFullArchive({
51
+ await ThemeArchiveUtils.create({
50
52
  dist: themeArchivePath,
51
- actionValue: THEME_WILDCARD_ACTION_NAME,
52
- actionType: THEME_ACTIONS_TYPES.push,
53
- logger: this.#loggerApi
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 ThemeFilesStructureUtils.removeAFilesListFile(themeRootDir);
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 ThemeFilesStructureUtils.createAFilesListFile(themeRootDir, ThemeFilesStructureUtils.mapFilesRecordsToFilesList(filesRecords));
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,4 @@
1
+ export const THEME_WATCH_FEATURE_NAME = 'ThemeWatch';
2
+ export const THEME_WATCH_API_NAME = 'ThemeWatchApi';
3
+ export const THEME_BATCH_DELAY_MS = 750;
4
+ export const SHOP_BROWSER_TAB_ID = 'shop-page';
@@ -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
+ }
@@ -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,12 @@
1
+ export class LogEntry {
2
+ id;
3
+ type;
4
+ content;
5
+ timestamp;
6
+ constructor({ id, type, content, timestamp }) {
7
+ this.id = id;
8
+ this.type = type;
9
+ this.content = content;
10
+ this.timestamp = timestamp;
11
+ }
12
+ }
@@ -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 }, "A copy of theme ID: 1 (\"Storefront\") has been created with the name \"Storefront Copy 2\" and ID: 5.")),
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 }, "A copy of theme ID: 1 (\"Storefront\") has been created with the name \"Storefront Copy 2\" and ID: 5.")),
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 }, "A copy of theme ID: 1 (\"Storefront\") has been created with the name \"Storefront Copy 2\" and ID: 5.")),
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 }, "A copy of theme ID: 1 (\"Storefront\") has been created with the name \"Storefront Copy 2\" and ID: 5.")),
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
  };
@@ -4,3 +4,6 @@ export const asyncFilter = async (array, callback) => {
4
4
  return array.filter((_v, index) => results[index]);
5
5
  });
6
6
  };
7
+ export const asyncMap = async (array, callback) => {
8
+ return Promise.all(array.map(callback));
9
+ };
@@ -12,16 +12,12 @@ 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', () => {
20
- console.log('file read completed, finalizing checksum computation');
21
18
  resolve(hash.digest('hex'));
22
19
  });
23
20
  stream.on('error', (err) => {
24
- console.log('error while reading file for checksum computation', err);
25
21
  reject(err);
26
22
  });
27
23
  });
@@ -0,0 +1,6 @@
1
+ export const FILE_STATES = {
2
+ created: 'created',
3
+ modified: 'modified',
4
+ deleted: 'deleted',
5
+ unchanged: 'unchanged'
6
+ };
@@ -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.6.4-3",
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
- "figlet": "1.9.4"
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",