@shoper/cli 0.5.2-2 → 0.5.2-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 (28) hide show
  1. package/build/cli/class/errors/http/http_errors_factory.js +1 -1
  2. package/build/cli/core/cli_setup.js +7 -6
  3. package/build/cli/index.js +0 -1
  4. package/build/theme/class/archive/theme_archive.js +44 -0
  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 +9 -47
  7. package/build/theme/commands/pull/theme_pull_command.js +3 -4
  8. package/build/theme/commands/push/theme_push_command.js +8 -20
  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 +28 -86
  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 +10 -0
  20. package/build/theme/features/theme/utils/{files/theme_files_utils.js → files_structure/theme_files_structure_utils.js} +23 -28
  21. package/build/theme/features/theme/verify/verify/theme_verify_service.js +11 -17
  22. package/build/utils/array_utils.js +0 -3
  23. package/build/utils/fs/fs_utils.js +1 -1
  24. package/build/utils/stream_transforms/json_indent_transform.js +7 -2
  25. package/package.json +10 -12
  26. package/build/theme/features/theme/utils/archive/theme_archive_utils.js +0 -24
  27. package/build/theme/features/theme/utils/files/them_files_constants.js +0 -1
  28. 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
  }
@@ -26,7 +26,7 @@ export const cliSetup = async () => {
26
26
  ExecutionContextInitializer,
27
27
  CliAuthTokensInitializer,
28
28
  CliAuthInitializer,
29
- ...getInitializersBasedOnCommand()
29
+ ...getCommandBaseInitializers()
30
30
  ],
31
31
  options: {
32
32
  featuresTracking: false,
@@ -34,15 +34,16 @@ export const cliSetup = async () => {
34
34
  }
35
35
  });
36
36
  };
37
- const getInitializersBasedOnCommand = () => {
38
- if (isCommandWithTopic(process.argv[2]))
39
- return getInitializersBasedOnCommandWithTopic();
37
+ const getCommandBaseInitializers = () => {
38
+ // console.log('process.argv', process.argv);
39
+ if (isCommandsTopic(process.argv[2]))
40
+ return getCommandWithTopicBaseInitializers();
40
41
  return getCommandWithoutTopicBaseInitializers();
41
42
  };
42
- const isCommandWithTopic = (arg) => {
43
+ const isCommandsTopic = (arg) => {
43
44
  return arg === THEME_TOPIC_NAME || arg === CLI_AUTH_TOPIC_NAME;
44
45
  };
45
- const getInitializersBasedOnCommandWithTopic = () => {
46
+ const getCommandWithTopicBaseInitializers = () => {
46
47
  const topic = process.argv[2];
47
48
  const command = process.argv[3];
48
49
  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
  };
@@ -0,0 +1,44 @@
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 }) {
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
+ baseDir: this.#themeRootDir,
32
+ dist
33
+ });
34
+ }
35
+ catch (err) {
36
+ throw ThemeArchiveErrorsFactory.createErrorWhileCreatingThemeArchive(err);
37
+ }
38
+ }
39
+ async _formatJsonFiles(themeRootDir, filesToUpload) {
40
+ await Promise.all(filesToUpload
41
+ .filter((path) => extname(path).toLowerCase() === '.json')
42
+ .map((jsonFile) => formatJSONFile(join(themeRootDir, jsonFile))));
43
+ }
44
+ }
@@ -1,4 +1,4 @@
1
- import { AppError } from '../../../../../cli/class/errors/app/app_error.js';
1
+ import { AppError } from '../../../cli/class/errors/app/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
  #currentChecksumsFilePath;
@@ -32,6 +31,12 @@ export class ThemeChecksums {
32
31
  this.#initialChecksums = await this._getChecksums(this.#initialChecksumsFilePath);
33
32
  return this.#initialChecksums;
34
33
  }
