@shoper/cli 0.1.0-9 → 0.2.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 (170) 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/errors/file_system_errors_factory.js +1 -1
  5. package/build/cli/class/errors/http/http_errors_constants.js +1 -0
  6. package/build/cli/class/errors/{http_errors_factory.js → http/http_errors_factory.js} +3 -2
  7. package/build/cli/commands/auth/cli_auth_add_token_command.js +22 -19
  8. package/build/cli/commands/auth/cli_auth_commands_constants.js +3 -0
  9. package/build/cli/commands/auth/cli_auth_commands_utils.js +46 -0
  10. package/build/cli/commands/auth/cli_auth_list_tokens_command.js +19 -6
  11. package/build/cli/commands/auth/cli_auth_remove_token_command.js +36 -10
  12. package/build/cli/commands/auth/cli_auth_switch_token_command.js +19 -9
  13. package/build/cli/commands/auth/ui/invalid_token_index_error.js +11 -0
  14. package/build/cli/commands/auth/ui/missing_credentials_error.js +9 -0
  15. package/build/cli/commands/auth/ui/missing_token_index_error.js +19 -0
  16. package/build/cli/commands/cli_ui_dump_command.js +11 -0
  17. package/build/cli/commands/cli_update_command.js +13 -4
  18. package/build/cli/commands/commands_constants.js +2 -1
  19. package/build/cli/core/cli_setup.js +5 -1
  20. package/build/cli/features/controls/controls_constants.js +12 -0
  21. package/build/cli/features/controls/controls_dto_mappers.js +55 -0
  22. package/build/cli/features/controls/ui/controls_mappers.js +62 -0
  23. package/build/cli/features/controls/ui/form.js +4 -0
  24. package/build/cli/features/controls/ui/form_constants.js +7 -0
  25. package/build/cli/features/controls/validators/greater_eq_than_validator.js +5 -0
  26. package/build/cli/features/controls/validators/length_validator.js +6 -0
  27. package/build/cli/features/controls/validators/required_validator.js +5 -0
  28. package/build/cli/features/controls/validators/validator_constants.js +13 -0
  29. package/build/cli/features/execution_context/execution_context_service.js +5 -5
  30. package/build/cli/features/http_requester/http_requester_initializer.js +1 -1
  31. package/build/cli/hooks/authorization/ensure_authorization_hook.js +2 -2
  32. package/build/cli/hooks/authorization/ensure_authorization_hook_constants.js +8 -8
  33. package/build/index.js +37 -19
  34. package/build/theme/class/checksums/theme_checksums.js +174 -0
  35. package/build/theme/{features/theme/utils → class}/checksums/theme_checksums_error_factory.js +1 -1
  36. package/build/theme/class/checksums/theme_checksums_utils.js +17 -0
  37. package/build/theme/class/fetch_resources/fetch_resources.js +1 -0
  38. package/build/theme/class/fetch_resources/fetch_resources_errors_factory.js +1 -1
  39. package/build/theme/class/fetch_resources/fetch_resources_utils.js +4 -1
  40. package/build/theme/commands/delete/theme_delete_command.js +111 -0
  41. package/build/theme/commands/delete/ui/theme_deleted_successfully.js +15 -0
  42. package/build/theme/commands/delete/ui/theme_deletion_warning.js +10 -0
  43. package/build/theme/commands/info/theme_info_command.js +94 -0
  44. package/build/theme/commands/info/theme_info_command_utils.js +39 -0
  45. package/build/theme/commands/init/theme_init_command.js +97 -0
  46. package/build/theme/commands/init/ui/theme_created_success.js +15 -0
  47. package/build/theme/commands/list/theme_list_command.js +25 -0
  48. package/build/theme/commands/list/theme_list_command_utils.js +27 -0
  49. package/build/theme/commands/publish/theme_publish_command.js +92 -0
  50. package/build/theme/commands/pull/theme_pull_command.js +194 -0
  51. package/build/theme/commands/pull/ui/theme_pull_id_mismatch_error.js +7 -0
  52. package/build/theme/commands/pull/ui/theme_pull_unpublished_changes_warning.js +10 -0
  53. package/build/theme/commands/pull/ui/theme_pulled_success.js +5 -0
  54. package/build/theme/commands/push/theme_push_command.js +134 -0
  55. package/build/theme/commands/push/ui/theme_push_skip_into.js +7 -0
  56. package/build/theme/commands/push/ui/theme_pushed_success.js +5 -0
  57. package/build/theme/commands/push/ui/theme_unpermitted_actions_error.js +12 -0
  58. package/build/theme/commands/theme_commands_constants.js +4 -1
  59. package/build/theme/commands/theme_show_changes_command.js +1 -0
  60. package/build/theme/commands/theme_verify_command.js +8 -7
  61. package/build/theme/commands/ui/invalid_theme_id.js +11 -0
  62. package/build/theme/commands/ui/missing_theme_files.js +15 -0
  63. package/build/theme/commands/ui/missing_theme_id_error.js +13 -0
  64. package/build/theme/commands/ui/ouside_of_theme_directory_context_error.js +7 -0
  65. package/build/theme/commands/ui/theme_directory_context_error.js +7 -0
  66. package/build/theme/commands/ui/theme_work_url_mismatch.js +17 -0
  67. package/build/theme/commands/ui/unpermitted_command_error.js +18 -0
  68. package/build/theme/features/theme/actions/api/theme_actions_api.js +3 -0
  69. package/build/theme/features/theme/actions/service/theme_actions_service.js +15 -3
  70. package/build/theme/features/theme/actions/service/theme_actions_service_constants.js +1 -0
  71. package/build/theme/features/theme/actions/theme_actions_constants.js +3 -1
  72. package/build/theme/features/theme/actions/theme_actions_initializer.js +1 -1
  73. package/build/theme/features/theme/actions/theme_actions_utils.js +20 -7
  74. package/build/theme/features/theme/delete/api/theme_delete_api.js +13 -0
  75. package/build/theme/features/theme/delete/http/theme_delete_http_api.js +17 -0
  76. package/build/theme/features/theme/delete/service/theme_delete_service.js +31 -0
  77. package/build/theme/features/theme/delete/theme_delete_constants.js +2 -0
  78. package/build/theme/features/theme/delete/theme_delete_initalizer.js +20 -0
  79. package/build/theme/features/theme/fetch/service/theme_fetch_service.js +30 -10
  80. package/build/theme/features/theme/info/theme_info_utils.js +9 -0
  81. package/build/theme/features/theme/init/service/theme_init_service.js +9 -4
  82. package/build/theme/features/theme/merge/api/theme_merge_api.js +0 -12
  83. package/build/theme/features/theme/merge/service/theme_merge_service.js +20 -28
  84. package/build/theme/features/theme/push/service/theme_push_service.js +92 -69
  85. package/build/theme/features/theme/push/theme_push_constants.js +2 -0
  86. package/build/theme/features/theme/push/theme_push_errors_factory.js +7 -4
  87. package/build/theme/features/theme/push/theme_push_initializer.js +0 -3
  88. package/build/theme/features/theme/push/theme_push_utils.js +25 -0
  89. package/build/theme/features/theme/skinstore/api/theme_skinstore_api.js +19 -0
  90. package/build/theme/features/theme/skinstore/http/theme_skinstore_http_api.js +17 -0
  91. package/build/theme/features/theme/skinstore/service/theme_skinstore_service.js +32 -0
  92. package/build/theme/features/theme/skinstore/theme_publish_constants.js +4 -0
  93. package/build/theme/features/theme/skinstore/theme_skinstore_initialzier.js +20 -0
  94. package/build/theme/features/theme/utils/files_structure/theme_files_structure_utils.js +40 -0
  95. package/build/theme/features/theme/utils/hidden_directory/hidden_directory_utils.js +32 -0
  96. package/build/theme/features/theme/utils/meta_data/theme_meta_data_constants.js +1 -0
  97. package/build/theme/features/theme/utils/meta_data/theme_meta_data_error_factory.js +15 -0
  98. package/build/theme/features/theme/utils/meta_data/theme_meta_data_utils.js +39 -0
  99. package/build/theme/features/theme/utils/{directories → resources}/theme_resources_with_id_directory_utils.js +3 -14
  100. package/build/theme/features/theme/utils/theme_images_utils.js +1 -1
  101. package/build/theme/features/themes/list/api/themes_list_api.js +3 -0
  102. package/build/theme/features/themes/list/services/themes_list_service.js +13 -7
  103. package/build/theme/hooks/{ensure_theme_meta_data_untouched.js → ensure_theme_meta_data_untouched_hook.js} +2 -2
  104. package/build/theme/hooks/theme_checksums/ensure_theme_current_checksums_up_to_date_constants.js +1 -1
  105. 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
  106. package/build/theme/hooks/themes_actions/ensure_themes_actions_hook.js +1 -0
  107. package/build/theme/hooks/themes_actions/ensure_themes_actions_hook_constants.js +1 -0
  108. package/build/theme/index.js +11 -5
  109. package/build/theme/utils/directory_validator/directory_validator_constants.js +2 -6
  110. package/build/theme/utils/directory_validator/directory_validator_utils.js +7 -25
  111. package/build/ui/box.js +2 -0
  112. package/build/ui/color_constants.js +30 -0
  113. package/build/ui/command.js +6 -0
  114. package/build/ui/file_name.js +6 -0
  115. package/build/ui/flag.js +6 -0
  116. package/build/ui/icons/error_icon.js +7 -0
  117. package/build/ui/icons/info_icon.js +7 -0
  118. package/build/ui/icons/success_icon.js +7 -0
  119. package/build/ui/icons/warning_icon.js +7 -0
  120. package/build/ui/link.js +8 -0
  121. package/build/ui/list/list.js +10 -0
  122. package/build/ui/list/list_constants.js +4 -0
  123. package/build/ui/list/list_item.js +11 -0
  124. package/build/ui/message_box/error.js +4 -0
  125. package/build/ui/message_box/info.js +4 -0
  126. package/build/ui/message_box/message_box.js +11 -0
  127. package/build/ui/message_box/message_box_constants.js +24 -0
  128. package/build/ui/message_box/success.js +4 -0
  129. package/build/ui/message_box/warning.js +4 -0
  130. package/build/ui/prompts/prompt_confirmation.js +11 -0
  131. package/build/ui/prompts/prompt_input.js +10 -0
  132. package/build/ui/table/t_cell.js +7 -0
  133. package/build/ui/table/t_header.js +9 -0
  134. package/build/ui/table/t_row.js +5 -0
  135. package/build/ui/table/table.js +18 -0
  136. package/build/ui/text.js +2 -0
  137. package/build/ui/tip.js +9 -0
  138. package/build/ui/ui_dump/ui_component_box.js +9 -0
  139. package/build/ui/ui_dump/ui_dump.js +53 -0
  140. package/build/ui/ui_dump/ui_dump_constants.js +21 -0
  141. package/build/ui/ui_utils.js +11 -0
  142. package/build/ui/validation_errors/validation_error_content.js +19 -0
  143. package/build/ui/validation_errors/validation_errors.js +21 -0
  144. package/build/ui/validation_errors/validation_errors_utils.js +17 -0
  145. package/build/utils/checksums/checksums_utils.js +9 -26
  146. package/build/utils/download_file/download_file_errors_factory.js +1 -1
  147. package/build/utils/download_file/download_file_utils.js +4 -2
  148. package/build/utils/fs/errors/stream_read_error.js +1 -1
  149. package/build/utils/fs/errors/stream_write_error.js +1 -1
  150. package/build/utils/fs/fs_utils.js +12 -1
  151. package/build/utils/path_utils.js +18 -2
  152. package/build/utils/platform_utils.js +3 -0
  153. package/build/utils/zip/create_zip_utils.js +1 -1
  154. package/build/utils/zip/errors/create_zip_error.js +1 -1
  155. package/build/utils/zip/errors/open_zip_error.js +1 -1
  156. package/oclif.config.js +2 -2
  157. package/package.json +13 -8
  158. package/build/theme/commands/theme_init_command.js +0 -53
  159. package/build/theme/commands/theme_list_command.js +0 -16
  160. package/build/theme/commands/theme_pull_command.js +0 -126
  161. package/build/theme/commands/theme_push_command.js +0 -65
  162. package/build/theme/features/theme/directory/theme_directory_utils.js +0 -76
  163. package/build/theme/features/theme/publish/theme_publish_constants.js +0 -2
  164. package/build/theme/features/theme/utils/checksums/theme_checksums_utils.js +0 -94
  165. /package/build/cli/{features → class}/caches/cache_factory.js +0 -0
  166. /package/build/cli/{features → class}/caches/json_cache/json_cache.js +0 -0
  167. /package/build/cli/{features → class}/caches/memory_cache.js +0 -0
  168. /package/build/cli/class/errors/{app_error → app}/app_error.js +0 -0
  169. /package/build/cli/class/errors/{app_error → app}/app_error_constants.js +0 -0
  170. /package/build/theme/features/theme/{publish → skinstore}/theme_publish_utils.js +0 -0
