@shoper/cli 0.1.0-9 → 0.2.1-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.
Files changed (184) hide show
  1. package/build/cli/auth/cli_auth_errors_factory.js +1 -1
  2. package/build/cli/auth/tokens/cli_auth_tokens_errors_factory.js +1 -1
  3. package/build/cli/auth/tokens/cli_auth_tokens_initalizer.js +1 -1
  4. package/build/cli/class/base_command.js +14 -0
  5. package/build/cli/class/errors/file_system_errors_factory.js +1 -1
  6. package/build/cli/class/errors/http/http_errors_constants.js +1 -0
  7. package/build/cli/class/errors/{http_errors_factory.js → http/http_errors_factory.js} +3 -2
  8. package/build/cli/commands/auth/cli_auth_add_token_command.js +22 -19
  9. package/build/cli/commands/auth/cli_auth_commands_constants.js +3 -0
  10. package/build/cli/commands/auth/cli_auth_commands_utils.js +46 -0
  11. package/build/cli/commands/auth/cli_auth_list_tokens_command.js +19 -6
  12. package/build/cli/commands/auth/cli_auth_remove_token_command.js +37 -13
  13. package/build/cli/commands/auth/cli_auth_switch_token_command.js +20 -11
  14. package/build/cli/commands/auth/ui/invalid_token_index_error.js +11 -0
  15. package/build/cli/commands/auth/ui/missing_credentials_error.js +9 -0
  16. package/build/cli/commands/auth/ui/missing_token_index_error.js +19 -0
  17. package/build/cli/commands/cli_ui_dump_command.js +11 -0
  18. package/build/cli/commands/cli_update_command.js +13 -4
  19. package/build/cli/commands/commands_constants.js +6 -4
  20. package/build/cli/core/cli_setup.js +33 -14
  21. package/build/cli/features/controls/controls_constants.js +12 -0
  22. package/build/cli/features/controls/controls_dto_mappers.js +55 -0
  23. package/build/cli/features/controls/ui/controls_mappers.js +62 -0
  24. package/build/cli/features/controls/ui/form.js +4 -0
  25. package/build/cli/features/controls/ui/form_constants.js +7 -0
  26. package/build/cli/features/controls/validators/greater_eq_than_validator.js +5 -0
  27. package/build/cli/features/controls/validators/length_validator.js +6 -0
  28. package/build/cli/features/controls/validators/required_validator.js +5 -0
  29. package/build/cli/features/controls/validators/validator_constants.js +13 -0
  30. package/build/cli/features/execution_context/execution_context_service.js +5 -5
  31. package/build/cli/features/http_requester/http_requester_initializer.js +1 -1
  32. package/build/cli/hooks/authorization/ensure_authorization_hook.js +2 -2
  33. package/build/cli/hooks/authorization/ensure_authorization_hook_constants.js +7 -8
  34. package/build/index.js +37 -19
  35. package/build/theme/class/archive/theme_archive.js +44 -0
  36. package/build/theme/class/archive/theme_archive_errors_factory.js +11 -0
  37. package/build/theme/class/checksums/theme_checksums.js +170 -0
  38. package/build/theme/{features/theme/utils → class}/checksums/theme_checksums_error_factory.js +1 -1
  39. package/build/theme/class/checksums/theme_checksums_utils.js +17 -0
  40. package/build/theme/class/fetch_resources/fetch_resources.js +1 -0
  41. package/build/theme/class/fetch_resources/fetch_resources_errors_factory.js +1 -1
  42. package/build/theme/class/fetch_resources/fetch_resources_utils.js +4 -1
  43. package/build/theme/class/files_upload/theme_files_upload.js +61 -0
  44. package/build/theme/class/files_upload/theme_files_upload_http_api.js +23 -0
  45. package/build/theme/commands/delete/theme_delete_command.js +97 -0
  46. package/build/theme/commands/delete/ui/theme_deleted_successfully.js +15 -0
  47. package/build/theme/commands/delete/ui/theme_deletion_warning.js +10 -0
  48. package/build/theme/commands/info/theme_info_command.js +79 -0
  49. package/build/theme/commands/info/theme_info_command_utils.js +39 -0
  50. package/build/theme/commands/init/theme_init_command.js +84 -0
  51. package/build/theme/commands/init/ui/theme_created_success.js +15 -0
  52. package/build/theme/commands/list/theme_list_command.js +25 -0
  53. package/build/theme/commands/list/theme_list_command_utils.js +27 -0
  54. package/build/theme/commands/publish/theme_publish_command.js +76 -0
  55. package/build/theme/commands/pull/theme_pull_command.js +174 -0
  56. package/build/theme/commands/pull/ui/theme_pull_id_mismatch_error.js +7 -0
  57. package/build/theme/commands/pull/ui/theme_pull_unpublished_changes_warning.js +10 -0
  58. package/build/theme/commands/pull/ui/theme_pulled_success.js +5 -0
  59. package/build/theme/commands/push/theme_push_command.js +112 -0
  60. package/build/theme/commands/push/ui/theme_push_skip_into.js +7 -0
  61. package/build/theme/commands/push/ui/theme_pushed_success.js +5 -0
  62. package/build/theme/commands/push/ui/theme_unpermitted_actions_error.js +12 -0
  63. package/build/theme/commands/theme_commands_constants.js +9 -6
  64. package/build/theme/commands/theme_verify_command.js +64 -22
  65. package/build/theme/commands/ui/invalid_theme_id.js +11 -0
  66. package/build/theme/commands/ui/missing_theme_files.js +15 -0
  67. package/build/theme/commands/ui/missing_theme_id_error.js +13 -0
  68. package/build/theme/commands/ui/ouside_of_theme_directory_context_error.js +7 -0
  69. package/build/theme/commands/ui/theme_directory_context_error.js +7 -0
  70. package/build/theme/commands/ui/theme_error.js +29 -0
  71. package/build/theme/commands/ui/theme_work_url_mismatch.js +17 -0
  72. package/build/theme/commands/ui/unpermitted_command_error.js +18 -0
  73. package/build/theme/features/theme/actions/api/theme_actions_api.js +3 -0
  74. package/build/theme/features/theme/actions/service/theme_actions_service.js +15 -3
  75. package/build/theme/features/theme/actions/service/theme_actions_service_constants.js +1 -0
  76. package/build/theme/features/theme/actions/theme_actions_constants.js +5 -2
  77. package/build/theme/features/theme/actions/theme_actions_initializer.js +1 -1
  78. package/build/theme/features/theme/actions/theme_actions_utils.js +60 -7
  79. package/build/theme/features/theme/delete/api/theme_delete_api.js +13 -0
  80. package/build/theme/features/theme/delete/http/theme_delete_http_api.js +17 -0
  81. package/build/theme/features/theme/delete/service/theme_delete_service.js +31 -0
  82. package/build/theme/features/theme/delete/theme_delete_constants.js +2 -0
  83. package/build/theme/features/theme/delete/theme_delete_initalizer.js +20 -0
  84. package/build/theme/features/theme/fetch/service/theme_fetch_service.js +30 -10
  85. package/build/theme/features/theme/info/theme_info_utils.js +9 -0
  86. package/build/theme/features/theme/init/service/theme_init_service.js +9 -4
  87. package/build/theme/features/theme/init/theme_init_initializer.js +3 -3
  88. package/build/theme/features/theme/merge/api/theme_merge_api.js +0 -12
  89. package/build/theme/features/theme/merge/service/theme_merge_service.js +20 -28
  90. package/build/theme/features/theme/push/service/theme_push_service.js +67 -166
  91. package/build/theme/features/theme/push/theme_push_constants.js +2 -0
  92. package/build/theme/features/theme/push/theme_push_errors_factory.js +7 -4
  93. package/build/theme/features/theme/push/theme_push_initializer.js +1 -7
  94. package/build/theme/features/theme/push/theme_push_utils.js +25 -0
  95. package/build/theme/features/theme/skinstore/api/theme_skinstore_api.js +19 -0
  96. package/build/theme/features/theme/skinstore/http/theme_skinstore_http_api.js +17 -0
  97. package/build/theme/features/theme/skinstore/service/theme_skinstore_service.js +32 -0
  98. package/build/theme/features/theme/skinstore/theme_publish_constants.js +4 -0
  99. package/build/theme/features/theme/skinstore/theme_skinstore_initialzier.js +20 -0
  100. package/build/theme/features/theme/utils/files_structure/theme_file_structure_errors_factory.js +10 -0
  101. package/build/theme/features/theme/utils/files_structure/theme_files_structure_utils.js +76 -0
  102. package/build/theme/features/theme/utils/hidden_directory/hidden_directory_utils.js +32 -0
  103. package/build/theme/features/theme/utils/meta_data/theme_meta_data_constants.js +1 -0
  104. package/build/theme/features/theme/utils/meta_data/theme_meta_data_error_factory.js +15 -0
  105. package/build/theme/features/theme/utils/meta_data/theme_meta_data_utils.js +39 -0
  106. package/build/theme/features/theme/utils/{directories → resources}/theme_resources_with_id_directory_utils.js +3 -14
  107. package/build/theme/features/theme/utils/theme_images_utils.js +1 -19
  108. package/build/theme/features/theme/verify/api/theme_verify_api.js +13 -0
  109. package/build/theme/features/theme/verify/http/theme_verify_http_api.js +30 -0
  110. package/build/theme/features/theme/verify/theme_verify_constants.js +2 -0
  111. package/build/theme/features/theme/verify/theme_verify_initializer.js +19 -0
  112. package/build/theme/features/theme/verify/verify/theme_verify_service.js +55 -0
  113. package/build/theme/features/themes/list/api/themes_list_api.js +3 -0
  114. package/build/theme/features/themes/list/services/themes_list_service.js +13 -7
  115. package/build/theme/hooks/{ensure_theme_meta_data_untouched.js → ensure_theme_meta_data_untouched_hook.js} +2 -2
  116. package/build/theme/hooks/theme_checksums/ensure_theme_current_checksums_up_to_date_constants.js +1 -2
  117. package/build/theme/hooks/theme_checksums/{ensure_theme_current_checksums_up_to_date.js → ensure_theme_current_checksums_up_to_date_hook.js} +2 -5
  118. package/build/theme/hooks/themes_actions/ensure_themes_actions_hook.js +1 -0
  119. package/build/theme/hooks/themes_actions/ensure_themes_actions_hook_constants.js +1 -1
  120. package/build/theme/index.js +34 -8
  121. package/build/theme/utils/directory_validator/directory_validator_constants.js +2 -6
  122. package/build/theme/utils/directory_validator/directory_validator_utils.js +7 -25
  123. package/build/ui/box.js +2 -0
  124. package/build/ui/color_constants.js +30 -0
  125. package/build/ui/command.js +6 -0
  126. package/build/ui/file_name.js +6 -0
  127. package/build/ui/flag.js +6 -0
  128. package/build/ui/icons/error_icon.js +7 -0
  129. package/build/ui/icons/info_icon.js +7 -0
  130. package/build/ui/icons/success_icon.js +7 -0
  131. package/build/ui/icons/warning_icon.js +7 -0
  132. package/build/ui/link.js +8 -0
  133. package/build/ui/list/list.js +10 -0
  134. package/build/ui/list/list_constants.js +4 -0
  135. package/build/ui/list/list_item.js +11 -0
  136. package/build/ui/message_box/error.js +4 -0
  137. package/build/ui/message_box/info.js +4 -0
  138. package/build/ui/message_box/message_box.js +11 -0
  139. package/build/ui/message_box/message_box_constants.js +24 -0
  140. package/build/ui/message_box/success.js +4 -0
  141. package/build/ui/message_box/warning.js +4 -0
  142. package/build/ui/prompts/prompt_confirmation.js +11 -0
  143. package/build/ui/prompts/prompt_input.js +10 -0
  144. package/build/ui/table/t_cell.js +7 -0
  145. package/build/ui/table/t_header.js +9 -0
  146. package/build/ui/table/t_row.js +5 -0
  147. package/build/ui/table/table.js +18 -0
  148. package/build/ui/text.js +2 -0
  149. package/build/ui/tip.js +9 -0
  150. package/build/ui/ui_dump/ui_component_box.js +9 -0
  151. package/build/ui/ui_dump/ui_dump.js +53 -0
  152. package/build/ui/ui_dump/ui_dump_constants.js +21 -0
  153. package/build/ui/ui_utils.js +11 -0
  154. package/build/ui/validation_errors/validation_error_content.js +19 -0
  155. package/build/ui/validation_errors/validation_errors.js +21 -0
  156. package/build/ui/validation_errors/validation_errors_utils.js +17 -0
  157. package/build/utils/checksums/checksums_utils.js +9 -26
  158. package/build/utils/download_file/download_file_errors_factory.js +1 -1
  159. package/build/utils/download_file/download_file_utils.js +4 -2
  160. package/build/utils/fs/errors/stream_read_error.js +1 -1
  161. package/build/utils/fs/errors/stream_write_error.js +1 -1
  162. package/build/utils/fs/fs_utils.js +12 -1
  163. package/build/utils/path_utils.js +18 -2
  164. package/build/utils/platform_utils.js +3 -0
  165. package/build/utils/zip/create_zip_utils.js +1 -1
  166. package/build/utils/zip/errors/create_zip_error.js +1 -1
  167. package/build/utils/zip/errors/open_zip_error.js +1 -1
  168. package/oclif.config.js +3 -3
  169. package/package.json +14 -9
  170. package/build/cli/commands/files_diff_command.js +0 -174
  171. package/build/theme/commands/theme_init_command.js +0 -53
  172. package/build/theme/commands/theme_list_command.js +0 -16
  173. package/build/theme/commands/theme_pull_command.js +0 -126
  174. package/build/theme/commands/theme_push_command.js +0 -65
  175. package/build/theme/commands/theme_show_changes_command.js +0 -60
  176. package/build/theme/features/theme/directory/theme_directory_utils.js +0 -76
  177. package/build/theme/features/theme/publish/theme_publish_constants.js +0 -2
  178. package/build/theme/features/theme/utils/checksums/theme_checksums_utils.js +0 -94
  179. /package/build/cli/{features → class}/caches/cache_factory.js +0 -0
  180. /package/build/cli/{features → class}/caches/json_cache/json_cache.js +0 -0
  181. /package/build/cli/{features → class}/caches/memory_cache.js +0 -0
  182. /package/build/cli/class/errors/{app_error → app}/app_error.js +0 -0
  183. /package/build/cli/class/errors/{app_error → app}/app_error_constants.js +0 -0
  184. /package/build/theme/features/theme/{publish → skinstore}/theme_publish_utils.js +0 -0
