@shoper/cli 0.6.4-0 → 0.6.4-3

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 (30) hide show
  1. package/build/cli/class/errors/http/http_errors_factory.js +1 -1
  2. package/build/cli/core/cli_setup.js +6 -6
  3. package/build/cli/index.js +0 -1
  4. package/build/theme/class/archive/theme_archive.js +45 -1
  5. package/build/theme/{features/theme/utils/archive/theme_archive_utils_errors_factory.js → class/archive/theme_archive_errors_factory.js} +1 -1
  6. package/build/theme/class/checksums/theme_checksums.js +12 -47
  7. package/build/theme/commands/pull/theme_pull_command.js +3 -4
  8. package/build/theme/commands/push/theme_push_command.js +8 -17
  9. package/build/theme/commands/theme_verify_command.js +3 -3
  10. package/build/theme/commands/ui/theme_error.js +2 -2
  11. package/build/theme/features/theme/actions/theme_actions_constants.js +1 -2
  12. package/build/theme/features/theme/actions/theme_actions_utils.js +5 -31
  13. package/build/theme/features/theme/fetch/service/theme_fetch_service.js +2 -2
  14. package/build/theme/features/theme/init/service/theme_init_service.js +2 -2
  15. package/build/theme/features/theme/merge/service/theme_merge_service.js +2 -2
  16. package/build/theme/features/theme/push/api/theme_push_api.js +2 -2
  17. package/build/theme/features/theme/push/service/theme_push_service.js +32 -90
  18. package/build/theme/features/theme/push/theme_push_utils.js +2 -2
  19. package/build/theme/features/theme/utils/files_structure/theme_file_structure_errors_factory.js +9 -1
  20. package/build/theme/features/theme/utils/{files/theme_files_utils.js → files_structure/theme_files_structure_utils.js} +23 -34
  21. package/build/theme/features/theme/utils/hidden_directory/hidden_directory_utils.js +1 -0
  22. package/build/theme/features/theme/verify/verify/theme_verify_service.js +12 -19
  23. package/build/utils/array_utils.js +0 -3
  24. package/build/utils/checksums/checksums_utils.js +4 -0
  25. package/build/utils/fs/fs_utils.js +1 -1
  26. package/package.json +3 -5
  27. package/build/theme/features/theme/push/service/theme_push_service_types.js +0 -1
  28. package/build/theme/features/theme/utils/archive/theme_archive_utils.js +0 -25
  29. package/build/theme/features/theme/utils/files/them_files_constants.js +0 -1
  30. package/build/utils/fs/fs_constants.js +0 -6
@@ -15,7 +15,7 @@ export class HttpErrorsFactory {
15
15
  }
16
16
  static createNotFoundError() {
17
17
  return new AppError({
18
- message: '404 Not found',
18
+ message: 'Not found',
19
19
  code: HTTP_NOT_FOUND_ERROR_CODE
20
20
  });
21
21
  }
@@ -29,7 +29,7 @@ export const cliSetup = async () => {
29
29
  LoggerInitializer,
30
30
  CliAuthTokensInitializer,
31
31
  CliAuthInitializer,
32
- ...getInitializersBasedOnCommand()
32
+ ...getCommandBaseInitializers()
33
33
  ],
34
34
  options: {
35
35
  featuresTracking: false,
@@ -37,15 +37,15 @@ export const cliSetup = async () => {
37
37
  }
38
38
  });
39
39
  };
