@shoper/cli 0.1.0-8 → 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 +5 -3
  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 +102 -73
  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 +5 -6
  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 +18 -38
  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 +17 -3
  151. package/build/utils/path_utils.js +20 -1
  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 +15 -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 -124
  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
@@ -1,92 +1,97 @@
1
1
  import tmp from 'tmp-promise';
2
2
  import { THEME_ACTION_DATA_TYPE, THEME_ACTIONS_TYPES } from '../../actions/theme_actions_constants.js';
3
3
  import { createZip } from '../../../../../utils/zip/create_zip_utils.js';
4
- import { basename, dirname, extname, join, looksLikeDirectory } from '../../../../../utils/path_utils.js';
4
+ import { basename, dirname, extname, join, looksLikeDirectory, toUnixPath } from '../../../../../utils/path_utils.js';
5
5
  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
- console.log('uploadData', uploadData);
38
- if (uploadData.length) {
39
- await this._uploadThemeFiles({
40
- filesToUpload: uploadData,
41
- 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,
42
63
  credentials,
43
- localFiles,
44
- themeRootDir: executionContext.themeRootDir
64
+ pushAction
45
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();
46
73
  }
47
- else {
48
- await this._createAFilesListFile(executionContext.themeRootDir, localFiles);
74
+ finally {
75
+ await removeFile(join(themeRootDir, THEME_FILES_LIST_FILE_NAME));
49
76
  }
50
- const themeArchivePath = join(tmpDir, `${uuid()}.zip`);
77
+ }
78
+ async _createThemeArchive({ themeRootDir, filesToArchive, dist, credentials }) {
51
79
  try {
52
- await this._createThemeArchive({
53
- themeRootDir: executionContext.themeRootDir,
54
- filesToArchive: [
55
- ...getFilesGlobsThatMatchesActionName(THEME_ACTIONS_TYPES.push, THEME_WILDCARD_ACTION_NAME, filesStructure),
56
- THEME_FILES_LIST_FILE_NAME
57
- ],
58
- 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
59
90
  });
60
91
  }
61
92
  catch (err) {
62
93
  throw ThemePushErrorsFactory.createErrorWhileCreatingThemeArchive(credentials.shopUrl, err);
63
94
  }
64
- const { resources, modules } = await this._uploadThemeArchive({
65
- themeArchivePath,
66
- credentials,
67
- pushAction
68
- });
69
- if (modules)
70
- await this._updateDataForNewCreatedModules({ modules, themeRootDir: executionContext.themeRootDir });
71
- console.log('resources', resources);
72
- if (resources)
73
- await this.#themeFetchApi.fetchResources(credentials.shopUrl, executionContext.themeRootDir, resources);
74
- await removeFile(join(executionContext.themeRootDir, THEME_FILES_LIST_FILE_NAME));
75
- const checksums = await computeChecksumsFromSource(executionContext.themeRootDir);
76
- await ThemeChecksumsUtils.createThemeChecksumsFiles(executionContext.themeRootDir, checksums);
77
- }
78
- async _createThemeArchive({ themeRootDir, filesToArchive, dist }) {
79
- const filesInThemeDirectory = await globs(filesToArchive, {
80
- suppressErrors: true,
81
- onlyFiles: true,
82
- cwd: themeRootDir
83
- });
84
- await this._formatJsonFiles(themeRootDir, filesInThemeDirectory);
85
- return createZip({
86
- files: filesInThemeDirectory,
87
- baseDir: themeRootDir,
88
- dist
89
- });
90
95
  }
91
96
  async _uploadThemeArchive({ themeArchivePath, credentials, pushAction }) {
92
97
  try {
@@ -101,7 +106,6 @@ export class ThemePushService {
101
106
  return response.data;
102
107
  }
103
108
  catch (err) {
104
- console.log('error', err.response.data.messages);
105
109
  throw ThemePushErrorsFactory.createErrorWhileUploadingTheme(credentials.shopUrl, err.response.data.messages);
106
110
  }
107
111
  }
@@ -110,20 +114,27 @@ export class ThemePushService {
110
114
  .filter((path) => extname(path).toLowerCase() === '.json')
111
115
  .map((jsonFile) => formatJSONFile(join(themeRootDir, jsonFile))));
112
116
  }
117
+ //TODO osobna klasa do uploadu
113
118
  async _uploadThemeFiles({ filesToUpload, themeRootDir, localFiles, credentials }) {
114
119
  try {
115
- const uploadedImageData = await this._uploadFiles(filesToUpload, credentials);
120
+ const { uploadedImageData, rejectedImageData } = await this._uploadFiles(filesToUpload, credentials);
116
121
  const newFilesList = ThemeImagesUtils.updateOriginalFilenameToUploadedFilename(localFiles, uploadedImageData);
117
122
  if (uploadedImageData.length)
118
- await ThemeImagesUtils.renameOriginalFilenameToUploadedFilename(themeRootDir, uploadedImageData);
119
- await this._createAFilesListFile(themeRootDir, newFilesList);
123
+ await ThemeImagesUtils.removeUploadedOriginalFiles(themeRootDir, uploadedImageData);
124
+ if (rejectedImageData.length)
125
+ await this._removeUploadedThemeFiles(rejectedImageData, themeRootDir);
126
+ return { newFiles: newFilesList };
120
127
  }
121
128
  catch (err) {
122
129
  throw ThemePushErrorsFactory.createErrorWhileUploadingThemeFiles(credentials.shopUrl, err);
123
130
  }
124
131
  }
132
+ async _removeUploadedThemeFiles(uploadedImageData, themeRootDir) {
133
+ await Promise.all(uploadedImageData.map(({ location, originalFilename }) => removeFile(join(themeRootDir, location, originalFilename))));
134
+ }
125
135
  async _uploadFiles(uploadData, credentials) {
126
136
  const uploadedImageData = [];
137
+ const rejectedImageData = [];
127
138
  await Promise.all(uploadData.map(({ actionData, path }) => this.#themePushHttpApi
128
139
  .pushThemeData({
129
140
  actionData,
@@ -131,22 +142,33 @@ export class ThemePushService {
131
142
  shopUrl: credentials.shopUrl
132
143
  })
133
144
  .response.then((response) => {
134
- if (response.status !== 200 || !response.data.isSuccess)
135
- throw ThemePushErrorsFactory.createErrorWhileUploadingThemeFiles(credentials.shopUrl, response.data?.messages ?? []);
136
145
  uploadedImageData.push({
137
146
  location: dirname(path),
138
147
  originalFilename: basename(path),
139
148
  uploadedFilename: response.data.imageId
140
149
  });
150
+ })
151
+ .catch(() => {
152
+ rejectedImageData.push({
153
+ location: dirname(path),
154
+ originalFilename: basename(path)
155
+ });
141
156
  })));
142
- return uploadedImageData;
157
+ return {
158
+ uploadedImageData,
159
+ rejectedImageData
160
+ };
143
161
  }
144
- async _getActionDataForFilesToUpload({ pushAction, filesStructure, executionContext }) {
162
+ async _getActionDataForFilesToUpload({ pushAction, filesStructure, executionContext, themeChecksums }) {
145
163
  const uploadData = [];
146
164
  const localFiles = {};
147
165
  for (const [actionKey, actionData] of Object.entries(pushAction.data)) {
148
166
  if (actionData.type === THEME_ACTION_DATA_TYPE.file) {
149
- 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
+ });
150
172
  for (const fileGlob of filesGlobs) {
151
173
  const files = await globs(fileGlob, {
152
174
  suppressErrors: true,
@@ -163,8 +185,8 @@ export class ThemePushService {
163
185
  localFiles[fileGlob] = files.length ? basename(files[0]) : null;
164
186
  }
165
187
  for (const filePath of files) {
166
- if ((await this.#themeMergeApi.hasFileBeenCreated(filePath, executionContext)) ||
167
- (await this.#themeMergeApi.hasFileBeenModified(filePath, executionContext))) {
188
+ if ((await themeChecksums.hasThemeFileBeenCreated(filePath)) ||
189
+ (await themeChecksums.hasThemeFileBeenModified(filePath))) {
168
190
  uploadData.push({
169
191
  actionData,
170
192
  actionKey,
@@ -186,9 +208,16 @@ export class ThemePushService {
186
208
  }
187
209
  }
188
210
  async _createAFilesListFile(themeRootDir, filesList) {
189
- console.log('filesList', filesList);
190
- //TODO replace pathSeparator to unix style if platform windows
191
- if (Object.keys(filesList))
192
- await writeJSONFile(join(themeRootDir, THEME_FILES_LIST_FILE_NAME), filesList);
211
+ if (!filesList || !Object.keys(filesList).length)
212
+ return;
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;
216
+ return {
217
+ ...acc,
218
+ [finalPath]: value
219
+ };
220
+ }, {});
221
+ await writeJSONFile(join(themeRootDir, THEME_FILES_LIST_FILE_NAME), toUnixStyleFilesList);
193
222
  }
194
223
  }
@@ -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) {
@@ -1,5 +1,5 @@
1
1
  import { join } from '../../../../utils/path_utils.js';
2
- import { renameFile } from '../../../../utils/fs/fs_utils.js';
2
+ import { removeFile } from '../../../../utils/fs/fs_utils.js';
3
3
  export class ThemeImagesUtils {
4
4
  static updateOriginalFilenameToUploadedFilename(filesList, uploadedImageData) {
5
5
  const newFilesList = { ...filesList };
@@ -19,13 +19,12 @@ export class ThemeImagesUtils {
19
19
  });
20
20
  return newFilesList;
21
21
  }
22
- static async renameOriginalFilenameToUploadedFilename(themeRootDir, uploadedImagesData) {
22
+ static async removeUploadedOriginalFiles(themeRootDir, uploadedImagesData) {
23
23
  await Promise.all(uploadedImagesData
24
- .filter(({ originalFilename }) => Boolean(originalFilename))
25
- .map(({ uploadedFilename, originalFilename, location }) => {
24
+ .filter(({ originalFilename, uploadedFilename }) => Boolean(originalFilename) && Boolean(uploadedFilename))
25
+ .map(({ originalFilename, location }) => {
26
26
  const originalFilePath = join(themeRootDir, location, originalFilename);
27
- const newFilePath = join(themeRootDir, location, uploadedFilename ?? originalFilename);
28
- return renameFile(originalFilePath, newFilePath);
27
+ return removeFile(originalFilePath);
29
28
  }));
30
29
  }
31
30
  }
@@ -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
  }