@shoper/cli 0.1.0-9 → 0.2.1-0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/cli/auth/cli_auth_errors_factory.js +1 -1
- package/build/cli/auth/tokens/cli_auth_tokens_errors_factory.js +1 -1
- package/build/cli/auth/tokens/cli_auth_tokens_initalizer.js +1 -1
- package/build/cli/class/base_command.js +14 -0
- package/build/cli/class/errors/file_system_errors_factory.js +1 -1
- package/build/cli/class/errors/http/http_errors_constants.js +1 -0
- package/build/cli/class/errors/{http_errors_factory.js → http/http_errors_factory.js} +3 -2
- package/build/cli/commands/auth/cli_auth_add_token_command.js +22 -19
- package/build/cli/commands/auth/cli_auth_commands_constants.js +3 -0
- package/build/cli/commands/auth/cli_auth_commands_utils.js +46 -0
- package/build/cli/commands/auth/cli_auth_list_tokens_command.js +19 -6
- package/build/cli/commands/auth/cli_auth_remove_token_command.js +37 -13
- package/build/cli/commands/auth/cli_auth_switch_token_command.js +20 -11
- package/build/cli/commands/auth/ui/invalid_token_index_error.js +11 -0
- package/build/cli/commands/auth/ui/missing_credentials_error.js +9 -0
- package/build/cli/commands/auth/ui/missing_token_index_error.js +19 -0
- package/build/cli/commands/cli_ui_dump_command.js +11 -0
- package/build/cli/commands/cli_update_command.js +13 -4
- package/build/cli/commands/commands_constants.js +6 -4
- package/build/cli/core/cli_setup.js +33 -14
- package/build/cli/features/controls/controls_constants.js +12 -0
- package/build/cli/features/controls/controls_dto_mappers.js +55 -0
- package/build/cli/features/controls/ui/controls_mappers.js +62 -0
- package/build/cli/features/controls/ui/form.js +4 -0
- package/build/cli/features/controls/ui/form_constants.js +7 -0
- package/build/cli/features/controls/validators/greater_eq_than_validator.js +5 -0
- package/build/cli/features/controls/validators/length_validator.js +6 -0
- package/build/cli/features/controls/validators/required_validator.js +5 -0
- package/build/cli/features/controls/validators/validator_constants.js +13 -0
- package/build/cli/features/execution_context/execution_context_service.js +5 -5
- package/build/cli/features/http_requester/http_requester_initializer.js +1 -1
- package/build/cli/hooks/authorization/ensure_authorization_hook.js +2 -2
- package/build/cli/hooks/authorization/ensure_authorization_hook_constants.js +7 -8
- package/build/index.js +37 -19
- package/build/theme/class/archive/theme_archive.js +44 -0
- package/build/theme/class/archive/theme_archive_errors_factory.js +11 -0
- package/build/theme/class/checksums/theme_checksums.js +170 -0
- package/build/theme/{features/theme/utils → class}/checksums/theme_checksums_error_factory.js +1 -1
- package/build/theme/class/checksums/theme_checksums_utils.js +17 -0
- package/build/theme/class/fetch_resources/fetch_resources.js +1 -0
- package/build/theme/class/fetch_resources/fetch_resources_errors_factory.js +1 -1
- package/build/theme/class/fetch_resources/fetch_resources_utils.js +4 -1
- package/build/theme/class/files_upload/theme_files_upload.js +61 -0
- package/build/theme/class/files_upload/theme_files_upload_http_api.js +23 -0
- package/build/theme/commands/delete/theme_delete_command.js +97 -0
- package/build/theme/commands/delete/ui/theme_deleted_successfully.js +15 -0
- package/build/theme/commands/delete/ui/theme_deletion_warning.js +10 -0
- package/build/theme/commands/info/theme_info_command.js +79 -0
- package/build/theme/commands/info/theme_info_command_utils.js +39 -0
- package/build/theme/commands/init/theme_init_command.js +84 -0
- package/build/theme/commands/init/ui/theme_created_success.js +15 -0
- package/build/theme/commands/list/theme_list_command.js +25 -0
- package/build/theme/commands/list/theme_list_command_utils.js +27 -0
- package/build/theme/commands/publish/theme_publish_command.js +76 -0
- package/build/theme/commands/pull/theme_pull_command.js +174 -0
- package/build/theme/commands/pull/ui/theme_pull_id_mismatch_error.js +7 -0
- package/build/theme/commands/pull/ui/theme_pull_unpublished_changes_warning.js +10 -0
- package/build/theme/commands/pull/ui/theme_pulled_success.js +5 -0
- package/build/theme/commands/push/theme_push_command.js +112 -0
- package/build/theme/commands/push/ui/theme_push_skip_into.js +7 -0
- package/build/theme/commands/push/ui/theme_pushed_success.js +5 -0
- package/build/theme/commands/push/ui/theme_unpermitted_actions_error.js +12 -0
- package/build/theme/commands/theme_commands_constants.js +9 -6
- package/build/theme/commands/theme_verify_command.js +64 -22
- package/build/theme/commands/ui/invalid_theme_id.js +11 -0
- package/build/theme/commands/ui/missing_theme_files.js +15 -0
- package/build/theme/commands/ui/missing_theme_id_error.js +13 -0
- package/build/theme/commands/ui/ouside_of_theme_directory_context_error.js +7 -0
- package/build/theme/commands/ui/theme_directory_context_error.js +7 -0
- package/build/theme/commands/ui/theme_error.js +29 -0
- package/build/theme/commands/ui/theme_work_url_mismatch.js +17 -0
- package/build/theme/commands/ui/unpermitted_command_error.js +18 -0
- package/build/theme/features/theme/actions/api/theme_actions_api.js +3 -0
- package/build/theme/features/theme/actions/service/theme_actions_service.js +15 -3
- package/build/theme/features/theme/actions/service/theme_actions_service_constants.js +1 -0
- package/build/theme/features/theme/actions/theme_actions_constants.js +5 -2
- package/build/theme/features/theme/actions/theme_actions_initializer.js +1 -1
- package/build/theme/features/theme/actions/theme_actions_utils.js +60 -7
- package/build/theme/features/theme/delete/api/theme_delete_api.js +13 -0
- package/build/theme/features/theme/delete/http/theme_delete_http_api.js +17 -0
- package/build/theme/features/theme/delete/service/theme_delete_service.js +31 -0
- package/build/theme/features/theme/delete/theme_delete_constants.js +2 -0
- package/build/theme/features/theme/delete/theme_delete_initalizer.js +20 -0
- package/build/theme/features/theme/fetch/service/theme_fetch_service.js +30 -10
- package/build/theme/features/theme/info/theme_info_utils.js +9 -0
- package/build/theme/features/theme/init/service/theme_init_service.js +9 -4
- package/build/theme/features/theme/init/theme_init_initializer.js +3 -3
- package/build/theme/features/theme/merge/api/theme_merge_api.js +0 -12
- package/build/theme/features/theme/merge/service/theme_merge_service.js +20 -28
- package/build/theme/features/theme/push/service/theme_push_service.js +67 -166
- package/build/theme/features/theme/push/theme_push_constants.js +2 -0
- package/build/theme/features/theme/push/theme_push_errors_factory.js +7 -4
- package/build/theme/features/theme/push/theme_push_initializer.js +1 -7
- package/build/theme/features/theme/push/theme_push_utils.js +25 -0
- package/build/theme/features/theme/skinstore/api/theme_skinstore_api.js +19 -0
- package/build/theme/features/theme/skinstore/http/theme_skinstore_http_api.js +17 -0
- package/build/theme/features/theme/skinstore/service/theme_skinstore_service.js +32 -0
- package/build/theme/features/theme/skinstore/theme_publish_constants.js +4 -0
- package/build/theme/features/theme/skinstore/theme_skinstore_initialzier.js +20 -0
- package/build/theme/features/theme/utils/files_structure/theme_file_structure_errors_factory.js +10 -0
- package/build/theme/features/theme/utils/files_structure/theme_files_structure_utils.js +76 -0
- package/build/theme/features/theme/utils/hidden_directory/hidden_directory_utils.js +32 -0
- package/build/theme/features/theme/utils/meta_data/theme_meta_data_constants.js +1 -0
- package/build/theme/features/theme/utils/meta_data/theme_meta_data_error_factory.js +15 -0
- package/build/theme/features/theme/utils/meta_data/theme_meta_data_utils.js +39 -0
- package/build/theme/features/theme/utils/{directories → resources}/theme_resources_with_id_directory_utils.js +3 -14
- package/build/theme/features/theme/utils/theme_images_utils.js +1 -19
- package/build/theme/features/theme/verify/api/theme_verify_api.js +13 -0
- package/build/theme/features/theme/verify/http/theme_verify_http_api.js +30 -0
- package/build/theme/features/theme/verify/theme_verify_constants.js +2 -0
- package/build/theme/features/theme/verify/theme_verify_initializer.js +19 -0
- package/build/theme/features/theme/verify/verify/theme_verify_service.js +55 -0
- package/build/theme/features/themes/list/api/themes_list_api.js +3 -0
- package/build/theme/features/themes/list/services/themes_list_service.js +13 -7
- package/build/theme/hooks/{ensure_theme_meta_data_untouched.js → ensure_theme_meta_data_untouched_hook.js} +2 -2
- package/build/theme/hooks/theme_checksums/ensure_theme_current_checksums_up_to_date_constants.js +1 -2
- package/build/theme/hooks/theme_checksums/{ensure_theme_current_checksums_up_to_date.js → ensure_theme_current_checksums_up_to_date_hook.js} +2 -5
- package/build/theme/hooks/themes_actions/ensure_themes_actions_hook.js +1 -0
- package/build/theme/hooks/themes_actions/ensure_themes_actions_hook_constants.js +1 -1
- package/build/theme/index.js +34 -8
- package/build/theme/utils/directory_validator/directory_validator_constants.js +2 -6
- package/build/theme/utils/directory_validator/directory_validator_utils.js +7 -25
- package/build/ui/box.js +2 -0
- package/build/ui/color_constants.js +30 -0
- package/build/ui/command.js +6 -0
- package/build/ui/file_name.js +6 -0
- package/build/ui/flag.js +6 -0
- package/build/ui/icons/error_icon.js +7 -0
- package/build/ui/icons/info_icon.js +7 -0
- package/build/ui/icons/success_icon.js +7 -0
- package/build/ui/icons/warning_icon.js +7 -0
- package/build/ui/link.js +8 -0
- package/build/ui/list/list.js +10 -0
- package/build/ui/list/list_constants.js +4 -0
- package/build/ui/list/list_item.js +11 -0
- package/build/ui/message_box/error.js +4 -0
- package/build/ui/message_box/info.js +4 -0
- package/build/ui/message_box/message_box.js +11 -0
- package/build/ui/message_box/message_box_constants.js +24 -0
- package/build/ui/message_box/success.js +4 -0
- package/build/ui/message_box/warning.js +4 -0
- package/build/ui/prompts/prompt_confirmation.js +11 -0
- package/build/ui/prompts/prompt_input.js +10 -0
- package/build/ui/table/t_cell.js +7 -0
- package/build/ui/table/t_header.js +9 -0
- package/build/ui/table/t_row.js +5 -0
- package/build/ui/table/table.js +18 -0
- package/build/ui/text.js +2 -0
- package/build/ui/tip.js +9 -0
- package/build/ui/ui_dump/ui_component_box.js +9 -0
- package/build/ui/ui_dump/ui_dump.js +53 -0
- package/build/ui/ui_dump/ui_dump_constants.js +21 -0
- package/build/ui/ui_utils.js +11 -0
- package/build/ui/validation_errors/validation_error_content.js +19 -0
- package/build/ui/validation_errors/validation_errors.js +21 -0
- package/build/ui/validation_errors/validation_errors_utils.js +17 -0
- package/build/utils/checksums/checksums_utils.js +9 -26
- package/build/utils/download_file/download_file_errors_factory.js +1 -1
- package/build/utils/download_file/download_file_utils.js +4 -2
- package/build/utils/fs/errors/stream_read_error.js +1 -1
- package/build/utils/fs/errors/stream_write_error.js +1 -1
- package/build/utils/fs/fs_utils.js +12 -1
- package/build/utils/path_utils.js +18 -2
- package/build/utils/platform_utils.js +3 -0
- package/build/utils/zip/create_zip_utils.js +1 -1
- package/build/utils/zip/errors/create_zip_error.js +1 -1
- package/build/utils/zip/errors/open_zip_error.js +1 -1
- package/oclif.config.js +3 -3
- package/package.json +14 -9
- package/build/cli/commands/files_diff_command.js +0 -174
- package/build/theme/commands/theme_init_command.js +0 -53
- package/build/theme/commands/theme_list_command.js +0 -16
- package/build/theme/commands/theme_pull_command.js +0 -126
- package/build/theme/commands/theme_push_command.js +0 -65
- package/build/theme/commands/theme_show_changes_command.js +0 -60
- package/build/theme/features/theme/directory/theme_directory_utils.js +0 -76
- package/build/theme/features/theme/publish/theme_publish_constants.js +0 -2
- package/build/theme/features/theme/utils/checksums/theme_checksums_utils.js +0 -94
- /package/build/cli/{features → class}/caches/cache_factory.js +0 -0
- /package/build/cli/{features → class}/caches/json_cache/json_cache.js +0 -0
- /package/build/cli/{features → class}/caches/memory_cache.js +0 -0
- /package/build/cli/class/errors/{app_error → app}/app_error.js +0 -0
- /package/build/cli/class/errors/{app_error → app}/app_error_constants.js +0 -0
- /package/build/theme/features/theme/{publish → skinstore}/theme_publish_utils.js +0 -0
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { Args } from '@oclif/core';
|
|
2
|
+
import { BaseThemeCommand } from '../../class/base_theme_command.js';
|
|
3
|
+
import { CLI_AUTH_API_NAME } from '../../../cli/auth/cli_auth_constants.js';
|
|
4
|
+
import { THEME_ACTIONS_API_NAME, THEME_ACTIONS_TYPES } from '../../features/theme/actions/theme_actions_constants.js';
|
|
5
|
+
import { EXECUTION_CONTEXT_API_NAME, EXECUTION_CONTEXTS } from '../../../cli/features/execution_context/execution_context_constants.js';
|
|
6
|
+
import { renderOnce } from '../../../ui/ui_utils.js';
|
|
7
|
+
import { MissingCredentialsError } from '../../../cli/commands/auth/ui/missing_credentials_error.js';
|
|
8
|
+
import React from 'react';
|
|
9
|
+
import { THEME_DELETE_API_NAME } from '../../features/theme/delete/theme_delete_constants.js';
|
|
10
|
+
import { UnpermittedCommandError } from '../ui/unpermitted_command_error.js';
|
|
11
|
+
import { Text } from '../../../ui/text.js';
|
|
12
|
+
import { Info } from '../../../ui/message_box/info.js';
|
|
13
|
+
import { MissingThemeIdError } from '../ui/missing_theme_id_error.js';
|
|
14
|
+
import { Box } from '../../../ui/box.js';
|
|
15
|
+
import { ThemeDeletedSuccessfully } from './ui/theme_deleted_successfully.js';
|
|
16
|
+
import { ThemeDeletionWarning } from './ui/theme_deletion_warning.js';
|
|
17
|
+
import { promptConfirmation } from '../../../ui/prompts/prompt_confirmation.js';
|
|
18
|
+
import { ThemeError } from '../ui/theme_error.js';
|
|
19
|
+
export class ThemeDeleteCommand extends BaseThemeCommand {
|
|
20
|
+
static summary = 'Permanently deletes the specified theme from your store.';
|
|
21
|
+
static description = 'This action cannot be undone, so make sure you really want to remove this theme.\n\nYou can run this command from a specific theme directory (ID not needed) or outside any theme directory (theme ID required).';
|
|
22
|
+
static examples = [
|
|
23
|
+
{
|
|
24
|
+
description: 'This will delete the theme with ID 123 permanently from your store. Make sure you have a backup if needed.',
|
|
25
|
+
command: '<%= config.bin %> <%= command.id %> 123'
|
|
26
|
+
}
|
|
27
|
+
];
|
|
28
|
+
static hidden = true;
|
|
29
|
+
static args = {
|
|
30
|
+
id: Args.string({
|
|
31
|
+
description: 'Theme id',
|
|
32
|
+
name: 'id',
|
|
33
|
+
required: false,
|
|
34
|
+
type: 'string'
|
|
35
|
+
})
|
|
36
|
+
};
|
|
37
|
+
async run() {
|
|
38
|
+
const themeId = this.args.id;
|
|
39
|
+
const cliAuthApi = this.getApi(CLI_AUTH_API_NAME);
|
|
40
|
+
const executionContextApi = this.getApi(EXECUTION_CONTEXT_API_NAME);
|
|
41
|
+
const credentials = cliAuthApi.getCredentials();
|
|
42
|
+
if (!credentials) {
|
|
43
|
+
renderOnce(React.createElement(MissingCredentialsError, null));
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
const executionContext = await executionContextApi.getExecutionContext();
|
|
47
|
+
try {
|
|
48
|
+
let _themeId = themeId;
|
|
49
|
+
if (executionContext.type !== EXECUTION_CONTEXTS.theme && !_themeId) {
|
|
50
|
+
renderOnce(React.createElement(MissingThemeIdError, null,
|
|
51
|
+
React.createElement(Box, { flexDirection: "column", gap: 1 },
|
|
52
|
+
React.createElement(Text, null, "Usage: shoper theme delete [ID]"),
|
|
53
|
+
React.createElement(Text, null, "Please run this command inside a theme directory or provide a theme ID."))));
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
if (executionContext.type === EXECUTION_CONTEXTS.theme)
|
|
57
|
+
_themeId = _themeId ?? executionContext.themeId;
|
|
58
|
+
if (!_themeId) {
|
|
59
|
+
renderOnce(React.createElement(MissingThemeIdError, null));
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
const themeActionsApi = this.getApi(THEME_ACTIONS_API_NAME);
|
|
63
|
+
const deleteAction = themeActionsApi.getThemeAction({
|
|
64
|
+
actionType: THEME_ACTIONS_TYPES.delete,
|
|
65
|
+
themeId: _themeId,
|
|
66
|
+
credentials
|
|
67
|
+
});
|
|
68
|
+
if (!deleteAction) {
|
|
69
|
+
renderOnce(React.createElement(UnpermittedCommandError, { themeId: _themeId, commandName: "delete" }));
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
renderOnce(React.createElement(ThemeDeletionWarning, { themeId: _themeId }));
|
|
73
|
+
const { proceed } = await promptConfirmation('Proceed?');
|
|
74
|
+
if (!proceed) {
|
|
75
|
+
renderOnce(React.createElement(Info, { header: "Theme deletion was cancelled." }));
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
const themeDeleteApi = this.getApi(THEME_DELETE_API_NAME);
|
|
79
|
+
const resp = await themeDeleteApi.deleteTheme({
|
|
80
|
+
actionData: deleteAction.data,
|
|
81
|
+
credentials
|
|
82
|
+
});
|
|
83
|
+
themeActionsApi.removeThemeActions({
|
|
84
|
+
themeId: _themeId,
|
|
85
|
+
credentials
|
|
86
|
+
});
|
|
87
|
+
if (!resp?.isSuccess)
|
|
88
|
+
return;
|
|
89
|
+
renderOnce(React.createElement(ThemeDeletedSuccessfully, null));
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
catch (err) {
|
|
93
|
+
renderOnce(React.createElement(ThemeError, { err: err, executionContext: executionContext }));
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { Text } from '../../../../ui/text.js';
|
|
3
|
+
import { List } from '../../../../ui/list/list.js';
|
|
4
|
+
import { Tip } from '../../../../ui/tip.js';
|
|
5
|
+
import { Success } from '../../../../ui/message_box/success.js';
|
|
6
|
+
export const ThemeDeletedSuccessfully = () => {
|
|
7
|
+
return (React.createElement(Success, { header: "Success: Theme was deleted from the store." },
|
|
8
|
+
React.createElement(Text, null, "The theme is no longer available in your store panel, but its files remain locally."),
|
|
9
|
+
React.createElement(Tip, null, "If you want to reuse this theme:"),
|
|
10
|
+
React.createElement(List, { items: [
|
|
11
|
+
'create a new theme using shoper theme init [id]',
|
|
12
|
+
'copy the local files into the new theme folder',
|
|
13
|
+
'and push it using shoper theme push'
|
|
14
|
+
] })));
|
|
15
|
+
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { Warning } from '../../../../ui/message_box/warning.js';
|
|
2
|
+
import { Text } from '../../../../ui/text.js';
|
|
3
|
+
import React from 'react';
|
|
4
|
+
export const ThemeDeletionWarning = ({ themeId }) => {
|
|
5
|
+
return (React.createElement(Warning, { header: `Warning: This will permanently delete the theme (ID: ${themeId}) from your store and cannot be undone.` },
|
|
6
|
+
React.createElement(Text, null,
|
|
7
|
+
"Your local files ",
|
|
8
|
+
React.createElement(Text, { bold: true }, "will not be removed"),
|
|
9
|
+
". You\u2019ll still be able to access and modify them.")));
|
|
10
|
+
};
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { BaseThemeCommand } from '../../class/base_theme_command.js';
|
|
2
|
+
import { CLI_AUTH_API_NAME } from '../../../cli/auth/cli_auth_constants.js';
|
|
3
|
+
import { EXECUTION_CONTEXT_API_NAME, EXECUTION_CONTEXTS } from '../../../cli/features/execution_context/execution_context_constants.js';
|
|
4
|
+
import { renderOnce } from '../../../ui/ui_utils.js';
|
|
5
|
+
import { MissingCredentialsError } from '../../../cli/commands/auth/ui/missing_credentials_error.js';
|
|
6
|
+
import React from 'react';
|
|
7
|
+
import { THEMES_LIST_API_NAME } from '../../features/themes/list/themes_list_constants.js';
|
|
8
|
+
import { Args } from '@oclif/core';
|
|
9
|
+
import { ThemeMetaDataUtils } from '../../features/theme/utils/meta_data/theme_meta_data_utils.js';
|
|
10
|
+
import { MissingThemeIdError } from '../ui/missing_theme_id_error.js';
|
|
11
|
+
import { Text } from '../../../ui/text.js';
|
|
12
|
+
import { Error } from '../../../ui/message_box/error.js';
|
|
13
|
+
import { Table } from '../../../ui/table/table.js';
|
|
14
|
+
import { ThemeInfoCommandUtils } from './theme_info_command_utils.js';
|
|
15
|
+
import { InvalidThemeIdError } from '../ui/invalid_theme_id.js';
|
|
16
|
+
import { Box } from '../../../ui/box.js';
|
|
17
|
+
import { ThemeError } from '../ui/theme_error.js';
|
|
18
|
+
export class ThemeInfoCommand extends BaseThemeCommand {
|
|
19
|
+
static summary = 'View details about the current theme.';
|
|
20
|
+
static description = 'Displays key information about the theme you’re working on — including its ID, name, description (if provided), and status.\n\nYou can run this command from a specific theme directory (ID not needed) or outside any theme directory (theme ID required).';
|
|
21
|
+
static examples = [
|
|
22
|
+
{
|
|
23
|
+
description: 'The command will display the theme ID, name, description, and status.',
|
|
24
|
+
command: '<%= config.bin %> <%= command.id %> 2'
|
|
25
|
+
}
|
|
26
|
+
];
|
|
27
|
+
static args = {
|
|
28
|
+
id: Args.string({
|
|
29
|
+
description: 'Theme id',
|
|
30
|
+
name: 'id',
|
|
31
|
+
type: 'string'
|
|
32
|
+
})
|
|
33
|
+
};
|
|
34
|
+
async run() {
|
|
35
|
+
const themeId = this.args.id;
|
|
36
|
+
const cliAuthApi = this.getApi(CLI_AUTH_API_NAME);
|
|
37
|
+
const executionContextApi = this.getApi(EXECUTION_CONTEXT_API_NAME);
|
|
38
|
+
const themesListApi = this.getApi(THEMES_LIST_API_NAME);
|
|
39
|
+
const credentials = cliAuthApi.getCredentials();
|
|
40
|
+
if (!credentials) {
|
|
41
|
+
renderOnce(React.createElement(MissingCredentialsError, null));
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
const executionContext = await executionContextApi.getExecutionContext();
|
|
45
|
+
try {
|
|
46
|
+
let _themeId = themeId;
|
|
47
|
+
if (executionContext.type !== EXECUTION_CONTEXTS.theme && !_themeId) {
|
|
48
|
+
renderOnce(React.createElement(MissingThemeIdError, null,
|
|
49
|
+
React.createElement(Box, { flexDirection: "column", gap: 1 },
|
|
50
|
+
React.createElement(Text, null, "Usage: shoper theme info [ID]"),
|
|
51
|
+
React.createElement(Text, null, "Please run this command inside a theme directory or provide a theme ID."))));
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
if (executionContext.type === EXECUTION_CONTEXTS.theme) {
|
|
55
|
+
await ThemeMetaDataUtils.ensureThemeWorkUrlMatch(executionContext.themeRootDir, credentials.shopUrl);
|
|
56
|
+
_themeId = themeId ?? executionContext.themeId;
|
|
57
|
+
}
|
|
58
|
+
if (!_themeId) {
|
|
59
|
+
renderOnce(React.createElement(MissingThemeIdError, null));
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
const theme = await themesListApi.getTheme({
|
|
63
|
+
themeId: _themeId,
|
|
64
|
+
shopUrl: credentials.shopUrl
|
|
65
|
+
});
|
|
66
|
+
if (!theme) {
|
|
67
|
+
renderOnce(_themeId ? React.createElement(InvalidThemeIdError, { themeId: _themeId }) : React.createElement(Error, { header: "Error: Theme information is not available." }));
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
this._displayThemeInfo(theme);
|
|
71
|
+
}
|
|
72
|
+
catch (err) {
|
|
73
|
+
renderOnce(React.createElement(ThemeError, { err: err, executionContext: executionContext }));
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
_displayThemeInfo(theme) {
|
|
77
|
+
renderOnce(React.createElement(Table, { data: ThemeInfoCommandUtils.mapThemeMetaDataToTableData(theme) }));
|
|
78
|
+
}
|
|
79
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { Text } from '../../../ui/text.js';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
export class ThemeInfoCommandUtils {
|
|
4
|
+
static mapThemeMetaDataToTableData(theme) {
|
|
5
|
+
return {
|
|
6
|
+
headers: [
|
|
7
|
+
{
|
|
8
|
+
content: 'Theme Info'
|
|
9
|
+
}
|
|
10
|
+
],
|
|
11
|
+
rows: [
|
|
12
|
+
[
|
|
13
|
+
React.createElement(Text, null,
|
|
14
|
+
React.createElement(Text, { bold: true }, "ID:"),
|
|
15
|
+
" ",
|
|
16
|
+
theme.skinId)
|
|
17
|
+
],
|
|
18
|
+
[
|
|
19
|
+
React.createElement(Text, null,
|
|
20
|
+
React.createElement(Text, { bold: true }, "Name:"),
|
|
21
|
+
" ",
|
|
22
|
+
theme.title)
|
|
23
|
+
],
|
|
24
|
+
[
|
|
25
|
+
React.createElement(Text, null,
|
|
26
|
+
React.createElement(Text, { bold: true }, "Description:"),
|
|
27
|
+
" ",
|
|
28
|
+
theme.description ? theme.description : '-')
|
|
29
|
+
],
|
|
30
|
+
[
|
|
31
|
+
React.createElement(Text, null,
|
|
32
|
+
React.createElement(Text, { bold: true }, "Status: "),
|
|
33
|
+
": ",
|
|
34
|
+
theme.isActive ? 'Active' : 'Inactive')
|
|
35
|
+
]
|
|
36
|
+
]
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { BaseThemeCommand } from '../../class/base_theme_command.js';
|
|
2
|
+
import { Args } from '@oclif/core';
|
|
3
|
+
import { THEME_INIT_API_NAME } from '../../features/theme/init/theme_init_constants.js';
|
|
4
|
+
import { CLI_AUTH_API_NAME } from '../../../cli/auth/cli_auth_constants.js';
|
|
5
|
+
import ora from 'ora';
|
|
6
|
+
import { EXECUTION_CONTEXT_API_NAME, EXECUTION_CONTEXTS } from '../../../cli/features/execution_context/execution_context_constants.js';
|
|
7
|
+
import { THEME_ACTIONS_API_NAME, THEME_ACTIONS_TYPES } from '../../features/theme/actions/theme_actions_constants.js';
|
|
8
|
+
import { renderOnce } from '../../../ui/ui_utils.js';
|
|
9
|
+
import { ThemeDirectoryContextError } from '../ui/theme_directory_context_error.js';
|
|
10
|
+
import React from 'react';
|
|
11
|
+
import { UnpermittedCommandError } from '../ui/unpermitted_command_error.js';
|
|
12
|
+
import { MissingCredentialsError } from '../../../cli/commands/auth/ui/missing_credentials_error.js';
|
|
13
|
+
import { ThemeCreatedSuccess } from './ui/theme_created_success.js';
|
|
14
|
+
import { Text } from '../../../ui/text.js';
|
|
15
|
+
import { MissingThemeIdError } from '../ui/missing_theme_id_error.js';
|
|
16
|
+
import { Box } from '../../../ui/box.js';
|
|
17
|
+
import { ThemeError } from '../ui/theme_error.js';
|
|
18
|
+
export class ThemeInitCommand extends BaseThemeCommand {
|
|
19
|
+
static summary = 'Creates a copy of an existing theme by duplicating it.';
|
|
20
|
+
static description = 'The new theme will be named after the source theme with a "Copy" suffix and an incremental number.\n\nYou must run this command from the root directory (not inside any theme folder) and provide the ID of the theme you want to duplicate.';
|
|
21
|
+
static examples = [
|
|
22
|
+
{
|
|
23
|
+
description: 'This will duplicate the theme with ID 123 (named SourceTheme Copy 3) and create a new theme named "SourceTheme Copy 4".\nUse this as a starting point for your customizations.\n',
|
|
24
|
+
command: '<%= config.bin %> <%= command.id %> 123'
|
|
25
|
+
}
|
|
26
|
+
];
|
|
27
|
+
static args = {
|
|
28
|
+
id: Args.string({
|
|
29
|
+
description: 'Theme id',
|
|
30
|
+
name: 'id',
|
|
31
|
+
required: false,
|
|
32
|
+
type: 'string'
|
|
33
|
+
})
|
|
34
|
+
};
|
|
35
|
+
async run() {
|
|
36
|
+
const themeId = this.args.id;
|
|
37
|
+
const cliAuthApi = this.getApi(CLI_AUTH_API_NAME);
|
|
38
|
+
const themeInitApi = this.getApi(THEME_INIT_API_NAME);
|
|
39
|
+
const themeActionsApi = this.getApi(THEME_ACTIONS_API_NAME);
|
|
40
|
+
const executionContextApi = this.getApi(EXECUTION_CONTEXT_API_NAME);
|
|
41
|
+
const credentials = cliAuthApi.getCredentials();
|
|
42
|
+
if (!credentials) {
|
|
43
|
+
renderOnce(React.createElement(MissingCredentialsError, null));
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
const executionContext = await executionContextApi.getExecutionContext();
|
|
47
|
+
if (executionContext.type === EXECUTION_CONTEXTS.theme) {
|
|
48
|
+
renderOnce(React.createElement(ThemeDirectoryContextError, null));
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
if (!themeId) {
|
|
52
|
+
this._renderMissingThemeIdError();
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
let spinner;
|
|
56
|
+
try {
|
|
57
|
+
const copyAction = themeActionsApi.getThemeAction({
|
|
58
|
+
actionType: THEME_ACTIONS_TYPES.copy,
|
|
59
|
+
themeId,
|
|
60
|
+
credentials
|
|
61
|
+
});
|
|
62
|
+
if (!copyAction) {
|
|
63
|
+
renderOnce(React.createElement(UnpermittedCommandError, { themeId: themeId, commandName: "init" }));
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
spinner = ora('Creating theme...').start();
|
|
67
|
+
const { themeId: createdThemeId, themeName } = await themeInitApi.initTheme({ action: copyAction, credentials });
|
|
68
|
+
spinner.stop();
|
|
69
|
+
renderOnce(React.createElement(ThemeCreatedSuccess, { createdThemeId: createdThemeId, themeName: themeName }));
|
|
70
|
+
}
|
|
71
|
+
catch (err) {
|
|
72
|
+
spinner?.stop();
|
|
73
|
+
renderOnce(React.createElement(ThemeError, { err: err, executionContext: executionContext }));
|
|
74
|
+
//TODO analyze why on nodejs v22 this command does not exit properly, even all promises are resolved
|
|
75
|
+
this.exit();
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
_renderMissingThemeIdError() {
|
|
79
|
+
renderOnce(React.createElement(MissingThemeIdError, null,
|
|
80
|
+
React.createElement(Box, { flexDirection: "column", gap: 1 },
|
|
81
|
+
React.createElement(Text, null, "Usage: shoper theme init [ID]"),
|
|
82
|
+
React.createElement(Text, null, "This command creates a copy of an existing theme based on its ID.To proceed, you must provide the ID of the theme you want to duplicate."))));
|
|
83
|
+
}
|
|
84
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { Box } from '../../../../ui/box.js';
|
|
2
|
+
import { Text } from '../../../../ui/text.js';
|
|
3
|
+
import { Success } from '../../../../ui/message_box/success.js';
|
|
4
|
+
import React from 'react';
|
|
5
|
+
export const ThemeCreatedSuccess = ({ createdThemeId, themeName }) => {
|
|
6
|
+
return (React.createElement(Success, { header: "Success: Theme was created!" },
|
|
7
|
+
React.createElement(Box, { gap: 1, flexDirection: "column" },
|
|
8
|
+
React.createElement(Text, null,
|
|
9
|
+
"Theme \"",
|
|
10
|
+
themeName,
|
|
11
|
+
"\" was created (ID: ",
|
|
12
|
+
createdThemeId,
|
|
13
|
+
")."),
|
|
14
|
+
React.createElement(Text, null, "You can now customize your new theme."))));
|
|
15
|
+
};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { BaseThemeCommand } from '../../class/base_theme_command.js';
|
|
2
|
+
import { THEMES_LIST_API_NAME } from '../../features/themes/list/themes_list_constants.js';
|
|
3
|
+
import { CLI_AUTH_API_NAME } from '../../../cli/auth/cli_auth_constants.js';
|
|
4
|
+
import { renderOnce } from '../../../ui/ui_utils.js';
|
|
5
|
+
import { Info } from '../../../ui/message_box/info.js';
|
|
6
|
+
import React from 'react';
|
|
7
|
+
import { Table } from '../../../ui/table/table.js';
|
|
8
|
+
import { ThemeListCommandUtils } from './theme_list_command_utils.js';
|
|
9
|
+
export class ThemeListCommand extends BaseThemeCommand {
|
|
10
|
+
static summary = 'Displays a list of all themes available in your store, including their IDs, names, and statuses (active/inactive).';
|
|
11
|
+
static description = 'Use this to check which theme is now active in your store.';
|
|
12
|
+
async run() {
|
|
13
|
+
const ThemesListApi = this.getApi(THEMES_LIST_API_NAME);
|
|
14
|
+
const cliAuthApi = this.getApi(CLI_AUTH_API_NAME);
|
|
15
|
+
const credentials = cliAuthApi.getCredentials();
|
|
16
|
+
if (!credentials)
|
|
17
|
+
this.error('Credentials not found. Please authorize first.');
|
|
18
|
+
const themes = await ThemesListApi.getThemes(credentials);
|
|
19
|
+
if (!themes?.length) {
|
|
20
|
+
renderOnce(React.createElement(Info, { header: "No themes found for this store." }));
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
renderOnce(React.createElement(Table, { data: ThemeListCommandUtils.mapThemeListToTableData(themes) }));
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { Text } from '../../../ui/text.js';
|
|
3
|
+
export class ThemeListCommandUtils {
|
|
4
|
+
static mapThemeListToTableData(themes) {
|
|
5
|
+
return {
|
|
6
|
+
headers: [
|
|
7
|
+
{
|
|
8
|
+
content: 'Theme ID',
|
|
9
|
+
width: 10
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
content: 'Theme Name',
|
|
13
|
+
width: 70
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
content: 'Status',
|
|
17
|
+
width: 10
|
|
18
|
+
}
|
|
19
|
+
],
|
|
20
|
+
rows: themes.map((theme) => [
|
|
21
|
+
React.createElement(Text, null, theme.skinId),
|
|
22
|
+
React.createElement(Text, null, theme.title),
|
|
23
|
+
React.createElement(Text, null, theme.isActive ? 'active' : 'inactive')
|
|
24
|
+
])
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { Args } from '@oclif/core';
|
|
2
|
+
import { BaseThemeCommand } from '../../class/base_theme_command.js';
|
|
3
|
+
import { CLI_AUTH_API_NAME } from '../../../cli/auth/cli_auth_constants.js';
|
|
4
|
+
import { THEME_ACTIONS_API_NAME, THEME_ACTIONS_TYPES } from '../../features/theme/actions/theme_actions_constants.js';
|
|
5
|
+
import { EXECUTION_CONTEXT_API_NAME, EXECUTION_CONTEXTS } from '../../../cli/features/execution_context/execution_context_constants.js';
|
|
6
|
+
import { renderOnce } from '../../../ui/ui_utils.js';
|
|
7
|
+
import { MissingCredentialsError } from '../../../cli/commands/auth/ui/missing_credentials_error.js';
|
|
8
|
+
import React from 'react';
|
|
9
|
+
import { MissingThemeIdError } from '../ui/missing_theme_id_error.js';
|
|
10
|
+
import { THEME_SKINSTORE_API_NAME } from '../../features/theme/skinstore/theme_publish_constants.js';
|
|
11
|
+
import { Form } from '../../../cli/features/controls/ui/form.js';
|
|
12
|
+
import { ThemeError } from '../ui/theme_error.js';
|
|
13
|
+
export class ThemePublishCommand extends BaseThemeCommand {
|
|
14
|
+
static summary = 'Permanently deletes the specified theme from your store.';
|
|
15
|
+
static description = 'This action cannot be undone, so make sure you really want to remove this theme.\n\nYou can run this command from a specific theme directory (ID not needed) or outside any theme directory (theme ID required).';
|
|
16
|
+
static examples = [
|
|
17
|
+
{
|
|
18
|
+
description: 'This will delete the theme with ID 123 permanently from your store. Make sure you have a backup if needed.',
|
|
19
|
+
command: '<%= config.bin %> <%= command.id %> 123'
|
|
20
|
+
}
|
|
21
|
+
];
|
|
22
|
+
static hidden = true;
|
|
23
|
+
static args = {
|
|
24
|
+
id: Args.string({
|
|
25
|
+
description: 'Theme id',
|
|
26
|
+
name: 'id',
|
|
27
|
+
required: false,
|
|
28
|
+
type: 'string'
|
|
29
|
+
})
|
|
30
|
+
};
|
|
31
|
+
async run() {
|
|
32
|
+
const themeId = this.args.id;
|
|
33
|
+
const cliAuthApi = this.getApi(CLI_AUTH_API_NAME);
|
|
34
|
+
const executionContextApi = this.getApi(EXECUTION_CONTEXT_API_NAME);
|
|
35
|
+
const credentials = cliAuthApi.getCredentials();
|
|
36
|
+
if (!credentials) {
|
|
37
|
+
renderOnce(React.createElement(MissingCredentialsError, null));
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
const executionContext = await executionContextApi.getExecutionContext();
|
|
41
|
+
try {
|
|
42
|
+
let _themeId = themeId;
|
|
43
|
+
if (executionContext.type !== EXECUTION_CONTEXTS.theme && !_themeId) {
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
if (executionContext.type === EXECUTION_CONTEXTS.theme) {
|
|
47
|
+
_themeId = _themeId ?? executionContext.themeId;
|
|
48
|
+
}
|
|
49
|
+
if (!_themeId) {
|
|
50
|
+
renderOnce(React.createElement(MissingThemeIdError, null));
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
const themeActionsApi = this.getApi(THEME_ACTIONS_API_NAME);
|
|
54
|
+
const themeSkinstoreApi = this.getApi(THEME_SKINSTORE_API_NAME);
|
|
55
|
+
const pushAction = themeActionsApi.getThemeAction({
|
|
56
|
+
actionType: THEME_ACTIONS_TYPES.publishForm,
|
|
57
|
+
themeId: _themeId,
|
|
58
|
+
credentials
|
|
59
|
+
});
|
|
60
|
+
const controls = await themeSkinstoreApi.getPublishFormData({
|
|
61
|
+
actionData: pushAction.data,
|
|
62
|
+
credentials
|
|
63
|
+
});
|
|
64
|
+
Form({
|
|
65
|
+
controls,
|
|
66
|
+
onSubmit: (formData) => {
|
|
67
|
+
console.log('formData', formData);
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
catch (err) {
|
|
72
|
+
renderOnce(React.createElement(ThemeError, { err: err, executionContext: executionContext }));
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import { Args, Flags } from '@oclif/core';
|
|
2
|
+
import { BaseThemeCommand } from '../../class/base_theme_command.js';
|
|
3
|
+
import { THEME_FETCH_API_NAME, THEME_FETCH_TYPES } from '../../features/theme/fetch/theme_fetch_constants.js';
|
|
4
|
+
import { CLI_AUTH_API_NAME } from '../../../cli/auth/cli_auth_constants.js';
|
|
5
|
+
import { EXECUTION_CONTEXT_API_NAME, EXECUTION_CONTEXTS } from '../../../cli/features/execution_context/execution_context_constants.js';
|
|
6
|
+
import ora from 'ora';
|
|
7
|
+
import { THEME_MERGE_API_NAME } from '../../features/theme/merge/theme_merge_constants.js';
|
|
8
|
+
import tmp from 'tmp-promise';
|
|
9
|
+
import { join } from '../../../utils/path_utils.js';
|
|
10
|
+
import { THEME_ACTIONS_API_NAME, THEME_ACTIONS_TYPES } from '../../features/theme/actions/theme_actions_constants.js';
|
|
11
|
+
import process from 'node:process';
|
|
12
|
+
import { ThemeResourcesWithIdDirectoryUtils } from '../../features/theme/utils/resources/theme_resources_with_id_directory_utils.js';
|
|
13
|
+
import { renderOnce } from '../../../ui/ui_utils.js';
|
|
14
|
+
import { MissingCredentialsError } from '../../../cli/commands/auth/ui/missing_credentials_error.js';
|
|
15
|
+
import React from 'react';
|
|
16
|
+
import { MissingThemeIdError } from '../ui/missing_theme_id_error.js';
|
|
17
|
+
import { Text } from '../../../ui/text.js';
|
|
18
|
+
import { UnpermittedCommandError } from '../ui/unpermitted_command_error.js';
|
|
19
|
+
import { ThemePullIdMismatchError } from './ui/theme_pull_id_mismatch_error.js';
|
|
20
|
+
import { ThemePullUnpublishedChangesWarning } from './ui/theme_pull_unpublished_changes_warning.js';
|
|
21
|
+
import { promptConfirmation } from '../../../ui/prompts/prompt_confirmation.js';
|
|
22
|
+
import { ThemePulledSuccess } from './ui/theme_pulled_success.js';
|
|
23
|
+
import { Info } from '../../../ui/message_box/info.js';
|
|
24
|
+
import { directoryExists } from '../../../utils/fs/fs_utils.js';
|
|
25
|
+
import { ThemeMetaDataUtils } from '../../features/theme/utils/meta_data/theme_meta_data_utils.js';
|
|
26
|
+
import { ThemeChecksums } from '../../class/checksums/theme_checksums.js';
|
|
27
|
+
import { ThemeError } from '../ui/theme_error.js'; //TODO jak jest error w pullu wowczas usuwamy docelowo pociagniety skin
|
|
28
|
+
//TODO jak jest error w pullu wowczas usuwamy docelowo pociagniety skin
|
|
29
|
+
export class ThemePullCommand extends BaseThemeCommand {
|
|
30
|
+
static summary = 'Downloads the current version of your theme from the store and overwrites your local theme files.';
|
|
31
|
+
static description = 'Make sure to back up any local modifications you wish to keep.\n\nYou can run this command from a specific theme directory (ID not needed) or outside any theme directory (theme ID required).\n\nThis keeps your local theme the same as the version in your store.';
|
|
32
|
+
static examples = [
|
|
33
|
+
{
|
|
34
|
+
description: 'This will update your local theme files with the current version from the store.',
|
|
35
|
+
command: '<%= config.bin %> <%= command.id %>'
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
description: 'This will download the theme with ID "2" from the store.',
|
|
39
|
+
command: '<%= config.bin %> <%= command.id %> 2'
|
|
40
|
+
}
|
|
41
|
+
];
|
|
42
|
+
static args = {
|
|
43
|
+
id: Args.string({
|
|
44
|
+
description: 'Theme id',
|
|
45
|
+
name: 'id',
|
|
46
|
+
type: 'string'
|
|
47
|
+
})
|
|
48
|
+
};
|
|
49
|
+
static flags = {
|
|
50
|
+
type: Flags.string({
|
|
51
|
+
default: THEME_FETCH_TYPES.full,
|
|
52
|
+
description: 'Type of theme to pull',
|
|
53
|
+
options: Object.values(THEME_FETCH_TYPES)
|
|
54
|
+
})
|
|
55
|
+
};
|
|
56
|
+
#spinner;
|
|
57
|
+
async run() {
|
|
58
|
+
const themeId = this.args.id;
|
|
59
|
+
const cliAuthApi = this.getApi(CLI_AUTH_API_NAME);
|
|
60
|
+
const themeFetchApi = this.getApi(THEME_FETCH_API_NAME);
|
|
61
|
+
const executionContextApi = this.getApi(EXECUTION_CONTEXT_API_NAME);
|
|
62
|
+
const themeActionsApi = this.getApi(THEME_ACTIONS_API_NAME);
|
|
63
|
+
const credentials = cliAuthApi.getCredentials();
|
|
64
|
+
if (!credentials) {
|
|
65
|
+
renderOnce(React.createElement(MissingCredentialsError, null));
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
const executionContext = await executionContextApi.getExecutionContext();
|
|
69
|
+
let _themeId = themeId;
|
|
70
|
+
try {
|
|
71
|
+
if (executionContext.type !== EXECUTION_CONTEXTS.theme) {
|
|
72
|
+
await this._pullNewTheme({
|
|
73
|
+
themeId,
|
|
74
|
+
themeFetchApi,
|
|
75
|
+
themeActionsApi,
|
|
76
|
+
credentials,
|
|
77
|
+
fetchType: this.flags.type
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
if (executionContext.type === EXECUTION_CONTEXTS.theme) {
|
|
81
|
+
_themeId = themeId ? themeId : executionContext.themeId;
|
|
82
|
+
await this._pullThemeIntoExistingOne({
|
|
83
|
+
themeId: _themeId,
|
|
84
|
+
themeFetchApi,
|
|
85
|
+
themeActionsApi,
|
|
86
|
+
fetchType: this.flags.type,
|
|
87
|
+
executionContext,
|
|
88
|
+
credentials
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
catch (err) {
|
|
93
|
+
this.#spinner?.stop();
|
|
94
|
+
renderOnce(React.createElement(ThemeError, { err: err, executionContext: executionContext }));
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
_renderUnpermittedCommandError(themeId) {
|
|
98
|
+
renderOnce(React.createElement(UnpermittedCommandError, { themeId: themeId, commandName: "pull" }));
|
|
99
|
+
}
|
|
100
|
+
async _pullNewTheme({ themeId, themeFetchApi, themeActionsApi, credentials, fetchType }) {
|
|
101
|
+
if (themeId === undefined) {
|
|
102
|
+
renderOnce(React.createElement(MissingThemeIdError, null,
|
|
103
|
+
React.createElement(Text, null, "Usage: shoper theme pull [ID]")));
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
const pullAction = themeActionsApi.getThemeAction({
|
|
107
|
+
actionType: THEME_ACTIONS_TYPES.pull,
|
|
108
|
+
themeId,
|
|
109
|
+
credentials
|
|
110
|
+
});
|
|
111
|
+
if (!pullAction) {
|
|
112
|
+
this._renderUnpermittedCommandError(themeId);
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
this.#spinner = ora('Pulling theme...').start();
|
|
116
|
+
await themeFetchApi.fetchTheme({
|
|
117
|
+
credentials,
|
|
118
|
+
action: pullAction,
|
|
119
|
+
config: {
|
|
120
|
+
dist: process.cwd(),
|
|
121
|
+
fetchType
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
this.#spinner.stop();
|
|
125
|
+
renderOnce(React.createElement(ThemePulledSuccess, null));
|
|
126
|
+
}
|
|
127
|
+
async _pullThemeIntoExistingOne({ themeId, themeFetchApi, themeActionsApi, fetchType, executionContext, credentials }) {
|
|
128
|
+
//TODO move these logi somvewhere else?
|
|
129
|
+
await ThemeMetaDataUtils.ensureThemeWorkUrlMatch(executionContext.themeRootDir, credentials.shopUrl);
|
|
130
|
+
const pullAction = themeActionsApi.getThemeAction({
|
|
131
|
+
actionType: THEME_ACTIONS_TYPES.pull,
|
|
132
|
+
themeId,
|
|
133
|
+
credentials
|
|
134
|
+
});
|
|
135
|
+
if (!pullAction) {
|
|
136
|
+
this._renderUnpermittedCommandError(themeId);
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
if (executionContext.themeId !== themeId) {
|
|
140
|
+
renderOnce(React.createElement(ThemePullIdMismatchError, { providedThemeId: themeId, currentThemeId: executionContext.themeId }));
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
const themeMergeApi = this.getApi(THEME_MERGE_API_NAME);
|
|
144
|
+
const { path: tmpDir } = await tmp.dir({ unsafeCleanup: true });
|
|
145
|
+
this.#spinner = ora('Pulling theme...').start();
|
|
146
|
+
const { name } = await themeFetchApi.fetchTheme({
|
|
147
|
+
credentials,
|
|
148
|
+
action: pullAction,
|
|
149
|
+
config: {
|
|
150
|
+
fetchType,
|
|
151
|
+
dist: tmpDir
|
|
152
|
+
}
|
|
153
|
+
});
|
|
154
|
+
const localModulesPath = join(executionContext.themeRootDir, 'modules');
|
|
155
|
+
const fetchedThemePath = join(tmpDir, name);
|
|
156
|
+
const fetchedModulesPath = join(fetchedThemePath, 'modules');
|
|
157
|
+
if ((await directoryExists(localModulesPath)) && (await directoryExists(fetchedModulesPath)))
|
|
158
|
+
await ThemeResourcesWithIdDirectoryUtils.updateDirectoryNamesOfResourcesWithIdAccordingToLocalThemeNames(localModulesPath, fetchedModulesPath, fetchedThemePath);
|
|
159
|
+
this.#spinner.stop();
|
|
160
|
+
const changes = await themeMergeApi.getChangesBetweenThemes(fetchedThemePath, executionContext.themeRootDir);
|
|
161
|
+
const themeChecksums = new ThemeChecksums(executionContext.themeRootDir);
|
|
162
|
+
if (changes.length && (await themeChecksums.hasThemeBeenModified())) {
|
|
163
|
+
renderOnce(React.createElement(ThemePullUnpublishedChangesWarning, { changes: changes }));
|
|
164
|
+
const { proceed } = await promptConfirmation('Do you want to continue and overwrite local changes?');
|
|
165
|
+
if (!proceed) {
|
|
166
|
+
renderOnce(React.createElement(Info, null,
|
|
167
|
+
React.createElement(Text, null, "Pull operation cancelled. Your local changes are safe.")));
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
await themeMergeApi.applyChanges(join(tmpDir, name), executionContext.themeRootDir, changes);
|
|
172
|
+
renderOnce(React.createElement(ThemePulledSuccess, null));
|
|
173
|
+
}
|
|
174
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { Error } from '../../../../ui/message_box/error.js';
|
|
3
|
+
import { Text } from '../../../../ui/text.js';
|
|
4
|
+
export const ThemePullIdMismatchError = ({ providedThemeId, currentThemeId }) => {
|
|
5
|
+
return (React.createElement(Error, { header: `Error: Provided ID (${providedThemeId}) doesn't match the current theme directory (${currentThemeId}).` },
|
|
6
|
+
React.createElement(Text, null, "Please move outside the theme directory and run the command again with the correct ID.")));
|
|
7
|
+
};
|