40
- const getInitializersBasedOnCommand = () => {
41
- if (isCommandWithTopic(process.argv[2]))
42
- return getInitializersBasedOnCommandWithTopic();
40
+ const getCommandBaseInitializers = () => {
41
+ if (isCommandsTopic(process.argv[2]))
42
+ return getCommandWithTopicBaseInitializers();
43
43
  return [];
44
44
  };
45
- const isCommandWithTopic = (arg) => {
45
+ const isCommandsTopic = (arg) => {
46
46
  return arg === THEME_TOPIC_NAME || arg === CLI_AUTH_TOPIC_NAME;
47
47
  };
48
- const getInitializersBasedOnCommandWithTopic = () => {
48
+ const getCommandWithTopicBaseInitializers = () => {
49
49
  const topic = process.argv[2];
50
50
  const command = process.argv[3];
51
51
  switch (topic) {
@@ -6,7 +6,6 @@ export const runCLI = async () => {
6
6
  await execute({ dir: import.meta.url });
7
7
  }
8
8
  catch (error) {
9
- console.log('Error executing CLI command:', error);
10
9
  // TODO handle error
11
10
  }
12
11
  };
@@ -1 +1,45 @@
1
- export {};
1
+ import globs from 'fast-glob';
2
+ import { createZip } from '../../../utils/zip/create_zip_utils.js';
3
+ import { ThemeFilesStructureUtils } from '../../features/theme/utils/files_structure/theme_files_structure_utils.js';
4
+ import { extname, join } from '../../../utils/path_utils.js';
5
+ import { formatJSONFile } from '../../../utils/fs/fs_utils.js';
6
+ import { ThemeArchiveErrorsFactory } from './theme_archive_errors_factory.js';
7
+ import { ThemeFileStructureErrorsFactory } from '../../features/theme/utils/files_structure/theme_file_structure_errors_factory.js';
8
+ import { ThemeActionsUtils } from '../../features/theme/actions/theme_actions_utils.js';
9
+ export class ThemeArchive {
10
+ #themeRootDir;
11
+ constructor(themeRootDir) {
12
+ this.#themeRootDir = themeRootDir;
13
+ }
14
+ async createFullArchive({ dist, actionValue, actionType, logger }) {
15
+ const filesStructure = await ThemeFilesStructureUtils.getThemeFilesStructure(this.#themeRootDir);
16
+ if (!filesStructure)
17
+ throw ThemeFileStructureErrorsFactory.createNoFilesStructureError();
18
+ try {
19
+ const filesInThemeDirectory = await globs(ThemeActionsUtils.getFilesGlobsThatMatchesActionName({
20
+ actionType,
21
+ actionValue,
22
+ filesStructure
23
+ }), {
24
+ suppressErrors: true,
25
+ onlyFiles: true,
26
+ cwd: this.#themeRootDir
27
+ });
28
+ await this._formatJsonFiles(this.#themeRootDir, filesInThemeDirectory);
29
+ return createZip({
30
+ files: filesInThemeDirectory,
31
+ logger,
32
+ baseDir: this.#themeRootDir,
33
+ dist
34
+ });
35
+ }
36
+ catch (err) {
37
+ throw ThemeArchiveErrorsFactory.createErrorWhileCreatingThemeArchive(err);
38
+ }
39
+ }
40
+ async _formatJsonFiles(themeRootDir, filesToUpload) {
41
+ await Promise.all(filesToUpload
42
+ .filter((path) => extname(path).toLowerCase() === '.json')
43
+ .map((jsonFile) => formatJSONFile(join(themeRootDir, jsonFile))));
44
+ }
45
+ }
@@ -1,4 +1,4 @@
1
- import { AppError } from '../../../../../cli/utilities/features/logger/logs/app_error.js';
1
+ import { AppError } from '../../../cli/utilities/features/logger/logs/app_error.js';
2
2
  export class ThemeArchiveErrorsFactory {
3
3
  static createErrorWhileCreatingThemeArchive(error) {
4
4
  return new AppError({
@@ -10,7 +10,6 @@ import { normalize } from 'node:path';
10
10
  import { ThemePushUtils } from '../../features/theme/push/theme_push_utils.js';
11
11
  import { SHOPER_THEME_METADATA_DIR } from '../../constants/directory_contstants.js';
12
12
  import { THEME_CURRENT_CHECKSUMS_FILE_NAME, THEME_CURRENT_CHECKSUMS_VERITY_FILE_NAME, THEME_INITIAL_CHECKSUMS_FILE_NAME, THEME_INITIAL_CHECKSUMS_VERITY_FILE_NAME } from '../../features/theme/theme_constants.js';
13
- import { FILE_STATES } from '../../../utils/fs/fs_constants.js';
14
13
  export class ThemeChecksums {
15
14
  #themeDir;
16
15
  #loggerApi;
@@ -34,6 +33,12 @@ export class ThemeChecksums {
34
33
  this.#initialChecksums = await this._getChecksums(this.#initialChecksumsFilePath);
35
34
  return this.#initialChecksums;
36
35
  }
36
+ getInitialChecksumsSync() {
37
+ if (this.#initialChecksums)
38
+ return this.#initialChecksums;
39
+ this.#initialChecksums = this._getChecksumsSync(this.#initialChecksumsFilePath);
40
+ return this.#initialChecksums;
41
+ }
37
42
  async getCurrentChecksums() {
38
43
  if (this.#currentChecksums)
39
44
  return this.#currentChecksums;
@@ -67,28 +72,19 @@ export class ThemeChecksums {
67
72
  const initialChecksum = await this.getInitialChecksumFromPath(path);
68
73
  return !initialChecksum && (await fileExists(join(this.#themeDir, path)));
69
74
  }
70
- async getFileState(path) {
71
- const initialChecksum = await this.getInitialChecksumFromPath(path);
75
+ async hasThemeFileBeenModified(path) {
72
76
  const currentChecksum = await this.getCurrentChecksumFromPath(path);
73
- const fileExistsInFs = await fileExists(join(this.#themeDir, path));
74
- if (!initialChecksum && fileExistsInFs) {
75
- return FILE_STATES.created;
76
- }
77
- else if (initialChecksum && !fileExistsInFs) {
78
- return FILE_STATES.deleted;
79
- }
80
- else if (initialChecksum && currentChecksum && initialChecksum !== currentChecksum) {
81
- return FILE_STATES.modified;
82
- }
83
- else {
84
- return FILE_STATES.unchanged;
85
- }
77
+ const initialChecksum = await this.getInitialChecksumFromPath(path);
78
+ return !!currentChecksum && !!initialChecksum && currentChecksum !== initialChecksum;
86
79
  }
87
80
  async verify() {
88
81
  const initialChecksumFilePath = this.#initialChecksumsFilePath;
89
82
  const initialChecksumVerifyFilePath = this.#initialChecksumsVerificationFilePath;
83
+ console.log('Verifying theme checksums:', { initialChecksumFilePath, initialChecksumVerifyFilePath });
90
84
  const initialChecksum = await computeFileChecksum(initialChecksumFilePath);
91
85
  const initialChecksumVerify = await readJSONFile(initialChecksumVerifyFilePath);
86
+ console.log('initialChecksum current:', initialChecksum);
87
+ console.log('initialChecksumVerify:', initialChecksumVerify);
92
88
  return initialChecksum === initialChecksumVerify;
93
89
  }
94
90
  async updateAllChecksums() {
@@ -161,37 +157,6 @@ export class ThemeChecksums {
161
157
  this.#loggerApi?.debug('Finished computing theme checksums.', { details: { count: Object.keys(allChecksums).length } });
162
158
  return allChecksums;
163
159
  }
164
- async groupFilesByStatus(files) {
165
- const initialChecksums = await this.getInitialChecksums();
166
- const currentChecksums = await this.getCurrentChecksums();
167
- return files.reduce((acc, filePath) => {
168
- const initialChecksum = initialChecksums[toCurrentPlatformPath(filePath)];
169
- const currentChecksum = currentChecksums[toCurrentPlatformPath(filePath)];
170
- if (!initialChecksum) {
171
- acc.created.push(filePath);
172
- }
173
- else if (currentChecksum && initialChecksum !== currentChecksum) {
174
- acc.modified.push(filePath);
175
- }
176
- else if (initialChecksum && !currentChecksum) {
177
- acc.deleted.push(filePath);
178
- }
179
- else {
180
- acc.unchanged.push(filePath);
181
- }
182
- return acc;
183
- }, {
184
- created: [],
185
- modified: [],
186
- unchanged: [],
187
- deleted: []
188
- });
189
- }
190
- async getDeletedFiles() {
191
- const initialChecksums = await this.getInitialChecksums();
192
- const currentChecksums = await this.getCurrentChecksums();
193
- return Object.keys(initialChecksums).filter((filePath) => !currentChecksums[filePath]);
194
- }
195
160
  async _getInitialChecksumsVerification() {
196
161
  return await readJSONFile(this.#initialChecksumsVerificationFilePath);
197
162
  }
@@ -24,9 +24,8 @@ import { Info } from '../../../ui/message_box/info.js';
24
24
  import { directoryExists } from '../../../utils/fs/fs_utils.js';
25
25
  import { ThemeMetaDataUtils } from '../../features/theme/utils/meta_data/theme_meta_data_utils.js';
26
26
  import { ThemeChecksums } from '../../class/checksums/theme_checksums.js';
27
- import { LOGGER_API_NAME } from '../../../cli/utilities/features/logger/logger_constants.js';
28
27
  import { ThemeError } from '../ui/theme_error.js';
29
- import { MODULES_DIRECTORY_NAME } from '../../features/theme/theme_constants.js';
28
+ import { LOGGER_API_NAME } from '../../../cli/utilities/features/logger/logger_constants.js';
30
29
  //TODO jak jest error w pullu wowczas usuwamy docelowo pociagniety skin
31
30
  export class ThemePullCommand extends BaseThemeCommand {
32
31
  static summary = 'Downloads the current version of your theme from the store and overwrites your local theme files.';
@@ -161,9 +160,9 @@ export class ThemePullCommand extends BaseThemeCommand {
161
160
  dist: tmpDir
162
161
  }
163
162
  });
164
- const localModulesPath = join(executionContext.themeRootDir, MODULES_DIRECTORY_NAME);
163
+ const localModulesPath = join(executionContext.themeRootDir, 'modules');
165
164
  const fetchedThemePath = join(tmpDir, name);
166
- const fetchedModulesPath = join(fetchedThemePath, MODULES_DIRECTORY_NAME);
165
+ const fetchedModulesPath = join(fetchedThemePath, 'modules');
167
166
  if ((await directoryExists(localModulesPath)) && (await directoryExists(fetchedModulesPath)))
168
167
  await ThemeResourcesWithIdDirectoryUtils.updateDirectoryNamesOfResourcesWithIdAccordingToLocalThemeNames(localModulesPath, fetchedModulesPath, fetchedThemePath, loggerApi);
169
168
  this.#spinner.stop();
@@ -4,6 +4,8 @@ import { EXECUTION_CONTEXT_API_NAME, EXECUTION_CONTEXTS } from '../../../cli/fea
4
4
  import { THEME_ACTIONS_API_NAME, THEME_ACTIONS_TYPES } from '../../features/theme/actions/theme_actions_constants.js';
5
5
  import { THEME_PUSH_API_NAME } from '../../features/theme/push/theme_push_constants.js';
6
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';
7
9
  import { renderOnce } from '../../../ui/ui_utils.js';
8
10
  import { UnpermittedCommandError } from '../ui/unpermitted_command_error.js';
9
11
  import React from 'react';
@@ -12,7 +14,7 @@ import { MissingCredentialsError } from '../../../cli/commands/auth/ui/missing_c
12
14
  import { ThemePushedSuccess } from './ui/theme_pushed_success.js';
13
15
  import ora from 'ora';
14
16
  import { ThemePushSkipInfo } from './ui/theme_push_skip_into.js';
15
- import { ThemeFilesUtils } from '../../features/theme/utils/files/theme_files_utils.js';
17
+ import { ThemeFilesStructureUtils } from '../../features/theme/utils/files_structure/theme_files_structure_utils.js';
16
18
  import { ThemeInfoUtils } from '../../features/theme/info/theme_info_utils.js';
17
19
  import { ThemeChecksums } from '../../class/checksums/theme_checksums.js';
18
20
  import { ThemeFilesUpload } from '../../class/files_upload/theme_files_upload.js';
@@ -22,8 +24,6 @@ import { MissingThemeFiles } from '../ui/missing_theme_files.js';
22
24
  import { SHOPER_THEME_METADATA_DIR } from '../../constants/directory_contstants.js';
23
25
  import { THEME_FILES_STRUCTURE_FILE_NAME } from '../../features/theme/theme_constants.js';
24
26
  import { ThemeError } from '../ui/theme_error.js';
25
- import { mapToPermissionsTree } from '../../utils/directory_validator/directory_validator_utils.js';
26
- import { mapChecksumToTree } from '../../../utils/checksums/checksums_utils.js';
27
27
  import { ThemeUnpermittedActionsError } from './ui/theme_unpermitted_actions_error.js';
28
28
  import { LOGGER_API_NAME } from '../../../cli/utilities/features/logger/logger_constants.js';
29
29
  export class ThemePushCommand extends BaseThemeCommand {
@@ -68,7 +68,7 @@ export class ThemePushCommand extends BaseThemeCommand {
68
68
  return;
69
69
  }
70
70
  const initialChecksums = await themeChecksums.getInitialChecksums();
71
- const permissions = await ThemeFilesUtils.getFilesPermissions(executionContext.themeRootDir);
71
+ const permissions = await ThemeFilesStructureUtils.getFilesPermissions(executionContext.themeRootDir);
72
72
  const themeFilesUploadApi = new ThemeFilesUpload({
73
73
  themeRootDir: executionContext.themeRootDir,
74
74
  credentials,
@@ -80,7 +80,7 @@ export class ThemePushCommand extends BaseThemeCommand {
80
80
  renderOnce(React.createElement(ThemePushSkipInfo, null));
81
81
  return;
82
82
  }
83
- const validationResult = await ThemeFilesUtils.validateThemeDirectoryStructure({
83
+ const validationResult = await ThemeFilesStructureUtils.validateThemeDirectoryStructure({
84
84
  //TDO przeniesc do theme checksums
85
85
  checksums: mapChecksumToTree(initialChecksums),
86
86
  permissions: mapToPermissionsTree(permissions),
@@ -92,25 +92,16 @@ export class ThemePushCommand extends BaseThemeCommand {
92
92
  renderOnce(React.createElement(ThemeUnpermittedActionsError, { unpermittedActions: validationResult.unpermittedActions }));
93
93
  return;
94
94
  }
95
- const filesStructure = await ThemeFilesUtils.getThemeFilesStructure(executionContext.themeRootDir);
95
+ const filesStructure = await ThemeFilesStructureUtils.getThemeFilesStructure(executionContext.themeRootDir);
96
96
  if (!filesStructure) {
97
97
  renderOnce(React.createElement(MissingThemeFiles, { files: `${SHOPER_THEME_METADATA_DIR}/${THEME_FILES_STRUCTURE_FILE_NAME}` }));
98
98
  return;
99
99
  }
100
100
  spinner = ora('Pushing theme...').start();
101
- const partialPushAction = themeActionsApi.getThemeAction({
102
- actionType: THEME_ACTIONS_TYPES.partialPush,
103
- themeId: executionContext.themeId,
104
- credentials
105
- });
106
- if (!pushAction) {
107
- renderOnce(React.createElement(UnpermittedCommandError, { themeId: executionContext.themeId, commandName: "push" }));
108
- return;
109
- }
110
- await themePushApi.partialPush({
101
+ await themePushApi.push({
111
102
  credentials,
112
103
  filesStructure,
113
- action: partialPushAction,
104
+ pushAction,
114
105
  themeChecksums,
115
106
  executionContext,
116
107
  themeFilesUploadApi
@@ -1,6 +1,7 @@
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 { ThemeFilesStructureUtils } from '../features/theme/utils/files_structure/theme_files_structure_utils.js';
4
5
  import { ThemeChecksums } from '../class/checksums/theme_checksums.js';
5
6
  import { THEME_VERIFY_API_NAME } from '../features/theme/verify/theme_verify_constants.js';
6
7
  import { THEME_ACTIONS_API_NAME, THEME_ACTIONS_TYPES } from '../features/theme/actions/theme_actions_constants.js';
@@ -17,7 +18,6 @@ import { MissingCredentialsError } from '../../cli/commands/auth/ui/missing_cred
17
18
  import { OutsideOfThemeDirectoryContextError } from './ui/ouside_of_theme_directory_context_error.js';
18
19
  import ora from 'ora';
19
20
  import { ThemeError } from './ui/theme_error.js';
20
- import { ThemeFilesUtils } from '../features/theme/utils/files/theme_files_utils.js';
21
21
  import { LOGGER_API_NAME } from '../../cli/utilities/features/logger/logger_constants.js';
22
22
  export class ThemeVerifyCommand extends BaseThemeCommand {
23
23
  static description = 'Verify theme files structure';
@@ -39,7 +39,7 @@ export class ThemeVerifyCommand extends BaseThemeCommand {
39
39
  themeDir: executionContext.themeRootDir,
40
40
  loggerApi
41
41
  });
42
- const permissions = await ThemeFilesUtils.getFilesPermissions(executionContext.themeRootDir);
42
+ const permissions = await ThemeFilesStructureUtils.getFilesPermissions(executionContext.themeRootDir);
43
43
  if (!themeChecksums || !permissions)
44
44
  this.error('Theme checksums or permissions not found. Please ensure you are in the correct theme directory.');
45
45
  let spinner;
@@ -56,7 +56,7 @@ export class ThemeVerifyCommand extends BaseThemeCommand {
56
56
  themeId: executionContext.themeId,
57
57
  credentials
58
58
  });
59
- const filesStructure = await ThemeFilesUtils.getThemeFilesStructure(executionContext.themeRootDir);
59
+ const filesStructure = await ThemeFilesStructureUtils.getThemeFilesStructure(executionContext.themeRootDir);
60
60
  if (!filesStructure) {
61
61
  renderOnce(React.createElement(MissingThemeFiles, { files: `${SHOPER_THEME_METADATA_DIR}/${THEME_FILES_STRUCTURE_FILE_NAME}` }));
62
62
  return;
@@ -19,8 +19,8 @@ export const ThemeError = ({ err, executionContext }) => {
19
19
  return React.createElement(ThemeWorkUrlMismatch, { command: "verify" });
20
20
  }
21
21
  if (err?.code === THEME_FILES_UPLOAD_ERROR) {
22
- return (React.createElement(Error, { header: "Uploading theme files to the shop failed." },
23
- React.createElement(Text, null, "Please ensure that the files are not corrupted and that the file extensions are supported or file size is not too large.")));
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
24
  }
25
25
  if (err?.message) {
26
26
  return React.createElement(ValidationErrors, { errors: err.message });
@@ -8,8 +8,7 @@ export const THEME_ACTIONS_TYPES = {
8
8
  delete: 'delete',
9
9
  publishForm: 'publish_form',
10
10
  publish: 'publish',
11
- verify: 'verify',
12
- partialPush: 'partial_push'
11
+ verify: 'verify'
13
12
  };
14
13
  export const THEME_ACTION_DATA_TYPE = {
15
14
  file: 'file',
@@ -3,10 +3,8 @@ import { basename, looksLikeDirectory, toUnixPath } from '../../../../utils/path
3
3
  import { THEME_ACTION_DATA_TYPE } from './theme_actions_constants.js';
4
4
  import globs from 'fast-glob';
5
5
  import { THEME_PUSH_WILDCARD_GLOBS_FOR_FILES } from '../push/service/theme_push_service_constants.js';
6
- import micromatch from 'micromatch';
7
- import difference from 'lodash/difference.js';
8
6
  export class ThemeActionsUtils {
9
- static getFilesGlobsThatMatchesAction({ filesStructure, actionValue, actionType }) {
7
+ static getFilesGlobsThatMatchesActionName({ filesStructure, actionValue, actionType }) {
10
8
  return Object.entries(filesStructure).reduce((acc, [filePath, fileStructureItem]) => {
11
9
  if (fileStructureItem._links?.[actionType] && this._doesActionValueMatch(fileStructureItem._links[actionType], actionValue)) {
12
10
  return [...acc, toUnixPath(filePath)];
@@ -14,32 +12,11 @@ export class ThemeActionsUtils {
14
12
  return acc;
15
13
  }, []);
16
14
  }
17
- static async getFilesThatMatchesAction({ actionType, actionValue, filesStructure, rootDir }) {
18
- return await globs(ThemeActionsUtils.getFilesGlobsThatMatchesAction({
19
- actionType,
20
- actionValue,
21
- filesStructure
22
- }), {
23
- suppressErrors: true,
24
- onlyFiles: true,
25
- cwd: rootDir
26
- });
27
- }
28
- static async getDeletedFilesThatMatchesAction({ actionType, actionValue, themeChecksums, filesStructure }) {
29
- const deletedFiles = await themeChecksums.getDeletedFiles();
30
- const globs = ThemeActionsUtils.getFilesGlobsThatMatchesAction({
31
- actionType,
32
- actionValue,
33
- filesStructure
34
- });
35
- return deletedFiles.filter((path) => !looksLikeDirectory(path) && micromatch.isMatch(path, globs));
36
- }
37
- static async getFilesRecordsFromActionData({ themeRootDir, themeAction, filesStructure, themeChecksums }) {
15
+ static async getFilesRecordsFromActionData({ themeRootDir, themeAction, filesStructure }) {
38
16
  const filesRecords = [];
39
- const initialChecksums = await themeChecksums.getInitialChecksums();
40
17
  for (const [actionKey, actionData] of Object.entries(themeAction.data)) {
41
18
  if (actionData.type === THEME_ACTION_DATA_TYPE.file) {
42
- const filesGlobs = ThemeActionsUtils.getFilesGlobsThatMatchesAction({
19
+ const filesGlobs = ThemeActionsUtils.getFilesGlobsThatMatchesActionName({
43
20
  //TODO remove when backend fixed
44
21
  actionType: 'push',
45
22
  // actionType: themeAction.actionType,
@@ -47,7 +24,6 @@ export class ThemeActionsUtils {
47
24
  filesStructure
48
25
  });
49
26
  for (const fileGlob of filesGlobs) {
50
- const checksumMatchedFiles = micromatch(Object.keys(initialChecksums), [fileGlob]);
51
27
  const files = await globs(fileGlob, {
52
28
  suppressErrors: true,
53
29
  onlyFiles: true,
@@ -59,15 +35,13 @@ export class ThemeActionsUtils {
59
35
  ? fileGlob.slice(0, fileGlob.length - 2)
60
36
  : fileGlob;
61
37
  }
62
- const deletedFiles = difference(checksumMatchedFiles, files);
63
- for (const filePath of [...files, ...deletedFiles]) {
38
+ for (const filePath of files) {
64
39
  filesRecords.push({
65
40
  actionData,
66
41
  actionKey,
67
42
  path: filePath,
68
43
  fileName: basename(filePath),
69
- fileGlob: processedFileGlob,
70
- state: await themeChecksums.getFileState(filePath)
44
+ fileGlob: processedFileGlob
71
45
  });
72
46
  }
73
47
  }
@@ -8,9 +8,9 @@ import { FetchResources } from '../../../../class/fetch_resources/fetch_resource
8
8
  import { jsonIndentTransform } from '../../../../../utils/stream_transforms/json_indent_transform.js';
9
9
  import { JSON_FILE_INDENT } from '../../../../../cli/cli_constants.js';
10
10
  import { AppError } from '../../../../../cli/utilities/features/logger/logs/app_error.js';
11
+ import { ThemeFilesStructureUtils } from '../../utils/files_structure/theme_files_structure_utils.js';
11
12
  import { ThemeChecksums } from '../../../../class/checksums/theme_checksums.js';
12
13
  import { ThemeMetaDataUtils } from '../../utils/meta_data/theme_meta_data_utils.js';
13
- import { ThemeFilesUtils } from '../../utils/files/theme_files_utils.js';
14
14
  export class ThemeFetchService {
15
15
  #themeHttpApi;
16
16
  #httpApi;
@@ -55,7 +55,7 @@ export class ThemeFetchService {
55
55
  }
56
56
  try {
57
57
  this.#loggerApi.debug('Updating theme files structure.');
58
- await ThemeFilesUtils.updateFilesStructure(themeDir);
58
+ await ThemeFilesStructureUtils.updateFilesStructure(themeDir);
59
59
  }
60
60
  catch (error) {
61
61
  throw new AppError({
@@ -6,8 +6,8 @@ import { extractZip } from '../../../../../utils/zip/extract_zip_utils.js';
6
6
  import { ThemeMetaDataUtils } from '../../utils/meta_data/theme_meta_data_utils.js';
7
7
  import { ThemeInfoUtils } from '../../info/theme_info_utils.js';
8
8
  import { ThemeChecksums } from '../../../../class/checksums/theme_checksums.js';
9
+ import { ThemeFilesStructureUtils } from '../../utils/files_structure/theme_files_structure_utils.js';
9
10
  import { AppError } from '../../../../../cli/utilities/features/logger/logs/app_error.js';
10
- import { ThemeFilesUtils } from '../../utils/files/theme_files_utils.js';
11
11
  export class ThemeInitService {
12
12
  #httpApi;
13
13
  #loggerApi;
@@ -41,7 +41,7 @@ export class ThemeInitService {
41
41
  this.#loggerApi.info('Theme archive extracted.');
42
42
  try {
43
43
  this.#loggerApi.debug('Updating theme files structure.');
44
- await ThemeFilesUtils.updateFilesStructure(themeDir);
44
+ await ThemeFilesStructureUtils.updateFilesStructure(themeDir);
45
45
  }
46
46
  catch (err) {
47
47
  throw new AppError({
@@ -2,9 +2,9 @@ import FSTree from 'fs-tree-diff';
2
2
  import { copyFileSync, getAllDirectoriesNamesInside } from '../../../../../utils/fs/fs_utils.js';
3
3
  import walkSync from 'walk-sync';
4
4
  import { ThemeChecksums } from '../../../../class/checksums/theme_checksums.js';
5
+ import { ThemeFilesStructureUtils } from '../../utils/files_structure/theme_files_structure_utils.js';
5
6
  import { SHOPER_THEME_METADATA_DIR } from '../../../../constants/directory_contstants.js';
6
7
  import { join, platformSeparator } from '../../../../../utils/path_utils.js';
7
- import { ThemeFilesUtils } from '../../utils/files/theme_files_utils.js';
8
8
  export class ThemeMergeService {
9
9
  #loggerApi;
10
10
  constructor({ loggerApi }) {
@@ -73,7 +73,7 @@ export class ThemeMergeService {
73
73
  return patch;
74
74
  }
75
75
  async _getUserRootDirectories(themeDir) {
76
- const filesStructure = await ThemeFilesUtils.getThemeRootDirectories(themeDir);
76
+ const filesStructure = await ThemeFilesStructureUtils.getThemeRootDirectories(themeDir);
77
77
  return (await getAllDirectoriesNamesInside(themeDir, {
78
78
  recursive: false,
79
79
  hidden: true
@@ -7,7 +7,7 @@ export class ThemePushApi extends FeatureApi {
7
7
  super();
8
8
  this.#service = service;
9
9
  }
10
- async partialPush(props) {
11
- return this.#service.partialPush(props);
10
+ async push(props) {
11
+ return this.#service.push(props);
12
12
  }
13
13
  }
@@ -1,21 +1,19 @@
1
1
  import tmp from 'tmp-promise';
2
2
  import { THEME_ACTIONS_TYPES } from '../../actions/theme_actions_constants.js';
3
- import { dirname, join } from '../../../../../utils/path_utils.js';
3
+ import { join } from '../../../../../utils/path_utils.js';
4
4
  import { v4 as uuid } from 'uuid';
5
5
  import { THEME_WILDCARD_ACTION_NAME } from '../../actions/service/theme_actions_service_constants.js';
6
- import { THEME_FILES_LIST_FILE_NAME, THEME_MODULE_SETTINGS_FILE_NAME } from '../theme_push_constants.js';
6
+ import { THEME_MODULE_SETTINGS_FILE_NAME } from '../theme_push_constants.js';
7
7
  import { ThemePushErrorsFactory } from '../theme_push_errors_factory.js';
8
8
  import { ThemeImagesUtils } from '../../utils/theme_images_utils.js';
9
9
  import { ThemePublishUtils } from '../../skinstore/theme_publish_utils.js';
10
- import { fileExists, getAllDirectoriesNamesInside, readJSONFile, writeJSONFile } from '../../../../../utils/fs/fs_utils.js';
10
+ import { writeJSONFile } from '../../../../../utils/fs/fs_utils.js';
11
11
  import { MODULES_DIRECTORY_NAME } from '../../theme_constants.js';
12
12
  import { removeOldResources } from '../../../../class/fetch_resources/fetch_resources_utils.js';
13
- import { ThemeFilesUtils } from '../../utils/files/theme_files_utils.js';
13
+ import { ThemeArchive } from '../../../../class/archive/theme_archive.js';
14
+ import { ThemeFilesStructureUtils } from '../../utils/files_structure/theme_files_structure_utils.js';
14
15
  import { ThemeActionsUtils } from '../../actions/theme_actions_utils.js';
15
- import { ThemeArchiveUtils } from '../../utils/archive/theme_archive_utils.js';
16
- import { FILE_STATES } from '../../../../../utils/fs/fs_constants.js';
17
- import uniq from 'lodash/uniq.js';
18
- import { FILES_LIST_CUSTOM_MODULES_TO_KEEP_IDS } from '../../utils/files/them_files_constants.js';
16
+ import { ArrayUtils } from '@dreamcommerce/utilities';
19
17
  export class ThemePushService {
20
18
  #themeFetchApi;
21
19
  #loggerApi;
@@ -23,7 +21,8 @@ export class ThemePushService {
23
21
  this.#themeFetchApi = themeFetchApi;
24
22
  this.#loggerApi = loggerApi;
25
23
  }
26
- async partialPush({ action, filesStructure, executionContext, themeChecksums, themeFilesUploadApi, credentials }) {
24
+ async push({ pushAction, credentials, filesStructure, executionContext, themeChecksums, themeFilesUploadApi }) {
25
+ this.#loggerApi.info('Pushing theme changes.');
27
26
  const { path: tmpDir } = await tmp.dir({ unsafeCleanup: true });
28
27
  this.#loggerApi.debug('Temporary directory created.', { details: { path: tmpDir } });
29
28
  const themeRootDir = executionContext.themeRootDir;
@@ -31,72 +30,48 @@ export class ThemePushService {
31
30
  throw ThemePushErrorsFactory.createErrorWhilePushingUnpublishedThemeWithSkinstoreData(credentials.shopUrl);
32
31
  try {
33
32
  this.#loggerApi.debug('Getting file records from action data.');
34
- //TODO to do api?
35
33
  const filesRecords = await ThemeActionsUtils.getFilesRecordsFromActionData({
36
34
  themeRootDir,
37
- themeAction: action,
38
- filesStructure,
39
- themeChecksums
35
+ themeAction: pushAction,
36
+ filesStructure
40
37
  });
41
38
  this.#loggerApi.debug('Filtering for created or modified files to upload.');
42
- const filesToUpload = filesRecords.filter(({ state }) => [FILE_STATES.modified, FILE_STATES.created].includes(state));
43
- let localFileNameToUploaded;
39
+ const filesToUpload = await ArrayUtils.asyncFilter(filesRecords, async ({ path }) => (await themeChecksums.hasThemeFileBeenCreated(path)) || (await themeChecksums.hasThemeFileBeenModified(path)));
44
40
  this.#loggerApi.debug('Files to upload determined.', { details: { count: filesToUpload.length } });
45
41
  if (filesToUpload.length) {
46
42
  this.#loggerApi.info(`Uploading ${filesToUpload.length} theme files.`);
47
- const uploadData = await this._uploadThemeFiles({
43
+ const { localFileNameToUploaded } = await this._uploadThemeFiles({
48
44
  filesToUpload,
49
45
  credentials,
50
46
  themeRootDir,
51
47
  themeFilesUploadApi
52
48
  });
53
- if (!uploadData.isSuccess)
54
- throw ThemePushErrorsFactory.createErrorWhileUploadingThemeFiles(credentials.shopUrl);
55
49
  this.#loggerApi.info('Theme files uploaded.');
56
- localFileNameToUploaded = uploadData.localFileNameToUploadedMap;
50
+ await this._createFilesList(themeRootDir, filesRecords, localFileNameToUploaded);
51
+ }
52
+ else {
53
+ this.#loggerApi.info('No new or modified files to upload. Creating files list for archive.');
54
+ await this._createFilesList(themeRootDir, filesRecords);
57
55
  }
58
56
  const themeArchivePath = join(tmpDir, `${uuid()}.zip`);
59
- const filesInArchive = await ThemeActionsUtils.getFilesThatMatchesAction({
60
- actionValue: THEME_WILDCARD_ACTION_NAME,
61
- actionType: THEME_ACTIONS_TYPES.push,
62
- filesStructure,
63
- rootDir: themeRootDir
64
- });
65
- const groupedFiles = await themeChecksums.groupFilesByStatus(filesInArchive);
66
- const deletedFiles = await ThemeActionsUtils.getDeletedFilesThatMatchesAction({
67
- actionType: THEME_ACTIONS_TYPES.push,
68
- actionValue: THEME_WILDCARD_ACTION_NAME,
69
- themeChecksums,
70
- filesStructure,
71
- rootDir: themeRootDir
72
- });
73
- await this._createFilesList({
74
- themeRootDir,
75
- filesRecords,
76
- localFileNameToUploaded,
77
- deletedFiles,
78
- allCustomModulesIds: await this._getAllModulesIds(themeRootDir)
79
- });
80
57
  this.#loggerApi.info('Creating theme archive.');
81
- await ThemeArchiveUtils.create({
58
+ await new ThemeArchive(themeRootDir).createFullArchive({
82
59
  dist: themeArchivePath,
83
- rootDir: themeRootDir,
84
- logger: this.#loggerApi,
85
- files: [
86
- ...(await this._appendSettingsIfModifiedFileFromFolderThatContainsOne(groupedFiles.modified)),
87
- ...groupedFiles.created,
88
- (await fileExists(ThemeFilesUtils.getFilesListFilePath(themeRootDir))) ? THEME_FILES_LIST_FILE_NAME : undefined
89
- ].filter(Boolean)
60
+ actionValue: THEME_WILDCARD_ACTION_NAME,
61
+ actionType: THEME_ACTIONS_TYPES.push,
62
+ logger: this.#loggerApi
90
63
  });
91
64
  this.#loggerApi.info('Theme archive created.');
92
- await ThemeFilesUtils.removeAFilesListFile(themeRootDir);
93
65
  const { resources, modules } = await themeFilesUploadApi.uploadArchive({
94
- action,
66
+ action: pushAction,
95
67
  themeArchivePath,
96
68
  credentials
97
69
  });
98
- if (modules)
70
+ this.#loggerApi.info('Theme archive uploaded.');
71
+ if (modules) {
72
+ this.#loggerApi.debug('Updating data for new modules.');
99
73
  await this._updateDataForNewCreatedModules({ modules, themeRootDir });
74
+ }
100
75
  if (resources) {
101
76
  this.#loggerApi.debug('Removing old resources.');
102
77
  await removeOldResources(themeRootDir, resources);
@@ -105,13 +80,12 @@ export class ThemePushService {
105
80
  this.#loggerApi.info('New resources fetched.');
106
81
  }
107
82
  this.#loggerApi.debug('Updating theme checksums.');
108
- //TODO only changed files checksum
109
83
  await themeChecksums.updateAllChecksums();
110
84
  this.#loggerApi.info('Push completed successfully.');
111
85
  }
112
- catch (err) {
113
- await ThemeFilesUtils.removeAFilesListFile(themeRootDir);
114
- throw err;
86
+ finally {
87
+ this.#loggerApi.debug('Cleaning up files list.');
88
+ await ThemeFilesStructureUtils.removeAFilesListFile(themeRootDir);
115
89
  }
116
90
  }
117
91
  async _uploadThemeFiles({ filesToUpload, themeRootDir, credentials, themeFilesUploadApi }) {
@@ -129,9 +103,7 @@ export class ThemePushService {
129
103
  await ThemeImagesUtils.removeUploadedOriginalFiles(themeRootDir, uploadedImageData);
130
104
  }
131
105
  return {
132
- isSuccess: rejectedImageData.length === 0,
133
- rejectedImageData,
134
- localFileNameToUploadedMap: uploadedImageData.reduce((acc, { originalFilename, uploadedFilename }) => {
106
+ localFileNameToUploaded: uploadedImageData.reduce((acc, { originalFilename, uploadedFilename }) => {
135
107
  return {
136
108
  ...acc,
137
109
  [originalFilename]: uploadedFilename ?? originalFilename
@@ -149,38 +121,8 @@ export class ThemePushService {
149
121
  await writeJSONFile(join(themeRootDir, MODULES_DIRECTORY_NAME, moduleDirectoryName, THEME_MODULE_SETTINGS_FILE_NAME), JSON.parse(metaData[THEME_MODULE_SETTINGS_FILE_NAME]));
150
122
  }
151
123
  }
152
- async _createFilesList({ deletedFiles, localFileNameToUploaded, filesRecords, themeRootDir, allCustomModulesIds }) {
124
+ async _createFilesList(themeRootDir, filesRecords, localFileNameToUploaded = {}) {
153
125
  this.#loggerApi.debug('Creating filesList.json for the archive.');
154
- const filesListContent = ThemeFilesUtils.mapFilesRecordsToFilesList(filesRecords, localFileNameToUploaded);
155
- if (deletedFiles?.length)
156
- filesListContent.removed = deletedFiles;
157
- if (allCustomModulesIds?.length)
158
- filesListContent[FILES_LIST_CUSTOM_MODULES_TO_KEEP_IDS] = allCustomModulesIds;
159
- await ThemeFilesUtils.createAFilesListFile(themeRootDir, filesListContent);
160
- }
161
- async _appendSettingsIfModifiedFileFromFolderThatContainsOne(modifiedFiles) {
162
- const withSettingsFiles = [...modifiedFiles];
163
- for (const filePath of modifiedFiles) {
164
- if (await fileExists(join(dirname(filePath), 'settings.json'))) {
165
- withSettingsFiles.push(join(dirname(filePath), 'settings.json'));
166
- }
167
- }
168
- return uniq(withSettingsFiles);
169
- }
170
- async _getAllModulesIds(themeRootDir) {
171
- const modulesPath = join(themeRootDir, MODULES_DIRECTORY_NAME);
172
- if (!(await fileExists(modulesPath)))
173
- return [];
174
- const moduleDirs = await getAllDirectoriesNamesInside(modulesPath);
175
- const modulesIds = [];
176
- for (const moduleDir of moduleDirs) {
177
- const moduleSettingsPath = join(themeRootDir, MODULES_DIRECTORY_NAME, moduleDir, THEME_MODULE_SETTINGS_FILE_NAME);
178
- if (await fileExists(moduleSettingsPath)) {
179
- const settingsContent = await readJSONFile(moduleSettingsPath);
180
- if (settingsContent.id && Number.isInteger(settingsContent.id))
181
- modulesIds.push(settingsContent.id);
182
- }
183
- }
184
- return modulesIds;
126
+ await ThemeFilesStructureUtils.createAFilesListFile(themeRootDir, ThemeFilesStructureUtils.mapFilesRecordsToFilesList(filesRecords, localFileNameToUploaded));
185
127
  }
186
128
  }
@@ -1,10 +1,10 @@
1
1
  import globs from 'fast-glob';
2
2
  import { AppError } from '../../../../cli/utilities/features/logger/logs/app_error.js';
3
3
  import { toUnixPath } from '../../../../utils/path_utils.js';
4
- import { ThemeFilesUtils } from '../utils/files/theme_files_utils.js';
4
+ import { ThemeFilesStructureUtils } from '../utils/files_structure/theme_files_structure_utils.js';
5
5
  export class ThemePushUtils {
6
6
  static async getAllFilesThatAreSendToRemote(themeDir) {
7
- const filesStructure = await ThemeFilesUtils.getThemeFilesStructure(themeDir);
7
+ const filesStructure = await ThemeFilesStructureUtils.getThemeFilesStructure(themeDir);
8
8
  if (!filesStructure)
9
9
  throw new AppError({
10
10
  message: `Files structure not found in theme directory: ${themeDir}`,
@@ -1 +1,9 @@
1
- export {};
1
+ import { AppError } from '../../../../../cli/utilities/features/logger/logs/app_error.js';
2
+ export class ThemeFileStructureErrorsFactory {
3
+ static createNoFilesStructureError() {
4
+ return new AppError({
5
+ code: 'theme.file_structure.no_files_structure',
6
+ message: 'No files structure found for the theme.'
7
+ });
8
+ }
9
+ }
@@ -1,4 +1,4 @@
1
- import { fileExists, isDirectory, readJSONFile, removeFile, writeJSONFile } from '../../../../../utils/fs/fs_utils.js';
1
+ import { readJSONFile, removeFile, writeJSONFile } from '../../../../../utils/fs/fs_utils.js';
2
2
  import { join, looksLikeDirectory, mapKeysPathsToWindowPlatform, toUnixPath } from '../../../../../utils/path_utils.js';
3
3
  import { SHOPER_THEME_METADATA_DIR } from '../../../../constants/directory_contstants.js';
4
4
  import { THEME_CURRENT_CHECKSUMS_FILE_NAME, THEME_CURRENT_CHECKSUMS_VERITY_FILE_NAME, THEME_FILES_STRUCTURE_FILE_NAME, THEME_INITIAL_CHECKSUMS_FILE_NAME, THEME_INITIAL_CHECKSUMS_VERITY_FILE_NAME } from '../../theme_constants.js';
@@ -7,8 +7,7 @@ import { validateDirectory } from '../../../../utils/directory_validator/directo
7
7
  import path from 'node:path';
8
8
  import { THEME_FILES_LIST_FILE_NAME } from '../../push/theme_push_constants.js';
9
9
  import { THEME_ACTIONS_TYPES } from '../../actions/theme_actions_constants.js';
10
- import { FILE_STATES } from '../../../../../utils/fs/fs_constants.js';
11
- export class ThemeFilesUtils {
10
+ export class ThemeFilesStructureUtils {
12
11
  static async getThemeFilesStructure(themeDirectory) {
13
12
  const filesStructure = await readJSONFile(join(themeDirectory, SHOPER_THEME_METADATA_DIR, THEME_FILES_STRUCTURE_FILE_NAME));
14
13
  if (!isWindowsOs())
@@ -20,7 +19,7 @@ export class ThemeFilesUtils {
20
19
  await writeJSONFile(filePath, filesStructure);
21
20
  }
22
21
  static async getFilesPermissions(themeDirectory) {
23
- const fileStructure = await ThemeFilesUtils.getThemeFilesStructure(themeDirectory);
22
+ const fileStructure = await ThemeFilesStructureUtils.getThemeFilesStructure(themeDirectory);
24
23
  return Object.entries(fileStructure).reduce((acc, [key, value]) => {
25
24
  return {
26
25
  ...acc,
@@ -36,25 +35,14 @@ export class ThemeFilesUtils {
36
35
  });
37
36
  }
38
37
  static async getThemeRootDirectories(themeDirectory) {
39
- const filesStructure = await ThemeFilesUtils.getThemeFilesStructure(themeDirectory);
38
+ const filesStructure = await ThemeFilesStructureUtils.getThemeFilesStructure(themeDirectory);
40
39
  return Object.keys(filesStructure).filter((path) => {
41
40
  return looksLikeDirectory(path);
42
41
  });
43
42
  }
44
43
  static mapFilesRecordsToFilesList(filesRecords, localFileNameToUploaded = {}) {
45
- let areAllSkinstoreFilesUnchanged = true;
46
- const filesList = filesRecords.reduce((acc, { fileGlob, fileName, state, actionKey }) => {
44
+ const mappedFilesRecords = filesRecords.reduce((acc, { fileGlob, fileName }) => {
47
45
  const name = localFileNameToUploaded[fileName] ?? fileName;
48
- if (state === FILE_STATES.deleted && actionKey === 'thumbnail') {
49
- return {
50
- ...acc,
51
- [fileGlob]: null
52
- };
53
- }
54
- if (actionKey === 'skinstore_files' && state !== FILE_STATES.unchanged)
55
- areAllSkinstoreFilesUnchanged = false;
56
- if (state === FILE_STATES.deleted || (state === FILE_STATES.unchanged && actionKey !== 'skinstore_files'))
57
- return acc;
58
46
  if (looksLikeDirectory(fileGlob)) {
59
47
  const existingFiles = acc[fileGlob] || [];
60
48
  return {
@@ -69,31 +57,32 @@ export class ThemeFilesUtils {
69
57
  };
70
58
  }
71
59
  }, {});
72
- if (areAllSkinstoreFilesUnchanged)
73
- delete filesList['skinstore/files'];
74
- return filesList;
60
+ /*
61
+ * Brzydki fix, poprawione w partial push na ładniej
62
+ */
63
+ if (!mappedFilesRecords['settings/thumbnail.jpg']) {
64
+ mappedFilesRecords['settings/thumbnail.jpg'] = null;
65
+ }
66
+ return mappedFilesRecords;
75
67
  }
76
68
  static async createAFilesListFile(themeRootDir, filesList) {
77
69
  if (!filesList || !Object.keys(filesList).length)
78
70
  return;
79
- const toUnixStyleFilesList = {};
80
- for (const [filePath, value] of Object.entries(filesList)) {
71
+ const toUnixStyleFilesList = Object.entries(filesList).reduce((acc, [filePath, value]) => {
81
72
  const unixPath = toUnixPath(filePath);
82
- const finalPath = (await fileExists(join(themeRootDir, filePath))) && (await isDirectory(join(themeRootDir, filePath)))
83
- ? `${unixPath}${path.posix.sep}`
84
- : unixPath;
85
- toUnixStyleFilesList[finalPath] = value;
86
- }
87
- await writeJSONFile(this.getFilesListFilePath(themeRootDir), toUnixStyleFilesList);
88
- }
89
- static getFilesListFilePath(themeRootDir) {
90
- return join(themeRootDir, THEME_FILES_LIST_FILE_NAME);
73
+ const finalPath = looksLikeDirectory(unixPath) ? `${unixPath}${path.posix.sep}` : unixPath;
74
+ return {
75
+ ...acc,
76
+ [finalPath]: value
77
+ };
78
+ }, {});
79
+ await writeJSONFile(join(themeRootDir, THEME_FILES_LIST_FILE_NAME), toUnixStyleFilesList);
91
80
  }
92
81
  static async removeAFilesListFile(themeRootDir) {
93
- await removeFile(this.getFilesListFilePath(themeRootDir));
82
+ await removeFile(join(themeRootDir, THEME_FILES_LIST_FILE_NAME));
94
83
  }
95
84
  static async updateFilesStructure(themeDir) {
96
- const fileStructure = await ThemeFilesUtils.getThemeFilesStructure(themeDir);
85
+ const fileStructure = await ThemeFilesStructureUtils.getThemeFilesStructure(themeDir);
97
86
  const checksumsFiles = [
98
87
  `${SHOPER_THEME_METADATA_DIR}/${THEME_INITIAL_CHECKSUMS_FILE_NAME}`,
99
88
  `${SHOPER_THEME_METADATA_DIR}/${THEME_INITIAL_CHECKSUMS_VERITY_FILE_NAME}`,
@@ -126,6 +115,6 @@ export class ThemeFilesUtils {
126
115
  }
127
116
  };
128
117
  });
129
- await ThemeFilesUtils.writeThemeFilesStructure(themeDir, fileStructure);
118
+ await ThemeFilesStructureUtils.writeThemeFilesStructure(themeDir, fileStructure);
130
119
  }
131
120
  }
@@ -7,6 +7,7 @@ import { ThemeChecksums } from '../../../../class/checksums/theme_checksums.js';
7
7
  export class HiddenDirectoryUtils {
8
8
  static async ensureFilesInsideThemeMetaDataDirectoryUntouched(themeDirectory, logger) {
9
9
  const themeMetadataPath = this.getThemeHiddenDirectoryPath(themeDirectory);
10
+ console.log('themeMetadataPath', themeMetadataPath);
10
11
  const themeChecksums = new ThemeChecksums({
11
12
  themeDir: themeDirectory,
12
13
  loggerApi: logger
@@ -3,12 +3,12 @@ import { ThemePublishUtils } from '../../skinstore/theme_publish_utils.js';
3
3
  import { ThemePushErrorsFactory } from '../../push/theme_push_errors_factory.js';
4
4
  import { join } from '../../../../../utils/path_utils.js';
5
5
  import { v4 as uuid } from 'uuid';
6
- import { ThemeArchiveUtils } from '../../utils/archive/theme_archive_utils.js';
7
- import { ThemeActionsUtils } from '../../actions/theme_actions_utils.js';
8
- import { ThemeFilesUtils } from '../../utils/files/theme_files_utils.js';
6
+ import { ThemeArchive } from '../../../../class/archive/theme_archive.js';
9
7
  import { THEME_WILDCARD_ACTION_NAME } from '../../actions/service/theme_actions_service_constants.js';
10
8
  import { THEME_ACTIONS_TYPES } from '../../actions/theme_actions_constants.js';
11
- import { FILE_STATES } from '../../../../../utils/fs/fs_constants.js';
9
+ import { ThemeFilesStructureUtils } from '../../utils/files_structure/theme_files_structure_utils.js';
10
+ import { ThemeActionsUtils } from '../../actions/theme_actions_utils.js';
11
+ import { ArrayUtils } from '@dreamcommerce/utilities';
12
12
  export class ThemeVerifyService {
13
13
  #loggerApi;
14
14
  constructor({ loggerApi }) {
@@ -23,15 +23,13 @@ export class ThemeVerifyService {
23
23
  throw ThemePushErrorsFactory.createErrorWhilePushingUnpublishedThemeWithSkinstoreData(credentials.shopUrl);
24
24
  try {
25
25
  this.#loggerApi.debug('Getting file records from action data.');
26
- //TODO to do api?
27
26
  const filesRecords = await ThemeActionsUtils.getFilesRecordsFromActionData({
28
27
  themeRootDir,
29
28
  themeAction: verifyAction,
30
- filesStructure,
31
- themeChecksums
29
+ filesStructure
32
30
  });
33
31
  this.#loggerApi.debug('Filtering for modified or not created files to verify.');
34
- const filesToVerify = filesRecords.filter(({ state }) => [FILE_STATES.modified, FILE_STATES.created].includes(state));
32
+ const filesToVerify = await ArrayUtils.asyncFilter(filesRecords, async ({ path }) => (await themeChecksums.hasThemeFileBeenModified(path)) || !(await themeChecksums.hasThemeFileBeenCreated(path)));
35
33
  this.#loggerApi.debug('Files to verify determined.', { details: { count: filesToVerify.length } });
36
34
  if (filesToVerify.length) {
37
35
  this.#loggerApi.info(`Verifying ${filesToVerify.length} theme files.`);
@@ -48,16 +46,11 @@ export class ThemeVerifyService {
48
46
  await this._createFilesList(themeRootDir, filesToVerify);
49
47
  const themeArchivePath = join(tmpDir, `${uuid()}.zip`);
50
48
  this.#loggerApi.info('Creating theme archive for verification.');
51
- await ThemeArchiveUtils.create({
49
+ await new ThemeArchive(themeRootDir).createFullArchive({
52
50
  dist: themeArchivePath,
53
- rootDir: themeRootDir,
54
- logger: this.#loggerApi,
55
- files: await ThemeActionsUtils.getFilesThatMatchesAction({
56
- actionValue: THEME_WILDCARD_ACTION_NAME,
57
- actionType: THEME_ACTIONS_TYPES.push,
58
- filesStructure,
59
- rootDir: themeRootDir
60
- })
51
+ actionValue: THEME_WILDCARD_ACTION_NAME,
52
+ actionType: THEME_ACTIONS_TYPES.push,
53
+ logger: this.#loggerApi
61
54
  });
62
55
  this.#loggerApi.info('Theme archive created.');
63
56
  const { isSuccess, messages } = await themeFilesUploadApi.uploadArchive({
@@ -70,11 +63,11 @@ export class ThemeVerifyService {
70
63
  }
71
64
  finally {
72
65
  this.#loggerApi.debug('Cleaning up files list.');
73
- await ThemeFilesUtils.removeAFilesListFile(themeRootDir);
66
+ await ThemeFilesStructureUtils.removeAFilesListFile(themeRootDir);
74
67
  }
75
68
  }
76
69
  async _createFilesList(themeRootDir, filesRecords) {
77
70
  this.#loggerApi.debug('Creating filesList.json for the archive.');
78
- await ThemeFilesUtils.createAFilesListFile(themeRootDir, ThemeFilesUtils.mapFilesRecordsToFilesList(filesRecords));
71
+ await ThemeFilesStructureUtils.createAFilesListFile(themeRootDir, ThemeFilesStructureUtils.mapFilesRecordsToFilesList(filesRecords));
79
72
  }
80
73
  }
@@ -4,6 +4,3 @@ export const asyncFilter = async (array, callback) => {
4
4
  return array.filter((_v, index) => results[index]);
5
5
  });
6
6
  };
7
- export const asyncMap = async (array, callback) => {
8
- return Promise.all(array.map(callback));
9
- };
@@ -12,12 +12,16 @@ export const computeFileChecksum = async (filePath, algorithm = 'md5') => {
12
12
  const hash = createHash(algorithm);
13
13
  const stream = createReadStream(filePath);
14
14
  stream.on('data', (data) => {
15
+ console.log('data chunk received for hashing');
16
+ console.log('data', data);
15
17
  hash.update(data);
16
18
  });
17
19
  stream.on('close', () => {
20
+ console.log('file read completed, finalizing checksum computation');
18
21
  resolve(hash.digest('hex'));
19
22
  });
20
23
  stream.on('error', (err) => {
24
+ console.log('error while reading file for checksum computation', err);
21
25
  reject(err);
22
26
  });
23
27
  });
@@ -100,7 +100,7 @@ export const getAllFilesAndDirectoriesInside = async (path, options) => {
100
100
  withFileTypes
101
101
  });
102
102
  };
103
- export const getAllDirectoriesNamesInside = async (path, options = { recursive: false, hidden: false }) => {
103
+ export const getAllDirectoriesNamesInside = async (path, options) => {
104
104
  const { recursive = true, hidden = true } = options;
105
105
  const files = await fsPromises.readdir(path, {
106
106
  recursive,
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@shoper/cli",
3
3
  "packageManager": "yarn@3.2.0",
4
4
  "sideEffects": false,
5
- "version": "0.6.4-0",
5
+ "version": "0.6.4-3",
6
6
  "description": "CLI tool for Shoper",
7
7
  "author": "Joanna Firek",
8
8
  "license": "MIT",
@@ -72,7 +72,6 @@
72
72
  "strip-ansi": "7.1.0",
73
73
  "tmp-promise": "3.0.3",
74
74
  "uuid": "11.1.0",
75
- "micromatch": "4.0.8",
76
75
  "walk-sync": "3.0.0",
77
76
  "yauzl": "3.2.0",
78
77
  "yazl": "3.3.1",
@@ -87,15 +86,14 @@
87
86
  "@types/fs-extra": "11.0.4",
88
87
  "@types/jest": "29.5.14",
89
88
  "@types/jsonwebtoken": "9.0.9",
89
+ "@types/klaw": "3.0.7",
90
+ "@types/lodash": "4.17.17",
90
91
  "@types/node": "18.19.84",
91
92
  "@types/react": "19.1.8",
92
93
  "@types/semver": "7.7.0",
93
94
  "@types/tmp": "0.2.6",
94
95
  "@types/yauzl": "2.10.3",
95
96
  "@types/yazl": "2.4.6",
96
- "@types/klaw": "3.0.7",
97
- "@types/lodash": "4.17.17",
98
- "@types/micromatch": "4.0.9",
99
97
  "@typescript-eslint/eslint-plugin": "8.29.1",
100
98
  "babel-jest": "29.7.0",
101
99
  "eslint": "9.39.1",
@@ -1,25 +0,0 @@
1
- import { createZip } from '../../../../../utils/zip/create_zip_utils.js';
2
- import { extname, join } from '../../../../../utils/path_utils.js';
3
- import { formatJSONFile } from '../../../../../utils/fs/fs_utils.js';
4
- import { ThemeArchiveErrorsFactory } from './theme_archive_utils_errors_factory.js';
5
- export class ThemeArchiveUtils {
6
- static async create({ dist, files, rootDir, logger }) {
7
- try {
8
- await this._formatJsonFiles(rootDir, files);
9
- return createZip({
10
- files,
11
- baseDir: rootDir,
12
- logger,
13
- dist
14
- });
15
- }
16
- catch (err) {
17
- throw ThemeArchiveErrorsFactory.createErrorWhileCreatingThemeArchive(err);
18
- }
19
- }
20
- static async _formatJsonFiles(themeRootDir, filesToUpload) {
21
- await Promise.all(filesToUpload
22
- .filter((path) => extname(path).toLowerCase() === '.json')
23
- .map((jsonFile) => formatJSONFile(join(themeRootDir, jsonFile))));
24
- }
25
- }
@@ -1 +0,0 @@
1
- export const FILES_LIST_CUSTOM_MODULES_TO_KEEP_IDS = 'customModulesIdsToKeep';
@@ -1,6 +0,0 @@
1
- export const FILE_STATES = {
2
- created: 'created',
3
- modified: 'modified',
4
- deleted: 'deleted',
5
- unchanged: 'unchanged'
6
- };