@shoper/cli 0.2.0 → 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/class/base_command.js +14 -0
- package/build/cli/commands/auth/cli_auth_remove_token_command.js +1 -3
- package/build/cli/commands/auth/cli_auth_switch_token_command.js +1 -2
- package/build/cli/commands/commands_constants.js +5 -4
- package/build/cli/core/cli_setup.js +33 -18
- package/build/cli/hooks/authorization/ensure_authorization_hook_constants.js +0 -1
- 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 +3 -7
- 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 +4 -18
- package/build/theme/commands/info/theme_info_command.js +3 -18
- package/build/theme/commands/init/theme_init_command.js +4 -17
- package/build/theme/commands/publish/theme_publish_command.js +4 -20
- package/build/theme/commands/pull/theme_pull_command.js +6 -26
- package/build/theme/commands/push/theme_push_command.js +17 -39
- package/build/theme/commands/theme_commands_constants.js +9 -9
- package/build/theme/commands/theme_verify_command.js +59 -18
- package/build/theme/commands/ui/theme_error.js +29 -0
- package/build/theme/features/theme/actions/theme_actions_constants.js +2 -1
- package/build/theme/features/theme/actions/theme_actions_utils.js +41 -1
- package/build/theme/features/theme/init/theme_init_initializer.js +3 -3
- package/build/theme/features/theme/push/service/theme_push_service.js +42 -164
- package/build/theme/features/theme/push/theme_push_initializer.js +1 -4
- 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 +38 -2
- package/build/theme/features/theme/utils/theme_images_utils.js +0 -18
- 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/hooks/theme_checksums/ensure_theme_current_checksums_up_to_date_constants.js +0 -1
- package/build/theme/hooks/themes_actions/ensure_themes_actions_hook_constants.js +1 -2
- package/build/theme/index.js +23 -3
- package/oclif.config.js +1 -1
- package/package.json +2 -2
- package/build/cli/commands/files_diff_command.js +0 -174
- package/build/theme/commands/theme_show_changes_command.js +0 -61
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { BaseThemeCommand } from '../../class/base_theme_command.js';
|
|
2
2
|
import { CLI_AUTH_API_NAME } from '../../../cli/auth/cli_auth_constants.js';
|
|
3
3
|
import { EXECUTION_CONTEXT_API_NAME, EXECUTION_CONTEXTS } from '../../../cli/features/execution_context/execution_context_constants.js';
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
4
|
+
import { THEME_ACTIONS_API_NAME, THEME_ACTIONS_TYPES } from '../../features/theme/actions/theme_actions_constants.js';
|
|
5
|
+
import { THEME_PUSH_API_NAME } from '../../features/theme/push/theme_push_constants.js';
|
|
6
6
|
import { ThemeMetaDataUtils } from '../../features/theme/utils/meta_data/theme_meta_data_utils.js';
|
|
7
7
|
import { mapChecksumToTree } from '../../../utils/checksums/checksums_utils.js';
|
|
8
8
|
import { mapToPermissionsTree } from '../../utils/directory_validator/directory_validator_utils.js';
|
|
@@ -13,19 +13,18 @@ import { OutsideOfThemeDirectoryContextError } from '../ui/ouside_of_theme_direc
|
|
|
13
13
|
import { MissingCredentialsError } from '../../../cli/commands/auth/ui/missing_credentials_error.js';
|
|
14
14
|
import { ThemePushedSuccess } from './ui/theme_pushed_success.js';
|
|
15
15
|
import ora from 'ora';
|
|
16
|
-
import { MissingThemeFiles } from '../ui/missing_theme_files.js';
|
|
17
|
-
import { SHOPER_THEME_METADATA_DIR } from '../../constants/directory_contstants.js';
|
|
18
|
-
import { THEME_FILES_STRUCTURE_FILE_NAME } from '../../features/theme/theme_constants.js';
|
|
19
16
|
import { ThemePushSkipInfo } from './ui/theme_push_skip_into.js';
|
|
20
17
|
import { ThemeUnpermittedActionsError } from './ui/theme_unpermitted_actions_error.js';
|
|
21
|
-
import { Error } from '../../../ui/message_box/error.js';
|
|
22
|
-
import { Text } from '../../../ui/text.js';
|
|
23
|
-
import { ThemeWorkUrlMismatch } from '../ui/theme_work_url_mismatch.js';
|
|
24
|
-
import { THEME_WORK_URL_MISMATCH_ERROR } from '../../features/theme/utils/meta_data/theme_meta_data_constants.js';
|
|
25
18
|
import { ThemeFilesStructureUtils } from '../../features/theme/utils/files_structure/theme_files_structure_utils.js';
|
|
26
19
|
import { ThemeInfoUtils } from '../../features/theme/info/theme_info_utils.js';
|
|
27
|
-
import { ValidationErrors } from '../../../ui/validation_errors/validation_errors.js';
|
|
28
20
|
import { ThemeChecksums } from '../../class/checksums/theme_checksums.js';
|
|
21
|
+
import { ThemeFilesUpload } from '../../class/files_upload/theme_files_upload.js';
|
|
22
|
+
import { ThemeFilesUploadHttpApi } from '../../class/files_upload/theme_files_upload_http_api.js';
|
|
23
|
+
import { HTTP_REQUESTER_API_NAME } from '@dreamcommerce/star_core';
|
|
24
|
+
import { MissingThemeFiles } from '../ui/missing_theme_files.js';
|
|
25
|
+
import { SHOPER_THEME_METADATA_DIR } from '../../constants/directory_contstants.js';
|
|
26
|
+
import { THEME_FILES_STRUCTURE_FILE_NAME } from '../../features/theme/theme_constants.js';
|
|
27
|
+
import { ThemeError } from '../ui/theme_error.js';
|
|
29
28
|
export class ThemePushCommand extends BaseThemeCommand {
|
|
30
29
|
static summary = 'Uploads your local theme files to the store and overwrites the current version of the theme in your store.';
|
|
31
30
|
static description = 'Check your local changes before pushing.\n\nYou must run this command from a specific theme directory (ID not needed).';
|
|
@@ -65,6 +64,11 @@ export class ThemePushCommand extends BaseThemeCommand {
|
|
|
65
64
|
}
|
|
66
65
|
const initialChecksums = await themeChecksums.getInitialChecksums();
|
|
67
66
|
const permissions = await ThemeFilesStructureUtils.getFilesPermissions(executionContext.themeRootDir);
|
|
67
|
+
const themeFilesUploadApi = new ThemeFilesUpload({
|
|
68
|
+
themeRootDir: executionContext.themeRootDir,
|
|
69
|
+
credentials,
|
|
70
|
+
themeFilesUploadHttpApi: new ThemeFilesUploadHttpApi(this.getApi(HTTP_REQUESTER_API_NAME))
|
|
71
|
+
});
|
|
68
72
|
if (!initialChecksums || !permissions)
|
|
69
73
|
this.error('Theme checksums or permissions not found. Please ensure you are in the correct theme directory.');
|
|
70
74
|
if (!(await themeChecksums.hasThemeBeenModified())) {
|
|
@@ -94,41 +98,15 @@ export class ThemePushCommand extends BaseThemeCommand {
|
|
|
94
98
|
filesStructure,
|
|
95
99
|
pushAction,
|
|
96
100
|
themeChecksums,
|
|
97
|
-
executionContext
|
|
101
|
+
executionContext,
|
|
102
|
+
themeFilesUploadApi
|
|
98
103
|
});
|
|
99
104
|
spinner.stop();
|
|
100
105
|
renderOnce(React.createElement(ThemePushedSuccess, { themeName: await ThemeInfoUtils.getThemeName(executionContext.themeRootDir) }));
|
|
101
106
|
}
|
|
102
107
|
catch (err) {
|
|
103
108
|
spinner?.stop();
|
|
104
|
-
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
_handleError(err, executionContext) {
|
|
108
|
-
if (err?.code === THEME_ACTION_NOT_FOUND_ERROR_CODE) {
|
|
109
|
-
renderOnce(React.createElement(UnpermittedCommandError, { themeId: executionContext.themeId, commandName: "push" }));
|
|
110
|
-
return;
|
|
111
|
-
}
|
|
112
|
-
if (err?.code === THEME_ARCHIVE_UPLOAD_ERROR) {
|
|
113
|
-
renderOnce(React.createElement(ValidationErrors, { errors: err?.details }));
|
|
114
|
-
return;
|
|
115
|
-
}
|
|
116
|
-
if (err?.code === THEME_WORK_URL_MISMATCH_ERROR) {
|
|
117
|
-
this._renderUrlMismatchError();
|
|
118
|
-
return;
|
|
109
|
+
renderOnce(React.createElement(ThemeError, { err: err, executionContext: executionContext }));
|
|
119
110
|
}
|
|
120
|
-
if (err?.code === THEME_FILES_UPLOAD_ERROR) {
|
|
121
|
-
renderOnce(React.createElement(Error, { header: "Uploading theme files to the shop failed.\n" },
|
|
122
|
-
React.createElement(Text, null, "The rejected files have been removed locally. Please ensure that the files are not corrupted and that the file extensions are supported.")));
|
|
123
|
-
return;
|
|
124
|
-
}
|
|
125
|
-
if (err?.message) {
|
|
126
|
-
renderOnce(React.createElement(ValidationErrors, { errors: err.message }));
|
|
127
|
-
return;
|
|
128
|
-
}
|
|
129
|
-
this.error(String(err));
|
|
130
|
-
}
|
|
131
|
-
_renderUrlMismatchError() {
|
|
132
|
-
renderOnce(React.createElement(ThemeWorkUrlMismatch, { command: "push" }));
|
|
133
111
|
}
|
|
134
112
|
}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
+
export const THEME_TOPIC_NAME = 'theme';
|
|
1
2
|
export const THEME_COMMANDS_NAME = {
|
|
2
|
-
list:
|
|
3
|
-
pull:
|
|
4
|
-
init:
|
|
5
|
-
push:
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
publish: 'theme:publish'
|
|
3
|
+
list: `${THEME_TOPIC_NAME}:list`,
|
|
4
|
+
pull: `${THEME_TOPIC_NAME}:pull`,
|
|
5
|
+
init: `${THEME_TOPIC_NAME}:init`,
|
|
6
|
+
push: `${THEME_TOPIC_NAME}:push`,
|
|
7
|
+
verify: `${THEME_TOPIC_NAME}:verify`,
|
|
8
|
+
info: `${THEME_TOPIC_NAME}:info`,
|
|
9
|
+
delete: `${THEME_TOPIC_NAME}:delete`,
|
|
10
|
+
publish: `${THEME_TOPIC_NAME}:publish`
|
|
11
11
|
};
|
|
@@ -1,44 +1,85 @@
|
|
|
1
1
|
import { BaseThemeCommand } from '../class/base_theme_command.js';
|
|
2
2
|
import { CLI_AUTH_API_NAME } from '../../cli/auth/cli_auth_constants.js';
|
|
3
3
|
import { EXECUTION_CONTEXT_API_NAME, EXECUTION_CONTEXTS } from '../../cli/features/execution_context/execution_context_constants.js';
|
|
4
|
-
import { mapToPermissionsTree } from '../utils/directory_validator/directory_validator_utils.js';
|
|
5
|
-
import { mapChecksumToTree } from '../../utils/checksums/checksums_utils.js';
|
|
6
4
|
import { ThemeFilesStructureUtils } from '../features/theme/utils/files_structure/theme_files_structure_utils.js';
|
|
7
5
|
import { ThemeChecksums } from '../class/checksums/theme_checksums.js';
|
|
6
|
+
import { THEME_VERIFY_API_NAME } from '../features/theme/verify/theme_verify_constants.js';
|
|
7
|
+
import { THEME_ACTIONS_API_NAME, THEME_ACTIONS_TYPES } from '../features/theme/actions/theme_actions_constants.js';
|
|
8
|
+
import { ThemeFilesUpload } from '../class/files_upload/theme_files_upload.js';
|
|
9
|
+
import { ThemeFilesUploadHttpApi } from '../class/files_upload/theme_files_upload_http_api.js';
|
|
10
|
+
import { HTTP_REQUESTER_API_NAME } from '@dreamcommerce/star_core';
|
|
11
|
+
import { renderOnce } from '../../ui/ui_utils.js';
|
|
12
|
+
import { MissingThemeFiles } from './ui/missing_theme_files.js';
|
|
13
|
+
import { SHOPER_THEME_METADATA_DIR } from '../constants/directory_contstants.js';
|
|
14
|
+
import { THEME_FILES_STRUCTURE_FILE_NAME } from '../features/theme/theme_constants.js';
|
|
15
|
+
import React from 'react';
|
|
16
|
+
import { Success } from '../../ui/message_box/success.js';
|
|
17
|
+
import { MissingCredentialsError } from '../../cli/commands/auth/ui/missing_credentials_error.js';
|
|
18
|
+
import { OutsideOfThemeDirectoryContextError } from './ui/ouside_of_theme_directory_context_error.js';
|
|
19
|
+
import ora from 'ora';
|
|
20
|
+
import { ThemeError } from './ui/theme_error.js';
|
|
8
21
|
export class ThemeVerifyCommand extends BaseThemeCommand {
|
|
9
22
|
static description = 'Verify theme files structure';
|
|
10
|
-
static hidden = true;
|
|
11
23
|
async run() {
|
|
12
24
|
const cliAuthApi = this.getApi(CLI_AUTH_API_NAME);
|
|
13
25
|
const credentials = cliAuthApi.getCredentials();
|
|
14
26
|
const executionContextApi = this.getApi(EXECUTION_CONTEXT_API_NAME);
|
|
15
|
-
if (!credentials)
|
|
16
|
-
|
|
27
|
+
if (!credentials) {
|
|
28
|
+
renderOnce(React.createElement(MissingCredentialsError, null));
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
17
31
|
const executionContext = await executionContextApi.getExecutionContext();
|
|
18
|
-
if (executionContext.type !== EXECUTION_CONTEXTS.theme)
|
|
19
|
-
|
|
32
|
+
if (executionContext.type !== EXECUTION_CONTEXTS.theme) {
|
|
33
|
+
renderOnce(React.createElement(OutsideOfThemeDirectoryContextError, null));
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
20
36
|
const themeChecksums = new ThemeChecksums(executionContext.themeRootDir);
|
|
21
37
|
const permissions = await ThemeFilesStructureUtils.getFilesPermissions(executionContext.themeRootDir);
|
|
22
38
|
if (!themeChecksums || !permissions)
|
|
23
39
|
this.error('Theme checksums or permissions not found. Please ensure you are in the correct theme directory.');
|
|
40
|
+
let spinner;
|
|
24
41
|
try {
|
|
25
|
-
const
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
42
|
+
const themeActionsApi = this.getApi(THEME_ACTIONS_API_NAME);
|
|
43
|
+
const themeVerifyApi = this.getApi(THEME_VERIFY_API_NAME);
|
|
44
|
+
const themeFilesUploadApi = new ThemeFilesUpload({
|
|
45
|
+
themeRootDir: executionContext.themeRootDir,
|
|
46
|
+
credentials,
|
|
47
|
+
themeFilesUploadHttpApi: new ThemeFilesUploadHttpApi(this.getApi(HTTP_REQUESTER_API_NAME))
|
|
48
|
+
});
|
|
49
|
+
const verifyAction = themeActionsApi.getThemeAction({
|
|
50
|
+
actionType: THEME_ACTIONS_TYPES.verify,
|
|
51
|
+
themeId: executionContext.themeId,
|
|
52
|
+
credentials
|
|
53
|
+
});
|
|
54
|
+
const filesStructure = await ThemeFilesStructureUtils.getThemeFilesStructure(executionContext.themeRootDir);
|
|
55
|
+
if (!filesStructure) {
|
|
56
|
+
renderOnce(React.createElement(MissingThemeFiles, { files: `${SHOPER_THEME_METADATA_DIR}/${THEME_FILES_STRUCTURE_FILE_NAME}` }));
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
spinner = ora('Verifying theme...').start();
|
|
60
|
+
const { isSuccess, messages } = await themeVerifyApi.verifyTheme({
|
|
61
|
+
verifyAction,
|
|
62
|
+
filesStructure,
|
|
63
|
+
executionContext,
|
|
64
|
+
credentials,
|
|
65
|
+
themeChecksums,
|
|
66
|
+
themeFilesUploadApi
|
|
29
67
|
});
|
|
30
|
-
|
|
31
|
-
|
|
68
|
+
spinner.stop();
|
|
69
|
+
if (isSuccess) {
|
|
70
|
+
renderOnce(React.createElement(Success, { header: `Success: Theme verified!` }));
|
|
32
71
|
}
|
|
33
72
|
else {
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
});
|
|
73
|
+
renderOnce(React.createElement(ThemeError, { err: {
|
|
74
|
+
massages: messages
|
|
75
|
+
}, executionContext: executionContext }));
|
|
38
76
|
}
|
|
77
|
+
// await promptConfirmation('jolo');
|
|
39
78
|
}
|
|
40
79
|
catch (err) {
|
|
41
|
-
|
|
80
|
+
spinner?.stop();
|
|
81
|
+
// await promptConfirmation('jolo');
|
|
82
|
+
renderOnce(React.createElement(ThemeError, { err: err, executionContext: executionContext }));
|
|
42
83
|
}
|
|
43
84
|
}
|
|
44
85
|
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { THEME_ACTION_NOT_FOUND_ERROR_CODE } from '../../features/theme/actions/theme_actions_constants.js';
|
|
2
|
+
import { UnpermittedCommandError } from './unpermitted_command_error.js';
|
|
3
|
+
import { THEME_ARCHIVE_UPLOAD_ERROR, THEME_FILES_UPLOAD_ERROR } from '../../features/theme/push/theme_push_constants.js';
|
|
4
|
+
import { ValidationErrors } from '../../../ui/validation_errors/validation_errors.js';
|
|
5
|
+
import { THEME_WORK_URL_MISMATCH_ERROR } from '../../features/theme/utils/meta_data/theme_meta_data_constants.js';
|
|
6
|
+
import { Error } from '../../../ui/message_box/error.js';
|
|
7
|
+
import { Text } from '../../../ui/text.js';
|
|
8
|
+
import React from 'react';
|
|
9
|
+
import { ThemeWorkUrlMismatch } from './theme_work_url_mismatch.js';
|
|
10
|
+
import { EXECUTION_CONTEXTS } from '../../../cli/features/execution_context/execution_context_constants.js';
|
|
11
|
+
export const ThemeError = ({ err, executionContext }) => {
|
|
12
|
+
if (err?.code === THEME_ACTION_NOT_FOUND_ERROR_CODE && executionContext.type === EXECUTION_CONTEXTS.theme) {
|
|
13
|
+
return (React.createElement(UnpermittedCommandError, { themeId: executionContext.themeId, commandName: "verify" }));
|
|
14
|
+
}
|
|
15
|
+
if (err?.code === THEME_ARCHIVE_UPLOAD_ERROR) {
|
|
16
|
+
return React.createElement(ValidationErrors, { errors: err?.details });
|
|
17
|
+
}
|
|
18
|
+
if (err?.code === THEME_WORK_URL_MISMATCH_ERROR) {
|
|
19
|
+
return React.createElement(ThemeWorkUrlMismatch, { command: "verify" });
|
|
20
|
+
}
|
|
21
|
+
if (err?.code === THEME_FILES_UPLOAD_ERROR) {
|
|
22
|
+
return (React.createElement(Error, { header: "Uploading theme files to the shop failed.\n" },
|
|
23
|
+
React.createElement(Text, null, "The rejected files have been removed locally. Please ensure that the files are not corrupted and that the file extensions are supported.")));
|
|
24
|
+
}
|
|
25
|
+
if (err?.message) {
|
|
26
|
+
return React.createElement(ValidationErrors, { errors: err.message });
|
|
27
|
+
}
|
|
28
|
+
return String(err);
|
|
29
|
+
};
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { THEME_ALL_ACTIONS_NAME } from './service/theme_actions_service_constants.js';
|
|
2
|
-
import { toUnixPath } from '../../../../utils/path_utils.js';
|
|
2
|
+
import { basename, looksLikeDirectory, toUnixPath } from '../../../../utils/path_utils.js';
|
|
3
|
+
import { THEME_ACTION_DATA_TYPE } from './theme_actions_constants.js';
|
|
4
|
+
import globs from 'fast-glob';
|
|
5
|
+
import { THEME_PUSH_WILDCARD_GLOBS_FOR_FILES } from '../push/service/theme_push_service_constants.js';
|
|
3
6
|
export class ThemeActionsUtils {
|
|
4
7
|
static getFilesGlobsThatMatchesActionName({ filesStructure, actionValue, actionType }) {
|
|
5
8
|
return Object.entries(filesStructure).reduce((acc, [filePath, fileStructureItem]) => {
|
|
@@ -9,6 +12,43 @@ export class ThemeActionsUtils {
|
|
|
9
12
|
return acc;
|
|
10
13
|
}, []);
|
|
11
14
|
}
|
|
15
|
+
static async getFilesRecordsFromActionData({ themeRootDir, themeAction, filesStructure }) {
|
|
16
|
+
const filesRecords = [];
|
|
17
|
+
for (const [actionKey, actionData] of Object.entries(themeAction.data)) {
|
|
18
|
+
if (actionData.type === THEME_ACTION_DATA_TYPE.file) {
|
|
19
|
+
const filesGlobs = ThemeActionsUtils.getFilesGlobsThatMatchesActionName({
|
|
20
|
+
//TODO remove when backend fixed
|
|
21
|
+
actionType: 'push',
|
|
22
|
+
// actionType: themeAction.actionType,
|
|
23
|
+
actionValue: actionKey,
|
|
24
|
+
filesStructure
|
|
25
|
+
});
|
|
26
|
+
for (const fileGlob of filesGlobs) {
|
|
27
|
+
const files = await globs(fileGlob, {
|
|
28
|
+
suppressErrors: true,
|
|
29
|
+
onlyFiles: true,
|
|
30
|
+
cwd: themeRootDir
|
|
31
|
+
});
|
|
32
|
+
let processedFileGlob = fileGlob;
|
|
33
|
+
if (looksLikeDirectory(fileGlob)) {
|
|
34
|
+
processedFileGlob = fileGlob.endsWith(THEME_PUSH_WILDCARD_GLOBS_FOR_FILES)
|
|
35
|
+
? fileGlob.slice(0, fileGlob.length - 2)
|
|
36
|
+
: fileGlob;
|
|
37
|
+
}
|
|
38
|
+
for (const filePath of files) {
|
|
39
|
+
filesRecords.push({
|
|
40
|
+
actionData,
|
|
41
|
+
actionKey,
|
|
42
|
+
path: filePath,
|
|
43
|
+
fileName: basename(filePath),
|
|
44
|
+
fileGlob: processedFileGlob
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return filesRecords;
|
|
51
|
+
}
|
|
12
52
|
static _doesActionValueMatch(currentActionValue, valuesToMatch) {
|
|
13
53
|
if (typeof valuesToMatch === 'string') {
|
|
14
54
|
return currentActionValue === valuesToMatch || valuesToMatch === THEME_ALL_ACTIONS_NAME;
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { FEATURE_CORES_TYPES, HTTP_REQUESTER_API_NAME, SyncFeatureInitializer } from '@dreamcommerce/star_core';
|
|
2
2
|
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
|
-
export class ThemeInitInitializer extends
|
|
6
|
+
export class ThemeInitInitializer extends SyncFeatureInitializer {
|
|
7
7
|
static featureName = THEME_INIT_FEATURE_NAME;
|
|
8
|
-
|
|
8
|
+
init() {
|
|
9
9
|
const httpApi = this.getApiSync(HTTP_REQUESTER_API_NAME);
|
|
10
10
|
const service = new ThemeInitService({
|
|
11
11
|
httpApi: new ThemeInitHttpApi(httpApi)
|
|
@@ -1,67 +1,59 @@
|
|
|
1
1
|
import tmp from 'tmp-promise';
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import { basename, dirname, extname, join, looksLikeDirectory, toUnixPath } from '../../../../../utils/path_utils.js';
|
|
5
|
-
import { createReadStream } from 'node:fs';
|
|
2
|
+
import { THEME_ACTIONS_TYPES } from '../../actions/theme_actions_constants.js';
|
|
3
|
+
import { join } from '../../../../../utils/path_utils.js';
|
|
6
4
|
import { v4 as uuid } from 'uuid';
|
|
7
|
-
import globs from 'fast-glob';
|
|
8
5
|
import { THEME_WILDCARD_ACTION_NAME } from '../../actions/service/theme_actions_service_constants.js';
|
|
9
|
-
import {
|
|
10
|
-
import { THEME_FILES_LIST_FILE_NAME, THEME_MODULE_SETTINGS_FILE_NAME } from '../theme_push_constants.js';
|
|
11
|
-
import { THEME_PUSH_WILDCARD_GLOBS_FOR_FILES } from './theme_push_service_constants.js';
|
|
6
|
+
import { THEME_MODULE_SETTINGS_FILE_NAME } from '../theme_push_constants.js';
|
|
12
7
|
import { ThemePushErrorsFactory } from '../theme_push_errors_factory.js';
|
|
13
8
|
import { ThemeImagesUtils } from '../../utils/theme_images_utils.js';
|
|
14
9
|
import { ThemePublishUtils } from '../../skinstore/theme_publish_utils.js';
|
|
15
|
-
import {
|
|
10
|
+
import { removeFile, writeJSONFile } from '../../../../../utils/fs/fs_utils.js';
|
|
16
11
|
import { MODULES_DIRECTORY_NAME } from '../../theme_constants.js';
|
|
17
|
-
import path from 'node:path';
|
|
18
12
|
import { removeOldResources } from '../../../../class/fetch_resources/fetch_resources_utils.js';
|
|
13
|
+
import { ThemeArchive } from '../../../../class/archive/theme_archive.js';
|
|
14
|
+
import { ThemeFilesStructureUtils } from '../../utils/files_structure/theme_files_structure_utils.js';
|
|
15
|
+
import { ThemeActionsUtils } from '../../actions/theme_actions_utils.js';
|
|
16
|
+
import { ArrayUtils } from '@dreamcommerce/utilities';
|
|
19
17
|
export class ThemePushService {
|
|
20
|
-
#themePushHttpApi;
|
|
21
18
|
#themeFetchApi;
|
|
22
|
-
constructor({
|
|
23
|
-
this.#themePushHttpApi = themePushHttpApi;
|
|
19
|
+
constructor({ themeFetchApi }) {
|
|
24
20
|
this.#themeFetchApi = themeFetchApi;
|
|
25
21
|
}
|
|
26
|
-
async push({ pushAction,
|
|
22
|
+
async push({ pushAction, credentials, filesStructure, executionContext, themeChecksums, themeFilesUploadApi }) {
|
|
27
23
|
const { path: tmpDir } = await tmp.dir({ unsafeCleanup: true });
|
|
28
24
|
const themeRootDir = executionContext.themeRootDir;
|
|
29
25
|
if (await themeChecksums.hasThemeFileBeenCreated(ThemePublishUtils.getSkinStoreSettingsFilePath(themeRootDir)))
|
|
30
26
|
throw ThemePushErrorsFactory.createErrorWhilePushingUnpublishedThemeWithSkinstoreData(credentials.shopUrl);
|
|
31
27
|
try {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
28
|
+
//TODO to do api?
|
|
29
|
+
const filesRecords = await ThemeActionsUtils.getFilesRecordsFromActionData({
|
|
30
|
+
themeRootDir,
|
|
31
|
+
themeAction: pushAction,
|
|
32
|
+
filesStructure
|
|
37
33
|
});
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
34
|
+
const filesToUpload = await ArrayUtils.asyncFilter(filesRecords, async ({ path }) => (await themeChecksums.hasThemeFileBeenCreated(path)) || (await themeChecksums.hasThemeFileBeenModified(path)));
|
|
35
|
+
if (filesToUpload.length) {
|
|
36
|
+
const { localFileNameToUploaded } = await this._uploadThemeFiles({
|
|
37
|
+
filesToUpload,
|
|
41
38
|
credentials,
|
|
42
|
-
|
|
43
|
-
|
|
39
|
+
themeRootDir,
|
|
40
|
+
themeFilesUploadApi
|
|
44
41
|
});
|
|
45
|
-
await this.
|
|
42
|
+
await this._createFilesList(themeRootDir, filesRecords, localFileNameToUploaded);
|
|
46
43
|
}
|
|
47
44
|
else {
|
|
48
|
-
await this.
|
|
45
|
+
await this._createFilesList(themeRootDir, filesRecords);
|
|
49
46
|
}
|
|
50
47
|
const themeArchivePath = join(tmpDir, `${uuid()}.zip`);
|
|
51
|
-
await
|
|
52
|
-
themeRootDir: themeRootDir,
|
|
53
|
-
filesToArchive: ThemeActionsUtils.getFilesGlobsThatMatchesActionName({
|
|
54
|
-
actionType: THEME_ACTIONS_TYPES.push,
|
|
55
|
-
actionValue: THEME_WILDCARD_ACTION_NAME,
|
|
56
|
-
filesStructure
|
|
57
|
-
}),
|
|
48
|
+
await new ThemeArchive(themeRootDir).createFullArchive({
|
|
58
49
|
dist: themeArchivePath,
|
|
59
|
-
|
|
50
|
+
actionValue: THEME_WILDCARD_ACTION_NAME,
|
|
51
|
+
actionType: THEME_ACTIONS_TYPES.push
|
|
60
52
|
});
|
|
61
|
-
const { resources, modules } = await
|
|
53
|
+
const { resources, modules } = await themeFilesUploadApi.uploadArchive({
|
|
54
|
+
action: pushAction,
|
|
62
55
|
themeArchivePath,
|
|
63
|
-
credentials
|
|
64
|
-
pushAction
|
|
56
|
+
credentials
|
|
65
57
|
});
|
|
66
58
|
if (modules)
|
|
67
59
|
await this._updateDataForNewCreatedModules({ modules, themeRootDir });
|
|
@@ -72,58 +64,24 @@ export class ThemePushService {
|
|
|
72
64
|
await themeChecksums.updateAllChecksums();
|
|
73
65
|
}
|
|
74
66
|
finally {
|
|
75
|
-
await
|
|
67
|
+
await ThemeFilesStructureUtils.removeAFilesListFile(themeRootDir);
|
|
76
68
|
}
|
|
77
69
|
}
|
|
78
|
-
async
|
|
70
|
+
async _uploadThemeFiles({ filesToUpload, themeRootDir, credentials, themeFilesUploadApi }) {
|
|
79
71
|
try {
|
|
80
|
-
const
|
|
81
|
-
suppressErrors: true,
|
|
82
|
-
onlyFiles: true,
|
|
83
|
-
cwd: themeRootDir
|
|
84
|
-
});
|
|
85
|
-
await this._formatJsonFiles(themeRootDir, filesInThemeDirectory);
|
|
86
|
-
return createZip({
|
|
87
|
-
files: filesInThemeDirectory,
|
|
88
|
-
baseDir: themeRootDir,
|
|
89
|
-
dist
|
|
90
|
-
});
|
|
91
|
-
}
|
|
92
|
-
catch (err) {
|
|
93
|
-
throw ThemePushErrorsFactory.createErrorWhileCreatingThemeArchive(credentials.shopUrl, err);
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
async _uploadThemeArchive({ themeArchivePath, credentials, pushAction }) {
|
|
97
|
-
try {
|
|
98
|
-
const request = this.#themePushHttpApi.pushThemeData({
|
|
99
|
-
actionData: pushAction.data[THEME_WILDCARD_ACTION_NAME],
|
|
100
|
-
stream: createReadStream(themeArchivePath),
|
|
101
|
-
shopUrl: credentials.shopUrl
|
|
102
|
-
});
|
|
103
|
-
const response = await request.response;
|
|
104
|
-
if (response.status !== 200 || !response.data.isSuccess)
|
|
105
|
-
throw response;
|
|
106
|
-
return response.data;
|
|
107
|
-
}
|
|
108
|
-
catch (err) {
|
|
109
|
-
throw ThemePushErrorsFactory.createErrorWhileUploadingTheme(credentials.shopUrl, err.response.data.messages);
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
async _formatJsonFiles(themeRootDir, filesToUpload) {
|
|
113
|
-
await Promise.all(filesToUpload
|
|
114
|
-
.filter((path) => extname(path).toLowerCase() === '.json')
|
|
115
|
-
.map((jsonFile) => formatJSONFile(join(themeRootDir, jsonFile))));
|
|
116
|
-
}
|
|
117
|
-
//TODO osobna klasa do uploadu
|
|
118
|
-
async _uploadThemeFiles({ filesToUpload, themeRootDir, localFiles, credentials }) {
|
|
119
|
-
try {
|
|
120
|
-
const { uploadedImageData, rejectedImageData } = await this._uploadFiles(filesToUpload, credentials);
|
|
121
|
-
const newFilesList = ThemeImagesUtils.updateOriginalFilenameToUploadedFilename(localFiles, uploadedImageData);
|
|
72
|
+
const { uploadedImageData, rejectedImageData } = await themeFilesUploadApi.uploadFiles(filesToUpload);
|
|
122
73
|
if (uploadedImageData.length)
|
|
123
74
|
await ThemeImagesUtils.removeUploadedOriginalFiles(themeRootDir, uploadedImageData);
|
|
124
75
|
if (rejectedImageData.length)
|
|
125
76
|
await this._removeUploadedThemeFiles(rejectedImageData, themeRootDir);
|
|
126
|
-
return {
|
|
77
|
+
return {
|
|
78
|
+
localFileNameToUploaded: uploadedImageData.reduce((acc, { originalFilename, uploadedFilename }) => {
|
|
79
|
+
return {
|
|
80
|
+
...acc,
|
|
81
|
+
[originalFilename]: uploadedFilename ?? originalFilename
|
|
82
|
+
};
|
|
83
|
+
}, {})
|
|
84
|
+
};
|
|
127
85
|
}
|
|
128
86
|
catch (err) {
|
|
129
87
|
throw ThemePushErrorsFactory.createErrorWhileUploadingThemeFiles(credentials.shopUrl, err);
|
|
@@ -132,92 +90,12 @@ export class ThemePushService {
|
|
|
132
90
|
async _removeUploadedThemeFiles(uploadedImageData, themeRootDir) {
|
|
133
91
|
await Promise.all(uploadedImageData.map(({ location, originalFilename }) => removeFile(join(themeRootDir, location, originalFilename))));
|
|
134
92
|
}
|
|
135
|
-
async _uploadFiles(uploadData, credentials) {
|
|
136
|
-
const uploadedImageData = [];
|
|
137
|
-
const rejectedImageData = [];
|
|
138
|
-
await Promise.all(uploadData.map(({ actionData, path }) => this.#themePushHttpApi
|
|
139
|
-
.pushThemeData({
|
|
140
|
-
actionData,
|
|
141
|
-
stream: createReadStream(path),
|
|
142
|
-
shopUrl: credentials.shopUrl
|
|
143
|
-
})
|
|
144
|
-
.response.then((response) => {
|
|
145
|
-
uploadedImageData.push({
|
|
146
|
-
location: dirname(path),
|
|
147
|
-
originalFilename: basename(path),
|
|
148
|
-
uploadedFilename: response.data.imageId
|
|
149
|
-
});
|
|
150
|
-
})
|
|
151
|
-
.catch(() => {
|
|
152
|
-
rejectedImageData.push({
|
|
153
|
-
location: dirname(path),
|
|
154
|
-
originalFilename: basename(path)
|
|
155
|
-
});
|
|
156
|
-
})));
|
|
157
|
-
return {
|
|
158
|
-
uploadedImageData,
|
|
159
|
-
rejectedImageData
|
|
160
|
-
};
|
|
161
|
-
}
|
|
162
|
-
async _getActionDataForFilesToUpload({ pushAction, filesStructure, executionContext, themeChecksums }) {
|
|
163
|
-
const uploadData = [];
|
|
164
|
-
const localFiles = {};
|
|
165
|
-
for (const [actionKey, actionData] of Object.entries(pushAction.data)) {
|
|
166
|
-
if (actionData.type === THEME_ACTION_DATA_TYPE.file) {
|
|
167
|
-
const filesGlobs = ThemeActionsUtils.getFilesGlobsThatMatchesActionName({
|
|
168
|
-
actionType: THEME_ACTIONS_TYPES.push,
|
|
169
|
-
actionValue: actionKey,
|
|
170
|
-
filesStructure
|
|
171
|
-
});
|
|
172
|
-
for (const fileGlob of filesGlobs) {
|
|
173
|
-
const files = await globs(fileGlob, {
|
|
174
|
-
suppressErrors: true,
|
|
175
|
-
onlyFiles: true,
|
|
176
|
-
cwd: executionContext.themeRootDir
|
|
177
|
-
});
|
|
178
|
-
if (looksLikeDirectory(fileGlob)) {
|
|
179
|
-
const processedFileGlob = fileGlob.endsWith(THEME_PUSH_WILDCARD_GLOBS_FOR_FILES)
|
|
180
|
-
? fileGlob.slice(0, fileGlob.length - 2)
|
|
181
|
-
: fileGlob;
|
|
182
|
-
localFiles[processedFileGlob] = files.length ? files.map((filePath) => basename(filePath)) : [];
|
|
183
|
-
}
|
|
184
|
-
else {
|
|
185
|
-
localFiles[fileGlob] = files.length ? basename(files[0]) : null;
|
|
186
|
-
}
|
|
187
|
-
for (const filePath of files) {
|
|
188
|
-
if ((await themeChecksums.hasThemeFileBeenCreated(filePath)) ||
|
|
189
|
-
(await themeChecksums.hasThemeFileBeenModified(filePath))) {
|
|
190
|
-
uploadData.push({
|
|
191
|
-
actionData,
|
|
192
|
-
actionKey,
|
|
193
|
-
path: filePath
|
|
194
|
-
});
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
return {
|
|
201
|
-
uploadData,
|
|
202
|
-
localFiles
|
|
203
|
-
};
|
|
204
|
-
}
|
|
205
93
|
async _updateDataForNewCreatedModules({ modules, themeRootDir }) {
|
|
206
94
|
for (const [moduleDirectoryName, metaData] of Object.entries(modules)) {
|
|
207
95
|
await writeJSONFile(join(themeRootDir, MODULES_DIRECTORY_NAME, moduleDirectoryName, THEME_MODULE_SETTINGS_FILE_NAME), JSON.parse(metaData[THEME_MODULE_SETTINGS_FILE_NAME]));
|
|
208
96
|
}
|
|
209
97
|
}
|
|
210
|
-
async
|
|
211
|
-
|
|
212
|
-
return;
|
|
213
|
-
const toUnixStyleFilesList = Object.entries(filesList).reduce((acc, [filePath, value]) => {
|
|
214
|
-
const unixPath = toUnixPath(filePath);
|
|
215
|
-
const finalPath = looksLikeDirectory(unixPath) ? `${unixPath}${path.posix.sep}` : unixPath;
|
|
216
|
-
return {
|
|
217
|
-
...acc,
|
|
218
|
-
[finalPath]: value
|
|
219
|
-
};
|
|
220
|
-
}, {});
|
|
221
|
-
await writeJSONFile(join(themeRootDir, THEME_FILES_LIST_FILE_NAME), toUnixStyleFilesList);
|
|
98
|
+
async _createFilesList(themeRootDir, filesRecords, localFileNameToUploaded = {}) {
|
|
99
|
+
await ThemeFilesStructureUtils.createAFilesListFile(themeRootDir, ThemeFilesStructureUtils.mapFilesRecordsToFilesList(filesRecords, localFileNameToUploaded));
|
|
222
100
|
}
|
|
223
101
|
}
|
|
@@ -1,15 +1,12 @@
|
|
|
1
|
-
import { FEATURE_CORES_TYPES,
|
|
1
|
+
import { FEATURE_CORES_TYPES, SyncFeatureInitializer } from '@dreamcommerce/star_core';
|
|
2
2
|
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
|
-
import { ThemePushHttpApi } from './http_api/theme_push_http_api.js';
|
|
6
5
|
import { THEME_FETCH_API_NAME } from '../fetch/theme_fetch_constants.js';
|
|
7
6
|
export class ThemePushInitializer extends SyncFeatureInitializer {
|
|
8
7
|
static featureName = THEME_PUSH_FEATURE_NAME;
|
|
9
8
|
init() {
|
|
10
|
-
const httpApi = this.getApiSync(HTTP_REQUESTER_API_NAME);
|
|
11
9
|
const service = new ThemePushService({
|
|
12
|
-
themePushHttpApi: new ThemePushHttpApi(httpApi),
|
|
13
10
|
themeFetchApi: this.getApiSync(THEME_FETCH_API_NAME)
|
|
14
11
|
});
|
|
15
12
|
return {
|
package/build/theme/features/theme/utils/files_structure/theme_file_structure_errors_factory.js
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { AppError } from '../../../../../cli/class/errors/app/app_error.js';
|
|
2
|
+
export class ThemeFileStructureErrorsFactory {
|
|
3
|
+
static createNoFilesStructureError() {
|
|
4
|
+
return new AppError({
|
|
5
|
+
code: 'theme.file_structure.no_files_structure',
|
|
6
|
+
message: 'No files structure found for the theme.',
|
|
7
|
+
level: 'error'
|
|
8
|
+
});
|
|
9
|
+
}
|
|
10
|
+
}
|