@shoper/cli 0.5.2-5 → 0.5.2-6
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.
- package/build/cli/auth/cli_auth_errors_factory.js +1 -1
- package/build/cli/auth/cli_auth_initializer.js +6 -1
- package/build/cli/auth/service/cli_auth_service.js +11 -2
- package/build/cli/auth/tokens/cli_auth_tokens_errors_factory.js +1 -1
- package/build/cli/auth/tokens/cli_auth_tokens_initalizer.js +6 -1
- package/build/cli/auth/tokens/service/cli_auth_tokens_service.js +8 -1
- package/build/cli/class/base_command.js +15 -1
- package/build/cli/class/errors/file_system_errors_factory.js +3 -3
- package/build/cli/class/errors/http/http_errors_factory.js +2 -2
- package/build/cli/commands/auth/cli_auth_add_token_command.js +5 -0
- package/build/cli/commands/auth/cli_auth_list_tokens_command.js +20 -11
- package/build/cli/commands/auth/cli_auth_logout_command.js +5 -0
- package/build/cli/commands/auth/cli_auth_remove_token_command.js +5 -0
- package/build/cli/commands/auth/cli_auth_switch_token_command.js +8 -0
- package/build/cli/commands/cli_update_command.js +5 -0
- package/build/cli/core/cli_setup.js +11 -18
- package/build/cli/features/execution_context/execution_context_service.js +2 -0
- package/build/cli/features/version/service/cli_version_service.js +2 -2
- package/build/cli/hooks/authorization/ensure_authorization_hook.js +9 -2
- package/build/cli/hooks/ensure_cli_initialized_hook.js +1 -6
- package/build/cli/hooks/ensure_logs_flushed_hook.js +7 -0
- package/build/cli/index.js +0 -1
- package/build/cli/{features → utilities/features}/http_requester/http_client.js +2 -2
- package/build/cli/{features → utilities/features}/http_requester/http_requester_initializer.js +1 -1
- package/build/cli/utilities/features/logger/api/logger_api.js +28 -0
- package/build/cli/utilities/features/logger/logger_constants.js +7 -0
- package/build/cli/utilities/features/logger/logger_initializer.js +44 -0
- package/build/cli/utilities/features/logger/logs/app_error.js +14 -0
- package/build/cli/utilities/features/logger/logs/app_log.js +40 -0
- package/build/cli/{class/errors/app/app_error_constants.js → utilities/features/logger/logs/app_logs_constants.js} +1 -1
- package/build/cli/utilities/features/logger/service/logger_service.js +108 -0
- package/build/cli/utilities/features/logger/transports/log_object_map_transport.js +15 -22
- package/build/index.js +13 -2
- package/build/theme/class/archive/theme_archive.js +45 -0
- package/build/theme/{features/theme/utils/archive/theme_archive_utils_errors_factory.js → class/archive/theme_archive_errors_factory.js} +2 -2
- package/build/theme/class/checksums/theme_checksums.js +34 -52
- package/build/theme/class/checksums/theme_checksums_error_factory.js +3 -3
- package/build/theme/class/fetch_resources/fetch_resources.js +34 -4
- package/build/theme/class/fetch_resources/fetch_resources_errors_factory.js +1 -1
- package/build/theme/commands/delete/theme_delete_command.js +3 -2
- package/build/theme/commands/info/theme_info_command.js +3 -0
- package/build/theme/commands/init/theme_init_command.js +10 -1
- package/build/theme/commands/list/theme_list_command.js +22 -8
- package/build/theme/commands/publish/theme_publish_command.js +5 -1
- package/build/theme/commands/pull/theme_pull_command.js +18 -7
- package/build/theme/commands/push/theme_push_command.js +15 -21
- package/build/theme/commands/theme_verify_command.js +9 -4
- package/build/theme/commands/ui/theme_error.js +3 -3
- package/build/theme/features/theme/actions/service/theme_actions_service.js +42 -2
- package/build/theme/features/theme/actions/theme_actions_constants.js +1 -2
- package/build/theme/features/theme/actions/theme_actions_initializer.js +3 -1
- package/build/theme/features/theme/actions/theme_actions_utils.js +5 -31
- package/build/theme/features/theme/delete/service/theme_delete_service.js +12 -1
- package/build/theme/features/theme/delete/theme_delete_initalizer.js +6 -1
- package/build/theme/features/theme/fetch/service/theme_fetch_service.js +37 -8
- package/build/theme/features/theme/fetch/theme_fetch_initializer.js +3 -0
- package/build/theme/features/theme/init/service/theme_init_service.js +34 -8
- package/build/theme/features/theme/init/theme_init_initializer.js +4 -1
- package/build/theme/features/theme/merge/service/theme_merge_service.js +40 -6
- package/build/theme/features/theme/merge/theme_merge_initializer.js +3 -1
- package/build/theme/features/theme/push/api/theme_push_api.js +2 -2
- package/build/theme/features/theme/push/service/theme_push_service.js +59 -90
- package/build/theme/features/theme/push/theme_push_errors_factory.js +2 -2
- package/build/theme/features/theme/push/theme_push_initializer.js +3 -1
- package/build/theme/features/theme/push/theme_push_utils.js +3 -3
- package/build/theme/features/theme/utils/files_structure/theme_file_structure_errors_factory.js +9 -0
- package/build/theme/features/theme/utils/{files/theme_files_utils.js → files_structure/theme_files_structure_utils.js} +23 -34
- package/build/theme/features/theme/utils/hidden_directory/hidden_directory_utils.js +5 -2
- package/build/theme/features/theme/utils/meta_data/theme_meta_data_error_factory.js +1 -1
- package/build/theme/features/theme/utils/resources/theme_resources_with_id_directory_utils.js +5 -2
- package/build/theme/features/theme/verify/theme_verify_initializer.js +4 -3
- package/build/theme/features/theme/verify/verify/theme_verify_service.js +30 -18
- package/build/theme/features/themes/list/services/themes_list_service.js +36 -5
- package/build/theme/features/themes/list/themes_list_initializer.js +3 -1
- package/build/theme/hooks/ensure_theme_meta_data_untouched_hook.js +4 -2
- package/build/theme/hooks/theme_checksums/ensure_theme_current_checksums_up_to_date_hook.js +7 -2
- package/build/theme/hooks/themes_actions/ensure_themes_actions_hook.js +2 -3
- package/build/ui/hooks/stream_hook.js +26 -0
- package/build/utils/array_utils.js +0 -3
- package/build/utils/download_file/download_file_errors_factory.js +1 -1
- package/build/utils/download_file/download_file_utils.js +19 -1
- package/build/utils/fs/errors/stream_read_error.js +7 -5
- package/build/utils/fs/errors/stream_write_error.js +7 -5
- package/build/utils/fs/fs_utils.js +1 -1
- package/build/utils/get_api.js +9 -0
- package/build/utils/use_api.js +5 -0
- package/build/utils/zip/create_zip_utils.js +21 -9
- package/build/utils/zip/errors/create_zip_error.js +7 -5
- package/build/utils/zip/errors/open_zip_error.js +7 -5
- package/build/utils/zip/extract_zip_utils.js +90 -15
- package/oclif.config.js +2 -1
- package/package.json +13 -13
- package/build/cli/class/errors/app/app_error.js +0 -17
- package/build/theme/features/theme/utils/archive/theme_archive_utils.js +0 -24
- package/build/theme/features/theme/utils/files/them_files_constants.js +0 -1
- 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
|
-
|
|
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
|
|
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/
|
|
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
|
-
|
|
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
|
-
|
|
57
|
+
this.#loggerApi.debug('Updating theme files structure.');
|
|
58
|
+
await ThemeFilesStructureUtils.updateFilesStructure(themeDir);
|
|
43
59
|
}
|
|
44
|
-
catch (
|
|
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
|
-
|
|
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(
|
|
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
|
-
|
|
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 {
|
|
10
|
-
import {
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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(
|
|
28
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
9
|
+
const loggerApi = this.getApiSync(LOGGER_API_NAME);
|
|
10
|
+
const themeMergeService = new ThemeMergeService({ loggerApi });
|
|
9
11
|
return {
|
|
10
12
|
cores: [
|
|
11
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 {
|
|
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 {
|
|
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 {
|
|
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 {
|
|
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 {
|
|
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;
|
|
20
|
+
constructor({ themeFetchApi, loggerApi }) {
|
|
22
21
|
this.#themeFetchApi = themeFetchApi;
|
|
22
|
+
this.#loggerApi = loggerApi;
|
|
23
23
|
}
|
|
24
|
-
async
|
|
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
|
-
|
|
32
|
+
this.#loggerApi.debug('Getting file records from action data.');
|
|
31
33
|
const filesRecords = await ThemeActionsUtils.getFilesRecordsFromActionData({
|
|
32
34
|
themeRootDir,
|
|
33
|
-
themeAction:
|
|
34
|
-
filesStructure
|
|
35
|
-
themeChecksums
|
|
35
|
+
themeAction: pushAction,
|
|
36
|
+
filesStructure
|
|
36
37
|
});
|
|
37
|
-
|
|
38
|
-
|
|
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
|
-
|
|
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
|
-
|
|
47
|
-
|
|
48
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
82
|
+
this.#loggerApi.debug('Updating theme checksums.');
|
|
94
83
|
await themeChecksums.updateAllChecksums();
|
|
84
|
+
this.#loggerApi.info('Push completed successfully.');
|
|
95
85
|
}
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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(
|
|
127
|
-
|
|
128
|
-
|
|
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/
|
|
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
|
-
|
|
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/
|
|
2
|
+
import { AppError } from '../../../../cli/utilities/features/logger/logs/app_error.js';
|
|
3
3
|
import { toUnixPath } from '../../../../utils/path_utils.js';
|
|
4
|
-
import {
|
|
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
|
|
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}`,
|
package/build/theme/features/theme/utils/files_structure/theme_file_structure_errors_factory.js
ADDED
|
@@ -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
|
+
}
|