@shoper/cli 0.5.1 → 0.5.2-0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/cli/class/errors/http/http_errors_factory.js +1 -1
- package/build/cli/core/cli_setup.js +6 -7
- package/build/cli/index.js +1 -0
- package/build/theme/class/checksums/theme_checksums.js +47 -9
- package/build/theme/commands/pull/theme_pull_command.js +4 -3
- package/build/theme/commands/push/theme_push_command.js +17 -8
- package/build/theme/commands/theme_verify_command.js +3 -3
- package/build/theme/features/theme/actions/theme_actions_constants.js +2 -1
- package/build/theme/features/theme/actions/theme_actions_utils.js +31 -5
- 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 +80 -28
- package/build/theme/features/theme/push/theme_push_utils.js +2 -2
- package/build/theme/features/theme/utils/archive/theme_archive_utils.js +24 -0
- package/build/theme/{class/archive/theme_archive_errors_factory.js → features/theme/utils/archive/theme_archive_utils_errors_factory.js} +1 -1
- package/build/theme/features/theme/utils/files/them_files_constants.js +1 -0
- package/build/theme/features/theme/utils/{files_structure/theme_files_structure_utils.js → files/theme_files_utils.js} +30 -23
- package/build/theme/features/theme/verify/verify/theme_verify_service.js +17 -11
- package/build/utils/array_utils.js +3 -0
- package/build/utils/fs/fs_constants.js +6 -0
- package/build/utils/fs/fs_utils.js +1 -1
- package/package.json +12 -10
- package/build/theme/class/archive/theme_archive.js +0 -44
- package/build/theme/features/theme/utils/files_structure/theme_file_structure_errors_factory.js +0 -10
|
@@ -26,7 +26,7 @@ export const cliSetup = async () => {
|
|
|
26
26
|
ExecutionContextInitializer,
|
|
27
27
|
CliAuthTokensInitializer,
|
|
28
28
|
CliAuthInitializer,
|
|
29
|
-
...
|
|
29
|
+
...getInitializersBasedOnCommand()
|
|
30
30
|
],
|
|
31
31
|
options: {
|
|
32
32
|
featuresTracking: false,
|
|
@@ -34,16 +34,15 @@ export const cliSetup = async () => {
|
|
|
34
34
|
}
|
|
35
35
|
});
|
|
36
36
|
};
|
|
37
|
-
const
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
return getCommandWithTopicBaseInitializers();
|
|
37
|
+
const getInitializersBasedOnCommand = () => {
|
|
38
|
+
if (isCommandWithTopic(process.argv[2]))
|
|
39
|
+
return getInitializersBasedOnCommandWithTopic();
|
|
41
40
|
return getCommandWithoutTopicBaseInitializers();
|
|
42
41
|
};
|
|
43
|
-
const
|
|
42
|
+
const isCommandWithTopic = (arg) => {
|
|
44
43
|
return arg === THEME_TOPIC_NAME || arg === CLI_AUTH_TOPIC_NAME;
|
|
45
44
|
};
|
|
46
|
-
const
|
|
45
|
+
const getInitializersBasedOnCommandWithTopic = () => {
|
|
47
46
|
const topic = process.argv[2];
|
|
48
47
|
const command = process.argv[3];
|
|
49
48
|
switch (topic) {
|
package/build/cli/index.js
CHANGED
|
@@ -10,6 +10,7 @@ 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';
|
|
13
14
|
export class ThemeChecksums {
|
|
14
15
|
#themeDir;
|
|
15
16
|
#currentChecksumsFilePath;
|
|
@@ -31,12 +32,6 @@ export class ThemeChecksums {
|
|
|
31
32
|
this.#initialChecksums = await this._getChecksums(this.#initialChecksumsFilePath);
|
|
32
33
|
return this.#initialChecksums;
|
|
33
34
|
}
|
|
34
|
-
getInitialChecksumsSync() {
|
|
35
|
-
if (this.#initialChecksums)
|
|
36
|
-
return this.#initialChecksums;
|
|
37
|
-
this.#initialChecksums = this._getChecksumsSync(this.#initialChecksumsFilePath);
|
|
38
|
-
return this.#initialChecksums;
|
|
39
|
-
}
|
|
40
35
|
async getCurrentChecksums() {
|
|
41
36
|
if (this.#currentChecksums)
|
|
42
37
|
return this.#currentChecksums;
|
|
@@ -70,10 +65,22 @@ export class ThemeChecksums {
|
|
|
70
65
|
const initialChecksum = await this.getInitialChecksumFromPath(path);
|
|
71
66
|
return !initialChecksum && (await fileExists(join(this.#themeDir, path)));
|
|
72
67
|
}
|
|
73
|
-
async
|
|
74
|
-
const currentChecksum = await this.getCurrentChecksumFromPath(path);
|
|
68
|
+
async getFileState(path) {
|
|
75
69
|
const initialChecksum = await this.getInitialChecksumFromPath(path);
|
|
76
|
-
|
|
70
|
+
const currentChecksum = await this.getCurrentChecksumFromPath(path);
|
|
71
|
+
const fileExistsInFs = await fileExists(join(this.#themeDir, path));
|
|
72
|
+
if (!initialChecksum && fileExistsInFs) {
|
|
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
|
+
}
|
|
77
84
|
}
|
|
78
85
|
async verify() {
|
|
79
86
|
const initialChecksumFilePath = this.#initialChecksumsFilePath;
|
|
@@ -138,6 +145,37 @@ export class ThemeChecksums {
|
|
|
138
145
|
const directoriesChecksums = computeDirectoriesChecksums(filesChecksumsInDirectories);
|
|
139
146
|
return { ...filesChecksums, ...directoriesChecksums };
|
|
140
147
|
}
|
|
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
|
+
}
|
|
141
179
|
async _getInitialChecksumsVerification() {
|
|
142
180
|
return await readJSONFile(this.#initialChecksumsVerificationFilePath);
|
|
143
181
|
}
|
|
@@ -24,7 +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 { ThemeError } from '../ui/theme_error.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
|
|
28
29
|
//TODO jak jest error w pullu wowczas usuwamy docelowo pociagniety skin
|
|
29
30
|
export class ThemePullCommand extends BaseThemeCommand {
|
|
30
31
|
static summary = 'Downloads the current version of your theme from the store and overwrites your local theme files.';
|
|
@@ -151,9 +152,9 @@ export class ThemePullCommand extends BaseThemeCommand {
|
|
|
151
152
|
dist: tmpDir
|
|
152
153
|
}
|
|
153
154
|
});
|
|
154
|
-
const localModulesPath = join(executionContext.themeRootDir,
|
|
155
|
+
const localModulesPath = join(executionContext.themeRootDir, MODULES_DIRECTORY_NAME);
|
|
155
156
|
const fetchedThemePath = join(tmpDir, name);
|
|
156
|
-
const fetchedModulesPath = join(fetchedThemePath,
|
|
157
|
+
const fetchedModulesPath = join(fetchedThemePath, MODULES_DIRECTORY_NAME);
|
|
157
158
|
if ((await directoryExists(localModulesPath)) && (await directoryExists(fetchedModulesPath)))
|
|
158
159
|
await ThemeResourcesWithIdDirectoryUtils.updateDirectoryNamesOfResourcesWithIdAccordingToLocalThemeNames(localModulesPath, fetchedModulesPath, fetchedThemePath);
|
|
159
160
|
this.#spinner.stop();
|
|
@@ -4,8 +4,6 @@ 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';
|
|
9
7
|
import { renderOnce } from '../../../ui/ui_utils.js';
|
|
10
8
|
import { UnpermittedCommandError } from '../ui/unpermitted_command_error.js';
|
|
11
9
|
import React from 'react';
|
|
@@ -14,7 +12,7 @@ import { MissingCredentialsError } from '../../../cli/commands/auth/ui/missing_c
|
|
|
14
12
|
import { ThemePushedSuccess } from './ui/theme_pushed_success.js';
|
|
15
13
|
import ora from 'ora';
|
|
16
14
|
import { ThemePushSkipInfo } from './ui/theme_push_skip_into.js';
|
|
17
|
-
import {
|
|
15
|
+
import { ThemeFilesUtils } from '../../features/theme/utils/files/theme_files_utils.js';
|
|
18
16
|
import { ThemeInfoUtils } from '../../features/theme/info/theme_info_utils.js';
|
|
19
17
|
import { ThemeChecksums } from '../../class/checksums/theme_checksums.js';
|
|
20
18
|
import { ThemeFilesUpload } from '../../class/files_upload/theme_files_upload.js';
|
|
@@ -24,6 +22,8 @@ import { MissingThemeFiles } from '../ui/missing_theme_files.js';
|
|
|
24
22
|
import { SHOPER_THEME_METADATA_DIR } from '../../constants/directory_contstants.js';
|
|
25
23
|
import { THEME_FILES_STRUCTURE_FILE_NAME } from '../../features/theme/theme_constants.js';
|
|
26
24
|
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
|
export class ThemePushCommand extends BaseThemeCommand {
|
|
29
29
|
static summary = 'Uploads your local theme files to the store and overwrites the current version of the theme in your store.';
|
|
@@ -63,7 +63,7 @@ export class ThemePushCommand extends BaseThemeCommand {
|
|
|
63
63
|
return;
|
|
64
64
|
}
|
|
65
65
|
const initialChecksums = await themeChecksums.getInitialChecksums();
|
|
66
|
-
const permissions = await
|
|
66
|
+
const permissions = await ThemeFilesUtils.getFilesPermissions(executionContext.themeRootDir);
|
|
67
67
|
const themeFilesUploadApi = new ThemeFilesUpload({
|
|
68
68
|
themeRootDir: executionContext.themeRootDir,
|
|
69
69
|
credentials,
|
|
@@ -75,7 +75,7 @@ export class ThemePushCommand extends BaseThemeCommand {
|
|
|
75
75
|
renderOnce(React.createElement(ThemePushSkipInfo, null));
|
|
76
76
|
return;
|
|
77
77
|
}
|
|
78
|
-
const validationResult = await
|
|
78
|
+
const validationResult = await ThemeFilesUtils.validateThemeDirectoryStructure({
|
|
79
79
|
//TDO przeniesc do theme checksums
|
|
80
80
|
checksums: mapChecksumToTree(initialChecksums),
|
|
81
81
|
permissions: mapToPermissionsTree(permissions),
|
|
@@ -87,16 +87,25 @@ export class ThemePushCommand extends BaseThemeCommand {
|
|
|
87
87
|
renderOnce(React.createElement(ThemeUnpermittedActionsError, { unpermittedActions: validationResult.unpermittedActions }));
|
|
88
88
|
return;
|
|
89
89
|
}
|
|
90
|
-
const filesStructure = await
|
|
90
|
+
const filesStructure = await ThemeFilesUtils.getThemeFilesStructure(executionContext.themeRootDir);
|
|
91
91
|
if (!filesStructure) {
|
|
92
92
|
renderOnce(React.createElement(MissingThemeFiles, { files: `${SHOPER_THEME_METADATA_DIR}/${THEME_FILES_STRUCTURE_FILE_NAME}` }));
|
|
93
93
|
return;
|
|
94
94
|
}
|
|
95
95
|
spinner = ora('Pushing theme...').start();
|
|
96
|
-
|
|
96
|
+
const partialPushAction = themeActionsApi.getThemeAction({
|
|
97
|
+
actionType: THEME_ACTIONS_TYPES.partialPush,
|
|
98
|
+
themeId: executionContext.themeId,
|
|
99
|
+
credentials
|
|
100
|
+
});
|
|
101
|
+
if (!pushAction) {
|
|
102
|
+
renderOnce(React.createElement(UnpermittedCommandError, { themeId: executionContext.themeId, commandName: "push" }));
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
await themePushApi.partialPush({
|
|
97
106
|
credentials,
|
|
98
107
|
filesStructure,
|
|
99
|
-
|
|
108
|
+
action: partialPushAction,
|
|
100
109
|
themeChecksums,
|
|
101
110
|
executionContext,
|
|
102
111
|
themeFilesUploadApi
|
|
@@ -1,7 +1,6 @@
|
|
|
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';
|
|
5
4
|
import { ThemeChecksums } from '../class/checksums/theme_checksums.js';
|
|
6
5
|
import { THEME_VERIFY_API_NAME } from '../features/theme/verify/theme_verify_constants.js';
|
|
7
6
|
import { THEME_ACTIONS_API_NAME, THEME_ACTIONS_TYPES } from '../features/theme/actions/theme_actions_constants.js';
|
|
@@ -18,6 +17,7 @@ import { MissingCredentialsError } from '../../cli/commands/auth/ui/missing_cred
|
|
|
18
17
|
import { OutsideOfThemeDirectoryContextError } from './ui/ouside_of_theme_directory_context_error.js';
|
|
19
18
|
import ora from 'ora';
|
|
20
19
|
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 ThemeFilesUtils.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 ThemeFilesUtils.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;
|
|
@@ -3,8 +3,10 @@ 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';
|
|
6
8
|
export class ThemeActionsUtils {
|
|
7
|
-
static
|
|
9
|
+
static getFilesGlobsThatMatchesAction({ filesStructure, actionValue, actionType }) {
|
|
8
10
|
return Object.entries(filesStructure).reduce((acc, [filePath, fileStructureItem]) => {
|
|
9
11
|
if (fileStructureItem._links?.[actionType] && this._doesActionValueMatch(fileStructureItem._links[actionType], actionValue)) {
|
|
10
12
|
return [...acc, toUnixPath(filePath)];
|
|
@@ -12,11 +14,32 @@ export class ThemeActionsUtils {
|
|
|
12
14
|
return acc;
|
|
13
15
|
}, []);
|
|
14
16
|
}
|
|
15
|
-
static async
|
|
17
|
+
static async getFilesThatMatchesAction({ actionType, actionValue, filesStructure, rootDir }) {
|
|
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 }) {
|
|
16
38
|
const filesRecords = [];
|
|
39
|
+
const initialChecksums = await themeChecksums.getInitialChecksums();
|
|
17
40
|
for (const [actionKey, actionData] of Object.entries(themeAction.data)) {
|
|
18
41
|
if (actionData.type === THEME_ACTION_DATA_TYPE.file) {
|
|
19
|
-
const filesGlobs = ThemeActionsUtils.
|
|
42
|
+
const filesGlobs = ThemeActionsUtils.getFilesGlobsThatMatchesAction({
|
|
20
43
|
//TODO remove when backend fixed
|
|
21
44
|
actionType: 'push',
|
|
22
45
|
// actionType: themeAction.actionType,
|
|
@@ -24,6 +47,7 @@ export class ThemeActionsUtils {
|
|
|
24
47
|
filesStructure
|
|
25
48
|
});
|
|
26
49
|
for (const fileGlob of filesGlobs) {
|
|
50
|
+
const checksumMatchedFiles = micromatch(Object.keys(initialChecksums), [fileGlob]);
|
|
27
51
|
const files = await globs(fileGlob, {
|
|
28
52
|
suppressErrors: true,
|
|
29
53
|
onlyFiles: true,
|
|
@@ -35,13 +59,15 @@ export class ThemeActionsUtils {
|
|
|
35
59
|
? fileGlob.slice(0, fileGlob.length - 2)
|
|
36
60
|
: fileGlob;
|
|
37
61
|
}
|
|
38
|
-
|
|
62
|
+
const deletedFiles = difference(checksumMatchedFiles, files);
|
|
63
|
+
for (const filePath of [...files, ...deletedFiles]) {
|
|
39
64
|
filesRecords.push({
|
|
40
65
|
actionData,
|
|
41
66
|
actionKey,
|
|
42
67
|
path: filePath,
|
|
43
68
|
fileName: basename(filePath),
|
|
44
|
-
fileGlob: processedFileGlob
|
|
69
|
+
fileGlob: processedFileGlob,
|
|
70
|
+
state: await themeChecksums.getFileState(filePath)
|
|
45
71
|
});
|
|
46
72
|
}
|
|
47
73
|
}
|
|
@@ -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';
|
|
12
11
|
import { ThemeChecksums } from '../../../../class/checksums/theme_checksums.js';
|
|
13
12
|
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 ThemeFilesUtils.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';
|
|
10
9
|
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 ThemeFilesUtils.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';
|
|
6
5
|
import { SHOPER_THEME_METADATA_DIR } from '../../../../constants/directory_contstants.js';
|
|
7
6
|
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 ThemeFilesUtils.getThemeRootDirectories(themeDir);
|
|
43
43
|
return (await getAllDirectoriesNamesInside(themeDir, {
|
|
44
44
|
recursive: false,
|
|
45
45
|
hidden: true
|
|
@@ -1,25 +1,27 @@
|
|
|
1
1
|
import tmp from 'tmp-promise';
|
|
2
2
|
import { THEME_ACTIONS_TYPES } from '../../actions/theme_actions_constants.js';
|
|
3
|
-
import { join } from '../../../../../utils/path_utils.js';
|
|
3
|
+
import { dirname, 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 { THEME_MODULE_SETTINGS_FILE_NAME } from '../theme_push_constants.js';
|
|
6
|
+
import { THEME_FILES_LIST_FILE_NAME, 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 { fileExists, getAllDirectoriesNamesInside, readJSONFile, 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 {
|
|
14
|
-
import { ThemeFilesStructureUtils } from '../../utils/files_structure/theme_files_structure_utils.js';
|
|
13
|
+
import { ThemeFilesUtils } from '../../utils/files/theme_files_utils.js';
|
|
15
14
|
import { ThemeActionsUtils } from '../../actions/theme_actions_utils.js';
|
|
16
|
-
import {
|
|
15
|
+
import { ThemeArchiveUtils } from '../../utils/archive/theme_archive_utils.js';
|
|
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';
|
|
17
19
|
export class ThemePushService {
|
|
18
20
|
#themeFetchApi;
|
|
19
21
|
constructor({ themeFetchApi }) {
|
|
20
22
|
this.#themeFetchApi = themeFetchApi;
|
|
21
23
|
}
|
|
22
|
-
async
|
|
24
|
+
async partialPush({ action, filesStructure, executionContext, themeChecksums, themeFilesUploadApi, credentials }) {
|
|
23
25
|
const { path: tmpDir } = await tmp.dir({ unsafeCleanup: true });
|
|
24
26
|
const themeRootDir = executionContext.themeRootDir;
|
|
25
27
|
if (await themeChecksums.hasThemeFileBeenCreated(ThemePublishUtils.getSkinStoreSettingsFilePath(themeRootDir)))
|
|
@@ -28,30 +30,54 @@ export class ThemePushService {
|
|
|
28
30
|
//TODO to do api?
|
|
29
31
|
const filesRecords = await ThemeActionsUtils.getFilesRecordsFromActionData({
|
|
30
32
|
themeRootDir,
|
|
31
|
-
themeAction:
|
|
32
|
-
filesStructure
|
|
33
|
+
themeAction: action,
|
|
34
|
+
filesStructure,
|
|
35
|
+
themeChecksums
|
|
33
36
|
});
|
|
34
|
-
const filesToUpload =
|
|
37
|
+
const filesToUpload = filesRecords.filter(({ state }) => [FILE_STATES.modified, FILE_STATES.created].includes(state));
|
|
38
|
+
let localFileNameToUploaded;
|
|
35
39
|
if (filesToUpload.length) {
|
|
36
|
-
const
|
|
40
|
+
const uploadData = await this._uploadThemeFiles({
|
|
37
41
|
filesToUpload,
|
|
38
42
|
credentials,
|
|
39
43
|
themeRootDir,
|
|
40
44
|
themeFilesUploadApi
|
|
41
45
|
});
|
|
42
|
-
|
|
43
|
-
}
|
|
44
|
-
else {
|
|
45
|
-
await this._createFilesList(themeRootDir, filesRecords);
|
|
46
|
+
localFileNameToUploaded = uploadData.localFileNameToUploaded;
|
|
46
47
|
}
|
|
47
48
|
const themeArchivePath = join(tmpDir, `${uuid()}.zip`);
|
|
48
|
-
await
|
|
49
|
-
|
|
49
|
+
const filesInArchive = await ThemeActionsUtils.getFilesThatMatchesAction({
|
|
50
|
+
actionValue: THEME_WILDCARD_ACTION_NAME,
|
|
51
|
+
actionType: THEME_ACTIONS_TYPES.push,
|
|
52
|
+
filesStructure,
|
|
53
|
+
rootDir: themeRootDir
|
|
54
|
+
});
|
|
55
|
+
const groupedFiles = await themeChecksums.groupFilesByStatus(filesInArchive);
|
|
56
|
+
const deletedFiles = await ThemeActionsUtils.getDeletedFilesThatMatchesAction({
|
|
57
|
+
actionType: THEME_ACTIONS_TYPES.push,
|
|
50
58
|
actionValue: THEME_WILDCARD_ACTION_NAME,
|
|
51
|
-
|
|
59
|
+
themeChecksums,
|
|
60
|
+
filesStructure
|
|
61
|
+
});
|
|
62
|
+
await this._createFilesList({
|
|
63
|
+
themeRootDir,
|
|
64
|
+
filesRecords,
|
|
65
|
+
localFileNameToUploaded,
|
|
66
|
+
deletedFiles,
|
|
67
|
+
allCustomModulesIds: await this._getAllModulesIds(themeRootDir)
|
|
68
|
+
});
|
|
69
|
+
await ThemeArchiveUtils.create({
|
|
70
|
+
dist: themeArchivePath,
|
|
71
|
+
rootDir: themeRootDir,
|
|
72
|
+
files: [
|
|
73
|
+
...(await this._appendSettingsIfModifiedFileFromFolderThatContainsOne(groupedFiles.modified)),
|
|
74
|
+
...groupedFiles.created,
|
|
75
|
+
(await fileExists(ThemeFilesUtils.getFilesListFilePath(themeRootDir))) ? THEME_FILES_LIST_FILE_NAME : undefined
|
|
76
|
+
].filter(Boolean)
|
|
52
77
|
});
|
|
78
|
+
await ThemeFilesUtils.removeAFilesListFile(themeRootDir);
|
|
53
79
|
const { resources, modules } = await themeFilesUploadApi.uploadArchive({
|
|
54
|
-
action
|
|
80
|
+
action,
|
|
55
81
|
themeArchivePath,
|
|
56
82
|
credentials
|
|
57
83
|
});
|
|
@@ -61,18 +87,20 @@ export class ThemePushService {
|
|
|
61
87
|
await removeOldResources(themeRootDir, resources);
|
|
62
88
|
await this.#themeFetchApi.fetchResources(credentials.shopUrl, themeRootDir, resources);
|
|
63
89
|
}
|
|
90
|
+
//TODO only changed files checksum
|
|
64
91
|
await themeChecksums.updateAllChecksums();
|
|
65
92
|
}
|
|
66
|
-
|
|
67
|
-
|
|
93
|
+
catch (err) {
|
|
94
|
+
console.log('err', err);
|
|
95
|
+
await ThemeFilesUtils.removeAFilesListFile(themeRootDir);
|
|
96
|
+
throw err;
|
|
68
97
|
}
|
|
69
98
|
}
|
|
70
99
|
async _uploadThemeFiles({ filesToUpload, themeRootDir, credentials, themeFilesUploadApi }) {
|
|
71
100
|
try {
|
|
72
|
-
const { uploadedImageData
|
|
101
|
+
const { uploadedImageData } = await themeFilesUploadApi.uploadFiles(filesToUpload);
|
|
73
102
|
if (uploadedImageData.length)
|
|
74
103
|
await ThemeImagesUtils.removeUploadedOriginalFiles(themeRootDir, uploadedImageData);
|
|
75
|
-
// if (rejectedImageData.length) await this._removeUploadedThemeFiles(rejectedImageData, themeRootDir);
|
|
76
104
|
return {
|
|
77
105
|
localFileNameToUploaded: uploadedImageData.reduce((acc, { originalFilename, uploadedFilename }) => {
|
|
78
106
|
return {
|
|
@@ -86,15 +114,39 @@ export class ThemePushService {
|
|
|
86
114
|
throw ThemePushErrorsFactory.createErrorWhileUploadingThemeFiles(credentials.shopUrl, err);
|
|
87
115
|
}
|
|
88
116
|
}
|
|
89
|
-
async _removeUploadedThemeFiles(uploadedImageData, themeRootDir) {
|
|
90
|
-
await Promise.all(uploadedImageData.map(({ location, originalFilename }) => removeFile(join(themeRootDir, location, originalFilename))));
|
|
91
|
-
}
|
|
92
117
|
async _updateDataForNewCreatedModules({ modules, themeRootDir }) {
|
|
93
118
|
for (const [moduleDirectoryName, metaData] of Object.entries(modules)) {
|
|
94
119
|
await writeJSONFile(join(themeRootDir, MODULES_DIRECTORY_NAME, moduleDirectoryName, THEME_MODULE_SETTINGS_FILE_NAME), JSON.parse(metaData[THEME_MODULE_SETTINGS_FILE_NAME]));
|
|
95
120
|
}
|
|
96
121
|
}
|
|
97
|
-
async _createFilesList(
|
|
98
|
-
|
|
122
|
+
async _createFilesList({ deletedFiles, localFileNameToUploaded, filesRecords, themeRootDir, allCustomModulesIds }) {
|
|
123
|
+
const filesListContent = ThemeFilesUtils.mapFilesRecordsToFilesList(filesRecords, localFileNameToUploaded);
|
|
124
|
+
if (deletedFiles?.length)
|
|
125
|
+
filesListContent.removed = deletedFiles;
|
|
126
|
+
if (allCustomModulesIds?.length)
|
|
127
|
+
filesListContent[FILES_LIST_CUSTOM_MODULES_TO_KEEP_IDS] = allCustomModulesIds;
|
|
128
|
+
await ThemeFilesUtils.createAFilesListFile(themeRootDir, filesListContent);
|
|
129
|
+
}
|
|
130
|
+
async _appendSettingsIfModifiedFileFromFolderThatContainsOne(modifiedFiles) {
|
|
131
|
+
const withSettingsFiles = [...modifiedFiles];
|
|
132
|
+
for (const filePath of modifiedFiles) {
|
|
133
|
+
if (await fileExists(join(dirname(filePath), 'settings.json'))) {
|
|
134
|
+
withSettingsFiles.push(join(dirname(filePath), 'settings.json'));
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
return uniq(withSettingsFiles);
|
|
138
|
+
}
|
|
139
|
+
async _getAllModulesIds(themeRootDir) {
|
|
140
|
+
const moduleDirs = await getAllDirectoriesNamesInside(join(themeRootDir, MODULES_DIRECTORY_NAME));
|
|
141
|
+
const modulesIds = [];
|
|
142
|
+
for (const moduleDir of moduleDirs) {
|
|
143
|
+
const moduleSettingsPath = join(themeRootDir, MODULES_DIRECTORY_NAME, moduleDir, THEME_MODULE_SETTINGS_FILE_NAME);
|
|
144
|
+
if (await fileExists(moduleSettingsPath)) {
|
|
145
|
+
const settingsContent = await readJSONFile(moduleSettingsPath);
|
|
146
|
+
if (settingsContent.id && Number.isInteger(settingsContent.id))
|
|
147
|
+
modulesIds.push(settingsContent.id);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
return modulesIds;
|
|
99
151
|
}
|
|
100
152
|
}
|
|
@@ -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 { ThemeFilesUtils } from '../utils/files/theme_files_utils.js';
|
|
5
5
|
export class ThemePushUtils {
|
|
6
6
|
static async getAllFilesThatAreSendToRemote(themeDir) {
|
|
7
|
-
const filesStructure = await
|
|
7
|
+
const filesStructure = await ThemeFilesUtils.getThemeFilesStructure(themeDir);
|
|
8
8
|
if (!filesStructure)
|
|
9
9
|
throw new AppError({
|
|
10
10
|
message: `Files structure not found in theme directory: ${themeDir}`,
|
|
@@ -0,0 +1,24 @@
|
|
|
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
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const FILES_LIST_CUSTOM_MODULES_TO_KEEP_IDS = 'customModulesIdsToKeep';
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { readJSONFile, removeFile, writeJSONFile } from '../../../../../utils/fs/fs_utils.js';
|
|
1
|
+
import { fileExists, isDirectory, 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,7 +7,8 @@ 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
|
-
|
|
10
|
+
import { FILE_STATES } from '../../../../../utils/fs/fs_constants.js';
|
|
11
|
+
export class ThemeFilesUtils {
|
|
11
12
|
static async getThemeFilesStructure(themeDirectory) {
|
|
12
13
|
const filesStructure = await readJSONFile(join(themeDirectory, SHOPER_THEME_METADATA_DIR, THEME_FILES_STRUCTURE_FILE_NAME));
|
|
13
14
|
if (!isWindowsOs())
|
|
@@ -19,7 +20,7 @@ export class ThemeFilesStructureUtils {
|
|
|
19
20
|
await writeJSONFile(filePath, filesStructure);
|
|
20
21
|
}
|
|
21
22
|
static async getFilesPermissions(themeDirectory) {
|
|
22
|
-
const fileStructure = await
|
|
23
|
+
const fileStructure = await ThemeFilesUtils.getThemeFilesStructure(themeDirectory);
|
|
23
24
|
return Object.entries(fileStructure).reduce((acc, [key, value]) => {
|
|
24
25
|
return {
|
|
25
26
|
...acc,
|
|
@@ -35,14 +36,24 @@ export class ThemeFilesStructureUtils {
|
|
|
35
36
|
});
|
|
36
37
|
}
|
|
37
38
|
static async getThemeRootDirectories(themeDirectory) {
|
|
38
|
-
const filesStructure = await
|
|
39
|
+
const filesStructure = await ThemeFilesUtils.getThemeFilesStructure(themeDirectory);
|
|
39
40
|
return Object.keys(filesStructure).filter((path) => {
|
|
40
41
|
return looksLikeDirectory(path);
|
|
41
42
|
});
|
|
42
43
|
}
|
|
43
44
|
static mapFilesRecordsToFilesList(filesRecords, localFileNameToUploaded = {}) {
|
|
44
|
-
|
|
45
|
+
return filesRecords.reduce((acc, { fileGlob, fileName, state, actionKey }) => {
|
|
45
46
|
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
|
+
if (state === FILE_STATES.unchanged)
|
|
56
|
+
return acc;
|
|
46
57
|
if (looksLikeDirectory(fileGlob)) {
|
|
47
58
|
const existingFiles = acc[fileGlob] || [];
|
|
48
59
|
return {
|
|
@@ -57,32 +68,28 @@ export class ThemeFilesStructureUtils {
|
|
|
57
68
|
};
|
|
58
69
|
}
|
|
59
70
|
}, {});
|
|
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;
|
|
67
71
|
}
|
|
68
72
|
static async createAFilesListFile(themeRootDir, filesList) {
|
|
69
73
|
if (!filesList || !Object.keys(filesList).length)
|
|
70
74
|
return;
|
|
71
|
-
const toUnixStyleFilesList =
|
|
75
|
+
const toUnixStyleFilesList = {};
|
|
76
|
+
for (const [filePath, value] of Object.entries(filesList)) {
|
|
72
77
|
const unixPath = toUnixPath(filePath);
|
|
73
|
-
const finalPath =
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
78
|
+
const finalPath = (await fileExists(join(themeRootDir, filePath))) && (await isDirectory(join(themeRootDir, filePath)))
|
|
79
|
+
? `${unixPath}${path.posix.sep}`
|
|
80
|
+
: unixPath;
|
|
81
|
+
toUnixStyleFilesList[finalPath] = value;
|
|
82
|
+
}
|
|
83
|
+
await writeJSONFile(this.getFilesListFilePath(themeRootDir), toUnixStyleFilesList);
|
|
84
|
+
}
|
|
85
|
+
static getFilesListFilePath(themeRootDir) {
|
|
86
|
+
return join(themeRootDir, THEME_FILES_LIST_FILE_NAME);
|
|
80
87
|
}
|
|
81
88
|
static async removeAFilesListFile(themeRootDir) {
|
|
82
|
-
await removeFile(
|
|
89
|
+
await removeFile(this.getFilesListFilePath(themeRootDir));
|
|
83
90
|
}
|
|
84
91
|
static async updateFilesStructure(themeDir) {
|
|
85
|
-
const fileStructure = await
|
|
92
|
+
const fileStructure = await ThemeFilesUtils.getThemeFilesStructure(themeDir);
|
|
86
93
|
const checksumsFiles = [
|
|
87
94
|
`${SHOPER_THEME_METADATA_DIR}/${THEME_INITIAL_CHECKSUMS_FILE_NAME}`,
|
|
88
95
|
`${SHOPER_THEME_METADATA_DIR}/${THEME_INITIAL_CHECKSUMS_VERITY_FILE_NAME}`,
|
|
@@ -115,6 +122,6 @@ export class ThemeFilesStructureUtils {
|
|
|
115
122
|
}
|
|
116
123
|
};
|
|
117
124
|
});
|
|
118
|
-
await
|
|
125
|
+
await ThemeFilesUtils.writeThemeFilesStructure(themeDir, fileStructure);
|
|
119
126
|
}
|
|
120
127
|
}
|
|
@@ -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 {
|
|
6
|
+
import { ThemeArchiveUtils } from '../../utils/archive/theme_archive_utils.js';
|
|
7
|
+
import { ThemeActionsUtils } from '../../actions/theme_actions_utils.js';
|
|
8
|
+
import { ThemeFilesUtils } from '../../utils/files/theme_files_utils.js';
|
|
7
9
|
import { THEME_WILDCARD_ACTION_NAME } from '../../actions/service/theme_actions_service_constants.js';
|
|
8
10
|
import { THEME_ACTIONS_TYPES } from '../../actions/theme_actions_constants.js';
|
|
9
|
-
import {
|
|
10
|
-
import { ThemeActionsUtils } from '../../actions/theme_actions_utils.js';
|
|
11
|
-
import { ArrayUtils } from '@dreamcommerce/utilities';
|
|
11
|
+
import { FILE_STATES } from '../../../../../utils/fs/fs_constants.js';
|
|
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,9 +20,10 @@ export class ThemeVerifyService {
|
|
|
20
20
|
const filesRecords = await ThemeActionsUtils.getFilesRecordsFromActionData({
|
|
21
21
|
themeRootDir,
|
|
22
22
|
themeAction: verifyAction,
|
|
23
|
-
filesStructure
|
|
23
|
+
filesStructure,
|
|
24
|
+
themeChecksums
|
|
24
25
|
});
|
|
25
|
-
const filesToVerify =
|
|
26
|
+
const filesToVerify = filesRecords.filter(({ state }) => [FILE_STATES.modified, FILE_STATES.created].includes(state));
|
|
26
27
|
if (filesToVerify.length) {
|
|
27
28
|
const { rejectedImageData } = await themeFilesUploadApi.uploadFiles(filesToVerify);
|
|
28
29
|
if (rejectedImageData.length)
|
|
@@ -33,10 +34,15 @@ export class ThemeVerifyService {
|
|
|
33
34
|
}
|
|
34
35
|
await this._createFilesList(themeRootDir, filesToVerify);
|
|
35
36
|
const themeArchivePath = join(tmpDir, `${uuid()}.zip`);
|
|
36
|
-
await
|
|
37
|
+
await ThemeArchiveUtils.create({
|
|
37
38
|
dist: themeArchivePath,
|
|
38
|
-
|
|
39
|
-
|
|
39
|
+
rootDir: themeRootDir,
|
|
40
|
+
files: await ThemeActionsUtils.getFilesThatMatchesAction({
|
|
41
|
+
actionValue: THEME_WILDCARD_ACTION_NAME,
|
|
42
|
+
actionType: THEME_ACTIONS_TYPES.push,
|
|
43
|
+
filesStructure,
|
|
44
|
+
rootDir: themeRootDir
|
|
45
|
+
})
|
|
40
46
|
});
|
|
41
47
|
const { isSuccess, messages } = await themeFilesUploadApi.uploadArchive({
|
|
42
48
|
action: verifyAction,
|
|
@@ -46,10 +52,10 @@ export class ThemeVerifyService {
|
|
|
46
52
|
return { isSuccess, messages };
|
|
47
53
|
}
|
|
48
54
|
finally {
|
|
49
|
-
await
|
|
55
|
+
await ThemeFilesUtils.removeAFilesListFile(themeRootDir);
|
|
50
56
|
}
|
|
51
57
|
}
|
|
52
58
|
async _createFilesList(themeRootDir, filesRecords) {
|
|
53
|
-
await
|
|
59
|
+
await ThemeFilesUtils.createAFilesListFile(themeRootDir, ThemeFilesUtils.mapFilesRecordsToFilesList(filesRecords));
|
|
54
60
|
}
|
|
55
61
|
}
|
|
@@ -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 = { recursive: false, hidden: false }) => {
|
|
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.5.
|
|
5
|
+
"version": "0.5.2-0",
|
|
6
6
|
"description": "CLI tool for Shoper",
|
|
7
7
|
"author": "Joanna Firek",
|
|
8
8
|
"license": "MIT",
|
|
@@ -39,31 +39,32 @@
|
|
|
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.12.0",
|
|
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",
|
|
48
46
|
"fs-tree-diff": "2.0.1",
|
|
49
47
|
"ink": "6.0.1",
|
|
50
|
-
"ink-link": "4.1.0",
|
|
51
48
|
"inquirer": "12.5.2",
|
|
52
|
-
"inquirer-select-line": "1.1.3",
|
|
53
49
|
"is-hidden-file": "1.1.2",
|
|
54
50
|
"jsonwebtoken": "9.0.2",
|
|
55
51
|
"klaw": "4.1.0",
|
|
56
52
|
"lodash": "4.17.21",
|
|
57
|
-
"log-symbols": "7.0.1",
|
|
58
53
|
"memfs": "4.17.0",
|
|
59
54
|
"ora": "8.2.0",
|
|
60
55
|
"react": "19.1.0",
|
|
61
56
|
"reflect-metadata": "0.2.2",
|
|
62
57
|
"rxjs": "7.8.2",
|
|
63
58
|
"semver": "7.7.1",
|
|
64
|
-
"strip-ansi": "7.1.0",
|
|
65
59
|
"tmp-promise": "3.0.3",
|
|
66
60
|
"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",
|
|
67
68
|
"walk-sync": "3.0.0",
|
|
68
69
|
"yauzl": "3.2.0",
|
|
69
70
|
"yazl": "3.3.1"
|
|
@@ -77,14 +78,15 @@
|
|
|
77
78
|
"@types/fs-extra": "11.0.4",
|
|
78
79
|
"@types/jest": "29.5.14",
|
|
79
80
|
"@types/jsonwebtoken": "9.0.9",
|
|
80
|
-
"@types/klaw": "3.0.7",
|
|
81
|
-
"@types/lodash": "4.17.17",
|
|
82
81
|
"@types/node": "18.19.84",
|
|
83
82
|
"@types/react": "19.1.8",
|
|
84
83
|
"@types/semver": "7.7.0",
|
|
85
84
|
"@types/tmp": "0.2.6",
|
|
86
85
|
"@types/yauzl": "2.10.3",
|
|
87
86
|
"@types/yazl": "2.4.6",
|
|
87
|
+
"@types/klaw": "3.0.7",
|
|
88
|
+
"@types/lodash": "4.17.17",
|
|
89
|
+
"@types/micromatch": "4.0.9",
|
|
88
90
|
"@typescript-eslint/eslint-plugin": "8.29.1",
|
|
89
91
|
"babel-jest": "29.7.0",
|
|
90
92
|
"eslint": "9.24.0",
|
|
@@ -1,44 +0,0 @@
|
|
|
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
|
-
}
|
package/build/theme/features/theme/utils/files_structure/theme_file_structure_errors_factory.js
DELETED
|
@@ -1,10 +0,0 @@
|
|
|
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
|
-
}
|