@shoper/cli 0.6.4-0 → 0.6.4-2
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 +6 -6
- package/build/cli/index.js +0 -1
- package/build/theme/class/archive/theme_archive.js +45 -1
- 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 +12 -47
- package/build/theme/commands/pull/theme_pull_command.js +3 -4
- package/build/theme/commands/push/theme_push_command.js +8 -17
- 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 +32 -90
- 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 +9 -1
- package/build/theme/features/theme/utils/{files/theme_files_utils.js → files_structure/theme_files_structure_utils.js} +23 -34
- package/build/theme/features/theme/utils/hidden_directory/hidden_directory_utils.js +1 -0
- package/build/theme/features/theme/verify/verify/theme_verify_service.js +12 -19
- package/build/utils/array_utils.js +0 -3
- package/build/utils/checksums/checksums_utils.js +2 -0
- package/build/utils/fs/fs_utils.js +1 -1
- package/package.json +3 -5
- package/build/theme/features/theme/push/service/theme_push_service_types.js +0 -1
- package/build/theme/features/theme/utils/archive/theme_archive_utils.js +0 -25
- package/build/theme/features/theme/utils/files/them_files_constants.js +0 -1
- package/build/utils/fs/fs_constants.js +0 -6
|
@@ -29,7 +29,7 @@ export const cliSetup = async () => {
|
|
|
29
29
|
LoggerInitializer,
|
|
30
30
|
CliAuthTokensInitializer,
|
|
31
31
|
CliAuthInitializer,
|
|
32
|
-
...
|
|
32
|
+
...getCommandBaseInitializers()
|
|
33
33
|
],
|
|
34
34
|
options: {
|
|
35
35
|
featuresTracking: false,
|
|
@@ -37,15 +37,15 @@ export const cliSetup = async () => {
|
|
|
37
37
|
}
|
|
38
38
|
});
|
|
39
39
|
};
|
|
40
|
-
const
|
|
41
|
-
if (
|
|
42
|
-
return
|
|
40
|
+
const getCommandBaseInitializers = () => {
|
|
41
|
+
if (isCommandsTopic(process.argv[2]))
|
|
42
|
+
return getCommandWithTopicBaseInitializers();
|
|
43
43
|
return [];
|
|
44
44
|
};
|
|
45
|
-
const
|
|
45
|
+
const isCommandsTopic = (arg) => {
|
|
46
46
|
return arg === THEME_TOPIC_NAME || arg === CLI_AUTH_TOPIC_NAME;
|
|
47
47
|
};
|
|
48
|
-
const
|
|
48
|
+
const getCommandWithTopicBaseInitializers = () => {
|
|
49
49
|
const topic = process.argv[2];
|
|
50
50
|
const command = process.argv[3];
|
|
51
51
|
switch (topic) {
|
package/build/cli/index.js
CHANGED
|
@@ -1 +1,45 @@
|
|
|
1
|
-
|
|
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, logger }) {
|
|
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
|
+
logger,
|
|
32
|
+
baseDir: this.#themeRootDir,
|
|
33
|
+
dist
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
catch (err) {
|
|
37
|
+
throw ThemeArchiveErrorsFactory.createErrorWhileCreatingThemeArchive(err);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
async _formatJsonFiles(themeRootDir, filesToUpload) {
|
|
41
|
+
await Promise.all(filesToUpload
|
|
42
|
+
.filter((path) => extname(path).toLowerCase() === '.json')
|
|
43
|
+
.map((jsonFile) => formatJSONFile(join(themeRootDir, jsonFile))));
|
|
44
|
+
}
|
|
45
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AppError } from '
|
|
1
|
+
import { AppError } from '../../../cli/utilities/features/logger/logs/app_error.js';
|
|
2
2
|
export class ThemeArchiveErrorsFactory {
|
|
3
3
|
static createErrorWhileCreatingThemeArchive(error) {
|
|
4
4
|
return new AppError({
|
|
@@ -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
|
#loggerApi;
|
|
@@ -34,6 +33,12 @@ export class ThemeChecksums {
|
|
|
34
33
|
this.#initialChecksums = await this._getChecksums(this.#initialChecksumsFilePath);
|
|
35
34
|
return this.#initialChecksums;
|
|
36
35
|
}
|
|
36
|
+
getInitialChecksumsSync() {
|
|
37
|
+
if (this.#initialChecksums)
|
|
38
|
+
return this.#initialChecksums;
|
|
39
|
+
this.#initialChecksums = this._getChecksumsSync(this.#initialChecksumsFilePath);
|
|
40
|
+
return this.#initialChecksums;
|
|
41
|
+
}
|
|
37
42
|
async getCurrentChecksums() {
|
|
38
43
|
if (this.#currentChecksums)
|
|
39
44
|
return this.#currentChecksums;
|
|
@@ -67,28 +72,19 @@ export class ThemeChecksums {
|
|
|
67
72
|
const initialChecksum = await this.getInitialChecksumFromPath(path);
|
|
68
73
|
return !initialChecksum && (await fileExists(join(this.#themeDir, path)));
|
|
69
74
|
}
|
|
70
|
-
async
|
|
71
|
-
const initialChecksum = await this.getInitialChecksumFromPath(path);
|
|
75
|
+
async hasThemeFileBeenModified(path) {
|
|
72
76
|
const currentChecksum = await this.getCurrentChecksumFromPath(path);
|
|
73
|
-
const
|
|
74
|
-
|
|
75
|
-
return FILE_STATES.created;
|
|
76
|
-
}
|
|
77
|
-
else if (initialChecksum && !fileExistsInFs) {
|
|
78
|
-
return FILE_STATES.deleted;
|
|
79
|
-
}
|
|
80
|
-
else if (initialChecksum && currentChecksum && initialChecksum !== currentChecksum) {
|
|
81
|
-
return FILE_STATES.modified;
|
|
82
|
-
}
|
|
83
|
-
else {
|
|
84
|
-
return FILE_STATES.unchanged;
|
|
85
|
-
}
|
|
77
|
+
const initialChecksum = await this.getInitialChecksumFromPath(path);
|
|
78
|
+
return !!currentChecksum && !!initialChecksum && currentChecksum !== initialChecksum;
|
|
86
79
|
}
|
|
87
80
|
async verify() {
|
|
88
81
|
const initialChecksumFilePath = this.#initialChecksumsFilePath;
|
|
89
82
|
const initialChecksumVerifyFilePath = this.#initialChecksumsVerificationFilePath;
|
|
83
|
+
console.log('Verifying theme checksums:', { initialChecksumFilePath, initialChecksumVerifyFilePath });
|
|
90
84
|
const initialChecksum = await computeFileChecksum(initialChecksumFilePath);
|
|
91
85
|
const initialChecksumVerify = await readJSONFile(initialChecksumVerifyFilePath);
|
|
86
|
+
console.log('initialChecksum current:', initialChecksum);
|
|
87
|
+
console.log('initialChecksumVerify:', initialChecksumVerify);
|
|
92
88
|
return initialChecksum === initialChecksumVerify;
|
|
93
89
|
}
|
|
94
90
|
async updateAllChecksums() {
|
|
@@ -161,37 +157,6 @@ export class ThemeChecksums {
|
|
|
161
157
|
this.#loggerApi?.debug('Finished computing theme checksums.', { details: { count: Object.keys(allChecksums).length } });
|
|
162
158
|
return allChecksums;
|
|
163
159
|
}
|
|
164
|
-
async groupFilesByStatus(files) {
|
|
165
|
-
const initialChecksums = await this.getInitialChecksums();
|
|
166
|
-
const currentChecksums = await this.getCurrentChecksums();
|
|
167
|
-
return files.reduce((acc, filePath) => {
|
|
168
|
-
const initialChecksum = initialChecksums[toCurrentPlatformPath(filePath)];
|
|
169
|
-
const currentChecksum = currentChecksums[toCurrentPlatformPath(filePath)];
|
|
170
|
-
if (!initialChecksum) {
|
|
171
|
-
acc.created.push(filePath);
|
|
172
|
-
}
|
|
173
|
-
else if (currentChecksum && initialChecksum !== currentChecksum) {
|
|
174
|
-
acc.modified.push(filePath);
|
|
175
|
-
}
|
|
176
|
-
else if (initialChecksum && !currentChecksum) {
|
|
177
|
-
acc.deleted.push(filePath);
|
|
178
|
-
}
|
|
179
|
-
else {
|
|
180
|
-
acc.unchanged.push(filePath);
|
|
181
|
-
}
|
|
182
|
-
return acc;
|
|
183
|
-
}, {
|
|
184
|
-
created: [],
|
|
185
|
-
modified: [],
|
|
186
|
-
unchanged: [],
|
|
187
|
-
deleted: []
|
|
188
|
-
});
|
|
189
|
-
}
|
|
190
|
-
async getDeletedFiles() {
|
|
191
|
-
const initialChecksums = await this.getInitialChecksums();
|
|
192
|
-
const currentChecksums = await this.getCurrentChecksums();
|
|
193
|
-
return Object.keys(initialChecksums).filter((filePath) => !currentChecksums[filePath]);
|
|
194
|
-
}
|
|
195
160
|
async _getInitialChecksumsVerification() {
|
|
196
161
|
return await readJSONFile(this.#initialChecksumsVerificationFilePath);
|
|
197
162
|
}
|
|
@@ -24,9 +24,8 @@ 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 { LOGGER_API_NAME } from '../../../cli/utilities/features/logger/logger_constants.js';
|
|
28
27
|
import { ThemeError } from '../ui/theme_error.js';
|
|
29
|
-
import {
|
|
28
|
+
import { LOGGER_API_NAME } from '../../../cli/utilities/features/logger/logger_constants.js';
|
|
30
29
|
//TODO jak jest error w pullu wowczas usuwamy docelowo pociagniety skin
|
|
31
30
|
export class ThemePullCommand extends BaseThemeCommand {
|
|
32
31
|
static summary = 'Downloads the current version of your theme from the store and overwrites your local theme files.';
|
|
@@ -161,9 +160,9 @@ export class ThemePullCommand extends BaseThemeCommand {
|
|
|
161
160
|
dist: tmpDir
|
|
162
161
|
}
|
|
163
162
|
});
|
|
164
|
-
const localModulesPath = join(executionContext.themeRootDir,
|
|
163
|
+
const localModulesPath = join(executionContext.themeRootDir, 'modules');
|
|
165
164
|
const fetchedThemePath = join(tmpDir, name);
|
|
166
|
-
const fetchedModulesPath = join(fetchedThemePath,
|
|
165
|
+
const fetchedModulesPath = join(fetchedThemePath, 'modules');
|
|
167
166
|
if ((await directoryExists(localModulesPath)) && (await directoryExists(fetchedModulesPath)))
|
|
168
167
|
await ThemeResourcesWithIdDirectoryUtils.updateDirectoryNamesOfResourcesWithIdAccordingToLocalThemeNames(localModulesPath, fetchedModulesPath, fetchedThemePath, loggerApi);
|
|
169
168
|
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,8 +24,6 @@ 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
28
|
import { LOGGER_API_NAME } from '../../../cli/utilities/features/logger/logger_constants.js';
|
|
29
29
|
export class ThemePushCommand extends BaseThemeCommand {
|
|
@@ -68,7 +68,7 @@ export class ThemePushCommand extends BaseThemeCommand {
|
|
|
68
68
|
return;
|
|
69
69
|
}
|
|
70
70
|
const initialChecksums = await themeChecksums.getInitialChecksums();
|
|
71
|
-
const permissions = await
|
|
71
|
+
const permissions = await ThemeFilesStructureUtils.getFilesPermissions(executionContext.themeRootDir);
|
|
72
72
|
const themeFilesUploadApi = new ThemeFilesUpload({
|
|
73
73
|
themeRootDir: executionContext.themeRootDir,
|
|
74
74
|
credentials,
|
|
@@ -80,7 +80,7 @@ export class ThemePushCommand extends BaseThemeCommand {
|
|
|
80
80
|
renderOnce(React.createElement(ThemePushSkipInfo, null));
|
|
81
81
|
return;
|
|
82
82
|
}
|
|
83
|
-
const validationResult = await
|
|
83
|
+
const validationResult = await ThemeFilesStructureUtils.validateThemeDirectoryStructure({
|
|
84
84
|
//TDO przeniesc do theme checksums
|
|
85
85
|
checksums: mapChecksumToTree(initialChecksums),
|
|
86
86
|
permissions: mapToPermissionsTree(permissions),
|
|
@@ -92,25 +92,16 @@ export class ThemePushCommand extends BaseThemeCommand {
|
|
|
92
92
|
renderOnce(React.createElement(ThemeUnpermittedActionsError, { unpermittedActions: validationResult.unpermittedActions }));
|
|
93
93
|
return;
|
|
94
94
|
}
|
|
95
|
-
const filesStructure = await
|
|
95
|
+
const filesStructure = await ThemeFilesStructureUtils.getThemeFilesStructure(executionContext.themeRootDir);
|
|
96
96
|
if (!filesStructure) {
|
|
97
97
|
renderOnce(React.createElement(MissingThemeFiles, { files: `${SHOPER_THEME_METADATA_DIR}/${THEME_FILES_STRUCTURE_FILE_NAME}` }));
|
|
98
98
|
return;
|
|
99
99
|
}
|
|
100
100
|
spinner = ora('Pushing theme...').start();
|
|
101
|
-
|
|
102
|
-
actionType: THEME_ACTIONS_TYPES.partialPush,
|
|
103
|
-
themeId: executionContext.themeId,
|
|
104
|
-
credentials
|
|
105
|
-
});
|
|
106
|
-
if (!pushAction) {
|
|
107
|
-
renderOnce(React.createElement(UnpermittedCommandError, { themeId: executionContext.themeId, commandName: "push" }));
|
|
108
|
-
return;
|
|
109
|
-
}
|
|
110
|
-
await themePushApi.partialPush({
|
|
101
|
+
await themePushApi.push({
|
|
111
102
|
credentials,
|
|
112
103
|
filesStructure,
|
|
113
|
-
|
|
104
|
+
pushAction,
|
|
114
105
|
themeChecksums,
|
|
115
106
|
executionContext,
|
|
116
107
|
themeFilesUploadApi
|
|
@@ -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
|
import { LOGGER_API_NAME } from '../../cli/utilities/features/logger/logger_constants.js';
|
|
22
22
|
export class ThemeVerifyCommand extends BaseThemeCommand {
|
|
23
23
|
static description = 'Verify theme files structure';
|
|
@@ -39,7 +39,7 @@ export class ThemeVerifyCommand extends BaseThemeCommand {
|
|
|
39
39
|
themeDir: executionContext.themeRootDir,
|
|
40
40
|
loggerApi
|
|
41
41
|
});
|
|
42
|
-
const permissions = await
|
|
42
|
+
const permissions = await ThemeFilesStructureUtils.getFilesPermissions(executionContext.themeRootDir);
|
|
43
43
|
if (!themeChecksums || !permissions)
|
|
44
44
|
this.error('Theme checksums or permissions not found. Please ensure you are in the correct theme directory.');
|
|
45
45
|
let spinner;
|
|
@@ -56,7 +56,7 @@ export class ThemeVerifyCommand extends BaseThemeCommand {
|
|
|
56
56
|
themeId: executionContext.themeId,
|
|
57
57
|
credentials
|
|
58
58
|
});
|
|
59
|
-
const filesStructure = await
|
|
59
|
+
const filesStructure = await ThemeFilesStructureUtils.getThemeFilesStructure(executionContext.themeRootDir);
|
|
60
60
|
if (!filesStructure) {
|
|
61
61
|
renderOnce(React.createElement(MissingThemeFiles, { files: `${SHOPER_THEME_METADATA_DIR}/${THEME_FILES_STRUCTURE_FILE_NAME}` }));
|
|
62
62
|
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) => !looksLikeDirectory(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/utilities/features/logger/logs/app_error.js';
|
|
11
|
+
import { ThemeFilesStructureUtils } from '../../utils/files_structure/theme_files_structure_utils.js';
|
|
11
12
|
import { ThemeChecksums } from '../../../../class/checksums/theme_checksums.js';
|
|
12
13
|
import { ThemeMetaDataUtils } from '../../utils/meta_data/theme_meta_data_utils.js';
|
|
13
|
-
import { ThemeFilesUtils } from '../../utils/files/theme_files_utils.js';
|
|
14
14
|
export class ThemeFetchService {
|
|
15
15
|
#themeHttpApi;
|
|
16
16
|
#httpApi;
|
|
@@ -55,7 +55,7 @@ export class ThemeFetchService {
|
|
|
55
55
|
}
|
|
56
56
|
try {
|
|
57
57
|
this.#loggerApi.debug('Updating theme files structure.');
|
|
58
|
-
await
|
|
58
|
+
await ThemeFilesStructureUtils.updateFilesStructure(themeDir);
|
|
59
59
|
}
|
|
60
60
|
catch (error) {
|
|
61
61
|
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/utilities/features/logger/logs/app_error.js';
|
|
10
|
-
import { ThemeFilesUtils } from '../../utils/files/theme_files_utils.js';
|
|
11
11
|
export class ThemeInitService {
|
|
12
12
|
#httpApi;
|
|
13
13
|
#loggerApi;
|
|
@@ -41,7 +41,7 @@ export class ThemeInitService {
|
|
|
41
41
|
this.#loggerApi.info('Theme archive extracted.');
|
|
42
42
|
try {
|
|
43
43
|
this.#loggerApi.debug('Updating theme files structure.');
|
|
44
|
-
await
|
|
44
|
+
await ThemeFilesStructureUtils.updateFilesStructure(themeDir);
|
|
45
45
|
}
|
|
46
46
|
catch (err) {
|
|
47
47
|
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
|
#loggerApi;
|
|
10
10
|
constructor({ loggerApi }) {
|
|
@@ -73,7 +73,7 @@ export class ThemeMergeService {
|
|
|
73
73
|
return patch;
|
|
74
74
|
}
|
|
75
75
|
async _getUserRootDirectories(themeDir) {
|
|
76
|
-
const filesStructure = await
|
|
76
|
+
const filesStructure = await ThemeFilesStructureUtils.getThemeRootDirectories(themeDir);
|
|
77
77
|
return (await getAllDirectoriesNamesInside(themeDir, {
|
|
78
78
|
recursive: false,
|
|
79
79
|
hidden: true
|
|
@@ -1,21 +1,19 @@
|
|
|
1
1
|
import tmp from 'tmp-promise';
|
|
2
2
|
import { THEME_ACTIONS_TYPES } from '../../actions/theme_actions_constants.js';
|
|
3
|
-
import {
|
|
3
|
+
import { join } from '../../../../../utils/path_utils.js';
|
|
4
4
|
import { v4 as uuid } from 'uuid';
|
|
5
5
|
import { THEME_WILDCARD_ACTION_NAME } from '../../actions/service/theme_actions_service_constants.js';
|
|
6
|
-
import {
|
|
6
|
+
import { THEME_MODULE_SETTINGS_FILE_NAME } from '../theme_push_constants.js';
|
|
7
7
|
import { ThemePushErrorsFactory } from '../theme_push_errors_factory.js';
|
|
8
8
|
import { ThemeImagesUtils } from '../../utils/theme_images_utils.js';
|
|
9
9
|
import { ThemePublishUtils } from '../../skinstore/theme_publish_utils.js';
|
|
10
|
-
import {
|
|
10
|
+
import { writeJSONFile } from '../../../../../utils/fs/fs_utils.js';
|
|
11
11
|
import { MODULES_DIRECTORY_NAME } from '../../theme_constants.js';
|
|
12
12
|
import { removeOldResources } from '../../../../class/fetch_resources/fetch_resources_utils.js';
|
|
13
|
-
import {
|
|
13
|
+
import { ThemeArchive } from '../../../../class/archive/theme_archive.js';
|
|
14
|
+
import { ThemeFilesStructureUtils } from '../../utils/files_structure/theme_files_structure_utils.js';
|
|
14
15
|
import { ThemeActionsUtils } from '../../actions/theme_actions_utils.js';
|
|
15
|
-
import {
|
|
16
|
-
import { FILE_STATES } from '../../../../../utils/fs/fs_constants.js';
|
|
17
|
-
import uniq from 'lodash/uniq.js';
|
|
18
|
-
import { FILES_LIST_CUSTOM_MODULES_TO_KEEP_IDS } from '../../utils/files/them_files_constants.js';
|
|
16
|
+
import { ArrayUtils } from '@dreamcommerce/utilities';
|
|
19
17
|
export class ThemePushService {
|
|
20
18
|
#themeFetchApi;
|
|
21
19
|
#loggerApi;
|
|
@@ -23,7 +21,8 @@ export class ThemePushService {
|
|
|
23
21
|
this.#themeFetchApi = themeFetchApi;
|
|
24
22
|
this.#loggerApi = loggerApi;
|
|
25
23
|
}
|
|
26
|
-
async
|
|
24
|
+
async push({ pushAction, credentials, filesStructure, executionContext, themeChecksums, themeFilesUploadApi }) {
|
|
25
|
+
this.#loggerApi.info('Pushing theme changes.');
|
|
27
26
|
const { path: tmpDir } = await tmp.dir({ unsafeCleanup: true });
|
|
28
27
|
this.#loggerApi.debug('Temporary directory created.', { details: { path: tmpDir } });
|
|
29
28
|
const themeRootDir = executionContext.themeRootDir;
|
|
@@ -31,72 +30,48 @@ export class ThemePushService {
|
|
|
31
30
|
throw ThemePushErrorsFactory.createErrorWhilePushingUnpublishedThemeWithSkinstoreData(credentials.shopUrl);
|
|
32
31
|
try {
|
|
33
32
|
this.#loggerApi.debug('Getting file records from action data.');
|
|
34
|
-
//TODO to do api?
|
|
35
33
|
const filesRecords = await ThemeActionsUtils.getFilesRecordsFromActionData({
|
|
36
34
|
themeRootDir,
|
|
37
|
-
themeAction:
|
|
38
|
-
filesStructure
|
|
39
|
-
themeChecksums
|
|
35
|
+
themeAction: pushAction,
|
|
36
|
+
filesStructure
|
|
40
37
|
});
|
|
41
38
|
this.#loggerApi.debug('Filtering for created or modified files to upload.');
|
|
42
|
-
const filesToUpload =
|
|
43
|
-
let localFileNameToUploaded;
|
|
39
|
+
const filesToUpload = await ArrayUtils.asyncFilter(filesRecords, async ({ path }) => (await themeChecksums.hasThemeFileBeenCreated(path)) || (await themeChecksums.hasThemeFileBeenModified(path)));
|
|
44
40
|
this.#loggerApi.debug('Files to upload determined.', { details: { count: filesToUpload.length } });
|
|
45
41
|
if (filesToUpload.length) {
|
|
46
42
|
this.#loggerApi.info(`Uploading ${filesToUpload.length} theme files.`);
|
|
47
|
-
const
|
|
43
|
+
const { localFileNameToUploaded } = await this._uploadThemeFiles({
|
|
48
44
|
filesToUpload,
|
|
49
45
|
credentials,
|
|
50
46
|
themeRootDir,
|
|
51
47
|
themeFilesUploadApi
|
|
52
48
|
});
|
|
53
|
-
if (!uploadData.isSuccess)
|
|
54
|
-
throw ThemePushErrorsFactory.createErrorWhileUploadingThemeFiles(credentials.shopUrl);
|
|
55
49
|
this.#loggerApi.info('Theme files uploaded.');
|
|
56
|
-
|
|
50
|
+
await this._createFilesList(themeRootDir, filesRecords, localFileNameToUploaded);
|
|
51
|
+
}
|
|
52
|
+
else {
|
|
53
|
+
this.#loggerApi.info('No new or modified files to upload. Creating files list for archive.');
|
|
54
|
+
await this._createFilesList(themeRootDir, filesRecords);
|
|
57
55
|
}
|
|
58
56
|
const themeArchivePath = join(tmpDir, `${uuid()}.zip`);
|
|
59
|
-
const filesInArchive = await ThemeActionsUtils.getFilesThatMatchesAction({
|
|
60
|
-
actionValue: THEME_WILDCARD_ACTION_NAME,
|
|
61
|
-
actionType: THEME_ACTIONS_TYPES.push,
|
|
62
|
-
filesStructure,
|
|
63
|
-
rootDir: themeRootDir
|
|
64
|
-
});
|
|
65
|
-
const groupedFiles = await themeChecksums.groupFilesByStatus(filesInArchive);
|
|
66
|
-
const deletedFiles = await ThemeActionsUtils.getDeletedFilesThatMatchesAction({
|
|
67
|
-
actionType: THEME_ACTIONS_TYPES.push,
|
|
68
|
-
actionValue: THEME_WILDCARD_ACTION_NAME,
|
|
69
|
-
themeChecksums,
|
|
70
|
-
filesStructure,
|
|
71
|
-
rootDir: themeRootDir
|
|
72
|
-
});
|
|
73
|
-
await this._createFilesList({
|
|
74
|
-
themeRootDir,
|
|
75
|
-
filesRecords,
|
|
76
|
-
localFileNameToUploaded,
|
|
77
|
-
deletedFiles,
|
|
78
|
-
allCustomModulesIds: await this._getAllModulesIds(themeRootDir)
|
|
79
|
-
});
|
|
80
57
|
this.#loggerApi.info('Creating theme archive.');
|
|
81
|
-
await
|
|
58
|
+
await new ThemeArchive(themeRootDir).createFullArchive({
|
|
82
59
|
dist: themeArchivePath,
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
...(await this._appendSettingsIfModifiedFileFromFolderThatContainsOne(groupedFiles.modified)),
|
|
87
|
-
...groupedFiles.created,
|
|
88
|
-
(await fileExists(ThemeFilesUtils.getFilesListFilePath(themeRootDir))) ? THEME_FILES_LIST_FILE_NAME : undefined
|
|
89
|
-
].filter(Boolean)
|
|
60
|
+
actionValue: THEME_WILDCARD_ACTION_NAME,
|
|
61
|
+
actionType: THEME_ACTIONS_TYPES.push,
|
|
62
|
+
logger: this.#loggerApi
|
|
90
63
|
});
|
|
91
64
|
this.#loggerApi.info('Theme archive created.');
|
|
92
|
-
await ThemeFilesUtils.removeAFilesListFile(themeRootDir);
|
|
93
65
|
const { resources, modules } = await themeFilesUploadApi.uploadArchive({
|
|
94
|
-
action,
|
|
66
|
+
action: pushAction,
|
|
95
67
|
themeArchivePath,
|
|
96
68
|
credentials
|
|
97
69
|
});
|
|
98
|
-
|
|
70
|
+
this.#loggerApi.info('Theme archive uploaded.');
|
|
71
|
+
if (modules) {
|
|
72
|
+
this.#loggerApi.debug('Updating data for new modules.');
|
|
99
73
|
await this._updateDataForNewCreatedModules({ modules, themeRootDir });
|
|
74
|
+
}
|
|
100
75
|
if (resources) {
|
|
101
76
|
this.#loggerApi.debug('Removing old resources.');
|
|
102
77
|
await removeOldResources(themeRootDir, resources);
|
|
@@ -105,13 +80,12 @@ export class ThemePushService {
|
|
|
105
80
|
this.#loggerApi.info('New resources fetched.');
|
|
106
81
|
}
|
|
107
82
|
this.#loggerApi.debug('Updating theme checksums.');
|
|
108
|
-
//TODO only changed files checksum
|
|
109
83
|
await themeChecksums.updateAllChecksums();
|
|
110
84
|
this.#loggerApi.info('Push completed successfully.');
|
|
111
85
|
}
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
86
|
+
finally {
|
|
87
|
+
this.#loggerApi.debug('Cleaning up files list.');
|
|
88
|
+
await ThemeFilesStructureUtils.removeAFilesListFile(themeRootDir);
|
|
115
89
|
}
|
|
116
90
|
}
|
|
117
91
|
async _uploadThemeFiles({ filesToUpload, themeRootDir, credentials, themeFilesUploadApi }) {
|
|
@@ -129,9 +103,7 @@ export class ThemePushService {
|
|
|
129
103
|
await ThemeImagesUtils.removeUploadedOriginalFiles(themeRootDir, uploadedImageData);
|
|
130
104
|
}
|
|
131
105
|
return {
|
|
132
|
-
|
|
133
|
-
rejectedImageData,
|
|
134
|
-
localFileNameToUploadedMap: uploadedImageData.reduce((acc, { originalFilename, uploadedFilename }) => {
|
|
106
|
+
localFileNameToUploaded: uploadedImageData.reduce((acc, { originalFilename, uploadedFilename }) => {
|
|
135
107
|
return {
|
|
136
108
|
...acc,
|
|
137
109
|
[originalFilename]: uploadedFilename ?? originalFilename
|
|
@@ -149,38 +121,8 @@ export class ThemePushService {
|
|
|
149
121
|
await writeJSONFile(join(themeRootDir, MODULES_DIRECTORY_NAME, moduleDirectoryName, THEME_MODULE_SETTINGS_FILE_NAME), JSON.parse(metaData[THEME_MODULE_SETTINGS_FILE_NAME]));
|
|
150
122
|
}
|
|
151
123
|
}
|
|
152
|
-
async _createFilesList(
|
|
124
|
+
async _createFilesList(themeRootDir, filesRecords, localFileNameToUploaded = {}) {
|
|
153
125
|
this.#loggerApi.debug('Creating filesList.json for the archive.');
|
|
154
|
-
|
|
155
|
-
if (deletedFiles?.length)
|
|
156
|
-
filesListContent.removed = deletedFiles;
|
|
157
|
-
if (allCustomModulesIds?.length)
|
|
158
|
-
filesListContent[FILES_LIST_CUSTOM_MODULES_TO_KEEP_IDS] = allCustomModulesIds;
|
|
159
|
-
await ThemeFilesUtils.createAFilesListFile(themeRootDir, filesListContent);
|
|
160
|
-
}
|
|
161
|
-
async _appendSettingsIfModifiedFileFromFolderThatContainsOne(modifiedFiles) {
|
|
162
|
-
const withSettingsFiles = [...modifiedFiles];
|
|
163
|
-
for (const filePath of modifiedFiles) {
|
|
164
|
-
if (await fileExists(join(dirname(filePath), 'settings.json'))) {
|
|
165
|
-
withSettingsFiles.push(join(dirname(filePath), 'settings.json'));
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
return uniq(withSettingsFiles);
|
|
169
|
-
}
|
|
170
|
-
async _getAllModulesIds(themeRootDir) {
|
|
171
|
-
const modulesPath = join(themeRootDir, MODULES_DIRECTORY_NAME);
|
|
172
|
-
if (!(await fileExists(modulesPath)))
|
|
173
|
-
return [];
|
|
174
|
-
const moduleDirs = await getAllDirectoriesNamesInside(modulesPath);
|
|
175
|
-
const modulesIds = [];
|
|
176
|
-
for (const moduleDir of moduleDirs) {
|
|
177
|
-
const moduleSettingsPath = join(themeRootDir, MODULES_DIRECTORY_NAME, moduleDir, THEME_MODULE_SETTINGS_FILE_NAME);
|
|
178
|
-
if (await fileExists(moduleSettingsPath)) {
|
|
179
|
-
const settingsContent = await readJSONFile(moduleSettingsPath);
|
|
180
|
-
if (settingsContent.id && Number.isInteger(settingsContent.id))
|
|
181
|
-
modulesIds.push(settingsContent.id);
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
return modulesIds;
|
|
126
|
+
await ThemeFilesStructureUtils.createAFilesListFile(themeRootDir, ThemeFilesStructureUtils.mapFilesRecordsToFilesList(filesRecords, localFileNameToUploaded));
|
|
185
127
|
}
|
|
186
128
|
}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import globs from 'fast-glob';
|
|
2
2
|
import { AppError } from '../../../../cli/utilities/features/logger/logs/app_error.js';
|
|
3
3
|
import { toUnixPath } from '../../../../utils/path_utils.js';
|
|
4
|
-
import {
|
|
4
|
+
import { ThemeFilesStructureUtils } from '../utils/files_structure/theme_files_structure_utils.js';
|
|
5
5
|
export class ThemePushUtils {
|
|
6
6
|
static async getAllFilesThatAreSendToRemote(themeDir) {
|
|
7
|
-
const filesStructure = await
|
|
7
|
+
const filesStructure = await ThemeFilesStructureUtils.getThemeFilesStructure(themeDir);
|
|
8
8
|
if (!filesStructure)
|
|
9
9
|
throw new AppError({
|
|
10
10
|
message: `Files structure not found in theme directory: ${themeDir}`,
|
package/build/theme/features/theme/utils/files_structure/theme_file_structure_errors_factory.js
CHANGED
|
@@ -1 +1,9 @@
|
|
|
1
|
-
|
|
1
|
+
import { AppError } from '../../../../../cli/utilities/features/logger/logs/app_error.js';
|
|
2
|
+
export class ThemeFileStructureErrorsFactory {
|
|
3
|
+
static createNoFilesStructureError() {
|
|
4
|
+
return new AppError({
|
|
5
|
+
code: 'theme.file_structure.no_files_structure',
|
|
6
|
+
message: 'No files structure found for the theme.'
|
|
7
|
+
});
|
|
8
|
+
}
|
|
9
|
+
}
|
|
@@ -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,25 +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
|
-
|
|
46
|
-
const filesList = filesRecords.reduce((acc, { fileGlob, fileName, state, actionKey }) => {
|
|
44
|
+
const mappedFilesRecords = filesRecords.reduce((acc, { fileGlob, fileName }) => {
|
|
47
45
|
const name = localFileNameToUploaded[fileName] ?? fileName;
|
|
48
|
-
if (state === FILE_STATES.deleted && actionKey === 'thumbnail') {
|
|
49
|
-
return {
|
|
50
|
-
...acc,
|
|
51
|
-
[fileGlob]: null
|
|
52
|
-
};
|
|
53
|
-
}
|
|
54
|
-
if (actionKey === 'skinstore_files' && state !== FILE_STATES.unchanged)
|
|
55
|
-
areAllSkinstoreFilesUnchanged = false;
|
|
56
|
-
if (state === FILE_STATES.deleted || (state === FILE_STATES.unchanged && actionKey !== 'skinstore_files'))
|
|
57
|
-
return acc;
|
|
58
46
|
if (looksLikeDirectory(fileGlob)) {
|
|
59
47
|
const existingFiles = acc[fileGlob] || [];
|
|
60
48
|
return {
|
|
@@ -69,31 +57,32 @@ export class ThemeFilesUtils {
|
|
|
69
57
|
};
|
|
70
58
|
}
|
|
71
59
|
}, {});
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
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;
|
|
75
67
|
}
|
|
76
68
|
static async createAFilesListFile(themeRootDir, filesList) {
|
|
77
69
|
if (!filesList || !Object.keys(filesList).length)
|
|
78
70
|
return;
|
|
79
|
-
const toUnixStyleFilesList = {
|
|
80
|
-
for (const [filePath, value] of Object.entries(filesList)) {
|
|
71
|
+
const toUnixStyleFilesList = Object.entries(filesList).reduce((acc, [filePath, value]) => {
|
|
81
72
|
const unixPath = toUnixPath(filePath);
|
|
82
|
-
const finalPath = (
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
static getFilesListFilePath(themeRootDir) {
|
|
90
|
-
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);
|
|
91
80
|
}
|
|
92
81
|
static async removeAFilesListFile(themeRootDir) {
|
|
93
|
-
await removeFile(
|
|
82
|
+
await removeFile(join(themeRootDir, THEME_FILES_LIST_FILE_NAME));
|
|
94
83
|
}
|
|
95
84
|
static async updateFilesStructure(themeDir) {
|
|
96
|
-
const fileStructure = await
|
|
85
|
+
const fileStructure = await ThemeFilesStructureUtils.getThemeFilesStructure(themeDir);
|
|
97
86
|
const checksumsFiles = [
|
|
98
87
|
`${SHOPER_THEME_METADATA_DIR}/${THEME_INITIAL_CHECKSUMS_FILE_NAME}`,
|
|
99
88
|
`${SHOPER_THEME_METADATA_DIR}/${THEME_INITIAL_CHECKSUMS_VERITY_FILE_NAME}`,
|
|
@@ -126,6 +115,6 @@ export class ThemeFilesUtils {
|
|
|
126
115
|
}
|
|
127
116
|
};
|
|
128
117
|
});
|
|
129
|
-
await
|
|
118
|
+
await ThemeFilesStructureUtils.writeThemeFilesStructure(themeDir, fileStructure);
|
|
130
119
|
}
|
|
131
120
|
}
|
|
@@ -7,6 +7,7 @@ import { ThemeChecksums } from '../../../../class/checksums/theme_checksums.js';
|
|
|
7
7
|
export class HiddenDirectoryUtils {
|
|
8
8
|
static async ensureFilesInsideThemeMetaDataDirectoryUntouched(themeDirectory, logger) {
|
|
9
9
|
const themeMetadataPath = this.getThemeHiddenDirectoryPath(themeDirectory);
|
|
10
|
+
console.log('themeMetadataPath', themeMetadataPath);
|
|
10
11
|
const themeChecksums = new ThemeChecksums({
|
|
11
12
|
themeDir: themeDirectory,
|
|
12
13
|
loggerApi: logger
|
|
@@ -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
|
#loggerApi;
|
|
14
14
|
constructor({ loggerApi }) {
|
|
@@ -23,15 +23,13 @@ export class ThemeVerifyService {
|
|
|
23
23
|
throw ThemePushErrorsFactory.createErrorWhilePushingUnpublishedThemeWithSkinstoreData(credentials.shopUrl);
|
|
24
24
|
try {
|
|
25
25
|
this.#loggerApi.debug('Getting file records from action data.');
|
|
26
|
-
//TODO to do api?
|
|
27
26
|
const filesRecords = await ThemeActionsUtils.getFilesRecordsFromActionData({
|
|
28
27
|
themeRootDir,
|
|
29
28
|
themeAction: verifyAction,
|
|
30
|
-
filesStructure
|
|
31
|
-
themeChecksums
|
|
29
|
+
filesStructure
|
|
32
30
|
});
|
|
33
31
|
this.#loggerApi.debug('Filtering for modified or not created files to verify.');
|
|
34
|
-
const filesToVerify =
|
|
32
|
+
const filesToVerify = await ArrayUtils.asyncFilter(filesRecords, async ({ path }) => (await themeChecksums.hasThemeFileBeenModified(path)) || !(await themeChecksums.hasThemeFileBeenCreated(path)));
|
|
35
33
|
this.#loggerApi.debug('Files to verify determined.', { details: { count: filesToVerify.length } });
|
|
36
34
|
if (filesToVerify.length) {
|
|
37
35
|
this.#loggerApi.info(`Verifying ${filesToVerify.length} theme files.`);
|
|
@@ -48,16 +46,11 @@ export class ThemeVerifyService {
|
|
|
48
46
|
await this._createFilesList(themeRootDir, filesToVerify);
|
|
49
47
|
const themeArchivePath = join(tmpDir, `${uuid()}.zip`);
|
|
50
48
|
this.#loggerApi.info('Creating theme archive for verification.');
|
|
51
|
-
await
|
|
49
|
+
await new ThemeArchive(themeRootDir).createFullArchive({
|
|
52
50
|
dist: themeArchivePath,
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
actionValue: THEME_WILDCARD_ACTION_NAME,
|
|
57
|
-
actionType: THEME_ACTIONS_TYPES.push,
|
|
58
|
-
filesStructure,
|
|
59
|
-
rootDir: themeRootDir
|
|
60
|
-
})
|
|
51
|
+
actionValue: THEME_WILDCARD_ACTION_NAME,
|
|
52
|
+
actionType: THEME_ACTIONS_TYPES.push,
|
|
53
|
+
logger: this.#loggerApi
|
|
61
54
|
});
|
|
62
55
|
this.#loggerApi.info('Theme archive created.');
|
|
63
56
|
const { isSuccess, messages } = await themeFilesUploadApi.uploadArchive({
|
|
@@ -70,11 +63,11 @@ export class ThemeVerifyService {
|
|
|
70
63
|
}
|
|
71
64
|
finally {
|
|
72
65
|
this.#loggerApi.debug('Cleaning up files list.');
|
|
73
|
-
await
|
|
66
|
+
await ThemeFilesStructureUtils.removeAFilesListFile(themeRootDir);
|
|
74
67
|
}
|
|
75
68
|
}
|
|
76
69
|
async _createFilesList(themeRootDir, filesRecords) {
|
|
77
70
|
this.#loggerApi.debug('Creating filesList.json for the archive.');
|
|
78
|
-
await
|
|
71
|
+
await ThemeFilesStructureUtils.createAFilesListFile(themeRootDir, ThemeFilesStructureUtils.mapFilesRecordsToFilesList(filesRecords));
|
|
79
72
|
}
|
|
80
73
|
}
|
|
@@ -12,6 +12,8 @@ export const computeFileChecksum = async (filePath, algorithm = 'md5') => {
|
|
|
12
12
|
const hash = createHash(algorithm);
|
|
13
13
|
const stream = createReadStream(filePath);
|
|
14
14
|
stream.on('data', (data) => {
|
|
15
|
+
console.log('data chunk received for hashing');
|
|
16
|
+
console.log('data', data);
|
|
15
17
|
hash.update(data);
|
|
16
18
|
});
|
|
17
19
|
stream.on('close', () => {
|
|
@@ -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,
|
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.6.4-
|
|
5
|
+
"version": "0.6.4-2",
|
|
6
6
|
"description": "CLI tool for Shoper",
|
|
7
7
|
"author": "Joanna Firek",
|
|
8
8
|
"license": "MIT",
|
|
@@ -72,7 +72,6 @@
|
|
|
72
72
|
"strip-ansi": "7.1.0",
|
|
73
73
|
"tmp-promise": "3.0.3",
|
|
74
74
|
"uuid": "11.1.0",
|
|
75
|
-
"micromatch": "4.0.8",
|
|
76
75
|
"walk-sync": "3.0.0",
|
|
77
76
|
"yauzl": "3.2.0",
|
|
78
77
|
"yazl": "3.3.1",
|
|
@@ -87,15 +86,14 @@
|
|
|
87
86
|
"@types/fs-extra": "11.0.4",
|
|
88
87
|
"@types/jest": "29.5.14",
|
|
89
88
|
"@types/jsonwebtoken": "9.0.9",
|
|
89
|
+
"@types/klaw": "3.0.7",
|
|
90
|
+
"@types/lodash": "4.17.17",
|
|
90
91
|
"@types/node": "18.19.84",
|
|
91
92
|
"@types/react": "19.1.8",
|
|
92
93
|
"@types/semver": "7.7.0",
|
|
93
94
|
"@types/tmp": "0.2.6",
|
|
94
95
|
"@types/yauzl": "2.10.3",
|
|
95
96
|
"@types/yazl": "2.4.6",
|
|
96
|
-
"@types/klaw": "3.0.7",
|
|
97
|
-
"@types/lodash": "4.17.17",
|
|
98
|
-
"@types/micromatch": "4.0.9",
|
|
99
97
|
"@typescript-eslint/eslint-plugin": "8.29.1",
|
|
100
98
|
"babel-jest": "29.7.0",
|
|
101
99
|
"eslint": "9.39.1",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,25 +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, logger }) {
|
|
7
|
-
try {
|
|
8
|
-
await this._formatJsonFiles(rootDir, files);
|
|
9
|
-
return createZip({
|
|
10
|
-
files,
|
|
11
|
-
baseDir: rootDir,
|
|
12
|
-
logger,
|
|
13
|
-
dist
|
|
14
|
-
});
|
|
15
|
-
}
|
|
16
|
-
catch (err) {
|
|
17
|
-
throw ThemeArchiveErrorsFactory.createErrorWhileCreatingThemeArchive(err);
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
static async _formatJsonFiles(themeRootDir, filesToUpload) {
|
|
21
|
-
await Promise.all(filesToUpload
|
|
22
|
-
.filter((path) => extname(path).toLowerCase() === '.json')
|
|
23
|
-
.map((jsonFile) => formatJSONFile(join(themeRootDir, jsonFile))));
|
|
24
|
-
}
|
|
25
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export const FILES_LIST_CUSTOM_MODULES_TO_KEEP_IDS = 'customModulesIdsToKeep';
|