34
+ getInitialChecksumsSync() {
35
+ if (this.#initialChecksums)
36
+ return this.#initialChecksums;
37
+ this.#initialChecksums = this._getChecksumsSync(this.#initialChecksumsFilePath);
38
+ return this.#initialChecksums;
39
+ }
35
40
  async getCurrentChecksums() {
36
41
  if (this.#currentChecksums)
37
42
  return this.#currentChecksums;
@@ -65,22 +70,10 @@ export class ThemeChecksums {
65
70
  const initialChecksum = await this.getInitialChecksumFromPath(path);
66
71
  return !initialChecksum && (await fileExists(join(this.#themeDir, path)));
67
72
  }
68
- async getFileState(path) {
69
- const initialChecksum = await this.getInitialChecksumFromPath(path);
73
+ async hasThemeFileBeenModified(path) {
70
74
  const currentChecksum = await this.getCurrentChecksumFromPath(path);
71
- const fileExistsInFs = await fileExists(join(this.#themeDir, path));
72
- if (!initialChecksum && fileExistsInFs) {
73
- return FILE_STATES.created;
74
- }
75
- else if (initialChecksum && !fileExistsInFs) {
76
- return FILE_STATES.deleted;
77
- }
78
- else if (initialChecksum && currentChecksum && initialChecksum !== currentChecksum) {
79
- return FILE_STATES.modified;
80
- }
81
- else {
82
- return FILE_STATES.unchanged;
83
- }
75
+ const initialChecksum = await this.getInitialChecksumFromPath(path);
76
+ return !!currentChecksum && !!initialChecksum && currentChecksum !== initialChecksum;
84
77
  }
85
78
  async verify() {
86
79
  const initialChecksumFilePath = this.#initialChecksumsFilePath;
@@ -145,37 +138,6 @@ export class ThemeChecksums {
145
138
  const directoriesChecksums = computeDirectoriesChecksums(filesChecksumsInDirectories);
146
139
  return { ...filesChecksums, ...directoriesChecksums };
147
140
  }
148
- async groupFilesByStatus(files) {
149
- const initialChecksums = await this.getInitialChecksums();
150
- const currentChecksums = await this.getCurrentChecksums();
151
- return files.reduce((acc, filePath) => {
152
- const initialChecksum = initialChecksums[toCurrentPlatformPath(filePath)];
153
- const currentChecksum = currentChecksums[toCurrentPlatformPath(filePath)];
154
- if (!initialChecksum) {
155
- acc.created.push(filePath);
156
- }
157
- else if (currentChecksum && initialChecksum !== currentChecksum) {
158
- acc.modified.push(filePath);
159
- }
160
- else if (initialChecksum && !currentChecksum) {
161
- acc.deleted.push(filePath);
162
- }
163
- else {
164
- acc.unchanged.push(filePath);
165
- }
166
- return acc;
167
- }, {
168
- created: [],
169
- modified: [],
170
- unchanged: [],
171
- deleted: []
172
- });
173
- }
174
- async getDeletedFiles() {
175
- const initialChecksums = await this.getInitialChecksums();
176
- const currentChecksums = await this.getCurrentChecksums();
177
- return Object.keys(initialChecksums).filter((filePath) => !currentChecksums[filePath]);
178
- }
179
141
  async _getInitialChecksumsVerification() {
180
142
  return await readJSONFile(this.#initialChecksumsVerificationFilePath);
181
143
  }
@@ -24,8 +24,7 @@ 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 { ThemeError } from '../ui/theme_error.js';
28
- import { MODULES_DIRECTORY_NAME } from '../../features/theme/theme_constants.js'; //TODO jak jest error w pullu wowczas usuwamy docelowo pociagniety skin
27
+ import { ThemeError } from '../ui/theme_error.js'; //TODO jak jest error w pullu wowczas usuwamy docelowo pociagniety skin
29
28
  //TODO jak jest error w pullu wowczas usuwamy docelowo pociagniety skin
30
29
  export class ThemePullCommand extends BaseThemeCommand {
31
30
  static summary = 'Downloads the current version of your theme from the store and overwrites your local theme files.';
@@ -152,9 +151,9 @@ export class ThemePullCommand extends BaseThemeCommand {
152
151
  dist: tmpDir
153
152
  }
154
153
  });
155
- const localModulesPath = join(executionContext.themeRootDir, MODULES_DIRECTORY_NAME);
154
+ const localModulesPath = join(executionContext.themeRootDir, 'modules');
156
155
  const fetchedThemePath = join(tmpDir, name);
157
- const fetchedModulesPath = join(fetchedThemePath, MODULES_DIRECTORY_NAME);
156
+ const fetchedModulesPath = join(fetchedThemePath, 'modules');
158
157
  if ((await directoryExists(localModulesPath)) && (await directoryExists(fetchedModulesPath)))
159
158
  await ThemeResourcesWithIdDirectoryUtils.updateDirectoryNamesOfResourcesWithIdAccordingToLocalThemeNames(localModulesPath, fetchedModulesPath, fetchedThemePath);
160
159
  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,10 +24,7 @@ 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
- import { promptConfirmation } from '../../../ui/prompts/prompt_confirmation.js';
29
28
  export class ThemePushCommand extends BaseThemeCommand {
30
29
  static summary = 'Uploads your local theme files to the store and overwrites the current version of the theme in your store.';
31
30
  static description = 'Check your local changes before pushing.\n\nYou must run this command from a specific theme directory (ID not needed).';
@@ -64,7 +63,7 @@ export class ThemePushCommand extends BaseThemeCommand {
64
63
  return;
65
64
  }
66
65
  const initialChecksums = await themeChecksums.getInitialChecksums();
67
- const permissions = await ThemeFilesUtils.getFilesPermissions(executionContext.themeRootDir);
66
+ const permissions = await ThemeFilesStructureUtils.getFilesPermissions(executionContext.themeRootDir);
68
67
  const themeFilesUploadApi = new ThemeFilesUpload({
69
68
  themeRootDir: executionContext.themeRootDir,
70
69
  credentials,
@@ -76,7 +75,7 @@ export class ThemePushCommand extends BaseThemeCommand {
76
75
  renderOnce(React.createElement(ThemePushSkipInfo, null));
77
76
  return;
78
77
  }
79
- const validationResult = await ThemeFilesUtils.validateThemeDirectoryStructure({
78
+ const validationResult = await ThemeFilesStructureUtils.validateThemeDirectoryStructure({
80
79
  //TDO przeniesc do theme checksums
81
80
  checksums: mapChecksumToTree(initialChecksums),
82
81
  permissions: mapToPermissionsTree(permissions),
@@ -88,36 +87,25 @@ export class ThemePushCommand extends BaseThemeCommand {
88
87
  renderOnce(React.createElement(ThemeUnpermittedActionsError, { unpermittedActions: validationResult.unpermittedActions }));
89
88
  return;
90
89
  }
91
- const filesStructure = await ThemeFilesUtils.getThemeFilesStructure(executionContext.themeRootDir);
90
+ const filesStructure = await ThemeFilesStructureUtils.getThemeFilesStructure(executionContext.themeRootDir);
92
91
  if (!filesStructure) {
93
92
  renderOnce(React.createElement(MissingThemeFiles, { files: `${SHOPER_THEME_METADATA_DIR}/${THEME_FILES_STRUCTURE_FILE_NAME}` }));
94
93
  return;
95
94
  }
96
95
  spinner = ora('Pushing theme...').start();
97
- const partialPushAction = themeActionsApi.getThemeAction({
98
- actionType: THEME_ACTIONS_TYPES.partialPush,
99
- themeId: executionContext.themeId,
100
- credentials
101
- });
102
- if (!pushAction) {
103
- renderOnce(React.createElement(UnpermittedCommandError, { themeId: executionContext.themeId, commandName: "push" }));
104
- return;
105
- }
106
- await themePushApi.partialPush({
96
+ await themePushApi.push({
107
97
  credentials,
108
98
  filesStructure,
109
- action: partialPushAction,
99
+ pushAction,
110
100
  themeChecksums,
111
101
  executionContext,
112
102
  themeFilesUploadApi
113
103
  });
114
104
  spinner.stop();
115
- await promptConfirmation('Confirm');
116
105
  renderOnce(React.createElement(ThemePushedSuccess, { themeName: await ThemeInfoUtils.getThemeName(executionContext.themeRootDir) }));
117
106
  }
118
107
  catch (err) {
119
108
  spinner?.stop();
120
- await promptConfirmation('Confirm');
121
109
  renderOnce(React.createElement(ThemeError, { err: err, executionContext: executionContext }));
122
110
  }
123
111
  }
@@ -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
  export class ThemeVerifyCommand extends BaseThemeCommand {
22
22
  static description = 'Verify theme files structure';
23
23
  async run() {
@@ -34,7 +34,7 @@ export class ThemeVerifyCommand extends BaseThemeCommand {
34
34
  return;
35
35
  }
36
36
  const themeChecksums = new ThemeChecksums(executionContext.themeRootDir);
37
- const permissions = await ThemeFilesUtils.getFilesPermissions(executionContext.themeRootDir);
37
+ const permissions = await ThemeFilesStructureUtils.getFilesPermissions(executionContext.themeRootDir);
38
38
  if (!themeChecksums || !permissions)
39
39
  this.error('Theme checksums or permissions not found. Please ensure you are in the correct theme directory.');
40
40
  let spinner;
@@ -51,7 +51,7 @@ export class ThemeVerifyCommand extends BaseThemeCommand {
51
51
  themeId: executionContext.themeId,
52
52
  credentials
53
53
  });
54
- const filesStructure = await ThemeFilesUtils.getThemeFilesStructure(executionContext.themeRootDir);
54
+ const filesStructure = await ThemeFilesStructureUtils.getThemeFilesStructure(executionContext.themeRootDir);
55
55
  if (!filesStructure) {
56
56
  renderOnce(React.createElement(MissingThemeFiles, { files: `${SHOPER_THEME_METADATA_DIR}/${THEME_FILES_STRUCTURE_FILE_NAME}` }));
57
57
  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.")));
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) => 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/class/errors/app/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;
@@ -39,7 +39,7 @@ export class ThemeFetchService {
39
39
  await this.fetchResources(shopUrl, themeDir, resources);
40
40
  }
41
41
  try {
42
- await ThemeFilesUtils.updateFilesStructure(themeDir);
42
+ await ThemeFilesStructureUtils.updateFilesStructure(themeDir);
43
43
  }
44
44
  catch (err) {
45
45
  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/class/errors/app/app_error.js';
10
- import { ThemeFilesUtils } from '../../utils/files/theme_files_utils.js';
11
11
  export class ThemeInitService {
12
12
  #httpApi;
13
13
  constructor({ httpApi }) {
@@ -26,7 +26,7 @@ export class ThemeInitService {
26
26
  dist: themeDir
27
27
  });
28
28
  try {
29
- await ThemeFilesUtils.updateFilesStructure(themeDir);
29
+ await ThemeFilesStructureUtils.updateFilesStructure(themeDir);
30
30
  }
31
31
  catch (err) {
32
32
  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
  async applyChanges(fromTheme, toTheme, changes) {
10
10
  FSTree.applyPatch(fromTheme, toTheme, changes, {
@@ -39,7 +39,7 @@ export class ThemeMergeService {
39
39
  });
40
40
  }
41
41
  async _getUserRootDirectories(themeDir) {
42
- const filesStructure = await ThemeFilesUtils.getThemeRootDirectories(themeDir);
42
+ const filesStructure = await ThemeFilesStructureUtils.getThemeRootDirectories(themeDir);
43
43
  return (await getAllDirectoriesNamesInside(themeDir, {
44
44
  recursive: false,
45
45
  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,27 +1,25 @@
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 { removeFile, 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
  constructor({ themeFetchApi }) {
22
20
  this.#themeFetchApi = themeFetchApi;
23
21
  }
24
- async partialPush({ action, filesStructure, executionContext, themeChecksums, themeFilesUploadApi, credentials }) {
22
+ async push({ pushAction, credentials, filesStructure, executionContext, themeChecksums, themeFilesUploadApi }) {
25
23
  const { path: tmpDir } = await tmp.dir({ unsafeCleanup: true });
26
24
  const themeRootDir = executionContext.themeRootDir;
27
25
  if (await themeChecksums.hasThemeFileBeenCreated(ThemePublishUtils.getSkinStoreSettingsFilePath(themeRootDir)))
@@ -30,56 +28,30 @@ export class ThemePushService {
30
28
  //TODO to do api?
31
29
  const filesRecords = await ThemeActionsUtils.getFilesRecordsFromActionData({
32
30
  themeRootDir,
33
- themeAction: action,
34
- filesStructure,
35
- themeChecksums
31
+ themeAction: pushAction,
32
+ filesStructure
36
33
  });
37
- const filesToUpload = filesRecords.filter(({ state }) => [FILE_STATES.modified, FILE_STATES.created].includes(state));
38
- let localFileNameToUploaded;
34
+ const filesToUpload = await ArrayUtils.asyncFilter(filesRecords, async ({ path }) => (await themeChecksums.hasThemeFileBeenCreated(path)) || (await themeChecksums.hasThemeFileBeenModified(path)));
39
35
  if (filesToUpload.length) {
40
- const uploadData = await this._uploadThemeFiles({
36
+ const { localFileNameToUploaded } = await this._uploadThemeFiles({
41
37
  filesToUpload,
42
38
  credentials,
43
39
  themeRootDir,
44
40
  themeFilesUploadApi
45
41
  });
46
- if (!uploadData.isSuccess)
47
- throw ThemePushErrorsFactory.createErrorWhileUploadingThemeFiles(credentials.shopUrl);
48
- localFileNameToUploaded = uploadData.localFileNameToUploadedMap;
42
+ await this._createFilesList(themeRootDir, filesRecords, localFileNameToUploaded);
43
+ }
44
+ else {
45
+ await this._createFilesList(themeRootDir, filesRecords);
49
46
  }
50
47
  const themeArchivePath = join(tmpDir, `${uuid()}.zip`);
51
- const filesInArchive = await ThemeActionsUtils.getFilesThatMatchesAction({
52
- actionValue: THEME_WILDCARD_ACTION_NAME,
53
- actionType: THEME_ACTIONS_TYPES.push,
54
- filesStructure,
55
- rootDir: themeRootDir
56
- });
57
- const groupedFiles = await themeChecksums.groupFilesByStatus(filesInArchive);
58
- const deletedFiles = await ThemeActionsUtils.getDeletedFilesThatMatchesAction({
59
- actionType: THEME_ACTIONS_TYPES.push,
60
- actionValue: THEME_WILDCARD_ACTION_NAME,
61
- themeChecksums,
62
- filesStructure
63
- });
64
- await this._createFilesList({
65
- themeRootDir,
66
- filesRecords,
67
- localFileNameToUploaded,
68
- deletedFiles,
69
- allCustomModulesIds: await this._getAllModulesIds(themeRootDir)
70
- });
71
- await ThemeArchiveUtils.create({
48
+ await new ThemeArchive(themeRootDir).createFullArchive({
72
49
  dist: themeArchivePath,
73
- rootDir: themeRootDir,
74
- files: [
75
- ...(await this._appendSettingsIfModifiedFileFromFolderThatContainsOne(groupedFiles.modified)),
76
- ...groupedFiles.created,
77
- (await fileExists(ThemeFilesUtils.getFilesListFilePath(themeRootDir))) ? THEME_FILES_LIST_FILE_NAME : undefined
78
- ].filter(Boolean)
50
+ actionValue: THEME_WILDCARD_ACTION_NAME,
51
+ actionType: THEME_ACTIONS_TYPES.push
79
52
  });
80
- await ThemeFilesUtils.removeAFilesListFile(themeRootDir);
81
53
  const { resources, modules } = await themeFilesUploadApi.uploadArchive({
82
- action,
54
+ action: pushAction,
83
55
  themeArchivePath,
84
56
  credentials
85
57
  });
@@ -89,12 +61,10 @@ export class ThemePushService {
89
61
  await removeOldResources(themeRootDir, resources);
90
62
  await this.#themeFetchApi.fetchResources(credentials.shopUrl, themeRootDir, resources);
91
63
  }
92
- //TODO only changed files checksum
93
64
  await themeChecksums.updateAllChecksums();
94
65
  }
95
- catch (err) {
96
- await ThemeFilesUtils.removeAFilesListFile(themeRootDir);
97
- throw err;
66
+ finally {
67
+ await ThemeFilesStructureUtils.removeAFilesListFile(themeRootDir);
98
68
  }
99
69
  }
100
70
  async _uploadThemeFiles({ filesToUpload, themeRootDir, credentials, themeFilesUploadApi }) {
@@ -102,10 +72,9 @@ export class ThemePushService {
102
72
  const { uploadedImageData, rejectedImageData } = await themeFilesUploadApi.uploadFiles(filesToUpload);
103
73
  if (uploadedImageData.length)
104
74
  await ThemeImagesUtils.removeUploadedOriginalFiles(themeRootDir, uploadedImageData);
75
+ // if (rejectedImageData.length) await this._removeUploadedThemeFiles(rejectedImageData, themeRootDir);
105
76
  return {
106
- isSuccess: rejectedImageData.length === 0,
107
- rejectedImageData,
108
- localFileNameToUploadedMap: uploadedImageData.reduce((acc, { originalFilename, uploadedFilename }) => {
77
+ localFileNameToUploaded: uploadedImageData.reduce((acc, { originalFilename, uploadedFilename }) => {
109
78
  return {
110
79
  ...acc,
111
80
  [originalFilename]: uploadedFilename ?? originalFilename
@@ -117,42 +86,15 @@ export class ThemePushService {
117
86
  throw ThemePushErrorsFactory.createErrorWhileUploadingThemeFiles(credentials.shopUrl, err);
118
87
  }
119
88
  }
89
+ async _removeUploadedThemeFiles(uploadedImageData, themeRootDir) {
90
+ await Promise.all(uploadedImageData.map(({ location, originalFilename }) => removeFile(join(themeRootDir, location, originalFilename))));
91
+ }
120
92
  async _updateDataForNewCreatedModules({ modules, themeRootDir }) {
121
93
  for (const [moduleDirectoryName, metaData] of Object.entries(modules)) {
122
94
  await writeJSONFile(join(themeRootDir, MODULES_DIRECTORY_NAME, moduleDirectoryName, THEME_MODULE_SETTINGS_FILE_NAME), JSON.parse(metaData[THEME_MODULE_SETTINGS_FILE_NAME]));
123
95
  }
124
96
  }
125
- async _createFilesList({ deletedFiles, localFileNameToUploaded, filesRecords, themeRootDir, allCustomModulesIds }) {
126
- const filesListContent = ThemeFilesUtils.mapFilesRecordsToFilesList(filesRecords, localFileNameToUploaded);
127
- if (deletedFiles?.length)
128
- filesListContent.removed = deletedFiles;
129
- if (allCustomModulesIds?.length)
130
- filesListContent[FILES_LIST_CUSTOM_MODULES_TO_KEEP_IDS] = allCustomModulesIds;
131
- await ThemeFilesUtils.createAFilesListFile(themeRootDir, filesListContent);
132
- }
133
- async _appendSettingsIfModifiedFileFromFolderThatContainsOne(modifiedFiles) {
134
- const withSettingsFiles = [...modifiedFiles];
135
- for (const filePath of modifiedFiles) {
136
- if (await fileExists(join(dirname(filePath), 'settings.json'))) {
137
- withSettingsFiles.push(join(dirname(filePath), 'settings.json'));
138
- }
139
- }
140
- return uniq(withSettingsFiles);
141
- }
142
- async _getAllModulesIds(themeRootDir) {
143
- const modulesPath = join(themeRootDir, MODULES_DIRECTORY_NAME);
144
- if (!(await fileExists(modulesPath)))
145
- return [];
146
- const moduleDirs = await getAllDirectoriesNamesInside(modulesPath);
147
- const modulesIds = [];
148
- for (const moduleDir of moduleDirs) {
149
- const moduleSettingsPath = join(themeRootDir, MODULES_DIRECTORY_NAME, moduleDir, THEME_MODULE_SETTINGS_FILE_NAME);
150
- if (await fileExists(moduleSettingsPath)) {
151
- const settingsContent = await readJSONFile(moduleSettingsPath);
152
- if (settingsContent.id && Number.isInteger(settingsContent.id))
153
- modulesIds.push(settingsContent.id);
154
- }
155
- }
156
- return modulesIds;
97
+ async _createFilesList(themeRootDir, filesRecords, localFileNameToUploaded = {}) {
98
+ await ThemeFilesStructureUtils.createAFilesListFile(themeRootDir, ThemeFilesStructureUtils.mapFilesRecordsToFilesList(filesRecords, localFileNameToUploaded));
157
99
  }
158
100
  }
@@ -1,10 +1,10 @@
1
1
  import globs from 'fast-glob';
2
2
  import { AppError } from '../../../../cli/class/errors/app/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}`,
@@ -0,0 +1,10 @@
1
+ import { AppError } from '../../../../../cli/class/errors/app/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
+ level: 'error'
8
+ });
9
+ }
10
+ }
@@ -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,22 +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
- return filesRecords.reduce((acc, { fileGlob, fileName, state, actionKey }) => {
44
+ const mappedFilesRecords = filesRecords.reduce((acc, { fileGlob, fileName }) => {
46
45
  const name = localFileNameToUploaded[fileName] ?? fileName;
47
- if (state === FILE_STATES.deleted && actionKey === 'thumbnail') {
48
- return {
49
- ...acc,
50
- [fileGlob]: null
51
- };
52
- }
53
- if (state === FILE_STATES.deleted)
54
- return acc;
55
46
  if (looksLikeDirectory(fileGlob)) {
56
47
  const existingFiles = acc[fileGlob] || [];
57
48
  return {
@@ -66,28 +57,32 @@ export class ThemeFilesUtils {
66
57
  };
67
58
  }
68
59
  }, {});
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;
69
67
  }
70
68
  static async createAFilesListFile(themeRootDir, filesList) {
71
69
  if (!filesList || !Object.keys(filesList).length)
72
70
  return;
73
- const toUnixStyleFilesList = {};
74
- for (const [filePath, value] of Object.entries(filesList)) {
71
+ const toUnixStyleFilesList = Object.entries(filesList).reduce((acc, [filePath, value]) => {
75
72
  const unixPath = toUnixPath(filePath);
76
- const finalPath = (await fileExists(join(themeRootDir, filePath))) && (await isDirectory(join(themeRootDir, filePath)))
77
- ? `${unixPath}${path.posix.sep}`
78
- : unixPath;
79
- toUnixStyleFilesList[finalPath] = value;
80
- }
81
- await writeJSONFile(this.getFilesListFilePath(themeRootDir), toUnixStyleFilesList);
82
- }
83
- static getFilesListFilePath(themeRootDir) {
84
- 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);
85
80
  }
86
81
  static async removeAFilesListFile(themeRootDir) {
87
- await removeFile(this.getFilesListFilePath(themeRootDir));
82
+ await removeFile(join(themeRootDir, THEME_FILES_LIST_FILE_NAME));
88
83
  }
89
84
  static async updateFilesStructure(themeDir) {
90
- const fileStructure = await ThemeFilesUtils.getThemeFilesStructure(themeDir);
85
+ const fileStructure = await ThemeFilesStructureUtils.getThemeFilesStructure(themeDir);
91
86
  const checksumsFiles = [
92
87
  `${SHOPER_THEME_METADATA_DIR}/${THEME_INITIAL_CHECKSUMS_FILE_NAME}`,
93
88
  `${SHOPER_THEME_METADATA_DIR}/${THEME_INITIAL_CHECKSUMS_VERITY_FILE_NAME}`,
@@ -120,6 +115,6 @@ export class ThemeFilesUtils {
120
115
  }
121
116
  };
122
117
  });
123
- await ThemeFilesUtils.writeThemeFilesStructure(themeDir, fileStructure);
118
+ await ThemeFilesStructureUtils.writeThemeFilesStructure(themeDir, fileStructure);
124
119
  }
125
120
  }
@@ -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
  async verifyTheme({ verifyAction, executionContext, credentials, themeChecksums, filesStructure, themeFilesUploadApi }) {
14
14
  const { path: tmpDir } = await tmp.dir({ unsafeCleanup: true });
@@ -20,10 +20,9 @@ export class ThemeVerifyService {
20
20
  const filesRecords = await ThemeActionsUtils.getFilesRecordsFromActionData({
21
21
  themeRootDir,
22
22
  themeAction: verifyAction,
23
- filesStructure,
24
- themeChecksums
23
+ filesStructure
25
24
  });
26
- const filesToVerify = filesRecords.filter(({ state }) => [FILE_STATES.modified, FILE_STATES.created].includes(state));
25
+ const filesToVerify = await ArrayUtils.asyncFilter(filesRecords, async ({ path }) => (await themeChecksums.hasThemeFileBeenModified(path)) || !(await themeChecksums.hasThemeFileBeenCreated(path)));
27
26
  if (filesToVerify.length) {
28
27
  const { rejectedImageData } = await themeFilesUploadApi.uploadFiles(filesToVerify);
29
28
  if (rejectedImageData.length)
@@ -34,15 +33,10 @@ export class ThemeVerifyService {
34
33
  }
35
34
  await this._createFilesList(themeRootDir, filesToVerify);
36
35
  const themeArchivePath = join(tmpDir, `${uuid()}.zip`);
37
- await ThemeArchiveUtils.create({
36
+ await new ThemeArchive(themeRootDir).createFullArchive({
38
37
  dist: themeArchivePath,
39
- rootDir: themeRootDir,
40
- files: await ThemeActionsUtils.getFilesThatMatchesAction({
41
- actionValue: THEME_WILDCARD_ACTION_NAME,
42
- actionType: THEME_ACTIONS_TYPES.push,
43
- filesStructure,
44
- rootDir: themeRootDir
45
- })
38
+ actionValue: THEME_WILDCARD_ACTION_NAME,
39
+ actionType: THEME_ACTIONS_TYPES.push
46
40
  });
47
41
  const { isSuccess, messages } = await themeFilesUploadApi.uploadArchive({
48
42
  action: verifyAction,
@@ -52,10 +46,10 @@ export class ThemeVerifyService {
52
46
  return { isSuccess, messages };
53
47
  }
54
48
  finally {
55
- await ThemeFilesUtils.removeAFilesListFile(themeRootDir);
49
+ await ThemeFilesStructureUtils.removeAFilesListFile(themeRootDir);
56
50
  }
57
51
  }
58
52
  async _createFilesList(themeRootDir, filesRecords) {
59
- await ThemeFilesUtils.createAFilesListFile(themeRootDir, ThemeFilesUtils.mapFilesRecordsToFilesList(filesRecords));
53
+ await ThemeFilesStructureUtils.createAFilesListFile(themeRootDir, ThemeFilesStructureUtils.mapFilesRecordsToFilesList(filesRecords));
60
54
  }
61
55
  }
@@ -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
- };
@@ -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,
@@ -12,9 +12,14 @@ export const jsonIndentTransform = (space = JSON_FILE_INDENT) => {
12
12
  if (!data) {
13
13
  return callback();
14
14
  }
15
- const json = JSON.parse(data);
15
+ // 1. Escape backslashes to preserve literal '\u' during parsing
16
+ const escapedJsonString = data.replace(/\\u/g, '\\\\u');
17
+ const json = JSON.parse(escapedJsonString);
18
+ // 2. Stringify the object, which will re-escape the backslashes (e.g., '\\u' -> '\\\\u')
16
19
  const pretty = JSON.stringify(json, null, space);
17
- this.push(pretty);
20
+ // 3. Un-escape the double backslashes to get the desired literal '\u'
21
+ const finalString = pretty.replace(/\\\\u/g, '\\u');
22
+ this.push(finalString);
18
23
  callback();
19
24
  }
20
25
  });
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.5.2-2",
5
+ "version": "0.5.2-3",
6
6
  "description": "CLI tool for Shoper",
7
7
  "author": "Joanna Firek",
8
8
  "license": "MIT",
@@ -39,32 +39,31 @@
39
39
  "@oclif/plugin-help": "6.2.27",
40
40
  "@oclif/plugin-version": "2.2.27",
41
41
  "@oclif/plugin-warn-if-update-available": "3.1.38",
42
- "axios": "1.12.0",
42
+ "axios": "1.8.4",
43
43
  "chalk": "5.4.1",
44
44
  "conf": "13.1.0",
45
45
  "fast-glob": "3.3.3",
46
+ "figures": "6.1.0",
47
+ "fs-extra": "11.3.0",
46
48
  "fs-tree-diff": "2.0.1",
47
49
  "ink": "6.0.1",
50
+ "ink-link": "4.1.0",
48
51
  "inquirer": "12.5.2",
52
+ "inquirer-select-line": "1.1.3",
49
53
  "is-hidden-file": "1.1.2",
50
54
  "jsonwebtoken": "9.0.2",
51
55
  "klaw": "4.1.0",
52
56
  "lodash": "4.17.21",
57
+ "log-symbols": "7.0.1",
53
58
  "memfs": "4.17.0",
54
59
  "ora": "8.2.0",
55
60
  "react": "19.1.0",
56
61
  "reflect-metadata": "0.2.2",
57
62
  "rxjs": "7.8.2",
58
63
  "semver": "7.7.1",
64
+ "strip-ansi": "7.1.0",
59
65
  "tmp-promise": "3.0.3",
60
66
  "uuid": "11.1.0",
61
- "fs-extra": "11.3.0",
62
- "ink-link": "4.1.0",
63
- "log-symbols": "7.0.1",
64
- "figures": "6.1.0",
65
- "strip-ansi": "7.1.0",
66
- "inquirer-select-line": "1.1.3",
67
- "micromatch": "4.0.8",
68
67
  "walk-sync": "3.0.0",
69
68
  "yauzl": "3.2.0",
70
69
  "yazl": "3.3.1"
@@ -78,15 +77,14 @@
78
77
  "@types/fs-extra": "11.0.4",
79
78
  "@types/jest": "29.5.14",
80
79
  "@types/jsonwebtoken": "9.0.9",
80
+ "@types/klaw": "3.0.7",
81
+ "@types/lodash": "4.17.17",
81
82
  "@types/node": "18.19.84",
82
83
  "@types/react": "19.1.8",
83
84
  "@types/semver": "7.7.0",
84
85
  "@types/tmp": "0.2.6",
85
86
  "@types/yauzl": "2.10.3",
86
87
  "@types/yazl": "2.4.6",
87
- "@types/klaw": "3.0.7",
88
- "@types/lodash": "4.17.17",
89
- "@types/micromatch": "4.0.9",
90
88
  "@typescript-eslint/eslint-plugin": "8.29.1",
91
89
  "babel-jest": "29.7.0",
92
90
  "eslint": "9.24.0",
@@ -1,24 +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 }) {
7
- try {
8
- await this._formatJsonFiles(rootDir, files);
9
- return createZip({
10
- files,
11
- baseDir: rootDir,
12
- dist
13
- });
14
- }
15
- catch (err) {
16
- throw ThemeArchiveErrorsFactory.createErrorWhileCreatingThemeArchive(err);
17
- }
18
- }
19
- static async _formatJsonFiles(themeRootDir, filesToUpload) {
20
- await Promise.all(filesToUpload
21
- .filter((path) => extname(path).toLowerCase() === '.json')
22
- .map((jsonFile) => formatJSONFile(join(themeRootDir, jsonFile))));
23
- }
24
- }
@@ -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
- };