@@ -0,0 +1,10 @@
1
+ import { Warning } from '../../../../ui/message_box/warning.js';
2
+ import { List } from '../../../../ui/list/list.js';
3
+ import React from 'react';
4
+ export const ThemePullUnpublishedChangesWarning = ({ changes }) => {
5
+ const items = changes.map(([action, path]) => ({
6
+ content: `${action} - ${path}`
7
+ }));
8
+ return (React.createElement(Warning, { header: "Warning: You have unpushed local changes in this theme directory. Pulling now may overwrite your local modifications." },
9
+ React.createElement(List, { items: items })));
10
+ };
@@ -0,0 +1,5 @@
1
+ import React from 'react';
2
+ import { Success } from '../../../../ui/message_box/success.js';
3
+ export const ThemePulledSuccess = () => {
4
+ return React.createElement(Success, { header: "Theme was pulled" });
5
+ };
@@ -0,0 +1,112 @@
1
+ import { BaseThemeCommand } from '../../class/base_theme_command.js';
2
+ import { CLI_AUTH_API_NAME } from '../../../cli/auth/cli_auth_constants.js';
3
+ import { EXECUTION_CONTEXT_API_NAME, EXECUTION_CONTEXTS } from '../../../cli/features/execution_context/execution_context_constants.js';
4
+ import { THEME_ACTIONS_API_NAME, THEME_ACTIONS_TYPES } from '../../features/theme/actions/theme_actions_constants.js';
5
+ import { THEME_PUSH_API_NAME } from '../../features/theme/push/theme_push_constants.js';
6
+ import { ThemeMetaDataUtils } from '../../features/theme/utils/meta_data/theme_meta_data_utils.js';
7
+ import { mapChecksumToTree } from '../../../utils/checksums/checksums_utils.js';
8
+ import { mapToPermissionsTree } from '../../utils/directory_validator/directory_validator_utils.js';
9
+ import { renderOnce } from '../../../ui/ui_utils.js';
10
+ import { UnpermittedCommandError } from '../ui/unpermitted_command_error.js';
11
+ import React from 'react';
12
+ import { OutsideOfThemeDirectoryContextError } from '../ui/ouside_of_theme_directory_context_error.js';
13
+ import { MissingCredentialsError } from '../../../cli/commands/auth/ui/missing_credentials_error.js';
14
+ import { ThemePushedSuccess } from './ui/theme_pushed_success.js';
15
+ import ora from 'ora';
16
+ import { ThemePushSkipInfo } from './ui/theme_push_skip_into.js';
17
+ import { ThemeUnpermittedActionsError } from './ui/theme_unpermitted_actions_error.js';
18
+ import { ThemeFilesStructureUtils } from '../../features/theme/utils/files_structure/theme_files_structure_utils.js';
19
+ import { ThemeInfoUtils } from '../../features/theme/info/theme_info_utils.js';
20
+ import { ThemeChecksums } from '../../class/checksums/theme_checksums.js';
21
+ import { ThemeFilesUpload } from '../../class/files_upload/theme_files_upload.js';
22
+ import { ThemeFilesUploadHttpApi } from '../../class/files_upload/theme_files_upload_http_api.js';
23
+ import { HTTP_REQUESTER_API_NAME } from '@dreamcommerce/star_core';
24
+ import { MissingThemeFiles } from '../ui/missing_theme_files.js';
25
+ import { SHOPER_THEME_METADATA_DIR } from '../../constants/directory_contstants.js';
26
+ import { THEME_FILES_STRUCTURE_FILE_NAME } from '../../features/theme/theme_constants.js';
27
+ import { ThemeError } from '../ui/theme_error.js';
28
+ export class ThemePushCommand extends BaseThemeCommand {
29
+ static summary = 'Uploads your local theme files to the store and overwrites the current version of the theme in your store.';
30
+ static description = 'Check your local changes before pushing.\n\nYou must run this command from a specific theme directory (ID not needed).';
31
+ static examples = [
32
+ {
33
+ description: 'This will replace the store version with your local copy.',
34
+ command: '<%= config.bin %> <%= command.id %>'
35
+ }
36
+ ];
37
+ async run() {
38
+ const cliAuthApi = this.getApi(CLI_AUTH_API_NAME);
39
+ const themePushApi = this.getApi(THEME_PUSH_API_NAME);
40
+ const themeActionsApi = this.getApi(THEME_ACTIONS_API_NAME);
41
+ const executionContextApi = this.getApi(EXECUTION_CONTEXT_API_NAME);
42
+ const credentials = cliAuthApi.getCredentials();
43
+ if (!credentials) {
44
+ renderOnce(React.createElement(MissingCredentialsError, null));
45
+ return;
46
+ }
47
+ const executionContext = await executionContextApi.getExecutionContext();
48
+ if (executionContext.type !== EXECUTION_CONTEXTS.theme) {
49
+ renderOnce(React.createElement(OutsideOfThemeDirectoryContextError, null));
50
+ return;
51
+ }
52
+ let spinner;
53
+ try {
54
+ const themeChecksums = new ThemeChecksums(executionContext.themeRootDir);
55
+ await ThemeMetaDataUtils.ensureThemeWorkUrlMatch(executionContext.themeRootDir, credentials.shopUrl);
56
+ const pushAction = themeActionsApi.getThemeAction({
57
+ actionType: THEME_ACTIONS_TYPES.push,
58
+ themeId: executionContext.themeId,
59
+ credentials
60
+ });
61
+ if (!pushAction) {
62
+ renderOnce(React.createElement(UnpermittedCommandError, { themeId: executionContext.themeId, commandName: "push" }));
63
+ return;
64
+ }
65
+ const initialChecksums = await themeChecksums.getInitialChecksums();
66
+ const permissions = await ThemeFilesStructureUtils.getFilesPermissions(executionContext.themeRootDir);
67
+ const themeFilesUploadApi = new ThemeFilesUpload({
68
+ themeRootDir: executionContext.themeRootDir,
69
+ credentials,
70
+ themeFilesUploadHttpApi: new ThemeFilesUploadHttpApi(this.getApi(HTTP_REQUESTER_API_NAME))
71
+ });
72
+ if (!initialChecksums || !permissions)
73
+ this.error('Theme checksums or permissions not found. Please ensure you are in the correct theme directory.');
74
+ if (!(await themeChecksums.hasThemeBeenModified())) {
75
+ renderOnce(React.createElement(ThemePushSkipInfo, null));
76
+ return;
77
+ }
78
+ const validationResult = await ThemeFilesStructureUtils.validateThemeDirectoryStructure({
79
+ //TDO przeniesc do theme checksums
80
+ checksums: mapChecksumToTree(initialChecksums),
81
+ permissions: mapToPermissionsTree(permissions),
82
+ rootDirectory: executionContext.themeRootDir
83
+ });
84
+ //TODO jak wysyla folder z nazwa posiadajaco \ na windows, wychodzimy
85
+ //TODO validacja folderów przed pushem
86
+ if (!validationResult.isValid) {
87
+ renderOnce(React.createElement(ThemeUnpermittedActionsError, { unpermittedActions: validationResult.unpermittedActions }));
88
+ return;
89
+ }
90
+ const filesStructure = await ThemeFilesStructureUtils.getThemeFilesStructure(executionContext.themeRootDir);
91
+ if (!filesStructure) {
92
+ renderOnce(React.createElement(MissingThemeFiles, { files: `${SHOPER_THEME_METADATA_DIR}/${THEME_FILES_STRUCTURE_FILE_NAME}` }));
93
+ return;
94
+ }
95
+ spinner = ora('Pushing theme...').start();
96
+ await themePushApi.push({
97
+ credentials,
98
+ filesStructure,
99
+ pushAction,
100
+ themeChecksums,
101
+ executionContext,
102
+ themeFilesUploadApi
103
+ });
104
+ spinner.stop();
105
+ renderOnce(React.createElement(ThemePushedSuccess, { themeName: await ThemeInfoUtils.getThemeName(executionContext.themeRootDir) }));
106
+ }
107
+ catch (err) {
108
+ spinner?.stop();
109
+ renderOnce(React.createElement(ThemeError, { err: err, executionContext: executionContext }));
110
+ }
111
+ }
112
+ }
@@ -0,0 +1,7 @@
1
+ import React from 'react';
2
+ import { Info } from '../../../../ui/message_box/info.js';
3
+ import { Text } from '../../../../ui/text.js';
4
+ export const ThemePushSkipInfo = () => {
5
+ return (React.createElement(Info, null,
6
+ React.createElement(Text, null, "Nothing to push, skipped.")));
7
+ };
@@ -0,0 +1,5 @@
1
+ import React from 'react';
2
+ import { Success } from '../../../../ui/message_box/success.js';
3
+ export const ThemePushedSuccess = ({ themeName }) => {
4
+ return React.createElement(Success, { header: `Success: Theme "${themeName}" was pushed` });
5
+ };
@@ -0,0 +1,12 @@
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
+ export const ThemeUnpermittedActionsError = ({ unpermittedActions }) => {
6
+ const items = unpermittedActions.map((action) => ({
7
+ content: `${action.type} - ${action.relativePath}`
8
+ }));
9
+ return (React.createElement(Error, { header: "Theme validation error" },
10
+ React.createElement(Text, null, "Theme file structure is not valid. The following actions are not permitted:"),
11
+ React.createElement(List, { items: items })));
12
+ };
@@ -1,8 +1,11 @@
1
+ export const THEME_TOPIC_NAME = 'theme';
1
2
  export const THEME_COMMANDS_NAME = {
2
- list: 'theme:list',
3
- pull: 'theme:pull',
4
- init: 'theme:init',
5
- push: 'theme:push',
6
- showChanges: 'theme:show-changes',
7
- verify: 'theme:verify'
3
+ list: `${THEME_TOPIC_NAME}:list`,
4
+ pull: `${THEME_TOPIC_NAME}:pull`,
5
+ init: `${THEME_TOPIC_NAME}:init`,
6
+ push: `${THEME_TOPIC_NAME}:push`,
7
+ verify: `${THEME_TOPIC_NAME}:verify`,
8
+ info: `${THEME_TOPIC_NAME}:info`,
9
+ delete: `${THEME_TOPIC_NAME}:delete`,
10
+ publish: `${THEME_TOPIC_NAME}:publish`
8
11
  };
