@shoper/cli 0.1.0-29 → 0.1.0-30
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/index.js +37 -0
- package/build/theme/class/checksums/theme_checksums.js +174 -0
- package/build/theme/{features/theme/utils → class}/checksums/theme_checksums_error_factory.js +1 -1
- package/build/theme/class/checksums/theme_checksums_utils.js +17 -0
- package/build/theme/commands/pull/theme_pull_command.js +3 -1
- package/build/theme/commands/push/theme_push_command.js +8 -9
- package/build/theme/commands/theme_verify_command.js +4 -4
- package/build/theme/features/theme/fetch/service/theme_fetch_service.js +5 -3
- package/build/theme/features/theme/init/service/theme_init_service.js +2 -3
- package/build/theme/features/theme/merge/api/theme_merge_api.js +0 -9
- package/build/theme/features/theme/merge/service/theme_merge_service.js +7 -22
- package/build/theme/features/theme/push/service/theme_push_service.js +28 -37
- package/build/theme/features/theme/push/theme_push_initializer.js +0 -3
- package/build/theme/features/theme/utils/hidden_directory/hidden_directory_utils.js +5 -6
- package/build/theme/features/theme/utils/resources/theme_resources_with_id_directory_utils.js +2 -3
- package/build/theme/hooks/theme_checksums/ensure_theme_current_checksums_up_to_date_hook.js +2 -4
- package/build/utils/fs/fs_utils.js +12 -1
- package/build/utils/path_utils.js +2 -0
- package/package.json +1 -1
- package/build/theme/features/theme/utils/checksums/theme_checksums_utils.js +0 -137
package/build/index.js
CHANGED
|
@@ -47,3 +47,40 @@ export const COMMANDS = {
|
|
|
47
47
|
...THEME_COMMANDS
|
|
48
48
|
};
|
|
49
49
|
export { runCLI } from './cli/index.js';
|
|
50
|
+
/**
|
|
51
|
+
* Przetestowane na win
|
|
52
|
+
* oglne
|
|
53
|
+
* moduly:
|
|
54
|
+
* - stworzony w admince
|
|
55
|
+
* - pull w cli - v
|
|
56
|
+
* - updejt modulu w adminc i pull w cli - v
|
|
57
|
+
* - zmiana nazwy katalogu w cli, modzenie w admince i pull - v
|
|
58
|
+
* - kopiowanie stworzonego modulu w admince i push (usuniety id i code) - v
|
|
59
|
+
* - zminaa w cli i push - v
|
|
60
|
+
* - tworzenie modulu w cli, min plikow i push
|
|
61
|
+
* - aktualizowanie js/twig/settings/schema
|
|
62
|
+
* - zmiana w adminc i pull
|
|
63
|
+
* - usuwanie modulu z cli - v
|
|
64
|
+
* - usuwanie modulu z adminki - v
|
|
65
|
+
* - dodanie niedozwolonego pliku do folderu modulu
|
|
66
|
+
* - dodanie niedozwolonego pliku do folderu modules/
|
|
67
|
+
* - translacje - v
|
|
68
|
+
* macro:
|
|
69
|
+
* - dodawanie pliku do macros - v
|
|
70
|
+
* - usuniecie macro - v
|
|
71
|
+
* - modzneie makro - v
|
|
72
|
+
* - dodawanie customowego macro - v
|
|
73
|
+
* - push customowego macro - v
|
|
74
|
+
* - modzenie w admince i pull - v
|
|
75
|
+
* settingsy:
|
|
76
|
+
* - uzupelniania schemy w admince, pull w cli
|
|
77
|
+
* - uzupelnianie wartosci a admince i pull
|
|
78
|
+
* - modzenie w cli
|
|
79
|
+
* - usuniecie pliku w cli
|
|
80
|
+
* styles:
|
|
81
|
+
* - modzenie stylu w src - niedozwolone - v
|
|
82
|
+
* - modzenie custom - v
|
|
83
|
+
* - usuniecie custom - v
|
|
84
|
+
* - modzenie schema i settings - v
|
|
85
|
+
* dodanie czegos w .shoper
|
|
86
|
+
*/
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import { copyFile, fileExists, readJSONFile, readJSONFileSync, removeFile } from '../../../utils/fs/fs_utils.js';
|
|
2
|
+
import { ThemeChecksumsUtils } from './theme_checksums_utils.js';
|
|
3
|
+
import { isWindowsOs } from '../../../utils/platform_utils.js';
|
|
4
|
+
import { join, mapKeysPathsToWindowPlatform, toCurrentPlatformPath } from '../../../utils/path_utils.js';
|
|
5
|
+
import { createWriteStream } from 'node:fs';
|
|
6
|
+
import { JSON_FILE_INDENT } from '../../../cli/cli_constants.js';
|
|
7
|
+
import { ThemeChecksumsErrorFactory } from './theme_checksums_error_factory.js';
|
|
8
|
+
import { computeChecksumsFromFilesStructure, computeDirectoriesChecksums, computeFileChecksum } from '../../../utils/checksums/checksums_utils.js';
|
|
9
|
+
import { normalize } from 'node:path';
|
|
10
|
+
import { ThemePushUtils } from '../../../theme/features/theme/push/theme_push_utils.js';
|
|
11
|
+
import { SHOPER_THEME_METADATA_DIR } from '../../../theme/constants/directory_contstants.js';
|
|
12
|
+
import { THEME_CURRENT_CHECKSUMS_VERITY_FILE_NAME, THEME_INITIAL_CHECKSUMS_VERITY_FILE_NAME, THEME_CURRENT_CHECKSUMS_FILE_NAME, THEME_INITIAL_CHECKSUMS_FILE_NAME } from '../../../theme/features/theme/theme_constants.js';
|
|
13
|
+
export class ThemeChecksums {
|
|
14
|
+
#themeDir;
|
|
15
|
+
#currentChecksumsFilePath;
|
|
16
|
+
#currentChecksumsVerificationFilePath;
|
|
17
|
+
#initialChecksumsFilePath;
|
|
18
|
+
#initialChecksumsVerificationFilePath;
|
|
19
|
+
#initialChecksums;
|
|
20
|
+
#currentChecksums;
|
|
21
|
+
constructor(themeDir) {
|
|
22
|
+
this.#themeDir = themeDir;
|
|
23
|
+
this.#currentChecksumsFilePath = ThemeChecksumsUtils.getCurrentThemeChecksumsFilePath(themeDir);
|
|
24
|
+
this.#currentChecksumsVerificationFilePath = ThemeChecksumsUtils.getCurrentThemeChecksumsVerificationFilePath(themeDir);
|
|
25
|
+
this.#initialChecksumsFilePath = ThemeChecksumsUtils.getInitialThemeChecksumsFilePath(themeDir);
|
|
26
|
+
this.#initialChecksumsVerificationFilePath = ThemeChecksumsUtils.getInitialThemeChecksumsVerificationFilePath(themeDir);
|
|
27
|
+
}
|
|
28
|
+
async getInitialChecksums() {
|
|
29
|
+
if (this.#initialChecksums)
|
|
30
|
+
return this.#initialChecksums;
|
|
31
|
+
this.#initialChecksums = await this._getChecksums(this.#initialChecksumsFilePath);
|
|
32
|
+
return this.#initialChecksums;
|
|
33
|
+
}
|
|
34
|
+
getInitialChecksumsSync() {
|
|
35
|
+
if (this.#initialChecksums)
|
|
36
|
+
return this.#initialChecksums;
|
|
37
|
+
this.#initialChecksums = this._getChecksumsSync(this.#initialChecksumsFilePath);
|
|
38
|
+
return this.#initialChecksums;
|
|
39
|
+
}
|
|
40
|
+
async getCurrentChecksums() {
|
|
41
|
+
if (this.#currentChecksums)
|
|
42
|
+
return this.#currentChecksums;
|
|
43
|
+
this.#currentChecksums = await this._getChecksums(this.#currentChecksumsFilePath);
|
|
44
|
+
return this.#currentChecksums;
|
|
45
|
+
}
|
|
46
|
+
getCurrentChecksumsSync() {
|
|
47
|
+
if (this.#currentChecksums)
|
|
48
|
+
return this.#currentChecksums;
|
|
49
|
+
this.#currentChecksums = this._getChecksumsSync(this.#currentChecksumsFilePath);
|
|
50
|
+
return this.#currentChecksums;
|
|
51
|
+
}
|
|
52
|
+
async getCurrentChecksumFromPath(path) {
|
|
53
|
+
const checksums = await this.getCurrentChecksums();
|
|
54
|
+
return checksums[toCurrentPlatformPath(path)];
|
|
55
|
+
}
|
|
56
|
+
getCurrentChecksumFromPathSync(path) {
|
|
57
|
+
const checksums = this.getCurrentChecksumsSync();
|
|
58
|
+
return checksums[toCurrentPlatformPath(path)];
|
|
59
|
+
}
|
|
60
|
+
async getInitialChecksumFromPath(path) {
|
|
61
|
+
const checksums = await this.getInitialChecksums();
|
|
62
|
+
return checksums[toCurrentPlatformPath(path)];
|
|
63
|
+
}
|
|
64
|
+
getInitialChecksumFromPathSync(path) {
|
|
65
|
+
const checksums = this.getInitialChecksumsSync();
|
|
66
|
+
return checksums[toCurrentPlatformPath(path)];
|
|
67
|
+
}
|
|
68
|
+
async hasThemeBeenModified() {
|
|
69
|
+
const initialChecksums = await this._getInitialChecksumsVerification();
|
|
70
|
+
const currentChecksums = await this._getCurrentChecksumsVerification();
|
|
71
|
+
return !!initialChecksums && !!currentChecksums && initialChecksums !== currentChecksums;
|
|
72
|
+
}
|
|
73
|
+
async hasThemeFileBeenCreated(path) {
|
|
74
|
+
const initialChecksum = await this.getInitialChecksumFromPath(path);
|
|
75
|
+
return !initialChecksum && (await fileExists(join(this.#themeDir, path)));
|
|
76
|
+
}
|
|
77
|
+
async hasThemeFileBeenModified(path) {
|
|
78
|
+
const currentChecksum = await this.getCurrentChecksumFromPath(path);
|
|
79
|
+
const initialChecksum = await this.getInitialChecksumFromPath(path);
|
|
80
|
+
return !!currentChecksum && !!initialChecksum && currentChecksum !== initialChecksum;
|
|
81
|
+
}
|
|
82
|
+
async verify() {
|
|
83
|
+
const initialChecksumFilePath = this.#initialChecksumsFilePath;
|
|
84
|
+
const initialChecksumVerifyFilePath = this.#initialChecksumsVerificationFilePath;
|
|
85
|
+
const initialChecksum = await computeFileChecksum(initialChecksumFilePath);
|
|
86
|
+
const initialChecksumVerify = await readJSONFile(initialChecksumVerifyFilePath);
|
|
87
|
+
return initialChecksum === initialChecksumVerify;
|
|
88
|
+
}
|
|
89
|
+
async updateAllChecksums() {
|
|
90
|
+
const checksums = await this.computeThemeChecksums();
|
|
91
|
+
return new Promise((resolve, reject) => {
|
|
92
|
+
const currentChecksumFilePath = this.#currentChecksumsFilePath;
|
|
93
|
+
const initialChecksumFilePath = this.#initialChecksumsFilePath;
|
|
94
|
+
const checksumStream = createWriteStream(initialChecksumFilePath);
|
|
95
|
+
checksumStream.write(JSON.stringify(checksums, null, JSON_FILE_INDENT));
|
|
96
|
+
checksumStream.end();
|
|
97
|
+
checksumStream
|
|
98
|
+
.on('finish', async () => {
|
|
99
|
+
const initialChecksumVerifyFilePath = this.#initialChecksumsVerificationFilePath;
|
|
100
|
+
const currentChecksumVerifyFilePath = this.#currentChecksumsVerificationFilePath;
|
|
101
|
+
await this._createThemeChecksumVerifyFile(initialChecksumFilePath, initialChecksumVerifyFilePath);
|
|
102
|
+
await copyFile(initialChecksumFilePath, currentChecksumFilePath);
|
|
103
|
+
await copyFile(initialChecksumVerifyFilePath, currentChecksumVerifyFilePath);
|
|
104
|
+
resolve();
|
|
105
|
+
})
|
|
106
|
+
.on('error', async (err) => {
|
|
107
|
+
await removeFile(initialChecksumFilePath, { force: true });
|
|
108
|
+
reject(ThemeChecksumsErrorFactory.createThemeChecksumError(err.stack));
|
|
109
|
+
});
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
async updateCurrentChecksums() {
|
|
113
|
+
const checksums = await this.computeThemeChecksums();
|
|
114
|
+
return new Promise((resolve, reject) => {
|
|
115
|
+
const currentChecksumFilePath = this.#currentChecksumsFilePath;
|
|
116
|
+
const checksumStream = createWriteStream(currentChecksumFilePath);
|
|
117
|
+
checksumStream.write(JSON.stringify(checksums, null, JSON_FILE_INDENT));
|
|
118
|
+
checksumStream.end();
|
|
119
|
+
checksumStream
|
|
120
|
+
.on('finish', async () => {
|
|
121
|
+
const currentChecksumVerifyFilePath = this.#currentChecksumsVerificationFilePath;
|
|
122
|
+
await this._createThemeChecksumVerifyFile(currentChecksumFilePath, currentChecksumVerifyFilePath);
|
|
123
|
+
resolve();
|
|
124
|
+
})
|
|
125
|
+
.on('error', async (err) => {
|
|
126
|
+
await removeFile(currentChecksumFilePath, { force: true });
|
|
127
|
+
reject(ThemeChecksumsErrorFactory.createThemeChecksumError(err.stack));
|
|
128
|
+
});
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
async computeThemeChecksums() {
|
|
132
|
+
const filesToIgnoreInChecksums = [
|
|
133
|
+
join(SHOPER_THEME_METADATA_DIR, THEME_CURRENT_CHECKSUMS_FILE_NAME),
|
|
134
|
+
join(SHOPER_THEME_METADATA_DIR, THEME_CURRENT_CHECKSUMS_VERITY_FILE_NAME),
|
|
135
|
+
join(SHOPER_THEME_METADATA_DIR, THEME_INITIAL_CHECKSUMS_FILE_NAME),
|
|
136
|
+
join(SHOPER_THEME_METADATA_DIR, THEME_INITIAL_CHECKSUMS_VERITY_FILE_NAME)
|
|
137
|
+
];
|
|
138
|
+
const filesToComputeChecksums = (await ThemePushUtils.getAllFilesThatAreSendToRemote(this.#themeDir))
|
|
139
|
+
.filter((path) => !filesToIgnoreInChecksums.some((ignorePath) => normalize(path) === ignorePath))
|
|
140
|
+
.map((relativePath) => join(this.#themeDir, relativePath));
|
|
141
|
+
const { filesChecksumsInDirectories, filesChecksums } = await computeChecksumsFromFilesStructure(filesToComputeChecksums, this.#themeDir);
|
|
142
|
+
const directoriesChecksums = computeDirectoriesChecksums(filesChecksumsInDirectories);
|
|
143
|
+
return { ...filesChecksums, ...directoriesChecksums };
|
|
144
|
+
}
|
|
145
|
+
async _getInitialChecksumsVerification() {
|
|
146
|
+
return await readJSONFile(this.#initialChecksumsVerificationFilePath);
|
|
147
|
+
}
|
|
148
|
+
async _getCurrentChecksumsVerification() {
|
|
149
|
+
return await readJSONFile(this.#currentChecksumsVerificationFilePath);
|
|
150
|
+
}
|
|
151
|
+
async _getChecksums(path) {
|
|
152
|
+
const checksums = await readJSONFile(path);
|
|
153
|
+
if (!isWindowsOs())
|
|
154
|
+
return checksums;
|
|
155
|
+
return mapKeysPathsToWindowPlatform(checksums);
|
|
156
|
+
}
|
|
157
|
+
_getChecksumsSync(path) {
|
|
158
|
+
const checksums = readJSONFileSync(path);
|
|
159
|
+
if (!isWindowsOs())
|
|
160
|
+
return checksums;
|
|
161
|
+
return mapKeysPathsToWindowPlatform(checksums);
|
|
162
|
+
}
|
|
163
|
+
async _createThemeChecksumVerifyFile(checksumFilePath, checksumVerifyFullPath) {
|
|
164
|
+
const checksumVerifyStream = createWriteStream(checksumVerifyFullPath);
|
|
165
|
+
const checksumVerify = await computeFileChecksum(checksumFilePath);
|
|
166
|
+
checksumVerifyStream
|
|
167
|
+
.on('error', async (err) => {
|
|
168
|
+
await removeFile(checksumFilePath, { force: true });
|
|
169
|
+
throw ThemeChecksumsErrorFactory.createThemeChecksumError(err.stack);
|
|
170
|
+
})
|
|
171
|
+
.write(JSON.stringify(checksumVerify));
|
|
172
|
+
checksumVerifyStream.end();
|
|
173
|
+
}
|
|
174
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { join } from '../../../utils/path_utils.js';
|
|
2
|
+
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';
|
|
3
|
+
import { HiddenDirectoryUtils } from '../../features/theme/utils/hidden_directory/hidden_directory_utils.js';
|
|
4
|
+
export class ThemeChecksumsUtils {
|
|
5
|
+
static getCurrentThemeChecksumsFilePath(themeDir) {
|
|
6
|
+
return join(HiddenDirectoryUtils.getThemeHiddenDirectoryPath(themeDir), THEME_CURRENT_CHECKSUMS_FILE_NAME);
|
|
7
|
+
}
|
|
8
|
+
static getInitialThemeChecksumsFilePath(themeDir) {
|
|
9
|
+
return join(HiddenDirectoryUtils.getThemeHiddenDirectoryPath(themeDir), THEME_INITIAL_CHECKSUMS_FILE_NAME);
|
|
10
|
+
}
|
|
11
|
+
static getCurrentThemeChecksumsVerificationFilePath(themeDir) {
|
|
12
|
+
return join(HiddenDirectoryUtils.getThemeHiddenDirectoryPath(themeDir), THEME_CURRENT_CHECKSUMS_VERITY_FILE_NAME);
|
|
13
|
+
}
|
|
14
|
+
static getInitialThemeChecksumsVerificationFilePath(themeDir) {
|
|
15
|
+
return join(HiddenDirectoryUtils.getThemeHiddenDirectoryPath(themeDir), THEME_INITIAL_CHECKSUMS_VERITY_FILE_NAME);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
@@ -26,6 +26,7 @@ import { THEME_WORK_URL_MISMATCH_ERROR } from '../../features/theme/utils/meta_d
|
|
|
26
26
|
import { ThemeWorkUrlMismatch } from '../ui/theme_work_url_mismatch.js';
|
|
27
27
|
import { Error } from '../../../ui/message_box/error.js';
|
|
28
28
|
import { ThemeMetaDataUtils } from '../../features/theme/utils/meta_data/theme_meta_data_utils.js';
|
|
29
|
+
import { ThemeChecksums } from '../../class/checksums/theme_checksums.js';
|
|
29
30
|
//TODO jak jest error w pullu wowczas usuwamy docelowo pociagniety skin
|
|
30
31
|
export class ThemePullCommand extends BaseThemeCommand {
|
|
31
32
|
static summary = 'Downloads the current version of your theme from the store and overwrites your local theme files.';
|
|
@@ -175,7 +176,8 @@ export class ThemePullCommand extends BaseThemeCommand {
|
|
|
175
176
|
await ThemeResourcesWithIdDirectoryUtils.updateDirectoryNamesOfResourcesWithIdAccordingToLocalThemeNames(themeModulesPath, join(tmpDir, name, 'modules'), join(tmpDir, name));
|
|
176
177
|
this.#spinner.stop();
|
|
177
178
|
const changes = await themeMergeApi.getChangesBetweenThemes(join(tmpDir, name), executionContext.themeRootDir);
|
|
178
|
-
|
|
179
|
+
const themeChecksums = new ThemeChecksums(executionContext.themeRootDir);
|
|
180
|
+
if (changes.length && (await themeChecksums.hasThemeBeenModified())) {
|
|
179
181
|
renderOnce(React.createElement(ThemePullUnpublishedChangesWarning, { changes: changes }));
|
|
180
182
|
const { proceed } = await promptConfirmation('Do you want to continue and overwrite local changes?');
|
|
181
183
|
if (!proceed) {
|
|
@@ -6,7 +6,6 @@ import { THEME_ARCHIVE_UPLOAD_ERROR, THEME_FILES_UPLOAD_ERROR, THEME_PUSH_API_NA
|
|
|
6
6
|
import { ThemeMetaDataUtils } from '../../features/theme/utils/meta_data/theme_meta_data_utils.js';
|
|
7
7
|
import { mapChecksumToTree } from '../../../utils/checksums/checksums_utils.js';
|
|
8
8
|
import { mapToPermissionsTree } from '../../utils/directory_validator/directory_validator_utils.js';
|
|
9
|
-
import { ThemeChecksumsUtils } from '../../features/theme/utils/checksums/theme_checksums_utils.js';
|
|
10
9
|
import { renderOnce } from '../../../ui/ui_utils.js';
|
|
11
10
|
import { UnpermittedCommandError } from '../ui/unpermitted_command_error.js';
|
|
12
11
|
import React from 'react';
|
|
@@ -17,7 +16,6 @@ import ora from 'ora';
|
|
|
17
16
|
import { MissingThemeFiles } from '../ui/missing_theme_files.js';
|
|
18
17
|
import { SHOPER_THEME_METADATA_DIR } from '../../constants/directory_contstants.js';
|
|
19
18
|
import { THEME_FILES_STRUCTURE_FILE_NAME } from '../../features/theme/theme_constants.js';
|
|
20
|
-
import { THEME_MERGE_API_NAME } from '../../features/theme/merge/theme_merge_constants.js';
|
|
21
19
|
import { ThemePushSkipInfo } from './ui/theme_push_skip_into.js';
|
|
22
20
|
import { ThemeUnpermittedActionsError } from './ui/theme_unpermitted_actions_error.js';
|
|
23
21
|
import { Error } from '../../../ui/message_box/error.js';
|
|
@@ -27,6 +25,7 @@ import { THEME_WORK_URL_MISMATCH_ERROR } from '../../features/theme/utils/meta_d
|
|
|
27
25
|
import { ThemeFilesStructureUtils } from '../../features/theme/utils/files_structure/theme_files_structure_utils.js';
|
|
28
26
|
import { ThemeInfoUtils } from '../../features/theme/info/theme_info_utils.js';
|
|
29
27
|
import { ValidationErrors } from '../../../cli/ui/validation_errors/validation_errors.js';
|
|
28
|
+
import { ThemeChecksums } from '../../class/checksums/theme_checksums.js';
|
|
30
29
|
export class ThemePushCommand extends BaseThemeCommand {
|
|
31
30
|
static summary = 'Uploads your local theme files to the store and overwrites the current version of the theme in your store.';
|
|
32
31
|
static description = 'Check your local changes before pushing.\n\nYou must run this command from a specific theme directory (ID not needed).';
|
|
@@ -53,6 +52,7 @@ export class ThemePushCommand extends BaseThemeCommand {
|
|
|
53
52
|
}
|
|
54
53
|
let spinner;
|
|
55
54
|
try {
|
|
55
|
+
const themeChecksums = new ThemeChecksums(executionContext.themeRootDir);
|
|
56
56
|
await ThemeMetaDataUtils.ensureThemeWorkUrlMatch(executionContext.themeRootDir, credentials.shopUrl);
|
|
57
57
|
const pushAction = themeActionsApi.getThemeAction({
|
|
58
58
|
actionType: THEME_ACTIONS_TYPES.push,
|
|
@@ -63,18 +63,17 @@ export class ThemePushCommand extends BaseThemeCommand {
|
|
|
63
63
|
renderOnce(React.createElement(UnpermittedCommandError, { themeId: executionContext.themeId, commandName: "push" }));
|
|
64
64
|
return;
|
|
65
65
|
}
|
|
66
|
-
const
|
|
66
|
+
const initialChecksums = await themeChecksums.getInitialChecksums();
|
|
67
67
|
const permissions = await ThemeFilesStructureUtils.getFilesPermissions(executionContext.themeRootDir);
|
|
68
|
-
if (!
|
|
68
|
+
if (!initialChecksums || !permissions)
|
|
69
69
|
this.error('Theme checksums or permissions not found. Please ensure you are in the correct theme directory.');
|
|
70
|
-
|
|
71
|
-
const hasLocalChanges = await themeMergeApi.hasThemeBeenModified(executionContext.themeRootDir);
|
|
72
|
-
if (!hasLocalChanges) {
|
|
70
|
+
if (!(await themeChecksums.hasThemeBeenModified())) {
|
|
73
71
|
renderOnce(React.createElement(ThemePushSkipInfo, null));
|
|
74
72
|
return;
|
|
75
73
|
}
|
|
76
74
|
const validationResult = await ThemeFilesStructureUtils.validateThemeDirectoryStructure({
|
|
77
|
-
|
|
75
|
+
//TDO przeniesc do theme checksums
|
|
76
|
+
checksums: mapChecksumToTree(initialChecksums),
|
|
78
77
|
permissions: mapToPermissionsTree(permissions),
|
|
79
78
|
rootDirectory: executionContext.themeRootDir
|
|
80
79
|
});
|
|
@@ -92,9 +91,9 @@ export class ThemePushCommand extends BaseThemeCommand {
|
|
|
92
91
|
spinner = ora('Pushing theme...').start();
|
|
93
92
|
await themePushApi.push({
|
|
94
93
|
credentials,
|
|
95
|
-
checksums,
|
|
96
94
|
filesStructure,
|
|
97
95
|
pushAction,
|
|
96
|
+
themeChecksums,
|
|
98
97
|
executionContext
|
|
99
98
|
});
|
|
100
99
|
spinner.stop();
|
|
@@ -3,8 +3,8 @@ 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
4
|
import { mapToPermissionsTree } from '../utils/directory_validator/directory_validator_utils.js';
|
|
5
5
|
import { mapChecksumToTree } from '../../utils/checksums/checksums_utils.js';
|
|
6
|
-
import { ThemeChecksumsUtils } from '../features/theme/utils/checksums/theme_checksums_utils.js';
|
|
7
6
|
import { ThemeFilesStructureUtils } from '../features/theme/utils/files_structure/theme_files_structure_utils.js';
|
|
7
|
+
import { ThemeChecksums } from '../class/checksums/theme_checksums.js';
|
|
8
8
|
export class ThemeVerifyCommand extends BaseThemeCommand {
|
|
9
9
|
static description = 'Verify theme files structure';
|
|
10
10
|
static hidden = true;
|
|
@@ -17,13 +17,13 @@ export class ThemeVerifyCommand extends BaseThemeCommand {
|
|
|
17
17
|
const executionContext = await executionContextApi.getExecutionContext();
|
|
18
18
|
if (executionContext.type !== EXECUTION_CONTEXTS.theme)
|
|
19
19
|
this.error('You cannot run this command outside theme context.');
|
|
20
|
-
const
|
|
20
|
+
const themeChecksums = new ThemeChecksums(executionContext.themeRootDir);
|
|
21
21
|
const permissions = await ThemeFilesStructureUtils.getFilesPermissions(executionContext.themeRootDir);
|
|
22
|
-
if (!
|
|
22
|
+
if (!themeChecksums || !permissions)
|
|
23
23
|
this.error('Theme checksums or permissions not found. Please ensure you are in the correct theme directory.');
|
|
24
24
|
try {
|
|
25
25
|
const validationResult = await ThemeFilesStructureUtils.validateThemeDirectoryStructure({
|
|
26
|
-
checksums: mapChecksumToTree(
|
|
26
|
+
checksums: mapChecksumToTree(await themeChecksums.getInitialChecksums()),
|
|
27
27
|
permissions: mapToPermissionsTree(permissions),
|
|
28
28
|
rootDirectory: executionContext.themeRootDir
|
|
29
29
|
});
|
|
@@ -10,12 +10,12 @@ import { jsonIndentTransform } from '../../../../../utils/stream_transforms/json
|
|
|
10
10
|
import { THEME_CURRENT_CHECKSUMS_FILE_NAME, THEME_CURRENT_CHECKSUMS_VERITY_FILE_NAME, THEME_INITIAL_CHECKSUMS_FILE_NAME, THEME_INITIAL_CHECKSUMS_VERITY_FILE_NAME } from '../../theme_constants.js';
|
|
11
11
|
import { THEME_FILES_LIST_FILE_NAME } from '../../push/theme_push_constants.js';
|
|
12
12
|
import { JSON_FILE_INDENT } from '../../../../../cli/cli_constants.js';
|
|
13
|
-
import { ThemeChecksumsUtils } from '../../utils/checksums/theme_checksums_utils.js';
|
|
14
13
|
import { createWriteStream } from 'node:fs';
|
|
15
14
|
import { AppError } from '../../../../../cli/class/errors/app/app_error.js';
|
|
16
15
|
import { THEME_ACTIONS_TYPES } from '../../actions/theme_actions_constants.js';
|
|
17
16
|
import { ThemeFilesStructureUtils } from '../../utils/files_structure/theme_files_structure_utils.js';
|
|
18
17
|
import { ThemeMetaDataUtils } from '../../utils/meta_data/theme_meta_data_utils.js';
|
|
18
|
+
import { ThemeChecksums } from '../../../../../theme/class/checksums/theme_checksums.js';
|
|
19
19
|
export class ThemeFetchService {
|
|
20
20
|
#themeHttpApi;
|
|
21
21
|
#httpApi;
|
|
@@ -55,8 +55,10 @@ export class ThemeFetchService {
|
|
|
55
55
|
}
|
|
56
56
|
await this._createGitIgnoreFile(themeDir);
|
|
57
57
|
await this._updateMetadataFileWithWorkUrl(themeDir, credentials.shopUrl);
|
|
58
|
-
|
|
59
|
-
|
|
58
|
+
/**
|
|
59
|
+
* moze to nie powinno lezec tutaj :?
|
|
60
|
+
*/
|
|
61
|
+
await new ThemeChecksums(themeDir).updateAllChecksums();
|
|
60
62
|
return {
|
|
61
63
|
name: basename
|
|
62
64
|
};
|
|
@@ -3,9 +3,9 @@ import process from 'process';
|
|
|
3
3
|
import tmp from 'tmp-promise';
|
|
4
4
|
import { downloadFile } from '../../../../../utils/download_file/download_file_utils.js';
|
|
5
5
|
import { extractZip } from '../../../../../utils/zip/extract_zip_utils.js';
|
|
6
|
-
import { ThemeChecksumsUtils } from '../../utils/checksums/theme_checksums_utils.js';
|
|
7
6
|
import { ThemeMetaDataUtils } from '../../utils/meta_data/theme_meta_data_utils.js';
|
|
8
7
|
import { ThemeInfoUtils } from '../../info/theme_info_utils.js';
|
|
8
|
+
import { ThemeChecksums } from '../../../../../theme/class/checksums/theme_checksums.js';
|
|
9
9
|
export class ThemeInitService {
|
|
10
10
|
#httpApi;
|
|
11
11
|
constructor({ httpApi }) {
|
|
@@ -23,8 +23,7 @@ export class ThemeInitService {
|
|
|
23
23
|
source: join(tmpDir, filename),
|
|
24
24
|
dist: distDir
|
|
25
25
|
});
|
|
26
|
-
|
|
27
|
-
await ThemeChecksumsUtils.createThemeChecksumsFiles(join(process.cwd(), basename), checksums);
|
|
26
|
+
new ThemeChecksums(distDir).updateAllChecksums();
|
|
28
27
|
const themeMetaData = await ThemeMetaDataUtils.getThemeMetadata(distDir);
|
|
29
28
|
return {
|
|
30
29
|
themeId: themeMetaData.themeId,
|
|
@@ -7,19 +7,10 @@ export class ThemeMergeApi extends FeatureApi {
|
|
|
7
7
|
super();
|
|
8
8
|
this.#service = service;
|
|
9
9
|
}
|
|
10
|
-
hasThemeBeenModified(themeRootDir) {
|
|
11
|
-
return this.#service.hasThemeBeenModified(themeRootDir);
|
|
12
|
-
}
|
|
13
10
|
getChangesBetweenThemes(theme1, theme2) {
|
|
14
11
|
return this.#service.getChangesBetweenThemes(theme1, theme2);
|
|
15
12
|
}
|
|
16
13
|
applyChanges(fromTheme, toTheme, changes) {
|
|
17
14
|
return this.#service.applyChanges(fromTheme, toTheme, changes);
|
|
18
15
|
}
|
|
19
|
-
hasFileBeenCreated(path, themeDir) {
|
|
20
|
-
return this.#service.hasFileBeenCreated(path, themeDir);
|
|
21
|
-
}
|
|
22
|
-
hasFileBeenModified(path, themeDir) {
|
|
23
|
-
return this.#service.hasFileBeenModified(path, themeDir);
|
|
24
|
-
}
|
|
25
16
|
}
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import FSTree from 'fs-tree-diff';
|
|
2
|
-
import { copyFileSync,
|
|
3
|
-
import { ThemeChecksumsUtils } from '../../utils/checksums/theme_checksums_utils.js';
|
|
2
|
+
import { copyFileSync, getAllDirectoriesNamesInside } from '../../../../../utils/fs/fs_utils.js';
|
|
4
3
|
import walkSync from 'walk-sync';
|
|
5
4
|
import { SHOPER_THEME_METADATA_DIR } from '../../../../constants/directory_contstants.js';
|
|
6
|
-
import {
|
|
5
|
+
import { ThemeChecksums } from '../../../../class/checksums/theme_checksums.js';
|
|
7
6
|
export class ThemeMergeService {
|
|
8
7
|
async applyChanges(fromTheme, toTheme, changes) {
|
|
9
8
|
FSTree.applyPatch(fromTheme, toTheme, changes, {
|
|
@@ -14,8 +13,7 @@ export class ThemeMergeService {
|
|
|
14
13
|
copyFileSync(inputPath, outputPath);
|
|
15
14
|
}
|
|
16
15
|
});
|
|
17
|
-
|
|
18
|
-
await ThemeChecksumsUtils.createThemeChecksumsFiles(toTheme, checksums);
|
|
16
|
+
new ThemeChecksums(toTheme).updateAllChecksums();
|
|
19
17
|
}
|
|
20
18
|
async getChangesBetweenThemes(theme1, theme2) {
|
|
21
19
|
const theme1Tree = new FSTree({
|
|
@@ -24,8 +22,8 @@ export class ThemeMergeService {
|
|
|
24
22
|
const theme2Tree = new FSTree({
|
|
25
23
|
entries: walkSync.entries(theme2)
|
|
26
24
|
});
|
|
27
|
-
const theme1Checksums =
|
|
28
|
-
const theme2Checksums =
|
|
25
|
+
const theme1Checksums = new ThemeChecksums(theme1);
|
|
26
|
+
const theme2Checksums = new ThemeChecksums(theme2);
|
|
29
27
|
const rootDirectories = await getAllDirectoriesNamesInside(theme1, {
|
|
30
28
|
recursive: false,
|
|
31
29
|
hidden: true
|
|
@@ -34,24 +32,11 @@ export class ThemeMergeService {
|
|
|
34
32
|
.calculatePatch(theme1Tree, (entryA, entryB) => {
|
|
35
33
|
if (entryA.isDirectory() && entryB.isDirectory())
|
|
36
34
|
return true;
|
|
37
|
-
return theme1Checksums
|
|
35
|
+
return (theme1Checksums.getCurrentChecksumFromPathSync(entryA.relativePath) ===
|
|
36
|
+
theme2Checksums.getCurrentChecksumFromPathSync(entryB.relativePath));
|
|
38
37
|
})
|
|
39
38
|
.filter(([_, name]) => {
|
|
40
39
|
return !name.startsWith(SHOPER_THEME_METADATA_DIR) && rootDirectories.some((rootDir) => name.startsWith(rootDir));
|
|
41
40
|
});
|
|
42
41
|
}
|
|
43
|
-
async hasThemeBeenModified(themeRootDir) {
|
|
44
|
-
const initialChecksums = await ThemeChecksumsUtils.getInitialChecksumsVerification(themeRootDir);
|
|
45
|
-
const currentChecksums = await ThemeChecksumsUtils.getThemeCurrentChecksumsVerification(themeRootDir);
|
|
46
|
-
return initialChecksums !== currentChecksums;
|
|
47
|
-
}
|
|
48
|
-
async hasFileBeenCreated(path, themeDir) {
|
|
49
|
-
const initialChecksum = await ThemeChecksumsUtils.getInitialChecksumFromPath(path, themeDir);
|
|
50
|
-
return !initialChecksum && (await fileExists(join(themeDir, path)));
|
|
51
|
-
}
|
|
52
|
-
async hasFileBeenModified(path, themeDir) {
|
|
53
|
-
const currentChecksum = await ThemeChecksumsUtils.getCurrentChecksumFromPath(path, themeDir);
|
|
54
|
-
const initialChecksum = await ThemeChecksumsUtils.getInitialChecksumFromPath(path, themeDir);
|
|
55
|
-
return !!currentChecksum && !!initialChecksum && currentChecksum !== initialChecksum;
|
|
56
|
-
}
|
|
57
42
|
}
|
|
@@ -2,7 +2,7 @@ import tmp from 'tmp-promise';
|
|
|
2
2
|
import { THEME_ACTION_DATA_TYPE, THEME_ACTIONS_TYPES } from '../../actions/theme_actions_constants.js';
|
|
3
3
|
import { createZip } from '../../../../../utils/zip/create_zip_utils.js';
|
|
4
4
|
import { basename, dirname, extname, join, looksLikeDirectory, toUnixPath } from '../../../../../utils/path_utils.js';
|
|
5
|
-
import
|
|
5
|
+
import { createReadStream } from 'node:fs';
|
|
6
6
|
import { v4 as uuid } from 'uuid';
|
|
7
7
|
import globs from 'fast-glob';
|
|
8
8
|
import { THEME_WILDCARD_ACTION_NAME } from '../../actions/service/theme_actions_service_constants.js';
|
|
@@ -14,27 +14,24 @@ import { ThemeImagesUtils } from '../../utils/theme_images_utils.js';
|
|
|
14
14
|
import { ThemePublishUtils } from '../../publish/theme_publish_utils.js';
|
|
15
15
|
import { formatJSONFile, removeFile, writeJSONFile } from '../../../../../utils/fs/fs_utils.js';
|
|
16
16
|
import { MODULES_DIRECTORY_NAME } from '../../theme_constants.js';
|
|
17
|
-
import { ThemeChecksumsUtils } from '../../utils/checksums/theme_checksums_utils.js';
|
|
18
17
|
import path from 'node:path';
|
|
19
18
|
export class ThemePushService {
|
|
20
19
|
#themePushHttpApi;
|
|
21
|
-
#themeMergeApi;
|
|
22
20
|
#themeFetchApi;
|
|
23
|
-
constructor({ themePushHttpApi,
|
|
21
|
+
constructor({ themePushHttpApi, themeFetchApi }) {
|
|
24
22
|
this.#themePushHttpApi = themePushHttpApi;
|
|
25
|
-
this.#themeMergeApi = themeMergeApi;
|
|
26
23
|
this.#themeFetchApi = themeFetchApi;
|
|
27
24
|
}
|
|
28
|
-
async push({ pushAction, filesStructure, credentials, executionContext }) {
|
|
25
|
+
async push({ pushAction, filesStructure, credentials, executionContext, themeChecksums }) {
|
|
29
26
|
const { path: tmpDir } = await tmp.dir({ unsafeCleanup: true });
|
|
30
|
-
if (await
|
|
27
|
+
if (await themeChecksums.hasThemeFileBeenCreated(ThemePublishUtils.getSkinStoreSettingsFilePath(executionContext.themeRootDir)))
|
|
31
28
|
throw ThemePushErrorsFactory.createErrorWhilePushingUnpublishedThemeWithSkinstoreData(credentials.shopUrl);
|
|
32
29
|
const { uploadData, localFiles } = await this._getActionDataForFilesToUpload({
|
|
33
30
|
pushAction,
|
|
34
31
|
filesStructure,
|
|
35
|
-
executionContext
|
|
32
|
+
executionContext,
|
|
33
|
+
themeChecksums
|
|
36
34
|
});
|
|
37
|
-
console.log('uploadData', uploadData);
|
|
38
35
|
if (uploadData.length) {
|
|
39
36
|
await this._uploadThemeFiles({
|
|
40
37
|
filesToUpload: uploadData,
|
|
@@ -67,8 +64,7 @@ export class ThemePushService {
|
|
|
67
64
|
if (resources)
|
|
68
65
|
await this.#themeFetchApi.fetchResources(credentials.shopUrl, executionContext.themeRootDir, resources);
|
|
69
66
|
await removeFile(join(executionContext.themeRootDir, THEME_FILES_LIST_FILE_NAME));
|
|
70
|
-
|
|
71
|
-
await ThemeChecksumsUtils.createThemeChecksumsFiles(executionContext.themeRootDir, checksums);
|
|
67
|
+
await themeChecksums.updateAllChecksums();
|
|
72
68
|
}
|
|
73
69
|
async _createThemeArchive({ themeRootDir, filesToArchive, dist }) {
|
|
74
70
|
const filesInThemeDirectory = await globs(filesToArchive, {
|
|
@@ -124,36 +120,31 @@ export class ThemePushService {
|
|
|
124
120
|
async _uploadFiles(uploadData, credentials) {
|
|
125
121
|
const uploadedImageData = [];
|
|
126
122
|
const rejectedImageData = [];
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
.catch(() => {
|
|
145
|
-
rejectedImageData.push({
|
|
146
|
-
location: dirname(path),
|
|
147
|
-
originalFilename: basename(path)
|
|
148
|
-
});
|
|
123
|
+
await Promise.all(uploadData.map(({ actionData, path }) => this.#themePushHttpApi
|
|
124
|
+
.pushThemeData({
|
|
125
|
+
actionData,
|
|
126
|
+
stream: createReadStream(path),
|
|
127
|
+
shopUrl: credentials.shopUrl
|
|
128
|
+
})
|
|
129
|
+
.response.then((response) => {
|
|
130
|
+
uploadedImageData.push({
|
|
131
|
+
location: dirname(path),
|
|
132
|
+
originalFilename: basename(path),
|
|
133
|
+
uploadedFilename: response.data.imageId
|
|
134
|
+
});
|
|
135
|
+
})
|
|
136
|
+
.catch(() => {
|
|
137
|
+
rejectedImageData.push({
|
|
138
|
+
location: dirname(path),
|
|
139
|
+
originalFilename: basename(path)
|
|
149
140
|
});
|
|
150
|
-
}));
|
|
141
|
+
})));
|
|
151
142
|
return {
|
|
152
143
|
uploadedImageData,
|
|
153
144
|
rejectedImageData
|
|
154
145
|
};
|
|
155
146
|
}
|
|
156
|
-
async _getActionDataForFilesToUpload({ pushAction, filesStructure, executionContext }) {
|
|
147
|
+
async _getActionDataForFilesToUpload({ pushAction, filesStructure, executionContext, themeChecksums }) {
|
|
157
148
|
const uploadData = [];
|
|
158
149
|
const localFiles = {};
|
|
159
150
|
for (const [actionKey, actionData] of Object.entries(pushAction.data)) {
|
|
@@ -175,8 +166,8 @@ export class ThemePushService {
|
|
|
175
166
|
localFiles[fileGlob] = files.length ? basename(files[0]) : null;
|
|
176
167
|
}
|
|
177
168
|
for (const filePath of files) {
|
|
178
|
-
if ((await
|
|
179
|
-
(await
|
|
169
|
+
if ((await themeChecksums.hasThemeFileBeenCreated(filePath)) ||
|
|
170
|
+
(await themeChecksums.hasThemeFileBeenModified(filePath))) {
|
|
180
171
|
uploadData.push({
|
|
181
172
|
actionData,
|
|
182
173
|
actionKey,
|
|
@@ -3,16 +3,13 @@ import { ThemePushService } from './service/theme_push_service.js';
|
|
|
3
3
|
import { THEME_PUSH_FEATURE_NAME } from './theme_push_constants.js';
|
|
4
4
|
import { ThemePushApi } from './api/theme_push_api.js';
|
|
5
5
|
import { ThemePushHttpApi } from './http_api/theme_push_http_api.js';
|
|
6
|
-
import { THEME_MERGE_API_NAME } from '../merge/theme_merge_constants.js';
|
|
7
6
|
import { THEME_FETCH_API_NAME } from '../fetch/theme_fetch_constants.js';
|
|
8
7
|
export class ThemePushInitializer extends SyncFeatureInitializer {
|
|
9
8
|
static featureName = THEME_PUSH_FEATURE_NAME;
|
|
10
9
|
init() {
|
|
11
10
|
const httpApi = this.getApiSync(HTTP_REQUESTER_API_NAME);
|
|
12
|
-
const themeMergeApi = this.getApiSync(THEME_MERGE_API_NAME);
|
|
13
11
|
const service = new ThemePushService({
|
|
14
12
|
themePushHttpApi: new ThemePushHttpApi(httpApi),
|
|
15
|
-
themeMergeApi,
|
|
16
13
|
themeFetchApi: this.getApiSync(THEME_FETCH_API_NAME)
|
|
17
14
|
});
|
|
18
15
|
return {
|
|
@@ -1,16 +1,14 @@
|
|
|
1
|
-
import { ThemeChecksumsUtils } from '../checksums/theme_checksums_utils.js';
|
|
2
1
|
import { getAllFilesNamesInside } from '../../../../../utils/fs/fs_utils.js';
|
|
3
2
|
import { THEME_CURRENT_CHECKSUMS_FILE_NAME, THEME_CURRENT_CHECKSUMS_VERITY_FILE_NAME, THEME_INITIAL_CHECKSUMS_FILE_NAME, THEME_INITIAL_CHECKSUMS_VERITY_FILE_NAME } from '../../theme_constants.js';
|
|
4
3
|
import { join } from '../../../../../utils/path_utils.js';
|
|
5
4
|
import { SHOPER_THEME_METADATA_DIR } from '../../../../constants/directory_contstants.js';
|
|
6
5
|
import { computeFileChecksum } from '../../../../../utils/checksums/checksums_utils.js';
|
|
6
|
+
import { ThemeChecksums } from '../../../../class/checksums/theme_checksums.js';
|
|
7
7
|
export class HiddenDirectoryUtils {
|
|
8
8
|
static async ensureFilesInsideThemeMetaDataDirectoryUntouched(themeDirectory) {
|
|
9
9
|
const themeMetadataPath = this.getThemeHiddenDirectoryPath(themeDirectory);
|
|
10
|
-
const
|
|
11
|
-
if (!
|
|
12
|
-
throw new Error(`Checksums file not found in theme metadata directory: ${themeMetadataPath}`);
|
|
13
|
-
if (!(await ThemeChecksumsUtils.verifyThemeChecksums(themeDirectory)))
|
|
10
|
+
const themeChecksums = new ThemeChecksums(themeDirectory);
|
|
11
|
+
if (!(await themeChecksums.verify()))
|
|
14
12
|
throw new Error('Theme checksum file is not valid');
|
|
15
13
|
const filesNames = (await getAllFilesNamesInside(themeMetadataPath)).filter((fileName) => fileName !== THEME_CURRENT_CHECKSUMS_FILE_NAME &&
|
|
16
14
|
fileName !== THEME_CURRENT_CHECKSUMS_VERITY_FILE_NAME &&
|
|
@@ -22,7 +20,8 @@ export class HiddenDirectoryUtils {
|
|
|
22
20
|
const fileFullPath = join(themeMetadataPath, fileName);
|
|
23
21
|
const fileRelativePath = join(SHOPER_THEME_METADATA_DIR, fileName);
|
|
24
22
|
const currentChecksum = await computeFileChecksum(fileFullPath);
|
|
25
|
-
|
|
23
|
+
const initialChecksum = await themeChecksums.getInitialChecksumFromPath(fileRelativePath);
|
|
24
|
+
if (currentChecksum !== initialChecksum) {
|
|
26
25
|
throw new Error(`File ${fileName} inside theme metadata directory (${themeMetadataPath}) has been modified. You cannot modify files inside .shoper directory`);
|
|
27
26
|
}
|
|
28
27
|
}
|
package/build/theme/features/theme/utils/resources/theme_resources_with_id_directory_utils.js
CHANGED
|
@@ -3,7 +3,7 @@ import walkSync from 'walk-sync';
|
|
|
3
3
|
import { THEME_FILES_OPERATIONS } from '../../merge/theme_merge_constants.js';
|
|
4
4
|
import { fileExists, readJSONFile, renameFile } from '../../../../../utils/fs/fs_utils.js';
|
|
5
5
|
import { join } from '../../../../../utils/path_utils.js';
|
|
6
|
-
import {
|
|
6
|
+
import { ThemeChecksums } from '../../../../../theme/class/checksums/theme_checksums.js';
|
|
7
7
|
export class ThemeResourcesWithIdDirectoryUtils {
|
|
8
8
|
static async updateDirectoryNamesOfResourcesWithIdAccordingToLocalThemeNames(localResourcesDir, remoteResourcesDir, remoteThemeDir) {
|
|
9
9
|
const localThemeTree = new FSTree({
|
|
@@ -37,8 +37,7 @@ export class ThemeResourcesWithIdDirectoryUtils {
|
|
|
37
37
|
/**
|
|
38
38
|
* If these become performance bottlenecks, we can consider computing checksums only for the changed directories.
|
|
39
39
|
*/
|
|
40
|
-
|
|
41
|
-
await ThemeChecksumsUtils.createThemeChecksumsFiles(remoteThemeDir, checksums);
|
|
40
|
+
await new ThemeChecksums(remoteThemeDir).updateAllChecksums();
|
|
42
41
|
}
|
|
43
42
|
}
|
|
44
43
|
static async _getResourceIdToFilePathMap(entries) {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { EXECUTION_CONTEXT_API_NAME, EXECUTION_CONTEXTS } from '../../../cli/features/execution_context/execution_context_constants.js';
|
|
2
2
|
import { useApi } from '../../../cli/hooks/ensure_cli_initialized_hook.js';
|
|
3
|
-
import { ThemeChecksumsUtils } from '../../features/theme/utils/checksums/theme_checksums_utils.js';
|
|
4
3
|
import { THEME_COMMANDS_THAT_REQUIRES_UP_TO_DATE_CHECKSUMS } from './ensure_theme_current_checksums_up_to_date_constants.js';
|
|
4
|
+
import { ThemeChecksums } from '../../../theme/class/checksums/theme_checksums.js';
|
|
5
5
|
export const ensureThemeChecksumsUpToDate = async ({ Command }) => {
|
|
6
6
|
if (!THEME_COMMANDS_THAT_REQUIRES_UP_TO_DATE_CHECKSUMS.includes(Command.id))
|
|
7
7
|
return;
|
|
@@ -9,12 +9,10 @@ export const ensureThemeChecksumsUpToDate = async ({ Command }) => {
|
|
|
9
9
|
const executionContext = await executionContextApi.getExecutionContext();
|
|
10
10
|
if (executionContext.type !== EXECUTION_CONTEXTS.theme)
|
|
11
11
|
return;
|
|
12
|
-
await ThemeChecksumsUtils.removeCurrentChecksumsFiles(executionContext.themeRootDir);
|
|
13
12
|
/**
|
|
14
13
|
* Naive solution, recalculate checksums every time a command based on checksums calculation is executed;
|
|
15
14
|
* If performance becomes an issue, we can implement a more sophisticated solution, eg. recalculate checksums only when files in the theme directory have changed.
|
|
16
15
|
*/
|
|
17
|
-
|
|
18
|
-
await ThemeChecksumsUtils.createThemeCurrentChecksumsFile(executionContext.themeRootDir, checksums);
|
|
16
|
+
await new ThemeChecksums(executionContext.themeRootDir).updateCurrentChecksums();
|
|
19
17
|
};
|
|
20
18
|
export default ensureThemeChecksumsUpToDate;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import fsPromises from 'node:fs/promises';
|
|
2
|
-
import fs, { createReadStream } from 'node:fs';
|
|
2
|
+
import fs, { createReadStream, readFileSync } from 'node:fs';
|
|
3
3
|
import { isHiddenFile } from 'is-hidden-file';
|
|
4
4
|
import process from 'node:process';
|
|
5
5
|
import { basename, join, resolve } from '../path_utils.js';
|
|
@@ -39,6 +39,17 @@ export const readJSONFile = async (path, options = { encoding: 'utf-8', flag: 'r
|
|
|
39
39
|
throw new Error(`Failed to parse JSON from file ${path}: ${error}`);
|
|
40
40
|
}
|
|
41
41
|
};
|
|
42
|
+
export const readJSONFileSync = (path, options = { encoding: 'utf-8', flag: 'r' }) => {
|
|
43
|
+
const fileContent = readFileSync(path, options);
|
|
44
|
+
if (typeof fileContent !== 'string')
|
|
45
|
+
throw new Error('File content is not a string');
|
|
46
|
+
try {
|
|
47
|
+
return JSON.parse(fileContent);
|
|
48
|
+
}
|
|
49
|
+
catch (error) {
|
|
50
|
+
throw new Error(`Failed to parse JSON from file ${path}: ${error}`);
|
|
51
|
+
}
|
|
52
|
+
};
|
|
42
53
|
export const writeJSONFile = async (path, data) => {
|
|
43
54
|
try {
|
|
44
55
|
await writeFile(path, JSON.stringify(data, null, JSON_FILE_INDENT), {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import path, { basename as pathBaseName, dirname as pathDirname, extname as pathExtname, join as pathJoin, normalize, parse as parsePath, relative as pathRelative, resolve as resolvePath, sep } from 'node:path';
|
|
2
2
|
import fs from 'node:fs';
|
|
3
|
+
import { isWindowsOs } from './platform_utils.js';
|
|
3
4
|
export const resolve = (...paths) => {
|
|
4
5
|
return resolvePath(...paths);
|
|
5
6
|
};
|
|
@@ -49,3 +50,4 @@ export const mapKeysPathsToWindowPlatform = (paths) => {
|
|
|
49
50
|
};
|
|
50
51
|
}, {});
|
|
51
52
|
};
|
|
53
|
+
export const toCurrentPlatformPath = (path) => (isWindowsOs() ? toWinowsPath(path) : toUnixPath(path));
|
package/package.json
CHANGED
|
@@ -1,137 +0,0 @@
|
|
|
1
|
-
import { join, mapKeysPathsToWindowPlatform, toUnixPath, toWinowsPath } from '../../../../../utils/path_utils.js';
|
|
2
|
-
import { THEME_CURRENT_CHECKSUMS_FILE_NAME, THEME_CURRENT_CHECKSUMS_VERITY_FILE_NAME, THEME_INITIAL_CHECKSUMS_FILE_NAME, THEME_INITIAL_CHECKSUMS_VERITY_FILE_NAME } from '../../theme_constants.js';
|
|
3
|
-
import { createWriteStream } from 'fs';
|
|
4
|
-
import { copyFile, readJSONFile, removeFile } from '../../../../../utils/fs/fs_utils.js';
|
|
5
|
-
import { ThemeChecksumsErrorFactory } from './theme_checksums_error_factory.js';
|
|
6
|
-
import { computeChecksumsFromFilesStructure, computeDirectoriesChecksums, computeFileChecksum } from '../../../../../utils/checksums/checksums_utils.js';
|
|
7
|
-
import { JSON_FILE_INDENT } from '../../../../../cli/cli_constants.js';
|
|
8
|
-
import { ThemePushUtils } from '../../push/theme_push_utils.js';
|
|
9
|
-
import { SHOPER_THEME_METADATA_DIR } from '../../../../constants/directory_contstants.js';
|
|
10
|
-
import { isWindowsOs } from '../../../../../utils/platform_utils.js';
|
|
11
|
-
import { normalize } from 'path';
|
|
12
|
-
import { HiddenDirectoryUtils } from '../hidden_directory/hidden_directory_utils.js';
|
|
13
|
-
export class ThemeChecksumsUtils {
|
|
14
|
-
static getCurrentThemeChecksumsFilePath(themeDir) {
|
|
15
|
-
return join(HiddenDirectoryUtils.getThemeHiddenDirectoryPath(themeDir), THEME_CURRENT_CHECKSUMS_FILE_NAME);
|
|
16
|
-
}
|
|
17
|
-
static getInitialThemeChecksumsFilePath(themeDir) {
|
|
18
|
-
return join(HiddenDirectoryUtils.getThemeHiddenDirectoryPath(themeDir), THEME_INITIAL_CHECKSUMS_FILE_NAME);
|
|
19
|
-
}
|
|
20
|
-
static getCurrentThemeChecksumsVerificationFilePath(themeDir) {
|
|
21
|
-
return join(HiddenDirectoryUtils.getThemeHiddenDirectoryPath(themeDir), THEME_CURRENT_CHECKSUMS_VERITY_FILE_NAME);
|
|
22
|
-
}
|
|
23
|
-
static getInitialThemeChecksumsVerificationFilePath(themeDir) {
|
|
24
|
-
return join(HiddenDirectoryUtils.getThemeHiddenDirectoryPath(themeDir), THEME_INITIAL_CHECKSUMS_VERITY_FILE_NAME);
|
|
25
|
-
}
|
|
26
|
-
static async createThemeChecksumsFiles(themeDir, checksums) {
|
|
27
|
-
return new Promise((resolve, reject) => {
|
|
28
|
-
const currentChecksumFilePath = this.getCurrentThemeChecksumsFilePath(themeDir);
|
|
29
|
-
const initialChecksumFilePath = this.getInitialThemeChecksumsFilePath(themeDir);
|
|
30
|
-
const checksumStream = createWriteStream(initialChecksumFilePath);
|
|
31
|
-
checksumStream.write(JSON.stringify(checksums, null, JSON_FILE_INDENT));
|
|
32
|
-
checksumStream.end();
|
|
33
|
-
checksumStream
|
|
34
|
-
.on('finish', async () => {
|
|
35
|
-
const initialChecksumVerifyFilePath = this.getInitialThemeChecksumsVerificationFilePath(themeDir);
|
|
36
|
-
const currentChecksumVerifyFilePath = this.getCurrentThemeChecksumsVerificationFilePath(themeDir);
|
|
37
|
-
await this.createThemeChecksumVerifyFile(initialChecksumFilePath, initialChecksumVerifyFilePath);
|
|
38
|
-
await copyFile(initialChecksumFilePath, currentChecksumFilePath);
|
|
39
|
-
await copyFile(initialChecksumVerifyFilePath, currentChecksumVerifyFilePath);
|
|
40
|
-
resolve();
|
|
41
|
-
})
|
|
42
|
-
.on('error', async (err) => {
|
|
43
|
-
await removeFile(initialChecksumFilePath, { force: true });
|
|
44
|
-
reject(ThemeChecksumsErrorFactory.createThemeChecksumError(err.stack));
|
|
45
|
-
});
|
|
46
|
-
});
|
|
47
|
-
}
|
|
48
|
-
static async createThemeCurrentChecksumsFile(themeDir, checksums) {
|
|
49
|
-
return new Promise((resolve, reject) => {
|
|
50
|
-
const currentChecksumFilePath = this.getCurrentThemeChecksumsFilePath(themeDir);
|
|
51
|
-
const checksumStream = createWriteStream(currentChecksumFilePath);
|
|
52
|
-
checksumStream.write(JSON.stringify(checksums, null, JSON_FILE_INDENT));
|
|
53
|
-
checksumStream.end();
|
|
54
|
-
checksumStream
|
|
55
|
-
.on('finish', async () => {
|
|
56
|
-
const currentChecksumVerifyFilePath = this.getCurrentThemeChecksumsVerificationFilePath(themeDir);
|
|
57
|
-
await this.createThemeChecksumVerifyFile(currentChecksumFilePath, currentChecksumVerifyFilePath);
|
|
58
|
-
resolve();
|
|
59
|
-
})
|
|
60
|
-
.on('error', async (err) => {
|
|
61
|
-
await removeFile(currentChecksumFilePath, { force: true });
|
|
62
|
-
reject(ThemeChecksumsErrorFactory.createThemeChecksumError(err.stack));
|
|
63
|
-
});
|
|
64
|
-
});
|
|
65
|
-
}
|
|
66
|
-
static async createThemeChecksumVerifyFile(checksumFilePath, checksumVerifyFullPath) {
|
|
67
|
-
const checksumVerifyStream = createWriteStream(checksumVerifyFullPath);
|
|
68
|
-
const checksumVerify = await computeFileChecksum(checksumFilePath);
|
|
69
|
-
checksumVerifyStream
|
|
70
|
-
.on('error', async (err) => {
|
|
71
|
-
await removeFile(checksumFilePath, { force: true });
|
|
72
|
-
throw ThemeChecksumsErrorFactory.createThemeChecksumError(err.stack);
|
|
73
|
-
})
|
|
74
|
-
.write(JSON.stringify(checksumVerify));
|
|
75
|
-
checksumVerifyStream.end();
|
|
76
|
-
}
|
|
77
|
-
static async getThemeCurrentChecksums(themeDir) {
|
|
78
|
-
const checksumFilePath = this.getCurrentThemeChecksumsFilePath(themeDir);
|
|
79
|
-
const checksums = await readJSONFile(checksumFilePath);
|
|
80
|
-
if (!isWindowsOs())
|
|
81
|
-
return checksums;
|
|
82
|
-
return mapKeysPathsToWindowPlatform(checksums);
|
|
83
|
-
}
|
|
84
|
-
static async getThemeInitialChecksums(themeDir) {
|
|
85
|
-
const checksumFilePath = this.getInitialThemeChecksumsFilePath(themeDir);
|
|
86
|
-
const checksums = await readJSONFile(checksumFilePath);
|
|
87
|
-
if (!isWindowsOs())
|
|
88
|
-
return checksums;
|
|
89
|
-
return mapKeysPathsToWindowPlatform(checksums);
|
|
90
|
-
}
|
|
91
|
-
static async verifyThemeChecksums(themeDir) {
|
|
92
|
-
const initialChecksumFilePath = this.getInitialThemeChecksumsFilePath(themeDir);
|
|
93
|
-
const initialChecksumVerifyFilePath = this.getInitialThemeChecksumsVerificationFilePath(themeDir);
|
|
94
|
-
const initialChecksum = await computeFileChecksum(initialChecksumFilePath);
|
|
95
|
-
const initialChecksumVerify = await readJSONFile(initialChecksumVerifyFilePath);
|
|
96
|
-
return initialChecksum === initialChecksumVerify;
|
|
97
|
-
}
|
|
98
|
-
static async getThemeCurrentChecksumsVerification(themeDir) {
|
|
99
|
-
return await readJSONFile(this.getCurrentThemeChecksumsVerificationFilePath(themeDir));
|
|
100
|
-
}
|
|
101
|
-
static async getInitialChecksumsVerification(themeDir) {
|
|
102
|
-
return await readJSONFile(this.getInitialThemeChecksumsVerificationFilePath(themeDir));
|
|
103
|
-
}
|
|
104
|
-
static async removeCurrentChecksumsFiles(themeDir) {
|
|
105
|
-
const currentChecksumFilePath = this.getCurrentThemeChecksumsFilePath(themeDir);
|
|
106
|
-
const currentChecksumVerifyFilePath = this.getCurrentThemeChecksumsVerificationFilePath(themeDir);
|
|
107
|
-
try {
|
|
108
|
-
await removeFile(currentChecksumFilePath, { force: true });
|
|
109
|
-
await removeFile(currentChecksumVerifyFilePath, { force: true });
|
|
110
|
-
}
|
|
111
|
-
catch (err) {
|
|
112
|
-
throw ThemeChecksumsErrorFactory.createThemeChecksumError(err.stack);
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
static async computeThemeChecksums(themeDir) {
|
|
116
|
-
const filesToIgnoreInChecksums = [
|
|
117
|
-
join(SHOPER_THEME_METADATA_DIR, THEME_CURRENT_CHECKSUMS_FILE_NAME),
|
|
118
|
-
join(SHOPER_THEME_METADATA_DIR, THEME_CURRENT_CHECKSUMS_VERITY_FILE_NAME),
|
|
119
|
-
join(SHOPER_THEME_METADATA_DIR, THEME_INITIAL_CHECKSUMS_FILE_NAME),
|
|
120
|
-
join(SHOPER_THEME_METADATA_DIR, THEME_INITIAL_CHECKSUMS_VERITY_FILE_NAME)
|
|
121
|
-
];
|
|
122
|
-
const filesToComputeChecksums = (await ThemePushUtils.getAllFilesThatAreSendToRemote(themeDir))
|
|
123
|
-
.filter((path) => !filesToIgnoreInChecksums.some((ignorePath) => normalize(path) === ignorePath))
|
|
124
|
-
.map((relativePath) => join(themeDir, relativePath));
|
|
125
|
-
const { filesChecksumsInDirectories, filesChecksums } = await computeChecksumsFromFilesStructure(filesToComputeChecksums, themeDir);
|
|
126
|
-
const directoriesChecksums = computeDirectoriesChecksums(filesChecksumsInDirectories);
|
|
127
|
-
return { ...filesChecksums, ...directoriesChecksums };
|
|
128
|
-
}
|
|
129
|
-
static async getCurrentChecksumFromPath(path, themeDir) {
|
|
130
|
-
const checksums = await this.getThemeCurrentChecksums(themeDir);
|
|
131
|
-
return checksums[isWindowsOs() ? toWinowsPath(path) : toUnixPath(path)];
|
|
132
|
-
}
|
|
133
|
-
static async getInitialChecksumFromPath(path, themeDir) {
|
|
134
|
-
const checksums = await this.getThemeInitialChecksums(themeDir);
|
|
135
|
-
return checksums[isWindowsOs() ? toWinowsPath(path) : toUnixPath(path)];
|
|
136
|
-
}
|
|
137
|
-
}
|