@shoper/cli 0.5.2-2 → 0.5.2-3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/cli/class/errors/http/http_errors_factory.js +1 -1
- package/build/cli/core/cli_setup.js +7 -6
- package/build/cli/index.js +0 -1
- package/build/theme/class/archive/theme_archive.js +44 -0
- package/build/theme/{features/theme/utils/archive/theme_archive_utils_errors_factory.js → class/archive/theme_archive_errors_factory.js} +1 -1
- package/build/theme/class/checksums/theme_checksums.js +9 -47
- package/build/theme/commands/pull/theme_pull_command.js +3 -4
- package/build/theme/commands/push/theme_push_command.js +8 -20
- package/build/theme/commands/theme_verify_command.js +3 -3
- package/build/theme/commands/ui/theme_error.js +2 -2
- package/build/theme/features/theme/actions/theme_actions_constants.js +1 -2
- package/build/theme/features/theme/actions/theme_actions_utils.js +5 -31
- package/build/theme/features/theme/fetch/service/theme_fetch_service.js +2 -2
- package/build/theme/features/theme/init/service/theme_init_service.js +2 -2
- package/build/theme/features/theme/merge/service/theme_merge_service.js +2 -2
- package/build/theme/features/theme/push/api/theme_push_api.js +2 -2
- package/build/theme/features/theme/push/service/theme_push_service.js +28 -86
- package/build/theme/features/theme/push/theme_push_utils.js +2 -2
- package/build/theme/features/theme/utils/files_structure/theme_file_structure_errors_factory.js +10 -0
- package/build/theme/features/theme/utils/{files/theme_files_utils.js → files_structure/theme_files_structure_utils.js} +23 -28
- package/build/theme/features/theme/verify/verify/theme_verify_service.js +11 -17
- package/build/utils/array_utils.js +0 -3
- package/build/utils/fs/fs_utils.js +1 -1
- package/build/utils/stream_transforms/json_indent_transform.js +7 -2
- package/package.json +10 -12
- 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
|
@@ -26,7 +26,7 @@ export const cliSetup = async () => {
|
|
|
26
26
|
ExecutionContextInitializer,
|
|
27
27
|
CliAuthTokensInitializer,
|
|
28
28
|
CliAuthInitializer,
|
|
29
|
-
...
|
|
29
|
+
...getCommandBaseInitializers()
|
|
30
30
|
],
|
|
31
31
|
options: {
|
|
32
32
|
featuresTracking: false,
|
|
@@ -34,15 +34,16 @@ export const cliSetup = async () => {
|
|
|
34
34
|
}
|
|
35
35
|
});
|
|
36
36
|
};
|
|
37
|
-
const
|
|
38
|
-
|
|
39
|
-
|
|
37
|
+
const getCommandBaseInitializers = () => {
|
|
38
|
+
// console.log('process.argv', process.argv);
|
|
39
|
+
if (isCommandsTopic(process.argv[2]))
|
|
40
|
+
return getCommandWithTopicBaseInitializers();
|
|
40
41
|
return getCommandWithoutTopicBaseInitializers();
|
|
41
42
|
};
|
|
42
|
-
const
|
|
43
|
+
const isCommandsTopic = (arg) => {
|
|
43
44
|
return arg === THEME_TOPIC_NAME || arg === CLI_AUTH_TOPIC_NAME;
|
|
44
45
|
};
|
|
45
|
-
const
|
|
46
|
+
const getCommandWithTopicBaseInitializers = () => {
|
|
46
47
|
const topic = process.argv[2];
|
|
47
48
|
const command = process.argv[3];
|
|
48
49
|
switch (topic) {
|
package/build/cli/index.js
CHANGED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import globs from 'fast-glob';
|
|
2
|
+
import { createZip } from '../../../utils/zip/create_zip_utils.js';
|
|
3
|
+
import { ThemeFilesStructureUtils } from '../../features/theme/utils/files_structure/theme_files_structure_utils.js';
|
|
4
|
+
import { extname, join } from '../../../utils/path_utils.js';
|
|
5
|
+
import { formatJSONFile } from '../../../utils/fs/fs_utils.js';
|
|
6
|
+
import { ThemeArchiveErrorsFactory } from './theme_archive_errors_factory.js';
|
|
7
|
+
import { ThemeFileStructureErrorsFactory } from '../../features/theme/utils/files_structure/theme_file_structure_errors_factory.js';
|
|
8
|
+
import { ThemeActionsUtils } from '../../features/theme/actions/theme_actions_utils.js';
|
|
9
|
+
export class ThemeArchive {
|
|
10
|
+
#themeRootDir;
|
|
11
|
+
constructor(themeRootDir) {
|
|
12
|
+
this.#themeRootDir = themeRootDir;
|
|
13
|
+
}
|
|
14
|
+
async createFullArchive({ dist, actionValue, actionType }) {
|
|
15
|
+
const filesStructure = await ThemeFilesStructureUtils.getThemeFilesStructure(this.#themeRootDir);
|
|
16
|
+
if (!filesStructure)
|
|
17
|
+
throw ThemeFileStructureErrorsFactory.createNoFilesStructureError();
|
|
18
|
+
try {
|
|
19
|
+
const filesInThemeDirectory = await globs(ThemeActionsUtils.getFilesGlobsThatMatchesActionName({
|
|
20
|
+
actionType,
|
|
21
|
+
actionValue,
|
|
22
|
+
filesStructure
|
|
23
|
+
}), {
|
|
24
|
+
suppressErrors: true,
|
|
25
|
+
onlyFiles: true,
|
|
26
|
+
cwd: this.#themeRootDir
|
|
27
|
+
});
|
|
28
|
+
await this._formatJsonFiles(this.#themeRootDir, filesInThemeDirectory);
|
|
29
|
+
return createZip({
|
|
30
|
+
files: filesInThemeDirectory,
|
|
31
|
+
baseDir: this.#themeRootDir,
|
|
32
|
+
dist
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
catch (err) {
|
|
36
|
+
throw ThemeArchiveErrorsFactory.createErrorWhileCreatingThemeArchive(err);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
async _formatJsonFiles(themeRootDir, filesToUpload) {
|
|
40
|
+
await Promise.all(filesToUpload
|
|
41
|
+
.filter((path) => extname(path).toLowerCase() === '.json')
|
|
42
|
+
.map((jsonFile) => formatJSONFile(join(themeRootDir, jsonFile))));
|
|
43
|
+
}
|
|
44
|
+
}
|
|
@@ -10,7 +10,6 @@ import { normalize } from 'node:path';
|
|
|
10
10
|
import { ThemePushUtils } from '../../features/theme/push/theme_push_utils.js';
|
|
11
11
|
import { SHOPER_THEME_METADATA_DIR } from '../../constants/directory_contstants.js';
|
|
12
12
|
import { THEME_CURRENT_CHECKSUMS_FILE_NAME, THEME_CURRENT_CHECKSUMS_VERITY_FILE_NAME, THEME_INITIAL_CHECKSUMS_FILE_NAME, THEME_INITIAL_CHECKSUMS_VERITY_FILE_NAME } from '../../features/theme/theme_constants.js';
|
|
13
|
-
import { FILE_STATES } from '../../../utils/fs/fs_constants.js';
|
|
14
13
|
export class ThemeChecksums {
|
|
15
14
|
#themeDir;
|
|
16
15
|
#currentChecksumsFilePath;
|
|
@@ -32,6 +31,12 @@ export class ThemeChecksums {
|
|
|
32
31
|
this.#initialChecksums = await this._getChecksums(this.#initialChecksumsFilePath);
|
|
33
32
|
return this.#initialChecksums;
|
|
34
33
|
}
|
|
34
|
+
getInitialChecksumsSync() {
|
|
35
|
+
if (this.#initialChecksums)
|
|
36
|
+
return this.#initialChecksums;
|
|
37
|
+
this.#initialChecksums = this._getChecksumsSync(this.#initialChecksumsFilePath);
|
|
38
|
+
return this.#initialChecksums;
|
|
39
|
+
}
|
|
35
40
|
async getCurrentChecksums() {
|
|
36
41
|
if (this.#currentChecksums)
|
|
37
42
|
return this.#currentChecksums;
|
|
@@ -65,22 +70,10 @@ export class ThemeChecksums {
|
|
|
65
70
|
const initialChecksum = await this.getInitialChecksumFromPath(path);
|
|
66
71
|
return !initialChecksum && (await fileExists(join(this.#themeDir, path)));
|
|
67
72
|
}
|
|
68
|
-
async
|
|
69
|
-
const initialChecksum = await this.getInitialChecksumFromPath(path);
|
|
73
|
+
async hasThemeFileBeenModified(path) {
|
|
70
74
|
const currentChecksum = await this.getCurrentChecksumFromPath(path);
|
|
71
|
-
const
|
|
72
|
-
|
|
73
|
-
return FILE_STATES.created;
|
|
74
|
-
}
|
|
75
|
-
else if (initialChecksum && !fileExistsInFs) {
|
|
76
|
-
return FILE_STATES.deleted;
|
|
77
|
-
}
|
|
78
|
-
else if (initialChecksum && currentChecksum && initialChecksum !== currentChecksum) {
|
|
79
|
-
return FILE_STATES.modified;
|
|
80
|
-
}
|
|
81
|
-
else {
|
|
82
|
-
return FILE_STATES.unchanged;
|
|
83
|
-
}
|
|
75
|
+
const initialChecksum = await this.getInitialChecksumFromPath(path);
|
|
76
|
+
return !!currentChecksum && !!initialChecksum && currentChecksum !== initialChecksum;
|
|
84
77
|
}
|
|
85
78
|
async verify() {
|
|
86
79
|
const initialChecksumFilePath = this.#initialChecksumsFilePath;
|
|
@@ -145,37 +138,6 @@ export class ThemeChecksums {
|
|
|
145
138
|
const directoriesChecksums = computeDirectoriesChecksums(filesChecksumsInDirectories);
|
|
146
139
|
return { ...filesChecksums, ...directoriesChecksums };
|
|
147
140
|
}
|
|
148
|
-
async groupFilesByStatus(files) {
|
|
149
|
-
const initialChecksums = await this.getInitialChecksums();
|
|
150
|
-
const currentChecksums = await this.getCurrentChecksums();
|
|
151
|
-
return files.reduce((acc, filePath) => {
|
|
152
|
-
const initialChecksum = initialChecksums[toCurrentPlatformPath(filePath)];
|
|
153
|
-
const currentChecksum = currentChecksums[toCurrentPlatformPath(filePath)];
|
|
154
|
-
if (!initialChecksum) {
|
|
155
|
-
acc.created.push(filePath);
|
|
156
|
-
}
|
|
157
|
-
else if (currentChecksum && initialChecksum !== currentChecksum) {
|
|
158
|
-
acc.modified.push(filePath);
|
|
159
|
-
}
|
|
160
|
-
else if (initialChecksum && !currentChecksum) {
|
|
161
|
-
acc.deleted.push(filePath);
|
|
162
|
-
}
|
|
163
|
-
else {
|
|
164
|
-
acc.unchanged.push(filePath);
|
|
165
|
-
}
|
|
166
|
-
return acc;
|
|
167
|
-
}, {
|
|
168
|
-
created: [],
|
|
169
|
-
modified: [],
|
|
170
|
-
unchanged: [],
|
|
171
|
-
deleted: []
|
|
172
|
-
});
|
|
173
|
-
}
|
|
174
|
-
async getDeletedFiles() {
|
|
175
|
-
const initialChecksums = await this.getInitialChecksums();
|
|
176
|
-
const currentChecksums = await this.getCurrentChecksums();
|
|
177
|
-
return Object.keys(initialChecksums).filter((filePath) => !currentChecksums[filePath]);
|
|
178
|
-
}
|
|
179
141
|
async _getInitialChecksumsVerification() {
|
|
180
142
|
return await readJSONFile(this.#initialChecksumsVerificationFilePath);
|
|
181
143
|
}
|
|
@@ -24,8 +24,7 @@ import { Info } from '../../../ui/message_box/info.js';
|
|
|
24
24
|
import { directoryExists } from '../../../utils/fs/fs_utils.js';
|
|
25
25
|
import { ThemeMetaDataUtils } from '../../features/theme/utils/meta_data/theme_meta_data_utils.js';
|
|
26
26
|
import { ThemeChecksums } from '../../class/checksums/theme_checksums.js';
|
|
27
|
-
import { ThemeError } from '../ui/theme_error.js';
|
|
28
|
-
import { MODULES_DIRECTORY_NAME } from '../../features/theme/theme_constants.js'; //TODO jak jest error w pullu wowczas usuwamy docelowo pociagniety skin
|
|
27
|
+
import { ThemeError } from '../ui/theme_error.js'; //TODO jak jest error w pullu wowczas usuwamy docelowo pociagniety skin
|
|
29
28
|
//TODO jak jest error w pullu wowczas usuwamy docelowo pociagniety skin
|
|
30
29
|
export class ThemePullCommand extends BaseThemeCommand {
|
|
31
30
|
static summary = 'Downloads the current version of your theme from the store and overwrites your local theme files.';
|
|
@@ -152,9 +151,9 @@ export class ThemePullCommand extends BaseThemeCommand {
|
|
|
152
151
|
dist: tmpDir
|
|
153
152
|
}
|
|
154
153
|
});
|
|
155
|
-
const localModulesPath = join(executionContext.themeRootDir,
|
|
154
|
+
const localModulesPath = join(executionContext.themeRootDir, 'modules');
|
|
156
155
|
const fetchedThemePath = join(tmpDir, name);
|
|
157
|
-
const fetchedModulesPath = join(fetchedThemePath,
|
|
156
|
+
const fetchedModulesPath = join(fetchedThemePath, 'modules');
|
|
158
157
|
if ((await directoryExists(localModulesPath)) && (await directoryExists(fetchedModulesPath)))
|
|
159
158
|
await ThemeResourcesWithIdDirectoryUtils.updateDirectoryNamesOfResourcesWithIdAccordingToLocalThemeNames(localModulesPath, fetchedModulesPath, fetchedThemePath);
|
|
160
159
|
this.#spinner.stop();
|
|
@@ -4,6 +4,8 @@ import { EXECUTION_CONTEXT_API_NAME, EXECUTION_CONTEXTS } from '../../../cli/fea
|
|
|
4
4
|
import { THEME_ACTIONS_API_NAME, THEME_ACTIONS_TYPES } from '../../features/theme/actions/theme_actions_constants.js';
|
|
5
5
|
import { THEME_PUSH_API_NAME } from '../../features/theme/push/theme_push_constants.js';
|
|
6
6
|
import { ThemeMetaDataUtils } from '../../features/theme/utils/meta_data/theme_meta_data_utils.js';
|
|
7
|
+
import { mapChecksumToTree } from '../../../utils/checksums/checksums_utils.js';
|
|
8
|
+
import { mapToPermissionsTree } from '../../utils/directory_validator/directory_validator_utils.js';
|
|
7
9
|
import { renderOnce } from '../../../ui/ui_utils.js';
|
|
8
10
|
import { UnpermittedCommandError } from '../ui/unpermitted_command_error.js';
|
|
9
11
|
import React from 'react';
|
|
@@ -12,7 +14,7 @@ import { MissingCredentialsError } from '../../../cli/commands/auth/ui/missing_c
|
|
|
12
14
|
import { ThemePushedSuccess } from './ui/theme_pushed_success.js';
|
|
13
15
|
import ora from 'ora';
|
|
14
16
|
import { ThemePushSkipInfo } from './ui/theme_push_skip_into.js';
|
|
15
|
-
import {
|
|
17
|
+
import { ThemeFilesStructureUtils } from '../../features/theme/utils/files_structure/theme_files_structure_utils.js';
|
|
16
18
|
import { ThemeInfoUtils } from '../../features/theme/info/theme_info_utils.js';
|
|
17
19
|
import { ThemeChecksums } from '../../class/checksums/theme_checksums.js';
|
|
18
20
|
import { ThemeFilesUpload } from '../../class/files_upload/theme_files_upload.js';
|
|
@@ -22,10 +24,7 @@ import { MissingThemeFiles } from '../ui/missing_theme_files.js';
|
|
|
22
24
|
import { SHOPER_THEME_METADATA_DIR } from '../../constants/directory_contstants.js';
|
|
23
25
|
import { THEME_FILES_STRUCTURE_FILE_NAME } from '../../features/theme/theme_constants.js';
|
|
24
26
|
import { ThemeError } from '../ui/theme_error.js';
|
|
25
|
-
import { mapToPermissionsTree } from '../../utils/directory_validator/directory_validator_utils.js';
|
|
26
|
-
import { mapChecksumToTree } from '../../../utils/checksums/checksums_utils.js';
|
|
27
27
|
import { ThemeUnpermittedActionsError } from './ui/theme_unpermitted_actions_error.js';
|
|
28
|
-
import { promptConfirmation } from '../../../ui/prompts/prompt_confirmation.js';
|
|
29
28
|
export class ThemePushCommand extends BaseThemeCommand {
|
|
30
29
|
static summary = 'Uploads your local theme files to the store and overwrites the current version of the theme in your store.';
|
|
31
30
|
static description = 'Check your local changes before pushing.\n\nYou must run this command from a specific theme directory (ID not needed).';
|
|
@@ -64,7 +63,7 @@ export class ThemePushCommand extends BaseThemeCommand {
|
|
|
64
63
|
return;
|
|
65
64
|
}
|
|
66
65
|
const initialChecksums = await themeChecksums.getInitialChecksums();
|
|
67
|
-
const permissions = await
|
|
66
|
+
const permissions = await ThemeFilesStructureUtils.getFilesPermissions(executionContext.themeRootDir);
|
|
68
67
|
const themeFilesUploadApi = new ThemeFilesUpload({
|
|
69
68
|
themeRootDir: executionContext.themeRootDir,
|
|
70
69
|
credentials,
|
|
@@ -76,7 +75,7 @@ export class ThemePushCommand extends BaseThemeCommand {
|
|
|
76
75
|
renderOnce(React.createElement(ThemePushSkipInfo, null));
|
|
77
76
|
return;
|
|
78
77
|
}
|
|
79
|
-
const validationResult = await
|
|
78
|
+
const validationResult = await ThemeFilesStructureUtils.validateThemeDirectoryStructure({
|
|
80
79
|
//TDO przeniesc do theme checksums
|
|
81
80
|
checksums: mapChecksumToTree(initialChecksums),
|
|
82
81
|
permissions: mapToPermissionsTree(permissions),
|
|
@@ -88,36 +87,25 @@ export class ThemePushCommand extends BaseThemeCommand {
|
|
|
88
87
|
renderOnce(React.createElement(ThemeUnpermittedActionsError, { unpermittedActions: validationResult.unpermittedActions }));
|
|
89
88
|
return;
|
|
90
89
|
}
|
|
91
|
-
const filesStructure = await
|
|
90
|
+
const filesStructure = await ThemeFilesStructureUtils.getThemeFilesStructure(executionContext.themeRootDir);
|
|
92
91
|
if (!filesStructure) {
|
|
93
92
|
renderOnce(React.createElement(MissingThemeFiles, { files: `${SHOPER_THEME_METADATA_DIR}/${THEME_FILES_STRUCTURE_FILE_NAME}` }));
|
|
94
93
|
return;
|
|
95
94
|
}
|
|
96
95
|
spinner = ora('Pushing theme...').start();
|
|
97
|
-
|
|
98
|
-
actionType: THEME_ACTIONS_TYPES.partialPush,
|
|
99
|
-
themeId: executionContext.themeId,
|
|
100
|
-
credentials
|
|
101
|
-
});
|
|
102
|
-
if (!pushAction) {
|
|
103
|
-
renderOnce(React.createElement(UnpermittedCommandError, { themeId: executionContext.themeId, commandName: "push" }));
|
|
104
|
-
return;
|
|
105
|
-
}
|
|
106
|
-
await themePushApi.partialPush({
|
|
96
|
+
await themePushApi.push({
|
|
107
97
|
credentials,
|
|
108
98
|
filesStructure,
|
|
109
|
-
|
|
99
|
+
pushAction,
|
|
110
100
|
themeChecksums,
|
|
111
101
|
executionContext,
|
|
112
102
|
themeFilesUploadApi
|
|
113
103
|
});
|
|
114
104
|
spinner.stop();
|
|
115
|
-
await promptConfirmation('Confirm');
|
|
116
105
|
renderOnce(React.createElement(ThemePushedSuccess, { themeName: await ThemeInfoUtils.getThemeName(executionContext.themeRootDir) }));
|
|
117
106
|
}
|
|
118
107
|
catch (err) {
|
|
119
108
|
spinner?.stop();
|
|
120
|
-
await promptConfirmation('Confirm');
|
|
121
109
|
renderOnce(React.createElement(ThemeError, { err: err, executionContext: executionContext }));
|
|
122
110
|
}
|
|
123
111
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { BaseThemeCommand } from '../class/base_theme_command.js';
|
|
2
2
|
import { CLI_AUTH_API_NAME } from '../../cli/auth/cli_auth_constants.js';
|
|
3
3
|
import { EXECUTION_CONTEXT_API_NAME, EXECUTION_CONTEXTS } from '../../cli/features/execution_context/execution_context_constants.js';
|
|
4
|
+
import { ThemeFilesStructureUtils } from '../features/theme/utils/files_structure/theme_files_structure_utils.js';
|
|
4
5
|
import { ThemeChecksums } from '../class/checksums/theme_checksums.js';
|
|
5
6
|
import { THEME_VERIFY_API_NAME } from '../features/theme/verify/theme_verify_constants.js';
|
|
6
7
|
import { THEME_ACTIONS_API_NAME, THEME_ACTIONS_TYPES } from '../features/theme/actions/theme_actions_constants.js';
|
|
@@ -17,7 +18,6 @@ import { MissingCredentialsError } from '../../cli/commands/auth/ui/missing_cred
|
|
|
17
18
|
import { OutsideOfThemeDirectoryContextError } from './ui/ouside_of_theme_directory_context_error.js';
|
|
18
19
|
import ora from 'ora';
|
|
19
20
|
import { ThemeError } from './ui/theme_error.js';
|
|
20
|
-
import { ThemeFilesUtils } from '../features/theme/utils/files/theme_files_utils.js';
|
|
21
21
|
export class ThemeVerifyCommand extends BaseThemeCommand {
|
|
22
22
|
static description = 'Verify theme files structure';
|
|
23
23
|
async run() {
|
|
@@ -34,7 +34,7 @@ export class ThemeVerifyCommand extends BaseThemeCommand {
|
|
|
34
34
|
return;
|
|
35
35
|
}
|
|
36
36
|
const themeChecksums = new ThemeChecksums(executionContext.themeRootDir);
|
|
37
|
-
const permissions = await
|
|
37
|
+
const permissions = await ThemeFilesStructureUtils.getFilesPermissions(executionContext.themeRootDir);
|
|
38
38
|
if (!themeChecksums || !permissions)
|
|
39
39
|
this.error('Theme checksums or permissions not found. Please ensure you are in the correct theme directory.');
|
|
40
40
|
let spinner;
|
|
@@ -51,7 +51,7 @@ export class ThemeVerifyCommand extends BaseThemeCommand {
|
|
|
51
51
|
themeId: executionContext.themeId,
|
|
52
52
|
credentials
|
|
53
53
|
});
|
|
54
|
-
const filesStructure = await
|
|
54
|
+
const filesStructure = await ThemeFilesStructureUtils.getThemeFilesStructure(executionContext.themeRootDir);
|
|
55
55
|
if (!filesStructure) {
|
|
56
56
|
renderOnce(React.createElement(MissingThemeFiles, { files: `${SHOPER_THEME_METADATA_DIR}/${THEME_FILES_STRUCTURE_FILE_NAME}` }));
|
|
57
57
|
return;
|
|
@@ -19,8 +19,8 @@ export const ThemeError = ({ err, executionContext }) => {
|
|
|
19
19
|
return React.createElement(ThemeWorkUrlMismatch, { command: "verify" });
|
|
20
20
|
}
|
|
21
21
|
if (err?.code === THEME_FILES_UPLOAD_ERROR) {
|
|
22
|
-
return (React.createElement(Error, { header: "Uploading theme files to the shop failed
|
|
23
|
-
React.createElement(Text, null, "Please ensure that the files are not corrupted and that the file extensions are supported.")));
|
|
22
|
+
return (React.createElement(Error, { header: "Uploading theme files to the shop failed.\n" },
|
|
23
|
+
React.createElement(Text, null, "The rejected files have been removed locally. Please ensure that the files are not corrupted and that the file extensions are supported.")));
|
|
24
24
|
}
|
|
25
25
|
if (err?.message) {
|
|
26
26
|
return React.createElement(ValidationErrors, { errors: err.message });
|
|
@@ -3,10 +3,8 @@ import { basename, looksLikeDirectory, toUnixPath } from '../../../../utils/path
|
|
|
3
3
|
import { THEME_ACTION_DATA_TYPE } from './theme_actions_constants.js';
|
|
4
4
|
import globs from 'fast-glob';
|
|
5
5
|
import { THEME_PUSH_WILDCARD_GLOBS_FOR_FILES } from '../push/service/theme_push_service_constants.js';
|
|
6
|
-
import micromatch from 'micromatch';
|
|
7
|
-
import difference from 'lodash/difference.js';
|
|
8
6
|
export class ThemeActionsUtils {
|
|
9
|
-
static
|
|
7
|
+
static getFilesGlobsThatMatchesActionName({ filesStructure, actionValue, actionType }) {
|
|
10
8
|
return Object.entries(filesStructure).reduce((acc, [filePath, fileStructureItem]) => {
|
|
11
9
|
if (fileStructureItem._links?.[actionType] && this._doesActionValueMatch(fileStructureItem._links[actionType], actionValue)) {
|
|
12
10
|
return [...acc, toUnixPath(filePath)];
|
|
@@ -14,32 +12,11 @@ export class ThemeActionsUtils {
|
|
|
14
12
|
return acc;
|
|
15
13
|
}, []);
|
|
16
14
|
}
|
|
17
|
-
static async
|
|
18
|
-
return await globs(ThemeActionsUtils.getFilesGlobsThatMatchesAction({
|
|
19
|
-
actionType,
|
|
20
|
-
actionValue,
|
|
21
|
-
filesStructure
|
|
22
|
-
}), {
|
|
23
|
-
suppressErrors: true,
|
|
24
|
-
onlyFiles: true,
|
|
25
|
-
cwd: rootDir
|
|
26
|
-
});
|
|
27
|
-
}
|
|
28
|
-
static async getDeletedFilesThatMatchesAction({ actionType, actionValue, themeChecksums, filesStructure }) {
|
|
29
|
-
const deletedFiles = await themeChecksums.getDeletedFiles();
|
|
30
|
-
const globs = ThemeActionsUtils.getFilesGlobsThatMatchesAction({
|
|
31
|
-
actionType,
|
|
32
|
-
actionValue,
|
|
33
|
-
filesStructure
|
|
34
|
-
});
|
|
35
|
-
return deletedFiles.filter((path) => micromatch.isMatch(path, globs));
|
|
36
|
-
}
|
|
37
|
-
static async getFilesRecordsFromActionData({ themeRootDir, themeAction, filesStructure, themeChecksums }) {
|
|
15
|
+
static async getFilesRecordsFromActionData({ themeRootDir, themeAction, filesStructure }) {
|
|
38
16
|
const filesRecords = [];
|
|
39
|
-
const initialChecksums = await themeChecksums.getInitialChecksums();
|
|
40
17
|
for (const [actionKey, actionData] of Object.entries(themeAction.data)) {
|
|
41
18
|
if (actionData.type === THEME_ACTION_DATA_TYPE.file) {
|
|
42
|
-
const filesGlobs = ThemeActionsUtils.
|
|
19
|
+
const filesGlobs = ThemeActionsUtils.getFilesGlobsThatMatchesActionName({
|
|
43
20
|
//TODO remove when backend fixed
|
|
44
21
|
actionType: 'push',
|
|
45
22
|
// actionType: themeAction.actionType,
|
|
@@ -47,7 +24,6 @@ export class ThemeActionsUtils {
|
|
|
47
24
|
filesStructure
|
|
48
25
|
});
|
|
49
26
|
for (const fileGlob of filesGlobs) {
|
|
50
|
-
const checksumMatchedFiles = micromatch(Object.keys(initialChecksums), [fileGlob]);
|
|
51
27
|
const files = await globs(fileGlob, {
|
|
52
28
|
suppressErrors: true,
|
|
53
29
|
onlyFiles: true,
|
|
@@ -59,15 +35,13 @@ export class ThemeActionsUtils {
|
|
|
59
35
|
? fileGlob.slice(0, fileGlob.length - 2)
|
|
60
36
|
: fileGlob;
|
|
61
37
|
}
|
|
62
|
-
const
|
|
63
|
-
for (const filePath of [...files, ...deletedFiles]) {
|
|
38
|
+
for (const filePath of files) {
|
|
64
39
|
filesRecords.push({
|
|
65
40
|
actionData,
|
|
66
41
|
actionKey,
|
|
67
42
|
path: filePath,
|
|
68
43
|
fileName: basename(filePath),
|
|
69
|
-
fileGlob: processedFileGlob
|
|
70
|
-
state: await themeChecksums.getFileState(filePath)
|
|
44
|
+
fileGlob: processedFileGlob
|
|
71
45
|
});
|
|
72
46
|
}
|
|
73
47
|
}
|
|
@@ -8,9 +8,9 @@ import { FetchResources } from '../../../../class/fetch_resources/fetch_resource
|
|
|
8
8
|
import { jsonIndentTransform } from '../../../../../utils/stream_transforms/json_indent_transform.js';
|
|
9
9
|
import { JSON_FILE_INDENT } from '../../../../../cli/cli_constants.js';
|
|
10
10
|
import { AppError } from '../../../../../cli/class/errors/app/app_error.js';
|
|
11
|
+
import { ThemeFilesStructureUtils } from '../../utils/files_structure/theme_files_structure_utils.js';
|
|
11
12
|
import { ThemeChecksums } from '../../../../class/checksums/theme_checksums.js';
|
|
12
13
|
import { ThemeMetaDataUtils } from '../../utils/meta_data/theme_meta_data_utils.js';
|
|
13
|
-
import { ThemeFilesUtils } from '../../utils/files/theme_files_utils.js';
|
|
14
14
|
export class ThemeFetchService {
|
|
15
15
|
#themeHttpApi;
|
|
16
16
|
#httpApi;
|
|
@@ -39,7 +39,7 @@ export class ThemeFetchService {
|
|
|
39
39
|
await this.fetchResources(shopUrl, themeDir, resources);
|
|
40
40
|
}
|
|
41
41
|
try {
|
|
42
|
-
await
|
|
42
|
+
await ThemeFilesStructureUtils.updateFilesStructure(themeDir);
|
|
43
43
|
}
|
|
44
44
|
catch (err) {
|
|
45
45
|
throw new AppError({
|
|
@@ -6,8 +6,8 @@ import { extractZip } from '../../../../../utils/zip/extract_zip_utils.js';
|
|
|
6
6
|
import { ThemeMetaDataUtils } from '../../utils/meta_data/theme_meta_data_utils.js';
|
|
7
7
|
import { ThemeInfoUtils } from '../../info/theme_info_utils.js';
|
|
8
8
|
import { ThemeChecksums } from '../../../../class/checksums/theme_checksums.js';
|
|
9
|
+
import { ThemeFilesStructureUtils } from '../../utils/files_structure/theme_files_structure_utils.js';
|
|
9
10
|
import { AppError } from '../../../../../cli/class/errors/app/app_error.js';
|
|
10
|
-
import { ThemeFilesUtils } from '../../utils/files/theme_files_utils.js';
|
|
11
11
|
export class ThemeInitService {
|
|
12
12
|
#httpApi;
|
|
13
13
|
constructor({ httpApi }) {
|
|
@@ -26,7 +26,7 @@ export class ThemeInitService {
|
|
|
26
26
|
dist: themeDir
|
|
27
27
|
});
|
|
28
28
|
try {
|
|
29
|
-
await
|
|
29
|
+
await ThemeFilesStructureUtils.updateFilesStructure(themeDir);
|
|
30
30
|
}
|
|
31
31
|
catch (err) {
|
|
32
32
|
throw new AppError({
|
|
@@ -2,9 +2,9 @@ import FSTree from 'fs-tree-diff';
|
|
|
2
2
|
import { copyFileSync, getAllDirectoriesNamesInside } from '../../../../../utils/fs/fs_utils.js';
|
|
3
3
|
import walkSync from 'walk-sync';
|
|
4
4
|
import { ThemeChecksums } from '../../../../class/checksums/theme_checksums.js';
|
|
5
|
+
import { ThemeFilesStructureUtils } from '../../utils/files_structure/theme_files_structure_utils.js';
|
|
5
6
|
import { SHOPER_THEME_METADATA_DIR } from '../../../../constants/directory_contstants.js';
|
|
6
7
|
import { join, platformSeparator } from '../../../../../utils/path_utils.js';
|
|
7
|
-
import { ThemeFilesUtils } from '../../utils/files/theme_files_utils.js';
|
|
8
8
|
export class ThemeMergeService {
|
|
9
9
|
async applyChanges(fromTheme, toTheme, changes) {
|
|
10
10
|
FSTree.applyPatch(fromTheme, toTheme, changes, {
|
|
@@ -39,7 +39,7 @@ export class ThemeMergeService {
|
|
|
39
39
|
});
|
|
40
40
|
}
|
|
41
41
|
async _getUserRootDirectories(themeDir) {
|
|
42
|
-
const filesStructure = await
|
|
42
|
+
const filesStructure = await ThemeFilesStructureUtils.getThemeRootDirectories(themeDir);
|
|
43
43
|
return (await getAllDirectoriesNamesInside(themeDir, {
|
|
44
44
|
recursive: false,
|
|
45
45
|
hidden: true
|
|
@@ -1,27 +1,25 @@
|
|
|
1
1
|
import tmp from 'tmp-promise';
|
|
2
2
|
import { THEME_ACTIONS_TYPES } from '../../actions/theme_actions_constants.js';
|
|
3
|
-
import {
|
|
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 { removeFile, writeJSONFile } from '../../../../../utils/fs/fs_utils.js';
|
|
11
11
|
import { MODULES_DIRECTORY_NAME } from '../../theme_constants.js';
|
|
12
12
|
import { removeOldResources } from '../../../../class/fetch_resources/fetch_resources_utils.js';
|
|
13
|
-
import {
|
|
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
|
constructor({ themeFetchApi }) {
|
|
22
20
|
this.#themeFetchApi = themeFetchApi;
|
|
23
21
|
}
|
|
24
|
-
async
|
|
22
|
+
async push({ pushAction, credentials, filesStructure, executionContext, themeChecksums, themeFilesUploadApi }) {
|
|
25
23
|
const { path: tmpDir } = await tmp.dir({ unsafeCleanup: true });
|
|
26
24
|
const themeRootDir = executionContext.themeRootDir;
|
|
27
25
|
if (await themeChecksums.hasThemeFileBeenCreated(ThemePublishUtils.getSkinStoreSettingsFilePath(themeRootDir)))
|
|
@@ -30,56 +28,30 @@ export class ThemePushService {
|
|
|
30
28
|
//TODO to do api?
|
|
31
29
|
const filesRecords = await ThemeActionsUtils.getFilesRecordsFromActionData({
|
|
32
30
|
themeRootDir,
|
|
33
|
-
themeAction:
|
|
34
|
-
filesStructure
|
|
35
|
-
themeChecksums
|
|
31
|
+
themeAction: pushAction,
|
|
32
|
+
filesStructure
|
|
36
33
|
});
|
|
37
|
-
const filesToUpload =
|
|
38
|
-
let localFileNameToUploaded;
|
|
34
|
+
const filesToUpload = await ArrayUtils.asyncFilter(filesRecords, async ({ path }) => (await themeChecksums.hasThemeFileBeenCreated(path)) || (await themeChecksums.hasThemeFileBeenModified(path)));
|
|
39
35
|
if (filesToUpload.length) {
|
|
40
|
-
const
|
|
36
|
+
const { localFileNameToUploaded } = await this._uploadThemeFiles({
|
|
41
37
|
filesToUpload,
|
|
42
38
|
credentials,
|
|
43
39
|
themeRootDir,
|
|
44
40
|
themeFilesUploadApi
|
|
45
41
|
});
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
42
|
+
await this._createFilesList(themeRootDir, filesRecords, localFileNameToUploaded);
|
|
43
|
+
}
|
|
44
|
+
else {
|
|
45
|
+
await this._createFilesList(themeRootDir, filesRecords);
|
|
49
46
|
}
|
|
50
47
|
const themeArchivePath = join(tmpDir, `${uuid()}.zip`);
|
|
51
|
-
|
|
52
|
-
actionValue: THEME_WILDCARD_ACTION_NAME,
|
|
53
|
-
actionType: THEME_ACTIONS_TYPES.push,
|
|
54
|
-
filesStructure,
|
|
55
|
-
rootDir: themeRootDir
|
|
56
|
-
});
|
|
57
|
-
const groupedFiles = await themeChecksums.groupFilesByStatus(filesInArchive);
|
|
58
|
-
const deletedFiles = await ThemeActionsUtils.getDeletedFilesThatMatchesAction({
|
|
59
|
-
actionType: THEME_ACTIONS_TYPES.push,
|
|
60
|
-
actionValue: THEME_WILDCARD_ACTION_NAME,
|
|
61
|
-
themeChecksums,
|
|
62
|
-
filesStructure
|
|
63
|
-
});
|
|
64
|
-
await this._createFilesList({
|
|
65
|
-
themeRootDir,
|
|
66
|
-
filesRecords,
|
|
67
|
-
localFileNameToUploaded,
|
|
68
|
-
deletedFiles,
|
|
69
|
-
allCustomModulesIds: await this._getAllModulesIds(themeRootDir)
|
|
70
|
-
});
|
|
71
|
-
await ThemeArchiveUtils.create({
|
|
48
|
+
await new ThemeArchive(themeRootDir).createFullArchive({
|
|
72
49
|
dist: themeArchivePath,
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
...(await this._appendSettingsIfModifiedFileFromFolderThatContainsOne(groupedFiles.modified)),
|
|
76
|
-
...groupedFiles.created,
|
|
77
|
-
(await fileExists(ThemeFilesUtils.getFilesListFilePath(themeRootDir))) ? THEME_FILES_LIST_FILE_NAME : undefined
|
|
78
|
-
].filter(Boolean)
|
|
50
|
+
actionValue: THEME_WILDCARD_ACTION_NAME,
|
|
51
|
+
actionType: THEME_ACTIONS_TYPES.push
|
|
79
52
|
});
|
|
80
|
-
await ThemeFilesUtils.removeAFilesListFile(themeRootDir);
|
|
81
53
|
const { resources, modules } = await themeFilesUploadApi.uploadArchive({
|
|
82
|
-
action,
|
|
54
|
+
action: pushAction,
|
|
83
55
|
themeArchivePath,
|
|
84
56
|
credentials
|
|
85
57
|
});
|
|
@@ -89,12 +61,10 @@ export class ThemePushService {
|
|
|
89
61
|
await removeOldResources(themeRootDir, resources);
|
|
90
62
|
await this.#themeFetchApi.fetchResources(credentials.shopUrl, themeRootDir, resources);
|
|
91
63
|
}
|
|
92
|
-
//TODO only changed files checksum
|
|
93
64
|
await themeChecksums.updateAllChecksums();
|
|
94
65
|
}
|
|
95
|
-
|
|
96
|
-
await
|
|
97
|
-
throw err;
|
|
66
|
+
finally {
|
|
67
|
+
await ThemeFilesStructureUtils.removeAFilesListFile(themeRootDir);
|
|
98
68
|
}
|
|
99
69
|
}
|
|
100
70
|
async _uploadThemeFiles({ filesToUpload, themeRootDir, credentials, themeFilesUploadApi }) {
|
|
@@ -102,10 +72,9 @@ export class ThemePushService {
|
|
|
102
72
|
const { uploadedImageData, rejectedImageData } = await themeFilesUploadApi.uploadFiles(filesToUpload);
|
|
103
73
|
if (uploadedImageData.length)
|
|
104
74
|
await ThemeImagesUtils.removeUploadedOriginalFiles(themeRootDir, uploadedImageData);
|
|
75
|
+
// if (rejectedImageData.length) await this._removeUploadedThemeFiles(rejectedImageData, themeRootDir);
|
|
105
76
|
return {
|
|
106
|
-
|
|
107
|
-
rejectedImageData,
|
|
108
|
-
localFileNameToUploadedMap: uploadedImageData.reduce((acc, { originalFilename, uploadedFilename }) => {
|
|
77
|
+
localFileNameToUploaded: uploadedImageData.reduce((acc, { originalFilename, uploadedFilename }) => {
|
|
109
78
|
return {
|
|
110
79
|
...acc,
|
|
111
80
|
[originalFilename]: uploadedFilename ?? originalFilename
|
|
@@ -117,42 +86,15 @@ export class ThemePushService {
|
|
|
117
86
|
throw ThemePushErrorsFactory.createErrorWhileUploadingThemeFiles(credentials.shopUrl, err);
|
|
118
87
|
}
|
|
119
88
|
}
|
|
89
|
+
async _removeUploadedThemeFiles(uploadedImageData, themeRootDir) {
|
|
90
|
+
await Promise.all(uploadedImageData.map(({ location, originalFilename }) => removeFile(join(themeRootDir, location, originalFilename))));
|
|
91
|
+
}
|
|
120
92
|
async _updateDataForNewCreatedModules({ modules, themeRootDir }) {
|
|
121
93
|
for (const [moduleDirectoryName, metaData] of Object.entries(modules)) {
|
|
122
94
|
await writeJSONFile(join(themeRootDir, MODULES_DIRECTORY_NAME, moduleDirectoryName, THEME_MODULE_SETTINGS_FILE_NAME), JSON.parse(metaData[THEME_MODULE_SETTINGS_FILE_NAME]));
|
|
123
95
|
}
|
|
124
96
|
}
|
|
125
|
-
async _createFilesList(
|
|
126
|
-
|
|
127
|
-
if (deletedFiles?.length)
|
|
128
|
-
filesListContent.removed = deletedFiles;
|
|
129
|
-
if (allCustomModulesIds?.length)
|
|
130
|
-
filesListContent[FILES_LIST_CUSTOM_MODULES_TO_KEEP_IDS] = allCustomModulesIds;
|
|
131
|
-
await ThemeFilesUtils.createAFilesListFile(themeRootDir, filesListContent);
|
|
132
|
-
}
|
|
133
|
-
async _appendSettingsIfModifiedFileFromFolderThatContainsOne(modifiedFiles) {
|
|
134
|
-
const withSettingsFiles = [...modifiedFiles];
|
|
135
|
-
for (const filePath of modifiedFiles) {
|
|
136
|
-
if (await fileExists(join(dirname(filePath), 'settings.json'))) {
|
|
137
|
-
withSettingsFiles.push(join(dirname(filePath), 'settings.json'));
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
return uniq(withSettingsFiles);
|
|
141
|
-
}
|
|
142
|
-
async _getAllModulesIds(themeRootDir) {
|
|
143
|
-
const modulesPath = join(themeRootDir, MODULES_DIRECTORY_NAME);
|
|
144
|
-
if (!(await fileExists(modulesPath)))
|
|
145
|
-
return [];
|
|
146
|
-
const moduleDirs = await getAllDirectoriesNamesInside(modulesPath);
|
|
147
|
-
const modulesIds = [];
|
|
148
|
-
for (const moduleDir of moduleDirs) {
|
|
149
|
-
const moduleSettingsPath = join(themeRootDir, MODULES_DIRECTORY_NAME, moduleDir, THEME_MODULE_SETTINGS_FILE_NAME);
|
|
150
|
-
if (await fileExists(moduleSettingsPath)) {
|
|
151
|
-
const settingsContent = await readJSONFile(moduleSettingsPath);
|
|
152
|
-
if (settingsContent.id && Number.isInteger(settingsContent.id))
|
|
153
|
-
modulesIds.push(settingsContent.id);
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
return modulesIds;
|
|
97
|
+
async _createFilesList(themeRootDir, filesRecords, localFileNameToUploaded = {}) {
|
|
98
|
+
await ThemeFilesStructureUtils.createAFilesListFile(themeRootDir, ThemeFilesStructureUtils.mapFilesRecordsToFilesList(filesRecords, localFileNameToUploaded));
|
|
157
99
|
}
|
|
158
100
|
}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import globs from 'fast-glob';
|
|
2
2
|
import { AppError } from '../../../../cli/class/errors/app/app_error.js';
|
|
3
3
|
import { toUnixPath } from '../../../../utils/path_utils.js';
|
|
4
|
-
import {
|
|
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,10 @@
|
|
|
1
|
+
import { AppError } from '../../../../../cli/class/errors/app/app_error.js';
|
|
2
|
+
export class ThemeFileStructureErrorsFactory {
|
|
3
|
+
static createNoFilesStructureError() {
|
|
4
|
+
return new AppError({
|
|
5
|
+
code: 'theme.file_structure.no_files_structure',
|
|
6
|
+
message: 'No files structure found for the theme.',
|
|
7
|
+
level: 'error'
|
|
8
|
+
});
|
|
9
|
+
}
|
|
10
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { readJSONFile, removeFile, writeJSONFile } from '../../../../../utils/fs/fs_utils.js';
|
|
2
2
|
import { join, looksLikeDirectory, mapKeysPathsToWindowPlatform, toUnixPath } from '../../../../../utils/path_utils.js';
|
|
3
3
|
import { SHOPER_THEME_METADATA_DIR } from '../../../../constants/directory_contstants.js';
|
|
4
4
|
import { THEME_CURRENT_CHECKSUMS_FILE_NAME, THEME_CURRENT_CHECKSUMS_VERITY_FILE_NAME, THEME_FILES_STRUCTURE_FILE_NAME, THEME_INITIAL_CHECKSUMS_FILE_NAME, THEME_INITIAL_CHECKSUMS_VERITY_FILE_NAME } from '../../theme_constants.js';
|
|
@@ -7,8 +7,7 @@ import { validateDirectory } from '../../../../utils/directory_validator/directo
|
|
|
7
7
|
import path from 'node:path';
|
|
8
8
|
import { THEME_FILES_LIST_FILE_NAME } from '../../push/theme_push_constants.js';
|
|
9
9
|
import { THEME_ACTIONS_TYPES } from '../../actions/theme_actions_constants.js';
|
|
10
|
-
|
|
11
|
-
export class ThemeFilesUtils {
|
|
10
|
+
export class ThemeFilesStructureUtils {
|
|
12
11
|
static async getThemeFilesStructure(themeDirectory) {
|
|
13
12
|
const filesStructure = await readJSONFile(join(themeDirectory, SHOPER_THEME_METADATA_DIR, THEME_FILES_STRUCTURE_FILE_NAME));
|
|
14
13
|
if (!isWindowsOs())
|
|
@@ -20,7 +19,7 @@ export class ThemeFilesUtils {
|
|
|
20
19
|
await writeJSONFile(filePath, filesStructure);
|
|
21
20
|
}
|
|
22
21
|
static async getFilesPermissions(themeDirectory) {
|
|
23
|
-
const fileStructure = await
|
|
22
|
+
const fileStructure = await ThemeFilesStructureUtils.getThemeFilesStructure(themeDirectory);
|
|
24
23
|
return Object.entries(fileStructure).reduce((acc, [key, value]) => {
|
|
25
24
|
return {
|
|
26
25
|
...acc,
|
|
@@ -36,22 +35,14 @@ export class ThemeFilesUtils {
|
|
|
36
35
|
});
|
|
37
36
|
}
|
|
38
37
|
static async getThemeRootDirectories(themeDirectory) {
|
|
39
|
-
const filesStructure = await
|
|
38
|
+
const filesStructure = await ThemeFilesStructureUtils.getThemeFilesStructure(themeDirectory);
|
|
40
39
|
return Object.keys(filesStructure).filter((path) => {
|
|
41
40
|
return looksLikeDirectory(path);
|
|
42
41
|
});
|
|
43
42
|
}
|
|
44
43
|
static mapFilesRecordsToFilesList(filesRecords, localFileNameToUploaded = {}) {
|
|
45
|
-
|
|
44
|
+
const mappedFilesRecords = filesRecords.reduce((acc, { fileGlob, fileName }) => {
|
|
46
45
|
const name = localFileNameToUploaded[fileName] ?? fileName;
|
|
47
|
-
if (state === FILE_STATES.deleted && actionKey === 'thumbnail') {
|
|
48
|
-
return {
|
|
49
|
-
...acc,
|
|
50
|
-
[fileGlob]: null
|
|
51
|
-
};
|
|
52
|
-
}
|
|
53
|
-
if (state === FILE_STATES.deleted)
|
|
54
|
-
return acc;
|
|
55
46
|
if (looksLikeDirectory(fileGlob)) {
|
|
56
47
|
const existingFiles = acc[fileGlob] || [];
|
|
57
48
|
return {
|
|
@@ -66,28 +57,32 @@ export class ThemeFilesUtils {
|
|
|
66
57
|
};
|
|
67
58
|
}
|
|
68
59
|
}, {});
|
|
60
|
+
/*
|
|
61
|
+
* Brzydki fix, poprawione w partial push na ładniej
|
|
62
|
+
*/
|
|
63
|
+
if (!mappedFilesRecords['settings/thumbnail.jpg']) {
|
|
64
|
+
mappedFilesRecords['settings/thumbnail.jpg'] = null;
|
|
65
|
+
}
|
|
66
|
+
return mappedFilesRecords;
|
|
69
67
|
}
|
|
70
68
|
static async createAFilesListFile(themeRootDir, filesList) {
|
|
71
69
|
if (!filesList || !Object.keys(filesList).length)
|
|
72
70
|
return;
|
|
73
|
-
const toUnixStyleFilesList = {
|
|
74
|
-
for (const [filePath, value] of Object.entries(filesList)) {
|
|
71
|
+
const toUnixStyleFilesList = Object.entries(filesList).reduce((acc, [filePath, value]) => {
|
|
75
72
|
const unixPath = toUnixPath(filePath);
|
|
76
|
-
const finalPath = (
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
static getFilesListFilePath(themeRootDir) {
|
|
84
|
-
return join(themeRootDir, THEME_FILES_LIST_FILE_NAME);
|
|
73
|
+
const finalPath = looksLikeDirectory(unixPath) ? `${unixPath}${path.posix.sep}` : unixPath;
|
|
74
|
+
return {
|
|
75
|
+
...acc,
|
|
76
|
+
[finalPath]: value
|
|
77
|
+
};
|
|
78
|
+
}, {});
|
|
79
|
+
await writeJSONFile(join(themeRootDir, THEME_FILES_LIST_FILE_NAME), toUnixStyleFilesList);
|
|
85
80
|
}
|
|
86
81
|
static async removeAFilesListFile(themeRootDir) {
|
|
87
|
-
await removeFile(
|
|
82
|
+
await removeFile(join(themeRootDir, THEME_FILES_LIST_FILE_NAME));
|
|
88
83
|
}
|
|
89
84
|
static async updateFilesStructure(themeDir) {
|
|
90
|
-
const fileStructure = await
|
|
85
|
+
const fileStructure = await ThemeFilesStructureUtils.getThemeFilesStructure(themeDir);
|
|
91
86
|
const checksumsFiles = [
|
|
92
87
|
`${SHOPER_THEME_METADATA_DIR}/${THEME_INITIAL_CHECKSUMS_FILE_NAME}`,
|
|
93
88
|
`${SHOPER_THEME_METADATA_DIR}/${THEME_INITIAL_CHECKSUMS_VERITY_FILE_NAME}`,
|
|
@@ -120,6 +115,6 @@ export class ThemeFilesUtils {
|
|
|
120
115
|
}
|
|
121
116
|
};
|
|
122
117
|
});
|
|
123
|
-
await
|
|
118
|
+
await ThemeFilesStructureUtils.writeThemeFilesStructure(themeDir, fileStructure);
|
|
124
119
|
}
|
|
125
120
|
}
|
|
@@ -3,12 +3,12 @@ import { ThemePublishUtils } from '../../skinstore/theme_publish_utils.js';
|
|
|
3
3
|
import { ThemePushErrorsFactory } from '../../push/theme_push_errors_factory.js';
|
|
4
4
|
import { join } from '../../../../../utils/path_utils.js';
|
|
5
5
|
import { v4 as uuid } from 'uuid';
|
|
6
|
-
import {
|
|
7
|
-
import { ThemeActionsUtils } from '../../actions/theme_actions_utils.js';
|
|
8
|
-
import { ThemeFilesUtils } from '../../utils/files/theme_files_utils.js';
|
|
6
|
+
import { ThemeArchive } from '../../../../class/archive/theme_archive.js';
|
|
9
7
|
import { THEME_WILDCARD_ACTION_NAME } from '../../actions/service/theme_actions_service_constants.js';
|
|
10
8
|
import { THEME_ACTIONS_TYPES } from '../../actions/theme_actions_constants.js';
|
|
11
|
-
import {
|
|
9
|
+
import { ThemeFilesStructureUtils } from '../../utils/files_structure/theme_files_structure_utils.js';
|
|
10
|
+
import { ThemeActionsUtils } from '../../actions/theme_actions_utils.js';
|
|
11
|
+
import { ArrayUtils } from '@dreamcommerce/utilities';
|
|
12
12
|
export class ThemeVerifyService {
|
|
13
13
|
async verifyTheme({ verifyAction, executionContext, credentials, themeChecksums, filesStructure, themeFilesUploadApi }) {
|
|
14
14
|
const { path: tmpDir } = await tmp.dir({ unsafeCleanup: true });
|
|
@@ -20,10 +20,9 @@ export class ThemeVerifyService {
|
|
|
20
20
|
const filesRecords = await ThemeActionsUtils.getFilesRecordsFromActionData({
|
|
21
21
|
themeRootDir,
|
|
22
22
|
themeAction: verifyAction,
|
|
23
|
-
filesStructure
|
|
24
|
-
themeChecksums
|
|
23
|
+
filesStructure
|
|
25
24
|
});
|
|
26
|
-
const filesToVerify =
|
|
25
|
+
const filesToVerify = await ArrayUtils.asyncFilter(filesRecords, async ({ path }) => (await themeChecksums.hasThemeFileBeenModified(path)) || !(await themeChecksums.hasThemeFileBeenCreated(path)));
|
|
27
26
|
if (filesToVerify.length) {
|
|
28
27
|
const { rejectedImageData } = await themeFilesUploadApi.uploadFiles(filesToVerify);
|
|
29
28
|
if (rejectedImageData.length)
|
|
@@ -34,15 +33,10 @@ export class ThemeVerifyService {
|
|
|
34
33
|
}
|
|
35
34
|
await this._createFilesList(themeRootDir, filesToVerify);
|
|
36
35
|
const themeArchivePath = join(tmpDir, `${uuid()}.zip`);
|
|
37
|
-
await
|
|
36
|
+
await new ThemeArchive(themeRootDir).createFullArchive({
|
|
38
37
|
dist: themeArchivePath,
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
actionValue: THEME_WILDCARD_ACTION_NAME,
|
|
42
|
-
actionType: THEME_ACTIONS_TYPES.push,
|
|
43
|
-
filesStructure,
|
|
44
|
-
rootDir: themeRootDir
|
|
45
|
-
})
|
|
38
|
+
actionValue: THEME_WILDCARD_ACTION_NAME,
|
|
39
|
+
actionType: THEME_ACTIONS_TYPES.push
|
|
46
40
|
});
|
|
47
41
|
const { isSuccess, messages } = await themeFilesUploadApi.uploadArchive({
|
|
48
42
|
action: verifyAction,
|
|
@@ -52,10 +46,10 @@ export class ThemeVerifyService {
|
|
|
52
46
|
return { isSuccess, messages };
|
|
53
47
|
}
|
|
54
48
|
finally {
|
|
55
|
-
await
|
|
49
|
+
await ThemeFilesStructureUtils.removeAFilesListFile(themeRootDir);
|
|
56
50
|
}
|
|
57
51
|
}
|
|
58
52
|
async _createFilesList(themeRootDir, filesRecords) {
|
|
59
|
-
await
|
|
53
|
+
await ThemeFilesStructureUtils.createAFilesListFile(themeRootDir, ThemeFilesStructureUtils.mapFilesRecordsToFilesList(filesRecords));
|
|
60
54
|
}
|
|
61
55
|
}
|
|
@@ -100,7 +100,7 @@ export const getAllFilesAndDirectoriesInside = async (path, options) => {
|
|
|
100
100
|
withFileTypes
|
|
101
101
|
});
|
|
102
102
|
};
|
|
103
|
-
export const getAllDirectoriesNamesInside = async (path, options
|
|
103
|
+
export const getAllDirectoriesNamesInside = async (path, options) => {
|
|
104
104
|
const { recursive = true, hidden = true } = options;
|
|
105
105
|
const files = await fsPromises.readdir(path, {
|
|
106
106
|
recursive,
|
|
@@ -12,9 +12,14 @@ export const jsonIndentTransform = (space = JSON_FILE_INDENT) => {
|
|
|
12
12
|
if (!data) {
|
|
13
13
|
return callback();
|
|
14
14
|
}
|
|
15
|
-
|
|
15
|
+
// 1. Escape backslashes to preserve literal '\u' during parsing
|
|
16
|
+
const escapedJsonString = data.replace(/\\u/g, '\\\\u');
|
|
17
|
+
const json = JSON.parse(escapedJsonString);
|
|
18
|
+
// 2. Stringify the object, which will re-escape the backslashes (e.g., '\\u' -> '\\\\u')
|
|
16
19
|
const pretty = JSON.stringify(json, null, space);
|
|
17
|
-
|
|
20
|
+
// 3. Un-escape the double backslashes to get the desired literal '\u'
|
|
21
|
+
const finalString = pretty.replace(/\\\\u/g, '\\u');
|
|
22
|
+
this.push(finalString);
|
|
18
23
|
callback();
|
|
19
24
|
}
|
|
20
25
|
});
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@shoper/cli",
|
|
3
3
|
"packageManager": "yarn@3.2.0",
|
|
4
4
|
"sideEffects": false,
|
|
5
|
-
"version": "0.5.2-
|
|
5
|
+
"version": "0.5.2-3",
|
|
6
6
|
"description": "CLI tool for Shoper",
|
|
7
7
|
"author": "Joanna Firek",
|
|
8
8
|
"license": "MIT",
|
|
@@ -39,32 +39,31 @@
|
|
|
39
39
|
"@oclif/plugin-help": "6.2.27",
|
|
40
40
|
"@oclif/plugin-version": "2.2.27",
|
|
41
41
|
"@oclif/plugin-warn-if-update-available": "3.1.38",
|
|
42
|
-
"axios": "1.
|
|
42
|
+
"axios": "1.8.4",
|
|
43
43
|
"chalk": "5.4.1",
|
|
44
44
|
"conf": "13.1.0",
|
|
45
45
|
"fast-glob": "3.3.3",
|
|
46
|
+
"figures": "6.1.0",
|
|
47
|
+
"fs-extra": "11.3.0",
|
|
46
48
|
"fs-tree-diff": "2.0.1",
|
|
47
49
|
"ink": "6.0.1",
|
|
50
|
+
"ink-link": "4.1.0",
|
|
48
51
|
"inquirer": "12.5.2",
|
|
52
|
+
"inquirer-select-line": "1.1.3",
|
|
49
53
|
"is-hidden-file": "1.1.2",
|
|
50
54
|
"jsonwebtoken": "9.0.2",
|
|
51
55
|
"klaw": "4.1.0",
|
|
52
56
|
"lodash": "4.17.21",
|
|
57
|
+
"log-symbols": "7.0.1",
|
|
53
58
|
"memfs": "4.17.0",
|
|
54
59
|
"ora": "8.2.0",
|
|
55
60
|
"react": "19.1.0",
|
|
56
61
|
"reflect-metadata": "0.2.2",
|
|
57
62
|
"rxjs": "7.8.2",
|
|
58
63
|
"semver": "7.7.1",
|
|
64
|
+
"strip-ansi": "7.1.0",
|
|
59
65
|
"tmp-promise": "3.0.3",
|
|
60
66
|
"uuid": "11.1.0",
|
|
61
|
-
"fs-extra": "11.3.0",
|
|
62
|
-
"ink-link": "4.1.0",
|
|
63
|
-
"log-symbols": "7.0.1",
|
|
64
|
-
"figures": "6.1.0",
|
|
65
|
-
"strip-ansi": "7.1.0",
|
|
66
|
-
"inquirer-select-line": "1.1.3",
|
|
67
|
-
"micromatch": "4.0.8",
|
|
68
67
|
"walk-sync": "3.0.0",
|
|
69
68
|
"yauzl": "3.2.0",
|
|
70
69
|
"yazl": "3.3.1"
|
|
@@ -78,15 +77,14 @@
|
|
|
78
77
|
"@types/fs-extra": "11.0.4",
|
|
79
78
|
"@types/jest": "29.5.14",
|
|
80
79
|
"@types/jsonwebtoken": "9.0.9",
|
|
80
|
+
"@types/klaw": "3.0.7",
|
|
81
|
+
"@types/lodash": "4.17.17",
|
|
81
82
|
"@types/node": "18.19.84",
|
|
82
83
|
"@types/react": "19.1.8",
|
|
83
84
|
"@types/semver": "7.7.0",
|
|
84
85
|
"@types/tmp": "0.2.6",
|
|
85
86
|
"@types/yauzl": "2.10.3",
|
|
86
87
|
"@types/yazl": "2.4.6",
|
|
87
|
-
"@types/klaw": "3.0.7",
|
|
88
|
-
"@types/lodash": "4.17.17",
|
|
89
|
-
"@types/micromatch": "4.0.9",
|
|
90
88
|
"@typescript-eslint/eslint-plugin": "8.29.1",
|
|
91
89
|
"babel-jest": "29.7.0",
|
|
92
90
|
"eslint": "9.24.0",
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import { createZip } from '../../../../../utils/zip/create_zip_utils.js';
|
|
2
|
-
import { extname, join } from '../../../../../utils/path_utils.js';
|
|
3
|
-
import { formatJSONFile } from '../../../../../utils/fs/fs_utils.js';
|
|
4
|
-
import { ThemeArchiveErrorsFactory } from './theme_archive_utils_errors_factory.js';
|
|
5
|
-
export class ThemeArchiveUtils {
|
|
6
|
-
static async create({ dist, files, rootDir }) {
|
|
7
|
-
try {
|
|
8
|
-
await this._formatJsonFiles(rootDir, files);
|
|
9
|
-
return createZip({
|
|
10
|
-
files,
|
|
11
|
-
baseDir: rootDir,
|
|
12
|
-
dist
|
|
13
|
-
});
|
|
14
|
-
}
|
|
15
|
-
catch (err) {
|
|
16
|
-
throw ThemeArchiveErrorsFactory.createErrorWhileCreatingThemeArchive(err);
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
static async _formatJsonFiles(themeRootDir, filesToUpload) {
|
|
20
|
-
await Promise.all(filesToUpload
|
|
21
|
-
.filter((path) => extname(path).toLowerCase() === '.json')
|
|
22
|
-
.map((jsonFile) => formatJSONFile(join(themeRootDir, jsonFile))));
|
|
23
|
-
}
|
|
24
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export const FILES_LIST_CUSTOM_MODULES_TO_KEEP_IDS = 'customModulesIdsToKeep';
|