@@ -1,43 +1,85 @@
1
1
  import { BaseThemeCommand } from '../class/base_theme_command.js';
2
2
  import { CLI_AUTH_API_NAME } from '../../cli/auth/cli_auth_constants.js';
3
3
  import { EXECUTION_CONTEXT_API_NAME, EXECUTION_CONTEXTS } from '../../cli/features/execution_context/execution_context_constants.js';
4
- import { ThemeDirectoryUtils } from '../features/theme/directory/theme_directory_utils.js';
5
- import { mapToPermissionsTree } from '../utils/directory_validator/directory_validator_utils.js';
6
- import { mapChecksumToTree } from '../../utils/checksums/checksums_utils.js';
7
- import { ThemeChecksumsUtils } from '../features/theme/utils/checksums/theme_checksums_utils.js';
4
+ import { ThemeFilesStructureUtils } from '../features/theme/utils/files_structure/theme_files_structure_utils.js';
5
+ import { ThemeChecksums } from '../class/checksums/theme_checksums.js';
6
+ import { THEME_VERIFY_API_NAME } from '../features/theme/verify/theme_verify_constants.js';
7
+ import { THEME_ACTIONS_API_NAME, THEME_ACTIONS_TYPES } from '../features/theme/actions/theme_actions_constants.js';
8
+ import { ThemeFilesUpload } from '../class/files_upload/theme_files_upload.js';
9
+ import { ThemeFilesUploadHttpApi } from '../class/files_upload/theme_files_upload_http_api.js';
10
+ import { HTTP_REQUESTER_API_NAME } from '@dreamcommerce/star_core';
11
+ import { renderOnce } from '../../ui/ui_utils.js';
12
+ import { MissingThemeFiles } from './ui/missing_theme_files.js';
13
+ import { SHOPER_THEME_METADATA_DIR } from '../constants/directory_contstants.js';
14
+ import { THEME_FILES_STRUCTURE_FILE_NAME } from '../features/theme/theme_constants.js';
15
+ import React from 'react';
16
+ import { Success } from '../../ui/message_box/success.js';
17
+ import { MissingCredentialsError } from '../../cli/commands/auth/ui/missing_credentials_error.js';
18
+ import { OutsideOfThemeDirectoryContextError } from './ui/ouside_of_theme_directory_context_error.js';
19
+ import ora from 'ora';
20
+ import { ThemeError } from './ui/theme_error.js';
8
21
  export class ThemeVerifyCommand extends BaseThemeCommand {
9
22
  static description = 'Verify theme files structure';
10
23
  async run() {
11
24
  const cliAuthApi = this.getApi(CLI_AUTH_API_NAME);
12
25
  const credentials = cliAuthApi.getCredentials();
13
26
  const executionContextApi = this.getApi(EXECUTION_CONTEXT_API_NAME);
14
- if (!credentials)
15
- this.error('Credentials not found. Please authorize first.');
27
+ if (!credentials) {
28
+ renderOnce(React.createElement(MissingCredentialsError, null));
29
+ return;
30
+ }
16
31
  const executionContext = await executionContextApi.getExecutionContext();
17
- if (executionContext.type !== EXECUTION_CONTEXTS.theme)
18
- this.error('You cannot run this command outside theme context.');
19
- const checksums = await ThemeChecksumsUtils.getThemeInitialChecksums(executionContext.themeRootDir);
20
- const permissions = await ThemeDirectoryUtils.getFilesPermissions(executionContext.themeRootDir);
21
- if (!checksums || !permissions)
32
+ if (executionContext.type !== EXECUTION_CONTEXTS.theme) {
33
+ renderOnce(React.createElement(OutsideOfThemeDirectoryContextError, null));
34
+ return;
35
+ }
36
+ const themeChecksums = new ThemeChecksums(executionContext.themeRootDir);
37
+ const permissions = await ThemeFilesStructureUtils.getFilesPermissions(executionContext.themeRootDir);
38
+ if (!themeChecksums || !permissions)
22
39
  this.error('Theme checksums or permissions not found. Please ensure you are in the correct theme directory.');
40
+ let spinner;
23
41
  try {
24
- const validationResult = await ThemeDirectoryUtils.validateThemeDirectoryStructure({
25
- checksums: mapChecksumToTree(checksums),
26
- permissions: mapToPermissionsTree(permissions),
27
- rootDirectory: executionContext.themeRootDir
42
+ const themeActionsApi = this.getApi(THEME_ACTIONS_API_NAME);
43
+ const themeVerifyApi = this.getApi(THEME_VERIFY_API_NAME);
44
+ const themeFilesUploadApi = new ThemeFilesUpload({
45
+ themeRootDir: executionContext.themeRootDir,
46
+ credentials,
47
+ themeFilesUploadHttpApi: new ThemeFilesUploadHttpApi(this.getApi(HTTP_REQUESTER_API_NAME))
48
+ });
49
+ const verifyAction = themeActionsApi.getThemeAction({
50
+ actionType: THEME_ACTIONS_TYPES.verify,
51
+ themeId: executionContext.themeId,
52
+ credentials
53
+ });
54
+ const filesStructure = await ThemeFilesStructureUtils.getThemeFilesStructure(executionContext.themeRootDir);
55
+ if (!filesStructure) {
56
+ renderOnce(React.createElement(MissingThemeFiles, { files: `${SHOPER_THEME_METADATA_DIR}/${THEME_FILES_STRUCTURE_FILE_NAME}` }));
57
+ return;
58
+ }
59
+ spinner = ora('Verifying theme...').start();
60
+ const { isSuccess, messages } = await themeVerifyApi.verifyTheme({
61
+ verifyAction,
62
+ filesStructure,
63
+ executionContext,
64
+ credentials,
65
+ themeChecksums,
66
+ themeFilesUploadApi
28
67
  });
29
- if (validationResult.isValid) {
30
- this.log('Theme structure is valid.');
68
+ spinner.stop();
69
+ if (isSuccess) {
70
+ renderOnce(React.createElement(Success, { header: `Success: Theme verified!` }));
31
71
  }
32
72
  else {
33
- this.log('Verification failed.\n');
34
- validationResult.unpermittedActions.forEach((action) => {
35
- this.log(`Unpermitted action: ${action.type} path: ${action.path}`);
36
- });
73
+ renderOnce(React.createElement(ThemeError, { err: {
74
+ massages: messages
75
+ }, executionContext: executionContext }));
37
76
  }
77
+ // await promptConfirmation('jolo');
38
78
  }
39
79
  catch (err) {
40
- this.error(`Error during theme verification: ${err instanceof Error ? err.message : String(err)}`);
80
+ spinner?.stop();
81
+ // await promptConfirmation('jolo');
82
+ renderOnce(React.createElement(ThemeError, { err: err, executionContext: executionContext }));
41
83
  }
42
84
  }
43
85
  }
@@ -0,0 +1,11 @@
1
+ import { Error } from '../../../ui/message_box/error.js';
2
+ import { Tip } from '../../../ui/tip.js';
3
+ import { Command } from '../../../ui/command.js';
4
+ import React from 'react';
5
+ export const InvalidThemeIdError = ({ themeId }) => {
6
+ return (React.createElement(Error, { header: `Error: Invalid theme ID: ${themeId}` },
7
+ React.createElement(Tip, null,
8
+ "Run ",
9
+ React.createElement(Command, null, "shoper theme list"),
10
+ " to see available theme IDs.")));
11
+ };
@@ -0,0 +1,15 @@
1
+ import React from 'react';
2
+ import { Error } from '../../../ui/message_box/error.js';
3
+ import { Text } from '../../../ui/text.js';
4
+ import { List } from '../../../ui/list/list.js';
5
+ import { Command } from '../../../ui/command.js';
6
+ export const MissingThemeFiles = ({ files }) => {
7
+ return (React.createElement(Error, { header: `Error: Detected local changes in: ${Array.isArray(files) ? '' : files}` },
8
+ Array.isArray(files) ? (React.createElement(List, { items: files.map((file) => ({
9
+ content: file
10
+ })) })) : null,
11
+ React.createElement(Text, null,
12
+ "This file is managed by the system and should not be modified locally. Please revert it to its original version manually or run",
13
+ React.createElement(Command, null, "shoper theme pull"),
14
+ " to download the correct version from the server.")));
15
+ };
@@ -0,0 +1,13 @@
1
+ import React from 'react';
2
+ import { Error } from '../../../ui/message_box/error.js';
3
+ import { Box } from '../../../ui/box.js';
4
+ import { Tip } from '../../../ui/tip.js';
5
+ import { Command } from '../../../ui/command.js';
6
+ export const MissingThemeIdError = ({ children }) => {
7
+ return (React.createElement(Error, { header: "Error: Missing theme ID." },
8
+ React.createElement(Box, null, children),
9
+ React.createElement(Tip, null,
10
+ "Run ",
11
+ React.createElement(Command, null, "shoper theme list"),
12
+ " to see available themes and their IDs.")));
13
+ };
@@ -0,0 +1,7 @@
1
+ import { Error } from '../../../ui/message_box/error.js';
2
+ import { Text } from '../../../ui/text.js';
3
+ import React from 'react';
4
+ export const OutsideOfThemeDirectoryContextError = () => {
5
+ return (React.createElement(Error, null,
6
+ React.createElement(Text, null, "Error: Cannot run this command outside of a theme directory. Please run it inside a theme directory.")));
7
+ };
@@ -0,0 +1,7 @@
1
+ import React from 'react';
2
+ import { Error } from '../../../ui/message_box/error.js';
3
+ import { Text } from '../../../ui/text.js';
4
+ export const ThemeDirectoryContextError = () => {
5
+ return (React.createElement(Error, null,
6
+ React.createElement(Text, null, "Error: Cannot run this command inside a theme directory. Please run it outside of a theme directory.")));
7
+ };
@@ -0,0 +1,29 @@
1
+ import { THEME_ACTION_NOT_FOUND_ERROR_CODE } from '../../features/theme/actions/theme_actions_constants.js';
2
+ import { UnpermittedCommandError } from './unpermitted_command_error.js';
3
+ import { THEME_ARCHIVE_UPLOAD_ERROR, THEME_FILES_UPLOAD_ERROR } from '../../features/theme/push/theme_push_constants.js';
4
+ import { ValidationErrors } from '../../../ui/validation_errors/validation_errors.js';
5
+ import { THEME_WORK_URL_MISMATCH_ERROR } from '../../features/theme/utils/meta_data/theme_meta_data_constants.js';
6
+ import { Error } from '../../../ui/message_box/error.js';
7
+ import { Text } from '../../../ui/text.js';
8
+ import React from 'react';
9
+ import { ThemeWorkUrlMismatch } from './theme_work_url_mismatch.js';
10
+ import { EXECUTION_CONTEXTS } from '../../../cli/features/execution_context/execution_context_constants.js';
11
+ export const ThemeError = ({ err, executionContext }) => {
12
+ if (err?.code === THEME_ACTION_NOT_FOUND_ERROR_CODE && executionContext.type === EXECUTION_CONTEXTS.theme) {
13
+ return (React.createElement(UnpermittedCommandError, { themeId: executionContext.themeId, commandName: "verify" }));
14
+ }
15
+ if (err?.code === THEME_ARCHIVE_UPLOAD_ERROR) {
16
+ return React.createElement(ValidationErrors, { errors: err?.details });
17
+ }
18
+ if (err?.code === THEME_WORK_URL_MISMATCH_ERROR) {
19
+ return React.createElement(ThemeWorkUrlMismatch, { command: "verify" });
20
+ }
21
+ if (err?.code === THEME_FILES_UPLOAD_ERROR) {
22
+ return (React.createElement(Error, { header: "Uploading theme files to the shop failed.\n" },
23
+ React.createElement(Text, null, "The rejected files have been removed locally. Please ensure that the files are not corrupted and that the file extensions are supported.")));
24
+ }
25
+ if (err?.message) {
26
+ return React.createElement(ValidationErrors, { errors: err.message });
27
+ }
28
+ return String(err);
29
+ };
@@ -0,0 +1,17 @@
1
+ import React from 'react';
2
+ import { Error } from '../../../ui/message_box/error.js';
3
+ import { Command } from '../../../ui/command.js';
4
+ import { Tip } from '../../../ui/tip.js';
5
+ import { Text } from '../../../ui/text.js';
6
+ export const ThemeWorkUrlMismatch = ({ command }) => {
7
+ return (React.createElement(Error, { header: `Error: This theme belongs to a different store.` },
8
+ React.createElement(Text, null,
9
+ "Switch to the correct token to ",
10
+ command,
11
+ " this theme."),
12
+ React.createElement(Tip, null,
13
+ React.createElement(Text, null,
14
+ "Run ",
15
+ React.createElement(Command, null, "shoper theme auth token switch"),
16
+ " to change your token"))));
17
+ };
@@ -0,0 +1,18 @@
1
+ import { Text } from '../../../ui/text.js';
2
+ import { Tip } from '../../../ui/tip.js';
3
+ import { Command } from '../../../ui/command.js';
4
+ import { Error } from '../../../ui/message_box/error.js';
5
+ import React from 'react';
6
+ export const UnpermittedCommandError = ({ commandName, themeId }) => {
7
+ return (React.createElement(Error, null,
8
+ React.createElement(Text, null,
9
+ "You cannot ",
10
+ commandName,
11
+ " theme with id ",
12
+ themeId,
13
+ ". Please check if the theme exists or if you have the necessary permissions."),
14
+ React.createElement(Tip, null,
15
+ "Run ",
16
+ React.createElement(Command, null, "shoper auth list-tokens"),
17
+ " to see available token index values.")));
18
+ };
@@ -16,4 +16,7 @@ export class ThemeActionsApi extends FeatureApi {
16
16
  clearThemesActions(props) {
17
17
  return this.#service.clearThemesActions(props);
18
18
  }
19
+ removeThemeActions(props) {
20
+ return this.#service.removeThemeActions(props);
21
+ }
19
22
  }
@@ -1,7 +1,8 @@
1
1
  import { STORE_THEMES_ACTIONS_TTL } from './theme_actions_service_constants.js';
2
- import { AppError } from '../../../../../cli/class/errors/app_error/app_error.js';
2
+ import { AppError } from '../../../../../cli/class/errors/app/app_error.js';
3
3
  import { getNMinutesFromNow } from '../../../../../utils/date_utils.js';
4
4
  import { computeChecksum } from '../../../../../utils/checksums/checksums_utils.js';
5
+ import { THEME_ACTION_NOT_FOUND_ERROR_CODE, THEME_ACTION_NOTS_FOUND_ERROR_CODE } from '../theme_actions_constants.js';
5
6
  export class ThemeActionsService {
6
7
  #themesListApi;
7
8
  #themesActionsStore;
@@ -13,17 +14,28 @@ export class ThemeActionsService {
13
14
  const storeActions = this.#themesActionsStore.get(this._getKey(credentials.shopUrl));
14
15
  if (!storeActions)
15
16
  throw new AppError({
16
- code: 'theme_actions_not_found',
17
+ code: THEME_ACTION_NOTS_FOUND_ERROR_CODE,
17
18
  message: `Theme actions for store ${credentials.shopUrl} not found.`
18
19
  });
19
20
  const actions = storeActions.actions[themeId];
20
21
  if (!actions)
21
22
  throw new AppError({
22
- code: 'theme_action_not_found',
23
+ code: THEME_ACTION_NOT_FOUND_ERROR_CODE,
23
24
  message: `Theme action for theme ${themeId} not found.`
24
25
  });
25
26
  return actions[actionType];
26
27
  }
28
+ removeThemeActions({ themeId, credentials }) {
29
+ const storeActions = this.#themesActionsStore.get(this._getKey(credentials.shopUrl));
30
+ if (!storeActions || !storeActions.actions[themeId]) {
31
+ throw new AppError({
32
+ code: THEME_ACTION_NOTS_FOUND_ERROR_CODE,
33
+ message: `Theme actions for store ${credentials.shopUrl} not found.`
34
+ });
35
+ }
36
+ delete storeActions.actions[themeId];
37
+ this.#themesActionsStore.set(this._getKey(credentials.shopUrl), storeActions);
38
+ }
27
39
  async ensureThemesActions({ credentials, themeId }) {
28
40
  if (!themeId)
29
41
  return;
@@ -1,2 +1,3 @@
1
1
  export const STORE_THEMES_ACTIONS_TTL = 10;
2
2
  export const THEME_WILDCARD_ACTION_NAME = '*';
3
+ export const THEME_ALL_ACTIONS_NAME = 'all';
@@ -6,10 +6,13 @@ export const THEME_ACTIONS_TYPES = {
6
6
  push: 'push',
7
7
  pull: 'pull',
8
8
  delete: 'delete',
9
- publish_form: 'publish_form',
10
- publish: 'publish'
9
+ publishForm: 'publish_form',
10
+ publish: 'publish',
11
+ verify: 'verify'
11
12
  };
12
13
  export const THEME_ACTION_DATA_TYPE = {
13
14
  file: 'file',
14
15
  zip: 'zip'
15
16
  };
17
+ export const THEME_ACTION_NOT_FOUND_ERROR_CODE = 'theme_action_not_found';
18
+ export const THEME_ACTION_NOTS_FOUND_ERROR_CODE = 'theme_actions_not_found';
@@ -1,6 +1,6 @@
1
1
  import { AsyncFeatureInitializer, FEATURE_CORES_TYPES } from '@dreamcommerce/star_core';
2
2
  import { THEMES_LIST_API_NAME } from '../../themes/list/themes_list_constants.js';
3
- import { JsonCache } from '../../../../cli/features/caches/json_cache/json_cache.js';
3
+ import { JsonCache } from '../../../../cli/class/caches/json_cache/json_cache.js';
4
4
  import { CLI_DATA_DIRECTORY_API_NAME } from '../../../../cli/features/data_directory/cli_data_directory_constants.js';
5
5
  import { THEME_ACTIONS_FEATURE_NAME, THEMES_ACTIONS_FILE_NAME } from './theme_actions_constants.js';
6
6
  import { ThemeActionsApi } from './api/theme_actions_api.js';
@@ -1,8 +1,61 @@
1
- export const getFilesGlobsThatMatchesActionName = (actionType, actionValue, filesStructure) => {
2
- return Object.entries(filesStructure).reduce((acc, [filePath, fileStructureItem]) => {
3
- if (fileStructureItem._links?.[actionType] === actionValue) {
4
- return [...acc, filePath];
1
+ import { THEME_ALL_ACTIONS_NAME } from './service/theme_actions_service_constants.js';
2
+ import { basename, looksLikeDirectory, toUnixPath } from '../../../../utils/path_utils.js';
3
+ import { THEME_ACTION_DATA_TYPE } from './theme_actions_constants.js';
4
+ import globs from 'fast-glob';
5
+ import { THEME_PUSH_WILDCARD_GLOBS_FOR_FILES } from '../push/service/theme_push_service_constants.js';
6
+ export class ThemeActionsUtils {
7
+ static getFilesGlobsThatMatchesActionName({ filesStructure, actionValue, actionType }) {
8
+ return Object.entries(filesStructure).reduce((acc, [filePath, fileStructureItem]) => {
9
+ if (fileStructureItem._links?.[actionType] && this._doesActionValueMatch(fileStructureItem._links[actionType], actionValue)) {
10
+ return [...acc, toUnixPath(filePath)];
11
+ }
12
+ return acc;
13
+ }, []);
14
+ }
15
+ static async getFilesRecordsFromActionData({ themeRootDir, themeAction, filesStructure }) {
16
+ const filesRecords = [];
17
+ for (const [actionKey, actionData] of Object.entries(themeAction.data)) {
18
+ if (actionData.type === THEME_ACTION_DATA_TYPE.file) {
19
+ const filesGlobs = ThemeActionsUtils.getFilesGlobsThatMatchesActionName({
20
+ //TODO remove when backend fixed
21
+ actionType: 'push',
22
+ // actionType: themeAction.actionType,
23
+ actionValue: actionKey,
24
+ filesStructure
25
+ });
26
+ for (const fileGlob of filesGlobs) {
27
+ const files = await globs(fileGlob, {
28
+ suppressErrors: true,
29
+ onlyFiles: true,
30
+ cwd: themeRootDir
31
+ });
32
+ let processedFileGlob = fileGlob;
33
+ if (looksLikeDirectory(fileGlob)) {
34
+ processedFileGlob = fileGlob.endsWith(THEME_PUSH_WILDCARD_GLOBS_FOR_FILES)
35
+ ? fileGlob.slice(0, fileGlob.length - 2)
36
+ : fileGlob;
37
+ }
38
+ for (const filePath of files) {
39
+ filesRecords.push({
40
+ actionData,
41
+ actionKey,
42
+ path: filePath,
43
+ fileName: basename(filePath),
44
+ fileGlob: processedFileGlob
45
+ });
46
+ }
47
+ }
48
+ }
5
49
  }
6
- return acc;
7
- }, []);
8
- };
50
+ return filesRecords;
51
+ }
52
+ static _doesActionValueMatch(currentActionValue, valuesToMatch) {
53
+ if (typeof valuesToMatch === 'string') {
54
+ return currentActionValue === valuesToMatch || valuesToMatch === THEME_ALL_ACTIONS_NAME;
55
+ }
56
+ if (Array.isArray(valuesToMatch)) {
57
+ return valuesToMatch.includes(currentActionValue) || valuesToMatch.includes(THEME_ALL_ACTIONS_NAME);
58
+ }
59
+ return false;
60
+ }
61
+ }
@@ -0,0 +1,13 @@
1
+ import { FeatureApi } from '@dreamcommerce/star_core';
2
+ import { THEME_DELETE_API_NAME } from '../theme_delete_constants.js';
3
+ export class ThemeDeleteApi extends FeatureApi {
4
+ moduleName = THEME_DELETE_API_NAME;
5
+ #service;
6
+ constructor(service) {
7
+ super();
8
+ this.#service = service;
9
+ }
10
+ async deleteTheme(props) {
11
+ return this.#service.deleteTheme(props);
12
+ }
13
+ }
@@ -0,0 +1,17 @@
1
+ export class ThemeDeleteHttpApi {
2
+ #httpApi;
3
+ constructor(httpApi) {
4
+ this.#httpApi = httpApi;
5
+ }
6
+ deleteTheme({ shopUrl, actionData }) {
7
+ const { method, url } = actionData;
8
+ return this.#httpApi.fetch({
9
+ url: `${shopUrl}${url}`,
10
+ method,
11
+ sanitizeOptions: {
12
+ disable: true
13
+ },
14
+ isPrivate: true
15
+ });
16
+ }
17
+ }
@@ -0,0 +1,31 @@
1
+ import { HttpErrorsFactory } from '../../../../../cli/class/errors/http/http_errors_factory.js';
2
+ import { DownloadFileErrorsFactory } from '../../../../../utils/download_file/download_file_errors_factory.js';
3
+ import { STATUS_CODES } from '@dreamcommerce/star_core';
4
+ export class ThemeDeleteService {
5
+ #httpApi;
6
+ constructor(httpApi) {
7
+ this.#httpApi = httpApi;
8
+ }
9
+ async deleteTheme({ actionData, credentials }) {
10
+ try {
11
+ const { response: request } = this.#httpApi.deleteTheme({ actionData, shopUrl: credentials.shopUrl });
12
+ const response = await request;
13
+ if (response?.status !== STATUS_CODES.ok)
14
+ return;
15
+ return response?.data;
16
+ }
17
+ catch (err) {
18
+ //TODO to basic class
19
+ switch (err.response?.status) {
20
+ case 403:
21
+ throw HttpErrorsFactory.createForbiddenError();
22
+ case 401:
23
+ throw HttpErrorsFactory.createUnauthorizedError();
24
+ case 404:
25
+ throw HttpErrorsFactory.createNotFoundError();
26
+ default:
27
+ throw DownloadFileErrorsFactory.downloadError(err.response.status);
28
+ }
29
+ }
30
+ }
31
+ }