@@ -6,86 +6,92 @@ import { createReadStream } from 'node:fs';
6
6
  import { v4 as uuid } from 'uuid';
7
7
  import globs from 'fast-glob';
8
8
  import { THEME_WILDCARD_ACTION_NAME } from '../../actions/service/theme_actions_service_constants.js';
9
- import { getFilesGlobsThatMatchesActionName } from '../../actions/theme_actions_utils.js';
9
+ import { ThemeActionsUtils } from '../../actions/theme_actions_utils.js';
10
10
  import { THEME_FILES_LIST_FILE_NAME, THEME_MODULE_SETTINGS_FILE_NAME } from '../theme_push_constants.js';
11
11
  import { THEME_PUSH_WILDCARD_GLOBS_FOR_FILES } from './theme_push_service_constants.js';
12
12
  import { ThemePushErrorsFactory } from '../theme_push_errors_factory.js';
13
13
  import { ThemeImagesUtils } from '../../utils/theme_images_utils.js';
14
- import { ThemePublishUtils } from '../../publish/theme_publish_utils.js';
14
+ import { ThemePublishUtils } from '../../skinstore/theme_publish_utils.js';
15
15
  import { formatJSONFile, removeFile, writeJSONFile } from '../../../../../utils/fs/fs_utils.js';
