@shoper/cli 0.5.2-5 → 0.5.2-7

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 (96) hide show
  1. package/build/cli/auth/cli_auth_errors_factory.js +1 -1
  2. package/build/cli/auth/cli_auth_initializer.js +6 -1
  3. package/build/cli/auth/service/cli_auth_service.js +11 -2
  4. package/build/cli/auth/tokens/cli_auth_tokens_errors_factory.js +1 -1
  5. package/build/cli/auth/tokens/cli_auth_tokens_initalizer.js +6 -1
  6. package/build/cli/auth/tokens/service/cli_auth_tokens_service.js +8 -1
  7. package/build/cli/class/base_command.js +15 -1
  8. package/build/cli/class/errors/file_system_errors_factory.js +3 -3
  9. package/build/cli/class/errors/http/http_errors_factory.js +2 -2
  10. package/build/cli/commands/auth/cli_auth_add_token_command.js +5 -0
  11. package/build/cli/commands/auth/cli_auth_list_tokens_command.js +20 -11
  12. package/build/cli/commands/auth/cli_auth_logout_command.js +5 -0
  13. package/build/cli/commands/auth/cli_auth_remove_token_command.js +5 -0
  14. package/build/cli/commands/auth/cli_auth_switch_token_command.js +8 -0
  15. package/build/cli/commands/cli_update_command.js +5 -0
  16. package/build/cli/core/cli_setup.js +11 -18
  17. package/build/cli/features/execution_context/execution_context_service.js +2 -0
  18. package/build/cli/features/version/service/cli_version_service.js +2 -2
  19. package/build/cli/hooks/authorization/ensure_authorization_hook.js +9 -2
  20. package/build/cli/hooks/ensure_cli_initialized_hook.js +1 -6
  21. package/build/cli/hooks/ensure_logs_flushed_hook.js +7 -0
  22. package/build/cli/index.js +0 -1
  23. package/build/cli/{features → utilities/features}/http_requester/http_client.js +2 -2
  24. package/build/cli/{features → utilities/features}/http_requester/http_requester_initializer.js +1 -1
  25. package/build/cli/utilities/features/logger/api/logger_api.js +28 -0
  26. package/build/cli/utilities/features/logger/logger_constants.js +7 -0
  27. package/build/cli/utilities/features/logger/logger_initializer.js +44 -0
  28. package/build/cli/utilities/features/logger/logs/app_error.js +14 -0
  29. package/build/cli/utilities/features/logger/logs/app_log.js +40 -0
  30. package/build/cli/{class/errors/app/app_error_constants.js → utilities/features/logger/logs/app_logs_constants.js} +1 -1
  31. package/build/cli/utilities/features/logger/service/logger_service.js +108 -0
  32. package/build/cli/utilities/features/logger/transports/log_object_map_transport.js +15 -22
  33. package/build/index.js +13 -2
  34. package/build/theme/class/archive/theme_archive.js +45 -0
  35. package/build/theme/{features/theme/utils/archive/theme_archive_utils_errors_factory.js → class/archive/theme_archive_errors_factory.js} +2 -2
  36. package/build/theme/class/checksums/theme_checksums.js +34 -52
  37. package/build/theme/class/checksums/theme_checksums_error_factory.js +3 -3
  38. package/build/theme/class/fetch_resources/fetch_resources.js +34 -4
  39. package/build/theme/class/fetch_resources/fetch_resources_errors_factory.js +1 -1
  40. package/build/theme/commands/delete/theme_delete_command.js +3 -2
  41. package/build/theme/commands/info/theme_info_command.js +3 -0
  42. package/build/theme/commands/init/theme_init_command.js +10 -1
  43. package/build/theme/commands/list/theme_list_command.js +22 -8
  44. package/build/theme/commands/publish/theme_publish_command.js +5 -1
  45. package/build/theme/commands/pull/theme_pull_command.js +18 -7
  46. package/build/theme/commands/push/theme_push_command.js +15 -21
  47. package/build/theme/commands/theme_verify_command.js +9 -4
  48. package/build/theme/commands/ui/theme_error.js +3 -3
  49. package/build/theme/features/theme/actions/service/theme_actions_service.js +42 -2
  50. package/build/theme/features/theme/actions/theme_actions_constants.js +1 -2
  51. package/build/theme/features/theme/actions/theme_actions_initializer.js +3 -1
  52. package/build/theme/features/theme/actions/theme_actions_utils.js +5 -31
  53. package/build/theme/features/theme/delete/service/theme_delete_service.js +12 -1
  54. package/build/theme/features/theme/delete/theme_delete_initalizer.js +6 -1
  55. package/build/theme/features/theme/fetch/service/theme_fetch_service.js +37 -8
  56. package/build/theme/features/theme/fetch/theme_fetch_initializer.js +3 -0
  57. package/build/theme/features/theme/init/service/theme_init_service.js +34 -8
  58. package/build/theme/features/theme/init/theme_init_initializer.js +4 -1
  59. package/build/theme/features/theme/merge/service/theme_merge_service.js +40 -6
  60. package/build/theme/features/theme/merge/theme_merge_initializer.js +3 -1
  61. package/build/theme/features/theme/push/api/theme_push_api.js +2 -2
  62. package/build/theme/features/theme/push/service/theme_push_service.js +59 -90
  63. package/build/theme/features/theme/push/theme_push_errors_factory.js +2 -2
  64. package/build/theme/features/theme/push/theme_push_initializer.js +3 -1
  65. package/build/theme/features/theme/push/theme_push_utils.js +3 -3
  66. package/build/theme/features/theme/utils/files_structure/theme_file_structure_errors_factory.js +9 -0
  67. package/build/theme/features/theme/utils/{files/theme_files_utils.js → files_structure/theme_files_structure_utils.js} +23 -34
  68. package/build/theme/features/theme/utils/hidden_directory/hidden_directory_utils.js +5 -2
  69. package/build/theme/features/theme/utils/meta_data/theme_meta_data_error_factory.js +1 -1
  70. package/build/theme/features/theme/utils/resources/theme_resources_with_id_directory_utils.js +5 -2
  71. package/build/theme/features/theme/verify/theme_verify_initializer.js +4 -3
  72. package/build/theme/features/theme/verify/verify/theme_verify_service.js +30 -18
  73. package/build/theme/features/themes/list/services/themes_list_service.js +36 -5
  74. package/build/theme/features/themes/list/themes_list_initializer.js +3 -1
  75. package/build/theme/hooks/ensure_theme_meta_data_untouched_hook.js +4 -2
  76. package/build/theme/hooks/theme_checksums/ensure_theme_current_checksums_up_to_date_hook.js +7 -2
  77. package/build/theme/hooks/themes_actions/ensure_themes_actions_hook.js +2 -3
  78. package/build/ui/hooks/stream_hook.js +26 -0
  79. package/build/utils/array_utils.js +0 -3
  80. package/build/utils/download_file/download_file_errors_factory.js +1 -1
  81. package/build/utils/download_file/download_file_utils.js +19 -1
  82. package/build/utils/fs/errors/stream_read_error.js +7 -5
  83. package/build/utils/fs/errors/stream_write_error.js +7 -5
  84. package/build/utils/fs/fs_utils.js +1 -1
  85. package/build/utils/get_api.js +9 -0
  86. package/build/utils/use_api.js +5 -0
  87. package/build/utils/zip/create_zip_utils.js +21 -9
  88. package/build/utils/zip/errors/create_zip_error.js +7 -5
  89. package/build/utils/zip/errors/open_zip_error.js +7 -5
  90. package/build/utils/zip/extract_zip_utils.js +90 -15
  91. package/oclif.config.js +2 -1
  92. package/package.json +13 -13
  93. package/build/cli/class/errors/app/app_error.js +0 -17
  94. package/build/theme/features/theme/utils/archive/theme_archive_utils.js +0 -24
  95. package/build/theme/features/theme/utils/files/them_files_constants.js +0 -1
  96. package/build/utils/fs/fs_constants.js +0 -6
@@ -3,18 +3,29 @@ import { DownloadFileErrorsFactory } from '../../../../../utils/download_file/do
3
3
  import { STATUS_CODES } from '@dreamcommerce/star_core';
4
4
  export class ThemeDeleteService {
5
5
  #httpApi;
6
- constructor(httpApi) {
6
+ #logger;
7
+ constructor({ logger, httpApi }) {
7
8
  this.#httpApi = httpApi;
9
+ this.#logger = logger;
8
10
  }
9
11
  async deleteTheme({ actionData, credentials }) {
10
12
  try {
13
+ this.#logger.info('Deleting theme', {
14
+ details: {
15
+ actionData
16
+ }
17
+ });
11
18
  const { response: request } = this.#httpApi.deleteTheme({ actionData, shopUrl: credentials.shopUrl });
12
19
  const response = await request;
13
20
  if (response?.status !== STATUS_CODES.ok)
14
21
  return;
22
+ this.#logger.info('Successfully deleted theme');
15
23
  return response?.data;
16
24
  }
17
25
  catch (err) {
26
+ this.#logger.error('Error deleting theme', {
27
+ error: err
28
+ });
18
29
  //TODO to basic class
19
30
  switch (err.response?.status) {
20
31
  case 403:
@@ -3,11 +3,16 @@ import { ThemeDeleteApi } from './api/theme_delete_api.js';
3
3
  import { ThemeDeleteService } from './service/theme_delete_service.js';
4
4
  import { ThemeDeleteHttpApi } from './http/theme_delete_http_api.js';
5
5
  import { THEME_DELETE_FEATURE_NAME } from './theme_delete_constants.js';
6
+ import { LOGGER_API_NAME } from '../../../../cli/utilities/features/logger/logger_constants.js';
6
7
  export class ThemeDeleteInitializer extends SyncFeatureInitializer {
7
8
  static featureName = THEME_DELETE_FEATURE_NAME;
8
9
  init() {
9
10
  const httpApi = this.getApiSync(HTTP_REQUESTER_API_NAME);
10
- const service = new ThemeDeleteService(new ThemeDeleteHttpApi(httpApi));
11
+ const loggerApi = this.getApiSync(LOGGER_API_NAME);
12
+ const service = new ThemeDeleteService({
13
+ httpApi: new ThemeDeleteHttpApi(httpApi),
14
+ logger: loggerApi
15
+ });
11
16
  return {
12
17
  cores: [
13
18
  {
@@ -7,61 +7,90 @@ import { getResources, mapResourcesToTree, removeOldResources } from '../../../.
7
7
  import { FetchResources } from '../../../../class/fetch_resources/fetch_resources.js';
8
8
  import { jsonIndentTransform } from '../../../../../utils/stream_transforms/json_indent_transform.js';
9
9
  import { JSON_FILE_INDENT } from '../../../../../cli/cli_constants.js';
10
- import { AppError } from '../../../../../cli/class/errors/app/app_error.js';
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;
17
- constructor({ themeHttpApi, httpApi }) {
17
+ #loggerApi;
18
+ constructor({ themeHttpApi, httpApi, loggerApi }) {
18
19
  this.#themeHttpApi = themeHttpApi;
19
20
  this.#httpApi = httpApi;
21
+ this.#loggerApi = loggerApi;
20
22
  }
21
23
  async fetchTheme({ credentials, action, config }) {
22
24
  const { dist } = config;
23
25
  const { path: tmpDir } = await tmp.dir({ unsafeCleanup: true });
24
26
  const fetchType = config?.fetchType ?? THEME_FETCH_TYPES.full;
27
+ this.#loggerApi.debug('Temporary directory created.', { details: { path: tmpDir } });
28
+ this.#loggerApi.info('Fetching theme', {
29
+ details: {
30
+ fetchType
31
+ }
32
+ });
25
33
  const { shopUrl } = credentials;
34
+ this.#loggerApi.info('Downloading theme archive.');
26
35
  //TODO think global mechanism to handle when user ctr+c during download
27
36
  const { basename, filename } = await downloadFile({
28
37
  dist: tmpDir,
38
+ logger: this.#loggerApi,
29
39
  request: this.#themeHttpApi.fetchTheme(shopUrl, action.data[fetchType]).response
30
40
  });
41
+ this.#loggerApi.info('Theme archive downloaded.');
31
42
  const themeDir = join(dist, basename);
43
+ this.#loggerApi.info('Extracting theme archive');
32
44
  await extractZip({
33
45
  source: join(tmpDir, filename),
34
46
  dist: themeDir,
47
+ logger: this.#loggerApi,
35
48
  getTransforms: (fileName) => (fileName.endsWith('.json') ? [jsonIndentTransform(JSON_FILE_INDENT)] : [])
36
49
  });
50
+ this.#loggerApi.info('Theme archive extracted');
37
51
  if (fetchType === THEME_FETCH_TYPES.full) {
52
+ this.#loggerApi.info('Fetching theme resources for full theme.');
38
53
  const resources = await getResources(themeDir);
39
54
  await this.fetchResources(shopUrl, themeDir, resources);
40
55
  }
41
56
  try {
42
- await ThemeFilesUtils.updateFilesStructure(themeDir);
57
+ this.#loggerApi.debug('Updating theme files structure.');
58
+ await ThemeFilesStructureUtils.updateFilesStructure(themeDir);
43
59
  }
44
- catch (err) {
60
+ catch (error) {
45
61
  throw new AppError({
46
62
  message: `Failed to update files structure in theme directory: ${themeDir}`,
47
63
  code: 'theme_files_structure_update_error',
48
- stack: err.stack
64
+ error
49
65
  });
50
66
  }
67
+ this.#loggerApi.debug('Creating .gitignore file.');
51
68
  await ThemeMetaDataUtils.createGitIgnoreFile(themeDir);
69
+ this.#loggerApi.debug('Updating metadata file.');
52
70
  await ThemeMetaDataUtils.updateMetadataFileWithWorkUrl(themeDir, credentials.shopUrl);
71
+ this.#loggerApi.debug('Updating theme checksums');
53
72
  /**
54
73
  * moze to nie powinno lezec tutaj :?
55
74
  */
56
- await new ThemeChecksums(themeDir).updateAllChecksums();
75
+ await new ThemeChecksums({
76
+ themeDir,
77
+ loggerApi: this.#loggerApi
78
+ }).updateAllChecksums();
79
+ this.#loggerApi.info('Theme fetch completed successfully');
57
80
  return {
58
81
  name: basename
59
82
  };
60
83
  }
61
84
  async fetchResources(shopUrl, dist, resources) {
85
+ this.#loggerApi.debug('Removing old resources.');
62
86
  await removeOldResources(dist, resources);
87
+ this.#loggerApi.debug('Mapping resources to tree.');
63
88
  const resourcesTree = mapResourcesToTree(resources);
64
- await Promise.all(Object.keys(resourcesTree).map((resource) => new FetchResources(this.#httpApi).fetchResources({
89
+ this.#loggerApi.debug('Fetching resources from remote.');
90
+ await Promise.all(Object.keys(resourcesTree).map((resource) => new FetchResources({
91
+ httpApi: this.#httpApi,
92
+ loggerApi: this.#loggerApi
93
+ }).fetchResources({
65
94
  resourcesPart: resourcesTree[resource],
66
95
  dist,
67
96
  parts: [resource],
@@ -3,12 +3,15 @@ import { ThemeFetchApi } from './api/theme_fetch_api.js';
3
3
  import { FEATURE_CORES_TYPES, HTTP_REQUESTER_API_NAME, SyncFeatureInitializer } from '@dreamcommerce/star_core';
4
4
  import { THEME_FETCH_FEATURE_NAME } from './theme_fetch_constants.js';
5
5
  import { ThemeFetchService } from './service/theme_fetch_service.js';
6
+ import { LOGGER_API_NAME } from '../../../../cli/utilities/features/logger/logger_constants.js';
6
7
  export class ThemeFetchInitializer extends SyncFeatureInitializer {
7
8
  static featureName = THEME_FETCH_FEATURE_NAME;
8
9
  init() {
9
10
  const httpApi = this.getApiSync(HTTP_REQUESTER_API_NAME);
11
+ const loggerApi = this.getApiSync(LOGGER_API_NAME);
10
12
  const themeFetchService = new ThemeFetchService({
11
13
  themeHttpApi: new ThemeFetchHttpApi(httpApi),
14
+ loggerApi,
12
15
  httpApi
13
16
  });
14
17
  return {
@@ -6,39 +6,65 @@ 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 { AppError } from '../../../../../cli/class/errors/app/app_error.js';
10
- import { ThemeFilesUtils } from '../../utils/files/theme_files_utils.js';
9
+ import { ThemeFilesStructureUtils } from '../../utils/files_structure/theme_files_structure_utils.js';
10
+ import { AppError } from '../../../../../cli/utilities/features/logger/logs/app_error.js';
11
11
  export class ThemeInitService {
12
12
  #httpApi;
13
- constructor({ httpApi }) {
13
+ #loggerApi;
14
+ constructor({ httpApi, loggerApi }) {
14
15
  this.#httpApi = httpApi;
16
+ this.#loggerApi = loggerApi;
15
17
  }
16
- async initTheme({ action, credentials }) {
18
+ async initTheme({ action, credentials, sourceThemeId }) {
19
+ this.#loggerApi.info('Initializing theme', {
20
+ details: {
21
+ sourceThemeId
22
+ }
23
+ });
17
24
  const { path: tmpDir } = await tmp.dir({ unsafeCleanup: true });
25
+ this.#loggerApi.debug('Temporary directory created.', { details: { path: tmpDir } });
26
+ this.#loggerApi.info('Downloading theme archive.');
18
27
  //TODO think global mechanism to handle when user ctr+c during download
19
28
  const { basename, filename } = await downloadFile({
20
29
  dist: tmpDir,
21
- request: this.#httpApi.initTheme(credentials.shopUrl, action.data).response
30
+ request: this.#httpApi.initTheme(credentials.shopUrl, action.data).response,
31
+ logger: this.#loggerApi
22
32
  });
33
+ this.#loggerApi.info('Theme archive downloaded.');
23
34
  const themeDir = join(process.cwd(), basename);
35
+ this.#loggerApi.info('Extracting theme archive.');
24
36
  await extractZip({
37
+ logger: this.#loggerApi,
25
38
  source: join(tmpDir, filename),
26
39
  dist: themeDir
27
40
  });
41
+ this.#loggerApi.info('Theme archive extracted.');
28
42
  try {
29
- await ThemeFilesUtils.updateFilesStructure(themeDir);
43
+ this.#loggerApi.debug('Updating theme files structure.');
44
+ await ThemeFilesStructureUtils.updateFilesStructure(themeDir);
30
45
  }
31
46
  catch (err) {
32
47
  throw new AppError({
33
48
  message: `Failed to update files structure in theme directory: ${themeDir}`,
34
49
  code: 'theme_files_structure_update_error',
35
- stack: err.stack
50
+ error: err
36
51
  });
37
52
  }
53
+ this.#loggerApi.debug('Creating .gitignore file.');
38
54
  await ThemeMetaDataUtils.createGitIgnoreFile(themeDir);
55
+ this.#loggerApi.debug('Updating metadata file.');
39
56
  await ThemeMetaDataUtils.updateMetadataFileWithWorkUrl(themeDir, credentials.shopUrl);
40
- await new ThemeChecksums(themeDir).updateAllChecksums();
57
+ this.#loggerApi.debug('Updating theme checksums.');
58
+ await new ThemeChecksums({
59
+ themeDir,
60
+ loggerApi: this.#loggerApi
61
+ }).updateAllChecksums();
41
62
  const themeMetaData = await ThemeMetaDataUtils.getThemeMetadata(themeDir);
63
+ this.#loggerApi.info('Theme initialized successfully.', {
64
+ details: {
65
+ sourceThemeId
66
+ }
67
+ });
42
68
  return {
43
69
  themeId: themeMetaData.themeId,
44
70
  themeName: await ThemeInfoUtils.getThemeName(themeDir)
@@ -3,12 +3,15 @@ import { ThemeInitHttpApi } from './http/theme_init_http_api.js';
3
3
  import { ThemeInitApi } from './api/theme_init_api.js';
4
4
  import { ThemeInitService } from './service/theme_init_service.js';
5
5
  import { THEME_INIT_FEATURE_NAME } from './theme_init_constants.js';
6
+ import { LOGGER_API_NAME } from '../../../../cli/utilities/features/logger/logger_constants.js';
6
7
  export class ThemeInitInitializer extends SyncFeatureInitializer {
7
8
  static featureName = THEME_INIT_FEATURE_NAME;
8
9
  init() {
9
10
  const httpApi = this.getApiSync(HTTP_REQUESTER_API_NAME);
11
+ const loggerApi = this.getApiSync(LOGGER_API_NAME);
10
12
  const service = new ThemeInitService({
11
- httpApi: new ThemeInitHttpApi(httpApi)
13
+ httpApi: new ThemeInitHttpApi(httpApi),
14
+ loggerApi
12
15
  });
13
16
  return {
14
17
  cores: [
@@ -2,11 +2,22 @@ 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
+ #loggerApi;
10
+ constructor({ loggerApi }) {
11
+ this.#loggerApi = loggerApi;
12
+ }
9
13
  async applyChanges(fromTheme, toTheme, changes) {
14
+ this.#loggerApi.info('Applying changes between themes.', {
15
+ details: {
16
+ from: fromTheme,
17
+ to: toTheme,
18
+ changesCount: changes.length
19
+ }
20
+ });
10
21
  FSTree.applyPatch(fromTheme, toTheme, changes, {
11
22
  create(inputPath, outputPath) {
12
23
  copyFileSync(inputPath, outputPath);
@@ -15,19 +26,36 @@ export class ThemeMergeService {
15
26
  copyFileSync(inputPath, outputPath);
16
27
  }
17
28
  });
18
- new ThemeChecksums(toTheme).updateAllChecksums();
29
+ this.#loggerApi.debug('Updating checksums after applying changes.');
30
+ await new ThemeChecksums({
31
+ themeDir: toTheme,
32
+ loggerApi: this.#loggerApi
33
+ }).updateAllChecksums();
19
34
  }
20
35
  async getChangesBetweenThemes(theme1, theme2) {
36
+ this.#loggerApi.info('Calculating changes between themes.', {
37
+ details: {
38
+ theme1,
39
+ theme2
40
+ }
41
+ });
21
42
  const theme1Tree = new FSTree({
22
43
  entries: walkSync.entries(theme1)
23
44
  });
24
45
  const theme2Tree = new FSTree({
25
46
  entries: walkSync.entries(theme2)
26
47
  });
27
- const theme1Checksums = new ThemeChecksums(theme1);
28
- const theme2Checksums = new ThemeChecksums(theme2);
48
+ const theme1Checksums = new ThemeChecksums({
49
+ themeDir: theme1,
50
+ loggerApi: this.#loggerApi
51
+ });
52
+ const theme2Checksums = new ThemeChecksums({
53
+ themeDir: theme2,
54
+ loggerApi: this.#loggerApi
55
+ });
56
+ this.#loggerApi.debug('Getting user-defined root directories to exclude from changes.');
29
57
  const userDirectories = await this._getUserRootDirectories(theme2);
30
- return theme2Tree
58
+ const patch = theme2Tree
31
59
  .calculatePatch(theme1Tree, (entryA, entryB) => {
32
60
  if (entryA.isDirectory() && entryB.isDirectory())
33
61
  return true;
@@ -37,9 +65,15 @@ export class ThemeMergeService {
37
65
  .filter(([_, name]) => {
38
66
  return !name.startsWith(SHOPER_THEME_METADATA_DIR) && !userDirectories.some((userDir) => name.startsWith(userDir));
39
67
  });
68
+ this.#loggerApi.debug('Calculated patch between themes.', {
69
+ details: {
70
+ patchSize: patch.length
71
+ }
72
+ });
73
+ return patch;
40
74
  }
41
75
  async _getUserRootDirectories(themeDir) {
42
- const filesStructure = await ThemeFilesUtils.getThemeRootDirectories(themeDir);
76
+ const filesStructure = await ThemeFilesStructureUtils.getThemeRootDirectories(themeDir);
43
77
  return (await getAllDirectoriesNamesInside(themeDir, {
44
78
  recursive: false,
45
79
  hidden: true
@@ -2,10 +2,12 @@ import { FEATURE_CORES_TYPES, SyncFeatureInitializer } from '@dreamcommerce/star
2
2
  import { ThemeMergeApi } from './api/theme_merge_api.js';
3
3
  import { ThemeMergeService } from './service/theme_merge_service.js';
4
4
  import { THEME_MERGE_FEATURE_NAME } from './theme_merge_constants.js';
5
+ import { LOGGER_API_NAME } from '../../../../cli/utilities/features/logger/logger_constants.js';
5
6
  export class ThemeMergeInitializer extends SyncFeatureInitializer {
6
7
  static featureName = THEME_MERGE_FEATURE_NAME;
7
8
  init() {
8
- const themeMergeService = new ThemeMergeService();
9
+ const loggerApi = this.getApiSync(LOGGER_API_NAME);
10
+ const themeMergeService = new ThemeMergeService({ loggerApi });
9
11
  return {
10
12
  cores: [
11
13
  {
@@ -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,112 +1,109 @@
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
- constructor({ themeFetchApi }) {
19
+ #loggerApi;
20
+ constructor({ themeFetchApi, loggerApi }) {
22
21
  this.#themeFetchApi = themeFetchApi;
22
+ this.#loggerApi = loggerApi;
23
23
  }
24
- async partialPush({ action, filesStructure, executionContext, themeChecksums, themeFilesUploadApi, credentials }) {
24
+ async push({ pushAction, credentials, filesStructure, executionContext, themeChecksums, themeFilesUploadApi }) {
25
+ this.#loggerApi.info('Pushing theme changes.');
25
26
  const { path: tmpDir } = await tmp.dir({ unsafeCleanup: true });
27
+ this.#loggerApi.debug('Temporary directory created.', { details: { path: tmpDir } });
26
28
  const themeRootDir = executionContext.themeRootDir;
27
29
  if (await themeChecksums.hasThemeFileBeenCreated(ThemePublishUtils.getSkinStoreSettingsFilePath(themeRootDir)))
28
30
  throw ThemePushErrorsFactory.createErrorWhilePushingUnpublishedThemeWithSkinstoreData(credentials.shopUrl);
29
31
  try {
30
- //TODO to do api?
32
+ this.#loggerApi.debug('Getting file records from action data.');
31
33
  const filesRecords = await ThemeActionsUtils.getFilesRecordsFromActionData({
32
34
  themeRootDir,
33
- themeAction: action,
34
- filesStructure,
35
- themeChecksums
35
+ themeAction: pushAction,
36
+ filesStructure
36
37
  });
37
- const filesToUpload = filesRecords.filter(({ state }) => [FILE_STATES.modified, FILE_STATES.created].includes(state));
38
- let localFileNameToUploaded;
38
+ this.#loggerApi.debug('Filtering for created or modified files to upload.');
39
+ const filesToUpload = await ArrayUtils.asyncFilter(filesRecords, async ({ path }) => (await themeChecksums.hasThemeFileBeenCreated(path)) || (await themeChecksums.hasThemeFileBeenModified(path)));
40
+ this.#loggerApi.debug('Files to upload determined.', { details: { count: filesToUpload.length } });
39
41
  if (filesToUpload.length) {
40
- const uploadData = await this._uploadThemeFiles({
42
+ this.#loggerApi.info(`Uploading ${filesToUpload.length} theme files.`);
43
+ const { localFileNameToUploaded } = await this._uploadThemeFiles({
41
44
  filesToUpload,
42
45
  credentials,
43
46
  themeRootDir,
44
47
  themeFilesUploadApi
45
48
  });
46
- if (!uploadData.isSuccess)
47
- throw ThemePushErrorsFactory.createErrorWhileUploadingThemeFiles(credentials.shopUrl);
48
- localFileNameToUploaded = uploadData.localFileNameToUploadedMap;
49
+ this.#loggerApi.info('Theme files uploaded.');
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);
49
55
  }
50
56
  const themeArchivePath = join(tmpDir, `${uuid()}.zip`);
51
- const filesInArchive = await ThemeActionsUtils.getFilesThatMatchesAction({
57
+ this.#loggerApi.info('Creating theme archive.');
58
+ await new ThemeArchive(themeRootDir).createFullArchive({
59
+ dist: themeArchivePath,
52
60
  actionValue: THEME_WILDCARD_ACTION_NAME,
53
61
  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
- rootDir: themeRootDir
62
+ logger: this.#loggerApi
64
63
  });
65
- await this._createFilesList({
66
- themeRootDir,
67
- filesRecords,
68
- localFileNameToUploaded,
69
- deletedFiles,
70
- allCustomModulesIds: await this._getAllModulesIds(themeRootDir)
71
- });
72
- await ThemeArchiveUtils.create({
73
- dist: themeArchivePath,
74
- rootDir: themeRootDir,
75
- files: [
76
- ...(await this._appendSettingsIfModifiedFileFromFolderThatContainsOne(groupedFiles.modified)),
77
- ...groupedFiles.created,
78
- (await fileExists(ThemeFilesUtils.getFilesListFilePath(themeRootDir))) ? THEME_FILES_LIST_FILE_NAME : undefined
79
- ].filter(Boolean)
80
- });
81
- await ThemeFilesUtils.removeAFilesListFile(themeRootDir);
64
+ this.#loggerApi.info('Theme archive created.');
82
65
  const { resources, modules } = await themeFilesUploadApi.uploadArchive({
83
- action,
66
+ action: pushAction,
84
67
  themeArchivePath,
85
68
  credentials
86
69
  });
87
- if (modules)
70
+ this.#loggerApi.info('Theme archive uploaded.');
71
+ if (modules) {
72
+ this.#loggerApi.debug('Updating data for new modules.');
88
73
  await this._updateDataForNewCreatedModules({ modules, themeRootDir });
74
+ }
89
75
  if (resources) {
76
+ this.#loggerApi.debug('Removing old resources.');
90
77
  await removeOldResources(themeRootDir, resources);
78
+ this.#loggerApi.info('Fetching new resources.');
91
79
  await this.#themeFetchApi.fetchResources(credentials.shopUrl, themeRootDir, resources);
80
+ this.#loggerApi.info('New resources fetched.');
92
81
  }
93
- //TODO only changed files checksum
82
+ this.#loggerApi.debug('Updating theme checksums.');
94
83
  await themeChecksums.updateAllChecksums();
84
+ this.#loggerApi.info('Push completed successfully.');
95
85
  }
96
- catch (err) {
97
- await ThemeFilesUtils.removeAFilesListFile(themeRootDir);
98
- throw err;
86
+ finally {
87
+ this.#loggerApi.debug('Cleaning up files list.');
88
+ await ThemeFilesStructureUtils.removeAFilesListFile(themeRootDir);
99
89
  }
100
90
  }
101
91
  async _uploadThemeFiles({ filesToUpload, themeRootDir, credentials, themeFilesUploadApi }) {
102
92
  try {
93
+ this.#loggerApi.debug('Uploading individual files.');
103
94
  const { uploadedImageData, rejectedImageData } = await themeFilesUploadApi.uploadFiles(filesToUpload);
104
- if (uploadedImageData.length)
95
+ this.#loggerApi.debug('Individual files upload finished.', {
96
+ details: {
97
+ uploadedCount: uploadedImageData.length,
98
+ rejectedCount: rejectedImageData.length
99
+ }
100
+ });
101
+ if (uploadedImageData.length) {
102
+ this.#loggerApi.debug('Removing original files for uploaded images.');
105
103
  await ThemeImagesUtils.removeUploadedOriginalFiles(themeRootDir, uploadedImageData);
104
+ }
106
105
  return {
107
- isSuccess: rejectedImageData.length === 0,
108
- rejectedImageData,
109
- localFileNameToUploadedMap: uploadedImageData.reduce((acc, { originalFilename, uploadedFilename }) => {
106
+ localFileNameToUploaded: uploadedImageData.reduce((acc, { originalFilename, uploadedFilename }) => {
110
107
  return {
111
108
  ...acc,
112
109
  [originalFilename]: uploadedFilename ?? originalFilename
@@ -120,40 +117,12 @@ export class ThemePushService {
120
117
  }
121
118
  async _updateDataForNewCreatedModules({ modules, themeRootDir }) {
122
119
  for (const [moduleDirectoryName, metaData] of Object.entries(modules)) {
120
+ this.#loggerApi.debug('Updating settings for new module.', { details: { module: moduleDirectoryName } });
123
121
  await writeJSONFile(join(themeRootDir, MODULES_DIRECTORY_NAME, moduleDirectoryName, THEME_MODULE_SETTINGS_FILE_NAME), JSON.parse(metaData[THEME_MODULE_SETTINGS_FILE_NAME]));
124
122
  }
125
123
  }
126
- async _createFilesList({ deletedFiles, localFileNameToUploaded, filesRecords, themeRootDir, allCustomModulesIds }) {
127
- const filesListContent = ThemeFilesUtils.mapFilesRecordsToFilesList(filesRecords, localFileNameToUploaded);
128
- if (deletedFiles?.length)
129
- filesListContent.removed = deletedFiles;
130
- if (allCustomModulesIds?.length)
131
- filesListContent[FILES_LIST_CUSTOM_MODULES_TO_KEEP_IDS] = allCustomModulesIds;
132
- await ThemeFilesUtils.createAFilesListFile(themeRootDir, filesListContent);
133
- }
134
- async _appendSettingsIfModifiedFileFromFolderThatContainsOne(modifiedFiles) {
135
- const withSettingsFiles = [...modifiedFiles];
136
- for (const filePath of modifiedFiles) {
137
- if (await fileExists(join(dirname(filePath), 'settings.json'))) {
138
- withSettingsFiles.push(join(dirname(filePath), 'settings.json'));
139
- }
140
- }
141
- return uniq(withSettingsFiles);
142
- }
143
- async _getAllModulesIds(themeRootDir) {
144
- const modulesPath = join(themeRootDir, MODULES_DIRECTORY_NAME);
145
- if (!(await fileExists(modulesPath)))
146
- return [];
147
- const moduleDirs = await getAllDirectoriesNamesInside(modulesPath);
148
- const modulesIds = [];
149
- for (const moduleDir of moduleDirs) {
150
- const moduleSettingsPath = join(themeRootDir, MODULES_DIRECTORY_NAME, moduleDir, THEME_MODULE_SETTINGS_FILE_NAME);
151
- if (await fileExists(moduleSettingsPath)) {
152
- const settingsContent = await readJSONFile(moduleSettingsPath);
153
- if (settingsContent.id && Number.isInteger(settingsContent.id))
154
- modulesIds.push(settingsContent.id);
155
- }
156
- }
157
- return modulesIds;
124
+ async _createFilesList(themeRootDir, filesRecords, localFileNameToUploaded = {}) {
125
+ this.#loggerApi.debug('Creating filesList.json for the archive.');
126
+ await ThemeFilesStructureUtils.createAFilesListFile(themeRootDir, ThemeFilesStructureUtils.mapFilesRecordsToFilesList(filesRecords, localFileNameToUploaded));
158
127
  }
159
128
  }
@@ -1,4 +1,4 @@
1
- import { AppError } from '../../../../cli/class/errors/app/app_error.js';
1
+ import { AppError } from '../../../../cli/utilities/features/logger/logs/app_error.js';
2
2
  import { THEME_ARCHIVE_UPLOAD_ERROR, THEME_FILES_UPLOAD_ERROR } from './theme_push_constants.js';
3
3
  export class ThemePushErrorsFactory {
4
4
  static createErrorWhileUploadingThemeFiles(shopUrl, messages) {
@@ -16,7 +16,7 @@ export class ThemePushErrorsFactory {
16
16
  code: 'theme.push.error_creating_theme_archive',
17
17
  message: `Error while creating theme archive for shop "${shopUrl}"`,
18
18
  level: 'error',
19
- stack: error.stack
19
+ error
20
20
  });
21
21
  }
22
22
  static createErrorWhilePushingUnpublishedThemeWithSkinstoreData(shopUrl) {
@@ -3,11 +3,13 @@ import { ThemePushService } from './service/theme_push_service.js';
3
3
  import { THEME_PUSH_FEATURE_NAME } from './theme_push_constants.js';
4
4
  import { ThemePushApi } from './api/theme_push_api.js';
5
5
  import { THEME_FETCH_API_NAME } from '../fetch/theme_fetch_constants.js';
6
+ import { LOGGER_API_NAME } from '../../../../cli/utilities/features/logger/logger_constants.js';
6
7
  export class ThemePushInitializer extends SyncFeatureInitializer {
7
8
  static featureName = THEME_PUSH_FEATURE_NAME;
8
9
  init() {
9
10
  const service = new ThemePushService({
10
- themeFetchApi: this.getApiSync(THEME_FETCH_API_NAME)
11
+ themeFetchApi: this.getApiSync(THEME_FETCH_API_NAME),
12
+ loggerApi: this.getApiSync(LOGGER_API_NAME)
11
13
  });
12
14
  return {
13
15
  cores: [
@@ -1,10 +1,10 @@
1
1
  import globs from 'fast-glob';
2
- import { AppError } from '../../../../cli/class/errors/app/app_error.js';
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}`,
@@ -0,0 +1,9 @@
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
+ }