16
- import { computeChecksumsFromSource } from '../../../../../utils/checksums/checksums_utils.js';
17
- import { ThemeChecksumsUtils } from '../../utils/checksums/theme_checksums_utils.js';
18
16
  import { MODULES_DIRECTORY_NAME } from '../../theme_constants.js';
17
+ import path from 'node:path';
18
+ import { removeOldResources } from '../../../../class/fetch_resources/fetch_resources_utils.js';
19
19
  export class ThemePushService {
20
20
  #themePushHttpApi;
21
- #themeMergeApi;
22
21
  #themeFetchApi;
23
- constructor({ themePushHttpApi, themeMergeApi, themeFetchApi }) {
22
+ constructor({ themePushHttpApi, themeFetchApi }) {
24
23
  this.#themePushHttpApi = themePushHttpApi;
25
- this.#themeMergeApi = themeMergeApi;
26
24
  this.#themeFetchApi = themeFetchApi;
27
25
  }
28
- async push({ pushAction, filesStructure, credentials, executionContext }) {
26
+ async push({ pushAction, filesStructure, credentials, executionContext, themeChecksums }) {
29
27
  const { path: tmpDir } = await tmp.dir({ unsafeCleanup: true });
30
- if (await this.#themeMergeApi.hasFileBeenCreated(ThemePublishUtils.getSkinStoreSettingsFilePath(executionContext.themeRootDir), executionContext))
28
+ const themeRootDir = executionContext.themeRootDir;
29
+ if (await themeChecksums.hasThemeFileBeenCreated(ThemePublishUtils.getSkinStoreSettingsFilePath(themeRootDir)))
31
30
  throw ThemePushErrorsFactory.createErrorWhilePushingUnpublishedThemeWithSkinstoreData(credentials.shopUrl);
32
- const { uploadData, localFiles } = await this._getActionDataForFilesToUpload({
33
- pushAction,
34
- filesStructure,
35
- executionContext
36
- });
37
- if (uploadData.length) {
38
- await this._uploadThemeFiles({
39
- filesToUpload: uploadData,
40
- actionData: pushAction.data[THEME_WILDCARD_ACTION_NAME],
31
+ try {
32
+ const { uploadData, localFiles } = await this._getActionDataForFilesToUpload({
33
+ pushAction,
34
+ filesStructure,
35
+ executionContext,
36
+ themeChecksums
37
+ });
38
+ if (uploadData.length) {
39
+ const { newFiles } = await this._uploadThemeFiles({
40
+ filesToUpload: uploadData,
41
+ credentials,
42
+ localFiles,
43
+ themeRootDir
44
+ });
45
+ await this._createAFilesListFile(themeRootDir, newFiles);
46
+ }
47
+ else {
48
+ await this._createAFilesListFile(themeRootDir, localFiles);
49
+ }
50
+ const themeArchivePath = join(tmpDir, `${uuid()}.zip`);
51
+ await this._createThemeArchive({
52
+ themeRootDir: themeRootDir,
53
+ filesToArchive: ThemeActionsUtils.getFilesGlobsThatMatchesActionName({
54
+ actionType: THEME_ACTIONS_TYPES.push,
55
+ actionValue: THEME_WILDCARD_ACTION_NAME,
56
+ filesStructure
57
+ }),
58
+ dist: themeArchivePath,
59
+ credentials
60
+ });
61
+ const { resources, modules } = await this._uploadThemeArchive({
62
+ themeArchivePath,
41
63
  credentials,
42
- localFiles,
43
- themeRootDir: executionContext.themeRootDir
64
+ pushAction
44
65
  });
66
+ if (modules)
67
+ await this._updateDataForNewCreatedModules({ modules, themeRootDir });
68
+ if (resources) {
69
+ await removeOldResources(themeRootDir, resources);
70
+ await this.#themeFetchApi.fetchResources(credentials.shopUrl, themeRootDir, resources);
71
+ }
72
+ await themeChecksums.updateAllChecksums();
45
73
  }
46
- else {
47
- await this._createAFilesListFile(executionContext.themeRootDir, localFiles);
74
+ finally {
75
+ await removeFile(join(themeRootDir, THEME_FILES_LIST_FILE_NAME));
48
76
  }
49
- const themeArchivePath = join(tmpDir, `${uuid()}.zip`);
77
+ }
78
+ async _createThemeArchive({ themeRootDir, filesToArchive, dist, credentials }) {
50
79
  try {
51
- await this._createThemeArchive({
52
- themeRootDir: executionContext.themeRootDir,
53
- filesToArchive: [
54
- ...getFilesGlobsThatMatchesActionName(THEME_ACTIONS_TYPES.push, THEME_WILDCARD_ACTION_NAME, filesStructure),
55
- THEME_FILES_LIST_FILE_NAME
56
- ],
57
- dist: themeArchivePath
80
+ const filesInThemeDirectory = await globs(filesToArchive, {
81
+ suppressErrors: true,
82
+ onlyFiles: true,
83
+ cwd: themeRootDir
84
+ });
85
+ await this._formatJsonFiles(themeRootDir, filesInThemeDirectory);
86
+ return createZip({
87
+ files: filesInThemeDirectory,
88
+ baseDir: themeRootDir,
89
+ dist
58
90
  });
59
91
  }
60
92
  catch (err) {
61
93
  throw ThemePushErrorsFactory.createErrorWhileCreatingThemeArchive(credentials.shopUrl, err);
62
94
  }
63
- const { resources, modules } = await this._uploadThemeArchive({
64
- themeArchivePath,
65
- credentials,
66
- pushAction
67
- });
68
- if (modules)
69
- await this._updateDataForNewCreatedModules({ modules, themeRootDir: executionContext.themeRootDir });
70
- console.log(JSON.stringify(resources));
71
- if (resources)
72
- await this.#themeFetchApi.fetchResources(credentials.shopUrl, executionContext.themeRootDir, resources);
73
- await removeFile(join(executionContext.themeRootDir, THEME_FILES_LIST_FILE_NAME));
74
- const checksums = await computeChecksumsFromSource(executionContext.themeRootDir);
75
- await ThemeChecksumsUtils.createThemeChecksumsFiles(executionContext.themeRootDir, checksums);
76
- }
77
- async _createThemeArchive({ themeRootDir, filesToArchive, dist }) {
78
- const filesInThemeDirectory = await globs(filesToArchive, {
79
- suppressErrors: true,
80
- onlyFiles: true,
81
- cwd: themeRootDir
82
- });
83
- await this._formatJsonFiles(themeRootDir, filesInThemeDirectory);
84
- return createZip({
85
- files: filesInThemeDirectory,
86
- baseDir: themeRootDir,
87
- dist
88
- });
89
95
  }
90
96
  async _uploadThemeArchive({ themeArchivePath, credentials, pushAction }) {
91
97
  try {
@@ -100,7 +106,6 @@ export class ThemePushService {
100
106
  return response.data;
101
107
  }
102
108
  catch (err) {
103
- console.log('error', err.response.data.messages);
104
109
  throw ThemePushErrorsFactory.createErrorWhileUploadingTheme(credentials.shopUrl, err.response.data.messages);
105
110
  }
106
111
  }
@@ -109,20 +114,27 @@ export class ThemePushService {
109
114
  .filter((path) => extname(path).toLowerCase() === '.json')
110
115
  .map((jsonFile) => formatJSONFile(join(themeRootDir, jsonFile))));
111
116
  }
117
+ //TODO osobna klasa do uploadu
112
118
  async _uploadThemeFiles({ filesToUpload, themeRootDir, localFiles, credentials }) {
113
119
  try {
114
- const uploadedImageData = await this._uploadFiles(filesToUpload, credentials);
120
+ const { uploadedImageData, rejectedImageData } = await this._uploadFiles(filesToUpload, credentials);
115
121
  const newFilesList = ThemeImagesUtils.updateOriginalFilenameToUploadedFilename(localFiles, uploadedImageData);
116
122
  if (uploadedImageData.length)
117
123
  await ThemeImagesUtils.removeUploadedOriginalFiles(themeRootDir, uploadedImageData);
118
- await this._createAFilesListFile(themeRootDir, newFilesList);
124
+ if (rejectedImageData.length)
125
+ await this._removeUploadedThemeFiles(rejectedImageData, themeRootDir);
126
+ return { newFiles: newFilesList };
119
127
  }
120
128
  catch (err) {
121
129
  throw ThemePushErrorsFactory.createErrorWhileUploadingThemeFiles(credentials.shopUrl, err);
122
130
  }
123
131
  }
132
+ async _removeUploadedThemeFiles(uploadedImageData, themeRootDir) {
133
+ await Promise.all(uploadedImageData.map(({ location, originalFilename }) => removeFile(join(themeRootDir, location, originalFilename))));
134
+ }
124
135
  async _uploadFiles(uploadData, credentials) {
125
136
  const uploadedImageData = [];
137
+ const rejectedImageData = [];
126
138
  await Promise.all(uploadData.map(({ actionData, path }) => this.#themePushHttpApi
127
139
  .pushThemeData({
128
140
  actionData,
@@ -130,22 +142,33 @@ export class ThemePushService {
130
142
  shopUrl: credentials.shopUrl
131
143
  })
132
144
  .response.then((response) => {
133
- if (response.status !== 200 || !response.data.isSuccess)
134
- throw ThemePushErrorsFactory.createErrorWhileUploadingThemeFiles(credentials.shopUrl, response.data?.messages ?? []);
135
145
  uploadedImageData.push({
136
146
  location: dirname(path),
137
147
  originalFilename: basename(path),
138
148
  uploadedFilename: response.data.imageId
139
149
  });
150
+ })
151
+ .catch(() => {
152
+ rejectedImageData.push({
153
+ location: dirname(path),
154
+ originalFilename: basename(path)
155
+ });
140
156
  })));
141
- return uploadedImageData;
157
+ return {
158
+ uploadedImageData,
159
+ rejectedImageData
160
+ };
142
161
  }
143
- async _getActionDataForFilesToUpload({ pushAction, filesStructure, executionContext }) {
162
+ async _getActionDataForFilesToUpload({ pushAction, filesStructure, executionContext, themeChecksums }) {
144
163
  const uploadData = [];
145
164
  const localFiles = {};
146
165
  for (const [actionKey, actionData] of Object.entries(pushAction.data)) {
147
166
  if (actionData.type === THEME_ACTION_DATA_TYPE.file) {
148
- const filesGlobs = getFilesGlobsThatMatchesActionName(THEME_ACTIONS_TYPES.push, actionKey, filesStructure);
167
+ const filesGlobs = ThemeActionsUtils.getFilesGlobsThatMatchesActionName({
168
+ actionType: THEME_ACTIONS_TYPES.push,
169
+ actionValue: actionKey,
170
+ filesStructure
171
+ });
149
172
  for (const fileGlob of filesGlobs) {
150
173
  const files = await globs(fileGlob, {
151
174
  suppressErrors: true,
@@ -162,8 +185,8 @@ export class ThemePushService {
162
185
  localFiles[fileGlob] = files.length ? basename(files[0]) : null;
163
186
  }
164
187
  for (const filePath of files) {
165
- if ((await this.#themeMergeApi.hasFileBeenCreated(filePath, executionContext)) ||
166
- (await this.#themeMergeApi.hasFileBeenModified(filePath, executionContext))) {
188
+ if ((await themeChecksums.hasThemeFileBeenCreated(filePath)) ||
189
+ (await themeChecksums.hasThemeFileBeenModified(filePath))) {
167
190
  uploadData.push({
168
191
  actionData,
169
192
  actionKey,
@@ -187,9 +210,9 @@ export class ThemePushService {
187
210
  async _createAFilesListFile(themeRootDir, filesList) {
188
211
  if (!filesList || !Object.keys(filesList).length)
189
212
  return;
190
- const toUnixStyleFilesList = Object.entries(filesList).reduce((acc, [path, value]) => {
191
- const unixPath = toUnixPath(path);
192
- const finalPath = looksLikeDirectory(path) ? `${unixPath}/` : unixPath;
213
+ const toUnixStyleFilesList = Object.entries(filesList).reduce((acc, [filePath, value]) => {
214
+ const unixPath = toUnixPath(filePath);
215
+ const finalPath = looksLikeDirectory(unixPath) ? `${unixPath}${path.posix.sep}` : unixPath;
193
216
  return {
194
217
  ...acc,
195
218
  [finalPath]: value
@@ -2,3 +2,5 @@ export const THEME_PUSH_FEATURE_NAME = 'ThemePush';
2
2
  export const THEME_PUSH_API_NAME = 'ThemePushApi';
3
3
  export const THEME_FILES_LIST_FILE_NAME = 'filesList.json';
4
4
  export const THEME_MODULE_SETTINGS_FILE_NAME = 'settings.json';
5
+ export const THEME_ARCHIVE_UPLOAD_ERROR = 'theme.push.error_uploading_theme_archive';
6
+ export const THEME_FILES_UPLOAD_ERROR = 'theme.push.error_uploading_theme_files';
@@ -1,11 +1,14 @@
1
- import { AppError } from '../../../../cli/class/errors/app_error/app_error.js';
1
+ import { AppError } from '../../../../cli/class/errors/app/app_error.js';
2
+ import { THEME_ARCHIVE_UPLOAD_ERROR, THEME_FILES_UPLOAD_ERROR } from './theme_push_constants.js';
2
3
  export class ThemePushErrorsFactory {
3
4
  static createErrorWhileUploadingThemeFiles(shopUrl, messages) {
4
5
  return new AppError({
5
- code: 'theme.push.error_uploading_theme_files',
6
+ code: THEME_FILES_UPLOAD_ERROR,
6
7
  message: `Error while uploading theme files to shop "${shopUrl}"`,
7
8
  level: 'error',
8
- details: messages
9
+ details: {
10
+ messages
11
+ }
9
12
  });
10
13
  }
11
14
  static createErrorWhileCreatingThemeArchive(shopUrl, error) {
@@ -32,7 +35,7 @@ export class ThemePushErrorsFactory {
32
35
  }
33
36
  static createErrorWhileUploadingTheme(shopUrl, messages) {
34
37
  return new AppError({
35
- code: 'theme.push.error_uploading_theme',
38
+ code: THEME_ARCHIVE_UPLOAD_ERROR,
36
39
  message: `Error while uploading theme to shop "${shopUrl}"`,
37
40
  level: 'error',
38
41
  details: messages
@@ -3,16 +3,13 @@ import { ThemePushService } from './service/theme_push_service.js';
3
3
  import { THEME_PUSH_FEATURE_NAME } from './theme_push_constants.js';
4
4
  import { ThemePushApi } from './api/theme_push_api.js';
5
5
  import { ThemePushHttpApi } from './http_api/theme_push_http_api.js';
6
- import { THEME_MERGE_API_NAME } from '../merge/theme_merge_constants.js';
7
6
  import { THEME_FETCH_API_NAME } from '../fetch/theme_fetch_constants.js';
8
7
  export class ThemePushInitializer extends SyncFeatureInitializer {
9
8
  static featureName = THEME_PUSH_FEATURE_NAME;
10
9
  init() {
11
10
  const httpApi = this.getApiSync(HTTP_REQUESTER_API_NAME);
12
- const themeMergeApi = this.getApiSync(THEME_MERGE_API_NAME);
13
11
  const service = new ThemePushService({
14
12
  themePushHttpApi: new ThemePushHttpApi(httpApi),
15
- themeMergeApi,
16
13
  themeFetchApi: this.getApiSync(THEME_FETCH_API_NAME)
17
14
  });
18
15
  return {
@@ -0,0 +1,25 @@
1
+ import globs from 'fast-glob';
2
+ import { AppError } from '../../../../cli/class/errors/app/app_error.js';
3
+ import { toUnixPath } from '../../../../utils/path_utils.js';
4
+ import { ThemeFilesStructureUtils } from '../utils/files_structure/theme_files_structure_utils.js';
5
+ export class ThemePushUtils {
6
+ static async getAllFilesThatAreSendToRemote(themeDir) {
7
+ const filesStructure = await ThemeFilesStructureUtils.getThemeFilesStructure(themeDir);
8
+ if (!filesStructure)
9
+ throw new AppError({
10
+ message: `Files structure not found in theme directory: ${themeDir}`,
11
+ code: 'theme_files_structure_not_found'
12
+ });
13
+ //need unix styles globs
14
+ const filesToArchive = Object.keys(filesStructure)
15
+ .map((path) => toUnixPath(path))
16
+ .filter((path) => path !== '*');
17
+ //need unix styles globs
18
+ filesToArchive.push('styles/src/**/*');
19
+ return await globs(filesToArchive, {
20
+ suppressErrors: true,
21
+ onlyFiles: true,
22
+ cwd: themeDir
23
+ }).then((files) => files.sort());
24
+ }
25
+ }
@@ -0,0 +1,19 @@
1
+ import { FeatureApi } from '@dreamcommerce/star_core';
2
+ import { THEME_SKINSTORE_API_NAME } from '../theme_publish_constants.js';
3
+ export class ThemeSkinstoreApi extends FeatureApi {
4
+ moduleName = THEME_SKINSTORE_API_NAME;
5
+ #service;
6
+ constructor(service) {
7
+ super();
8
+ this.#service = service;
9
+ }
10
+ async getPublishFormData(props) {
11
+ return this.#service.getPublishFormData(props);
12
+ }
13
+ async publish(themeId) {
14
+ // return this.#service.publish(themeId);
15
+ }
16
+ async update() {
17
+ // return this.#service.update();
18
+ }
19
+ }
@@ -0,0 +1,17 @@
1
+ export class ThemeSkinstoreHttpApi {
2
+ #httpApi;
3
+ constructor(httpApi) {
4
+ this.#httpApi = httpApi;
5
+ }
6
+ getPublishFormData({ actionData, shopUrl }) {
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,32 @@
1
+ import { STATUS_CODES } from '@dreamcommerce/star_core';
2
+ import { HttpErrorsFactory } from '../../../../../cli/class/errors/http/http_errors_factory.js';
3
+ import { DownloadFileErrorsFactory } from '../../../../../utils/download_file/download_file_errors_factory.js';
4
+ import { toControls } from '../../../../../cli/features/controls/controls_dto_mappers.js';
5
+ export class ThemeSkinstoreService {
6
+ #httpApi;
7
+ constructor(httpApi) {
8
+ this.#httpApi = httpApi;
9
+ }
10
+ async getPublishFormData({ credentials, actionData }) {
11
+ try {
12
+ const { response: request } = this.#httpApi.getPublishFormData({ actionData, shopUrl: credentials.shopUrl });
13
+ const response = await request;
14
+ if (response?.status !== STATUS_CODES.ok)
15
+ throw response;
16
+ return response?.data ? toControls(response.data) : [];
17
+ }
18
+ catch (err) {
19
+ //TODO to basic class
20
+ switch (err.response?.status) {
21
+ case 403:
22
+ throw HttpErrorsFactory.createForbiddenError();
23
+ case 401:
24
+ throw HttpErrorsFactory.createUnauthorizedError();
25
+ case 404:
26
+ throw HttpErrorsFactory.createNotFoundError();
27
+ default:
28
+ throw DownloadFileErrorsFactory.downloadError(err.response.status);
29
+ }
30
+ }
31
+ }
32
+ }
@@ -0,0 +1,4 @@
1
+ export const THEME_SKINSTORE_LOCATION = 'skinstore';
2
+ export const THEME_SKINSTORE_SETTINGS_FILE_NAME = 'settings.json';
3
+ export const THEME_SKINSTORE_API_NAME = 'ThemeSkinstoreApi';
4
+ export const THEME_SKINSTORE_FEATURE_NAME = 'ThemeSkinstore';
@@ -0,0 +1,20 @@
1
+ import { FEATURE_CORES_TYPES, HTTP_REQUESTER_API_NAME, SyncFeatureInitializer } from '@dreamcommerce/star_core';
2
+ import { THEME_SKINSTORE_FEATURE_NAME } from './theme_publish_constants.js';
3
+ import { ThemeSkinstoreService } from './service/theme_skinstore_service.js';
4
+ import { ThemeSkinstoreHttpApi } from './http/theme_skinstore_http_api.js';
5
+ import { ThemeSkinstoreApi } from './api/theme_skinstore_api.js';
6
+ export class ThemeSkinstoreInitializer extends SyncFeatureInitializer {
7
+ static featureName = THEME_SKINSTORE_FEATURE_NAME;
8
+ init() {
9
+ const httpApi = this.getApiSync(HTTP_REQUESTER_API_NAME);
10
+ const service = new ThemeSkinstoreService(new ThemeSkinstoreHttpApi(httpApi));
11
+ return {
12
+ cores: [
13
+ {
14
+ type: FEATURE_CORES_TYPES.api,
15
+ instance: new ThemeSkinstoreApi(service)
16
+ }
17
+ ]
18
+ };
19
+ }
20
+ }
@@ -0,0 +1,40 @@
1
+ import { readJSONFile, writeJSONFile } from '../../../../../utils/fs/fs_utils.js';
2
+ import { join, looksLikeDirectory, mapKeysPathsToWindowPlatform } from '../../../../../utils/path_utils.js';
3
+ import { SHOPER_THEME_METADATA_DIR } from '../../../../constants/directory_contstants.js';
4
+ import { THEME_FILES_STRUCTURE_FILE_NAME } from '../../theme_constants.js';
5
+ import { isWindowsOs } from '../../../../../utils/platform_utils.js';
6
+ import { validateDirectory } from '../../../../utils/directory_validator/directory_validator_utils.js';
7
+ export class ThemeFilesStructureUtils {
8
+ static async getThemeFilesStructure(themeDirectory) {
9
+ const filesStructure = await readJSONFile(join(themeDirectory, SHOPER_THEME_METADATA_DIR, THEME_FILES_STRUCTURE_FILE_NAME));
10
+ if (!isWindowsOs())
11
+ return filesStructure;
12
+ return mapKeysPathsToWindowPlatform(filesStructure);
13
+ }
14
+ static async writeThemeFilesStructure(themeDirectory, filesStructure) {
15
+ const filePath = join(themeDirectory, SHOPER_THEME_METADATA_DIR, THEME_FILES_STRUCTURE_FILE_NAME);
16
+ await writeJSONFile(filePath, filesStructure);
17
+ }
18
+ static async getFilesPermissions(themeDirectory) {
19
+ const fileStructure = await ThemeFilesStructureUtils.getThemeFilesStructure(themeDirectory);
20
+ return Object.entries(fileStructure).reduce((acc, [key, value]) => {
21
+ return {
22
+ ...acc,
23
+ [key]: value.permissions
24
+ };
25
+ }, {});
26
+ }
27
+ static async validateThemeDirectoryStructure({ checksums, permissions, rootDirectory }) {
28
+ return await validateDirectory({
29
+ permissions,
30
+ checksums,
31
+ rootDirectory
32
+ });
33
+ }
34
+ static async getThemeRootDirectories(themeDirectory) {
35
+ const filesStructure = await ThemeFilesStructureUtils.getThemeFilesStructure(themeDirectory);
36
+ return Object.keys(filesStructure).filter((path) => {
37
+ return looksLikeDirectory(path);
38
+ });
39
+ }
40
+ }
@@ -0,0 +1,32 @@
1
+ import { getAllFilesNamesInside } from '../../../../../utils/fs/fs_utils.js';
2
+ import { THEME_CURRENT_CHECKSUMS_FILE_NAME, THEME_CURRENT_CHECKSUMS_VERITY_FILE_NAME, THEME_INITIAL_CHECKSUMS_FILE_NAME, THEME_INITIAL_CHECKSUMS_VERITY_FILE_NAME } from '../../theme_constants.js';
3
+ import { join } from '../../../../../utils/path_utils.js';
4
+ import { SHOPER_THEME_METADATA_DIR } from '../../../../constants/directory_contstants.js';
5
+ import { computeFileChecksum } from '../../../../../utils/checksums/checksums_utils.js';
6
+ import { ThemeChecksums } from '../../../../class/checksums/theme_checksums.js';
7
+ export class HiddenDirectoryUtils {
8
+ static async ensureFilesInsideThemeMetaDataDirectoryUntouched(themeDirectory) {
9
+ const themeMetadataPath = this.getThemeHiddenDirectoryPath(themeDirectory);
10
+ const themeChecksums = new ThemeChecksums(themeDirectory);
11
+ if (!(await themeChecksums.verify()))
12
+ throw new Error('Theme checksum file is not valid');
13
+ const filesNames = (await getAllFilesNamesInside(themeMetadataPath)).filter((fileName) => fileName !== THEME_CURRENT_CHECKSUMS_FILE_NAME &&
14
+ fileName !== THEME_CURRENT_CHECKSUMS_VERITY_FILE_NAME &&
15
+ fileName !== THEME_INITIAL_CHECKSUMS_FILE_NAME &&
16
+ fileName !== THEME_INITIAL_CHECKSUMS_VERITY_FILE_NAME);
17
+ for (const fileName of filesNames) {
18
+ if (fileName === THEME_CURRENT_CHECKSUMS_FILE_NAME)
19
+ continue;
20
+ const fileFullPath = join(themeMetadataPath, fileName);
21
+ const fileRelativePath = join(SHOPER_THEME_METADATA_DIR, fileName);
22
+ const currentChecksum = await computeFileChecksum(fileFullPath);
23
+ const initialChecksum = await themeChecksums.getInitialChecksumFromPath(fileRelativePath);
24
+ if (currentChecksum !== initialChecksum) {
25
+ throw new Error(`File ${fileName} inside theme metadata directory (${themeMetadataPath}) has been modified. You cannot modify files inside .shoper directory`);
26
+ }
27
+ }
28
+ }
29
+ static getThemeHiddenDirectoryPath(themeDirectory) {
30
+ return join(themeDirectory, SHOPER_THEME_METADATA_DIR);
31
+ }
32
+ }
@@ -0,0 +1 @@
1
+ export const THEME_WORK_URL_MISMATCH_ERROR = 'theme.meta_data.error_work_url_mismatch';
@@ -0,0 +1,15 @@
1
+ import { AppError } from '../../../../../cli/class/errors/app/app_error.js';
2
+ import { THEME_WORK_URL_MISMATCH_ERROR } from './theme_meta_data_constants.js';
3
+ export class ThemeMetaDataErrorFactory {
4
+ static createThemeWorkUrlMismatchError(executionUrl, themeWorkUrl) {
5
+ return new AppError({
6
+ code: THEME_WORK_URL_MISMATCH_ERROR,
7
+ message: `Theme work URL "${themeWorkUrl}" does not match execution URL "${executionUrl}".`,
8
+ level: 'error',
9
+ details: {
10
+ executionUrl,
11
+ themeWorkUrl
12
+ }
13
+ });
14
+ }
15
+ }
@@ -0,0 +1,39 @@
1
+ import { closestFile, readJSONFile, writeJSONFile } from '../../../../../utils/fs/fs_utils.js';
2
+ import { SHOPER_THEME_METADATA_DIR } from '../../../../constants/directory_contstants.js';
3
+ import { join } from '../../../../../utils/path_utils.js';
4
+ import { HiddenDirectoryUtils } from '../hidden_directory/hidden_directory_utils.js';
5
+ import { ThemeMetaDataErrorFactory } from './theme_meta_data_error_factory.js';
6
+ export class ThemeMetaDataUtils {
7
+ static async closestThemeRootDirectory(path) {
8
+ return closestFile(SHOPER_THEME_METADATA_DIR, path);
9
+ }
10
+ static getThemeMetadataFilePath(themeDirectory) {
11
+ return join(HiddenDirectoryUtils.getThemeHiddenDirectoryPath(themeDirectory), 'metadata.json');
12
+ }
13
+ static async getThemeMetadata(themeDirectory) {
14
+ const filePath = this.getThemeMetadataFilePath(themeDirectory);
15
+ const metadataFileContent = await readJSONFile(filePath);
16
+ return {
17
+ themeId: String(metadataFileContent.skin_id)
18
+ };
19
+ }
20
+ static async updateThemeMetadata(themeDirectory, data) {
21
+ const filePath = this.getThemeMetadataFilePath(themeDirectory);
22
+ const newData = {
23
+ ...data,
24
+ skin_id: data.themeId
25
+ };
26
+ delete newData.themeId;
27
+ await writeJSONFile(filePath, newData);
28
+ }
29
+ static async getThemeWorkUrl(themeDirectory) {
30
+ const metadataFilePath = this.getThemeMetadataFilePath(themeDirectory);
31
+ const metadata = await readJSONFile(metadataFilePath);
32
+ return metadata.workUrl || '';
33
+ }
34
+ static async ensureThemeWorkUrlMatch(themeDirectory, executionWorkUrl) {
35
+ const metadataWorkUrl = await this.getThemeWorkUrl(themeDirectory);
36
+ if (metadataWorkUrl !== executionWorkUrl)
37
+ throw ThemeMetaDataErrorFactory.createThemeWorkUrlMismatchError(executionWorkUrl, metadataWorkUrl);
38
+ }
39
+ }
@@ -3,12 +3,9 @@ import walkSync from 'walk-sync';
3
3
  import { THEME_FILES_OPERATIONS } from '../../merge/theme_merge_constants.js';
4
4
  import { fileExists, readJSONFile, renameFile } from '../../../../../utils/fs/fs_utils.js';
5
5
  import { join } from '../../../../../utils/path_utils.js';
6
- import fs from 'node:fs';
7
- import fsPromises from 'node:fs/promises';
8
- import { computeChecksumsFromSource } from '../../../../../utils/checksums/checksums_utils.js';
9
- import { ThemeChecksumsUtils } from '../checksums/theme_checksums_utils.js';
6
+ import { ThemeChecksums } from '../../../../class/checksums/theme_checksums.js';
10
7
  export class ThemeResourcesWithIdDirectoryUtils {
11
- static async updateDirectoryNamesOfResourcesWithIdAccordingToLocalThemeNames(localResourcesDir, localThemeDir, remoteResourcesDir, remoteThemeDir) {
8
+ static async updateDirectoryNamesOfResourcesWithIdAccordingToLocalThemeNames(localResourcesDir, remoteResourcesDir, remoteThemeDir) {
12
9
  const localThemeTree = new FSTree({
13
10
  entries: walkSync.entries(localResourcesDir, {
14
11
  globs: ['*/'],
@@ -37,18 +34,10 @@ export class ThemeResourcesWithIdDirectoryUtils {
37
34
  if (!remoteResourceDirectoryRelativePath)
38
35
  continue;
39
36
  await renameFile(join(remoteResourcesDir, remoteResourceDirectoryRelativePath), join(remoteResourcesDir, resourceDirectoryRelativePath));
40
- const resultPath = join(remoteResourcesDir, resourceDirectoryRelativePath);
41
- const stat = fs.lstatSync(resultPath);
42
- console.log('stat', stat.isDirectory());
43
- if (stat.isSymbolicLink()) {
44
- console.warn(`[WARN] Renamed path became a symlink: ${resultPath}`);
45
- console.warn(`Points to: ${await fsPromises.readlink(resultPath)}`);
46
- }
47
37
  /**
48
38
  * If these become performance bottlenecks, we can consider computing checksums only for the changed directories.
49
39
  */
50
- const checksums = await computeChecksumsFromSource(remoteThemeDir);
51
- await ThemeChecksumsUtils.createThemeChecksumsFiles(remoteThemeDir, checksums);
40
+ await new ThemeChecksums(remoteThemeDir).updateAllChecksums();
52
41
  }
53
42
  }
54
43
  static async _getResourceIdToFilePathMap(entries) {
@@ -21,7 +21,7 @@ export class ThemeImagesUtils {
21
21
  }
22
22
  static async removeUploadedOriginalFiles(themeRootDir, uploadedImagesData) {
23
23
  await Promise.all(uploadedImagesData
24
- .filter(({ originalFilename }) => Boolean(originalFilename))
24
+ .filter(({ originalFilename, uploadedFilename }) => Boolean(originalFilename) && Boolean(uploadedFilename))
25
25
  .map(({ originalFilename, location }) => {
26
26
  const originalFilePath = join(themeRootDir, location, originalFilename);
27
27
  return removeFile(originalFilePath);
@@ -10,4 +10,7 @@ export class ThemesListApi extends FeatureApi {
10
10
  async getThemes(credentials) {
11
11
  return this.#service.getThemes(credentials);
12
12
  }
13
+ async getTheme(props) {
14
+ return this.#service.getTheme(props);
15
+ }
13
16
  }