@shoper/cli 0.7.1-1 → 0.8.1-3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -1
- package/build/cli/class/errors/http/http_errors_factory.js +1 -1
- package/build/cli/core/cli_setup.js +6 -6
- package/build/cli/index.js +0 -1
- package/build/cli/utilities/features/logger/logs/app_logs_constants.js +1 -2
- package/build/index.js +2 -2
- package/build/theme/class/archive/theme_archive.js +47 -1
- package/build/theme/{features/theme/utils/archive/theme_archive_utils_errors_factory.js → class/archive/theme_archive_errors_factory.js} +1 -1
- package/build/theme/class/checksums/theme_checksums.js +11 -51
- package/build/theme/commands/pull/theme_pull_command.js +3 -4
- package/build/theme/commands/push/theme_push_command.js +8 -17
- package/build/theme/commands/theme_commands_constants.js +1 -2
- package/build/theme/commands/theme_verify_command.js +3 -3
- package/build/theme/commands/ui/theme_error.js +6 -6
- package/build/theme/features/theme/actions/theme_actions_constants.js +1 -2
- package/build/theme/features/theme/actions/theme_actions_utils.js +17 -61
- package/build/theme/features/theme/fetch/service/theme_fetch_service.js +4 -2
- package/build/theme/features/theme/init/service/theme_init_service.js +4 -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 +33 -93
- package/build/theme/features/theme/push/theme_push_utils.js +17 -7
- package/build/theme/features/theme/utils/files_structure/theme_file_structure_errors_factory.js +9 -1
- package/build/theme/features/theme/utils/{files/theme_files_utils.js → files_structure/theme_files_structure_utils.js} +28 -36
- package/build/theme/features/theme/utils/meta_data/theme_meta_data_utils.js +28 -0
- package/build/theme/features/theme/verify/verify/theme_verify_service.js +12 -19
- package/build/theme/index.js +2 -6
- package/build/theme/utils/directory_validator/directory_validator_utils.js +11 -4
- package/build/theme/utils/shoperignore/shoperignore_utils.js +36 -0
- package/build/ui/ui_dump/ui_dump.js +4 -9
- package/build/utils/array_utils.js +0 -3
- package/build/utils/fs/fs_utils.js +1 -1
- package/package.json +6 -10
- package/build/theme/class/browser/browser.js +0 -108
- package/build/theme/commands/watch/theme_watch_command.js +0 -88
- package/build/theme/commands/watch/theme_watch_constants.js +0 -21
- package/build/theme/commands/watch/theme_watch_utils.js +0 -32
- package/build/theme/commands/watch/theme_watching_info.js +0 -14
- package/build/theme/commands/watch/watch.js +0 -55
- package/build/theme/features/theme/push/service/theme_push_service_types.js +0 -1
- package/build/theme/features/theme/utils/archive/theme_archive_utils.js +0 -26
- package/build/theme/features/theme/utils/files/them_files_constants.js +0 -1
- package/build/theme/features/theme/watch/api/theme_watch_api.js +0 -19
- package/build/theme/features/theme/watch/service/theme_watch_service.js +0 -167
- package/build/theme/features/theme/watch/theme_watch_constants.js +0 -4
- package/build/theme/features/theme/watch/theme_watch_initializer.js +0 -22
- package/build/ui/command_input/command_input.js +0 -25
- package/build/ui/logs/log_entry.js +0 -12
- package/build/ui/logs/logs_constants.js +0 -20
- package/build/ui/logs/logs_list.js +0 -18
- package/build/ui/logs/use_logs.js +0 -23
- package/build/utils/fs/fs_constants.js +0 -6
package/README.md
CHANGED
|
@@ -2,4 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
# Shoper CLI
|
|
4
4
|
|
|
5
|
-
Learn more in the [commands docs](https://storefront.developers.shoper.pl/cli/)
|
|
5
|
+
Learn more in the [commands docs](https://storefront.developers.shoper.pl/cli/)
|
|
6
|
+
|
|
7
|
+
## Features
|
|
8
|
+
|
|
9
|
+
### .shoperignore
|
|
10
|
+
|
|
11
|
+
Plik `.shoperignore` pozwala na wykluczenie określonych plików i katalogów z pakowania, uploadowania oraz weryfikacji checksum.
|
|
12
|
+
|
|
13
|
+
Używa tej samej składni co `.gitignore`. Plik jest automatycznie tworzony podczas inicjalizacji lub pobierania motywu.
|
|
14
|
+
|
|
15
|
+
Więcej informacji: [SHOPERIGNORE.md](docs/SHOPERIGNORE.md)
|
|
@@ -29,7 +29,7 @@ export const cliSetup = async () => {
|
|
|
29
29
|
LoggerInitializer,
|
|
30
30
|
CliAuthTokensInitializer,
|
|
31
31
|
CliAuthInitializer,
|
|
32
|
-
...
|
|
32
|
+
...getCommandBaseInitializers()
|
|
33
33
|
],
|
|
34
34
|
options: {
|
|
35
35
|
featuresTracking: false,
|
|
@@ -37,15 +37,15 @@ export const cliSetup = async () => {
|
|
|
37
37
|
}
|
|
38
38
|
});
|
|
39
39
|
};
|
|
40
|
-
const
|
|
41
|
-
if (
|
|
42
|
-
return
|
|
40
|
+
const getCommandBaseInitializers = () => {
|
|
41
|
+
if (isCommandsTopic(process.argv[2]))
|
|
42
|
+
return getCommandWithTopicBaseInitializers();
|
|
43
43
|
return [];
|
|
44
44
|
};
|
|
45
|
-
const
|
|
45
|
+
const isCommandsTopic = (arg) => {
|
|
46
46
|
return arg === THEME_TOPIC_NAME || arg === CLI_AUTH_TOPIC_NAME;
|
|
47
47
|
};
|
|
48
|
-
const
|
|
48
|
+
const getCommandWithTopicBaseInitializers = () => {
|
|
49
49
|
const topic = process.argv[2];
|
|
50
50
|
const command = process.argv[3];
|
|
51
51
|
switch (topic) {
|
package/build/cli/index.js
CHANGED
package/build/index.js
CHANGED
|
@@ -31,7 +31,7 @@ process.on('uncaughtException', (err) => {
|
|
|
31
31
|
const loggerApi = getApiSync(LOGGER_API_NAME);
|
|
32
32
|
if (loggerApi)
|
|
33
33
|
loggerApi.fatal('Uncaught Exception', { error: err });
|
|
34
|
-
|
|
34
|
+
process.exit(1);
|
|
35
35
|
});
|
|
36
36
|
process.on('unhandledRejection', (reason) => {
|
|
37
37
|
console.error('Unhandled Rejection:', reason);
|
|
@@ -42,7 +42,7 @@ process.on('unhandledRejection', (reason) => {
|
|
|
42
42
|
reason
|
|
43
43
|
}
|
|
44
44
|
});
|
|
45
|
-
|
|
45
|
+
process.exit(1);
|
|
46
46
|
});
|
|
47
47
|
const signals = ['SIGINT', 'SIGTERM', 'SIGQUIT'];
|
|
48
48
|
signals.forEach((signal) => {
|
|
@@ -1 +1,47 @@
|
|
|
1
|
-
|
|
1
|
+
import globs from 'fast-glob';
|
|
2
|
+
import { createZip } from '../../../utils/zip/create_zip_utils.js';
|
|
3
|
+
import { ThemeFilesStructureUtils } from '../../features/theme/utils/files_structure/theme_files_structure_utils.js';
|
|
4
|
+
import { extname, join } from '../../../utils/path_utils.js';
|
|
5
|
+
import { formatJSONFile } from '../../../utils/fs/fs_utils.js';
|
|
6
|
+
import { ThemeArchiveErrorsFactory } from './theme_archive_errors_factory.js';
|
|
7
|
+
import { ThemeFileStructureErrorsFactory } from '../../features/theme/utils/files_structure/theme_file_structure_errors_factory.js';
|
|
8
|
+
import { ThemeActionsUtils } from '../../features/theme/actions/theme_actions_utils.js';
|
|
9
|
+
import { filterFiles } from '../../utils/shoperignore/shoperignore_utils.js';
|
|
10
|
+
export class ThemeArchive {
|
|
11
|
+
#themeRootDir;
|
|
12
|
+
constructor(themeRootDir) {
|
|
13
|
+
this.#themeRootDir = themeRootDir;
|
|
14
|
+
}
|
|
15
|
+
async createFullArchive({ dist, actionValue, actionType, logger }) {
|
|
16
|
+
const filesStructure = await ThemeFilesStructureUtils.getThemeFilesStructure(this.#themeRootDir);
|
|
17
|
+
if (!filesStructure)
|
|
18
|
+
throw ThemeFileStructureErrorsFactory.createNoFilesStructureError();
|
|
19
|
+
try {
|
|
20
|
+
const filesInThemeDirectory = await globs(ThemeActionsUtils.getFilesGlobsThatMatchesActionName({
|
|
21
|
+
actionType,
|
|
22
|
+
actionValue,
|
|
23
|
+
filesStructure
|
|
24
|
+
}), {
|
|
25
|
+
suppressErrors: true,
|
|
26
|
+
onlyFiles: true,
|
|
27
|
+
cwd: this.#themeRootDir
|
|
28
|
+
});
|
|
29
|
+
const filteredFiles = await filterFiles(filesInThemeDirectory, this.#themeRootDir);
|
|
30
|
+
await this._formatJsonFiles(this.#themeRootDir, filteredFiles);
|
|
31
|
+
await createZip({
|
|
32
|
+
baseDir: this.#themeRootDir,
|
|
33
|
+
dist,
|
|
34
|
+
files: filteredFiles,
|
|
35
|
+
logger
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
catch (err) {
|
|
39
|
+
throw ThemeArchiveErrorsFactory.createErrorWhileCreatingThemeArchive(err);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
async _formatJsonFiles(themeRootDir, filesToUpload) {
|
|
43
|
+
await Promise.all(filesToUpload
|
|
44
|
+
.filter((path) => extname(path).toLowerCase() === '.json')
|
|
45
|
+
.map((jsonFile) => formatJSONFile(join(themeRootDir, jsonFile))));
|
|
46
|
+
}
|
|
47
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AppError } from '
|
|
1
|
+
import { AppError } from '../../../cli/utilities/features/logger/logs/app_error.js';
|
|
2
2
|
export class ThemeArchiveErrorsFactory {
|
|
3
3
|
static createErrorWhileCreatingThemeArchive(error) {
|
|
4
4
|
return new AppError({
|
|
@@ -10,7 +10,6 @@ import { normalize } from 'node:path';
|
|
|
10
10
|
import { ThemePushUtils } from '../../features/theme/push/theme_push_utils.js';
|
|
11
11
|
import { SHOPER_THEME_METADATA_DIR } from '../../constants/directory_contstants.js';
|
|
12
12
|
import { THEME_CURRENT_CHECKSUMS_FILE_NAME, THEME_CURRENT_CHECKSUMS_VERITY_FILE_NAME, THEME_INITIAL_CHECKSUMS_FILE_NAME, THEME_INITIAL_CHECKSUMS_VERITY_FILE_NAME } from '../../features/theme/theme_constants.js';
|
|
13
|
-
import { FILE_STATES } from '../../../utils/fs/fs_constants.js';
|
|
14
13
|
export class ThemeChecksums {
|
|
15
14
|
#themeDir;
|
|
16
15
|
#loggerApi;
|
|
@@ -34,6 +33,12 @@ export class ThemeChecksums {
|
|
|
34
33
|
this.#initialChecksums = await this._getChecksums(this.#initialChecksumsFilePath);
|
|
35
34
|
return this.#initialChecksums;
|
|
36
35
|
}
|
|
36
|
+
getInitialChecksumsSync() {
|
|
37
|
+
if (this.#initialChecksums)
|
|
38
|
+
return this.#initialChecksums;
|
|
39
|
+
this.#initialChecksums = this._getChecksumsSync(this.#initialChecksumsFilePath);
|
|
40
|
+
return this.#initialChecksums;
|
|
41
|
+
}
|
|
37
42
|
async getCurrentChecksums() {
|
|
38
43
|
if (this.#currentChecksums)
|
|
39
44
|
return this.#currentChecksums;
|
|
@@ -67,22 +72,10 @@ export class ThemeChecksums {
|
|
|
67
72
|
const initialChecksum = await this.getInitialChecksumFromPath(path);
|
|
68
73
|
return !initialChecksum && (await fileExists(join(this.#themeDir, path)));
|
|
69
74
|
}
|
|
70
|
-
async
|
|
71
|
-
const initialChecksum = await this.getInitialChecksumFromPath(path);
|
|
75
|
+
async hasThemeFileBeenModified(path) {
|
|
72
76
|
const currentChecksum = await this.getCurrentChecksumFromPath(path);
|
|
73
|
-
const
|
|
74
|
-
|
|
75
|
-
return FILE_STATES.created;
|
|
76
|
-
}
|
|
77
|
-
else if (initialChecksum && !fileExistsInFs) {
|
|
78
|
-
return FILE_STATES.deleted;
|
|
79
|
-
}
|
|
80
|
-
else if (initialChecksum && currentChecksum && initialChecksum !== currentChecksum) {
|
|
81
|
-
return FILE_STATES.modified;
|
|
82
|
-
}
|
|
83
|
-
else {
|
|
84
|
-
return FILE_STATES.unchanged;
|
|
85
|
-
}
|
|
77
|
+
const initialChecksum = await this.getInitialChecksumFromPath(path);
|
|
78
|
+
return !!currentChecksum && !!initialChecksum && currentChecksum !== initialChecksum;
|
|
86
79
|
}
|
|
87
80
|
async verify() {
|
|
88
81
|
const initialChecksumFilePath = this.#initialChecksumsFilePath;
|
|
@@ -94,8 +87,6 @@ export class ThemeChecksums {
|
|
|
94
87
|
async updateAllChecksums() {
|
|
95
88
|
this.#loggerApi?.debug('Updating all checksums.');
|
|
96
89
|
const checksums = await this.computeThemeChecksums();
|
|
97
|
-
this.#initialChecksums = checksums;
|
|
98
|
-
this.#currentChecksums = checksums;
|
|
99
90
|
return new Promise((resolve, reject) => {
|
|
100
91
|
const currentChecksumFilePath = this.#currentChecksumsFilePath;
|
|
101
92
|
const initialChecksumFilePath = this.#initialChecksumsFilePath;
|
|
@@ -123,7 +114,6 @@ export class ThemeChecksums {
|
|
|
123
114
|
async updateCurrentChecksums() {
|
|
124
115
|
this.#loggerApi?.debug('Updating current checksums.');
|
|
125
116
|
const checksums = await this.computeThemeChecksums();
|
|
126
|
-
this.#currentChecksums = checksums;
|
|
127
117
|
return new Promise((resolve, reject) => {
|
|
128
118
|
const currentChecksumFilePath = this.#currentChecksumsFilePath;
|
|
129
119
|
const checksumStream = createWriteStream(currentChecksumFilePath);
|
|
@@ -154,7 +144,8 @@ export class ThemeChecksums {
|
|
|
154
144
|
join(SHOPER_THEME_METADATA_DIR, THEME_INITIAL_CHECKSUMS_FILE_NAME),
|
|
155
145
|
join(SHOPER_THEME_METADATA_DIR, THEME_INITIAL_CHECKSUMS_VERITY_FILE_NAME)
|
|
156
146
|
];
|
|
157
|
-
const
|
|
147
|
+
const allFiles = await ThemePushUtils.getAllFilesThatAreSendToRemote(this.#themeDir);
|
|
148
|
+
const filesToComputeChecksums = allFiles
|
|
158
149
|
.filter((path) => !filesToIgnoreInChecksums.some((ignorePath) => normalize(path) === ignorePath))
|
|
159
150
|
.map((relativePath) => join(this.#themeDir, relativePath));
|
|
160
151
|
this.#loggerApi?.debug('Computing checksums from file structure.', { details: { count: filesToComputeChecksums.length } });
|
|
@@ -164,37 +155,6 @@ export class ThemeChecksums {
|
|
|
164
155
|
this.#loggerApi?.debug('Finished computing theme checksums.', { details: { count: Object.keys(allChecksums).length } });
|
|
165
156
|
return allChecksums;
|
|
166
157
|
}
|
|
167
|
-
async groupFilesByStatus(files) {
|
|
168
|
-
const initialChecksums = await this.getInitialChecksums();
|
|
169
|
-
const currentChecksums = await this.getCurrentChecksums();
|
|
170
|
-
return files.reduce((acc, filePath) => {
|
|
171
|
-
const initialChecksum = initialChecksums[toCurrentPlatformPath(filePath)];
|
|
172
|
-
const currentChecksum = currentChecksums[toCurrentPlatformPath(filePath)];
|
|
173
|
-
if (!initialChecksum) {
|
|
174
|
-
acc.created.push(filePath);
|
|
175
|
-
}
|
|
176
|
-
else if (currentChecksum && initialChecksum !== currentChecksum) {
|
|
177
|
-
acc.modified.push(filePath);
|
|
178
|
-
}
|
|
179
|
-
else if (initialChecksum && !currentChecksum) {
|
|
180
|
-
acc.deleted.push(filePath);
|
|
181
|
-
}
|
|
182
|
-
else {
|
|
183
|
-
acc.unchanged.push(filePath);
|
|
184
|
-
}
|
|
185
|
-
return acc;
|
|
186
|
-
}, {
|
|
187
|
-
created: [],
|
|
188
|
-
modified: [],
|
|
189
|
-
unchanged: [],
|
|
190
|
-
deleted: []
|
|
191
|
-
});
|
|
192
|
-
}
|
|
193
|
-
async getDeletedFiles() {
|
|
194
|
-
const initialChecksums = await this.getInitialChecksums();
|
|
195
|
-
const currentChecksums = await this.getCurrentChecksums();
|
|
196
|
-
return Object.keys(initialChecksums).filter((filePath) => !currentChecksums[filePath]);
|
|
197
|
-
}
|
|
198
158
|
async _getInitialChecksumsVerification() {
|
|
199
159
|
return await readJSONFile(this.#initialChecksumsVerificationFilePath);
|
|
200
160
|
}
|
|
@@ -24,9 +24,8 @@ import { Info } from '../../../ui/message_box/info.js';
|
|
|
24
24
|
import { directoryExists } from '../../../utils/fs/fs_utils.js';
|
|
25
25
|
import { ThemeMetaDataUtils } from '../../features/theme/utils/meta_data/theme_meta_data_utils.js';
|
|
26
26
|
import { ThemeChecksums } from '../../class/checksums/theme_checksums.js';
|
|
27
|
-
import { LOGGER_API_NAME } from '../../../cli/utilities/features/logger/logger_constants.js';
|
|
28
27
|
import { ThemeError } from '../ui/theme_error.js';
|
|
29
|
-
import {
|
|
28
|
+
import { LOGGER_API_NAME } from '../../../cli/utilities/features/logger/logger_constants.js';
|
|
30
29
|
//TODO jak jest error w pullu wowczas usuwamy docelowo pociagniety skin
|
|
31
30
|
export class ThemePullCommand extends BaseThemeCommand {
|
|
32
31
|
static summary = 'Downloads the current version of your theme from the store and overwrites your local theme files.';
|
|
@@ -161,9 +160,9 @@ export class ThemePullCommand extends BaseThemeCommand {
|
|
|
161
160
|
dist: tmpDir
|
|
162
161
|
}
|
|
163
162
|
});
|
|
164
|
-
const localModulesPath = join(executionContext.themeRootDir,
|
|
163
|
+
const localModulesPath = join(executionContext.themeRootDir, 'modules');
|
|
165
164
|
const fetchedThemePath = join(tmpDir, name);
|
|
166
|
-
const fetchedModulesPath = join(fetchedThemePath,
|
|
165
|
+
const fetchedModulesPath = join(fetchedThemePath, 'modules');
|
|
167
166
|
if ((await directoryExists(localModulesPath)) && (await directoryExists(fetchedModulesPath)))
|
|
168
167
|
await ThemeResourcesWithIdDirectoryUtils.updateDirectoryNamesOfResourcesWithIdAccordingToLocalThemeNames(localModulesPath, fetchedModulesPath, fetchedThemePath, loggerApi);
|
|
169
168
|
this.#spinner.stop();
|
|
@@ -4,6 +4,8 @@ import { EXECUTION_CONTEXT_API_NAME, EXECUTION_CONTEXTS } from '../../../cli/fea
|
|
|
4
4
|
import { THEME_ACTIONS_API_NAME, THEME_ACTIONS_TYPES } from '../../features/theme/actions/theme_actions_constants.js';
|
|
5
5
|
import { THEME_PUSH_API_NAME } from '../../features/theme/push/theme_push_constants.js';
|
|
6
6
|
import { ThemeMetaDataUtils } from '../../features/theme/utils/meta_data/theme_meta_data_utils.js';
|
|
7
|
+
import { mapChecksumToTree } from '../../../utils/checksums/checksums_utils.js';
|
|
8
|
+
import { mapToPermissionsTree } from '../../utils/directory_validator/directory_validator_utils.js';
|
|
7
9
|
import { renderOnce } from '../../../ui/ui_utils.js';
|
|
8
10
|
import { UnpermittedCommandError } from '../ui/unpermitted_command_error.js';
|
|
9
11
|
import React from 'react';
|
|
@@ -12,7 +14,7 @@ import { MissingCredentialsError } from '../../../cli/commands/auth/ui/missing_c
|
|
|
12
14
|
import { ThemePushedSuccess } from './ui/theme_pushed_success.js';
|
|
13
15
|
import ora from 'ora';
|
|
14
16
|
import { ThemePushSkipInfo } from './ui/theme_push_skip_into.js';
|
|
15
|
-
import {
|
|
17
|
+
import { ThemeFilesStructureUtils } from '../../features/theme/utils/files_structure/theme_files_structure_utils.js';
|
|
16
18
|
import { ThemeInfoUtils } from '../../features/theme/info/theme_info_utils.js';
|
|
17
19
|
import { ThemeChecksums } from '../../class/checksums/theme_checksums.js';
|
|
18
20
|
import { ThemeFilesUpload } from '../../class/files_upload/theme_files_upload.js';
|
|
@@ -22,8 +24,6 @@ import { MissingThemeFiles } from '../ui/missing_theme_files.js';
|
|
|
22
24
|
import { SHOPER_THEME_METADATA_DIR } from '../../constants/directory_contstants.js';
|
|
23
25
|
import { THEME_FILES_STRUCTURE_FILE_NAME } from '../../features/theme/theme_constants.js';
|
|
24
26
|
import { ThemeError } from '../ui/theme_error.js';
|
|
25
|
-
import { mapToPermissionsTree } from '../../utils/directory_validator/directory_validator_utils.js';
|
|
26
|
-
import { mapChecksumToTree } from '../../../utils/checksums/checksums_utils.js';
|
|
27
27
|
import { ThemeUnpermittedActionsError } from './ui/theme_unpermitted_actions_error.js';
|
|
28
28
|
import { LOGGER_API_NAME } from '../../../cli/utilities/features/logger/logger_constants.js';
|
|
29
29
|
export class ThemePushCommand extends BaseThemeCommand {
|
|
@@ -68,7 +68,7 @@ export class ThemePushCommand extends BaseThemeCommand {
|
|
|
68
68
|
return;
|
|
69
69
|
}
|
|
70
70
|
const initialChecksums = await themeChecksums.getInitialChecksums();
|
|
71
|
-
const permissions = await
|
|
71
|
+
const permissions = await ThemeFilesStructureUtils.getFilesPermissions(executionContext.themeRootDir);
|
|
72
72
|
const themeFilesUploadApi = new ThemeFilesUpload({
|
|
73
73
|
themeRootDir: executionContext.themeRootDir,
|
|
74
74
|
credentials,
|
|
@@ -80,7 +80,7 @@ export class ThemePushCommand extends BaseThemeCommand {
|
|
|
80
80
|
renderOnce(React.createElement(ThemePushSkipInfo, null));
|
|
81
81
|
return;
|
|
82
82
|
}
|
|
83
|
-
const validationResult = await
|
|
83
|
+
const validationResult = await ThemeFilesStructureUtils.validateThemeDirectoryStructure({
|
|
84
84
|
//TDO przeniesc do theme checksums
|
|
85
85
|
checksums: mapChecksumToTree(initialChecksums),
|
|
86
86
|
permissions: mapToPermissionsTree(permissions),
|
|
@@ -92,25 +92,16 @@ export class ThemePushCommand extends BaseThemeCommand {
|
|
|
92
92
|
renderOnce(React.createElement(ThemeUnpermittedActionsError, { unpermittedActions: validationResult.unpermittedActions }));
|
|
93
93
|
return;
|
|
94
94
|
}
|
|
95
|
-
const filesStructure = await
|
|
95
|
+
const filesStructure = await ThemeFilesStructureUtils.getThemeFilesStructure(executionContext.themeRootDir);
|
|
96
96
|
if (!filesStructure) {
|
|
97
97
|
renderOnce(React.createElement(MissingThemeFiles, { files: `${SHOPER_THEME_METADATA_DIR}/${THEME_FILES_STRUCTURE_FILE_NAME}` }));
|
|
98
98
|
return;
|
|
99
99
|
}
|
|
100
100
|
spinner = ora('Pushing theme...').start();
|
|
101
|
-
|
|
102
|
-
actionType: THEME_ACTIONS_TYPES.partialPush,
|
|
103
|
-
themeId: executionContext.themeId,
|
|
104
|
-
credentials
|
|
105
|
-
});
|
|
106
|
-
if (!pushAction) {
|
|
107
|
-
renderOnce(React.createElement(UnpermittedCommandError, { themeId: executionContext.themeId, commandName: "push" }));
|
|
108
|
-
return;
|
|
109
|
-
}
|
|
110
|
-
await themePushApi.partialPush({
|
|
101
|
+
await themePushApi.push({
|
|
111
102
|
credentials,
|
|
112
103
|
filesStructure,
|
|
113
|
-
|
|
104
|
+
pushAction,
|
|
114
105
|
themeChecksums,
|
|
115
106
|
executionContext,
|
|
116
107
|
themeFilesUploadApi
|
|
@@ -7,6 +7,5 @@ export const THEME_COMMANDS_NAME = {
|
|
|
7
7
|
verify: `${THEME_TOPIC_NAME}:verify`,
|
|
8
8
|
info: `${THEME_TOPIC_NAME}:info`,
|
|
9
9
|
delete: `${THEME_TOPIC_NAME}:delete`,
|
|
10
|
-
publish: `${THEME_TOPIC_NAME}:publish
|
|
11
|
-
watch: `${THEME_TOPIC_NAME}:watch`
|
|
10
|
+
publish: `${THEME_TOPIC_NAME}:publish`
|
|
12
11
|
};
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { BaseThemeCommand } from '../class/base_theme_command.js';
|
|
2
2
|
import { CLI_AUTH_API_NAME } from '../../cli/auth/cli_auth_constants.js';
|
|
3
3
|
import { EXECUTION_CONTEXT_API_NAME, EXECUTION_CONTEXTS } from '../../cli/features/execution_context/execution_context_constants.js';
|
|
4
|
+
import { ThemeFilesStructureUtils } from '../features/theme/utils/files_structure/theme_files_structure_utils.js';
|
|
4
5
|
import { ThemeChecksums } from '../class/checksums/theme_checksums.js';
|
|
5
6
|
import { THEME_VERIFY_API_NAME } from '../features/theme/verify/theme_verify_constants.js';
|
|
6
7
|
import { THEME_ACTIONS_API_NAME, THEME_ACTIONS_TYPES } from '../features/theme/actions/theme_actions_constants.js';
|
|
@@ -17,7 +18,6 @@ import { MissingCredentialsError } from '../../cli/commands/auth/ui/missing_cred
|
|
|
17
18
|
import { OutsideOfThemeDirectoryContextError } from './ui/ouside_of_theme_directory_context_error.js';
|
|
18
19
|
import ora from 'ora';
|
|
19
20
|
import { ThemeError } from './ui/theme_error.js';
|
|
20
|
-
import { ThemeFilesUtils } from '../features/theme/utils/files/theme_files_utils.js';
|
|
21
21
|
import { LOGGER_API_NAME } from '../../cli/utilities/features/logger/logger_constants.js';
|
|
22
22
|
export class ThemeVerifyCommand extends BaseThemeCommand {
|
|
23
23
|
static description = 'Verify theme files structure';
|
|
@@ -39,7 +39,7 @@ export class ThemeVerifyCommand extends BaseThemeCommand {
|
|
|
39
39
|
themeDir: executionContext.themeRootDir,
|
|
40
40
|
loggerApi
|
|
41
41
|
});
|
|
42
|
-
const permissions = await
|
|
42
|
+
const permissions = await ThemeFilesStructureUtils.getFilesPermissions(executionContext.themeRootDir);
|
|
43
43
|
if (!themeChecksums || !permissions)
|
|
44
44
|
this.error('Theme checksums or permissions not found. Please ensure you are in the correct theme directory.');
|
|
45
45
|
let spinner;
|
|
@@ -56,7 +56,7 @@ export class ThemeVerifyCommand extends BaseThemeCommand {
|
|
|
56
56
|
themeId: executionContext.themeId,
|
|
57
57
|
credentials
|
|
58
58
|
});
|
|
59
|
-
const filesStructure = await
|
|
59
|
+
const filesStructure = await ThemeFilesStructureUtils.getThemeFilesStructure(executionContext.themeRootDir);
|
|
60
60
|
if (!filesStructure) {
|
|
61
61
|
renderOnce(React.createElement(MissingThemeFiles, { files: `${SHOPER_THEME_METADATA_DIR}/${THEME_FILES_STRUCTURE_FILE_NAME}` }));
|
|
62
62
|
return;
|
|
@@ -9,18 +9,18 @@ import React from 'react';
|
|
|
9
9
|
import { ThemeWorkUrlMismatch } from './theme_work_url_mismatch.js';
|
|
10
10
|
import { EXECUTION_CONTEXTS } from '../../../cli/features/execution_context/execution_context_constants.js';
|
|
11
11
|
export const ThemeError = ({ err, executionContext }) => {
|
|
12
|
-
if (err?.
|
|
12
|
+
if (err?.code === THEME_ACTION_NOT_FOUND_ERROR_CODE && executionContext?.type === EXECUTION_CONTEXTS.theme) {
|
|
13
13
|
return (React.createElement(UnpermittedCommandError, { themeId: executionContext.themeId, commandName: "verify" }));
|
|
14
14
|
}
|
|
15
|
-
if (err?.
|
|
15
|
+
if (err?.code === THEME_ARCHIVE_UPLOAD_ERROR) {
|
|
16
16
|
return React.createElement(ValidationErrors, { errors: err?.details });
|
|
17
17
|
}
|
|
18
|
-
if (err?.
|
|
18
|
+
if (err?.code === THEME_WORK_URL_MISMATCH_ERROR) {
|
|
19
19
|
return React.createElement(ThemeWorkUrlMismatch, { command: "verify" });
|
|
20
20
|
}
|
|
21
|
-
if (err?.
|
|
22
|
-
return (React.createElement(Error, { header: "Uploading theme files to the shop failed
|
|
23
|
-
React.createElement(Text, null, "Please ensure that the files are not corrupted and that the file extensions are supported
|
|
21
|
+
if (err?.code === THEME_FILES_UPLOAD_ERROR) {
|
|
22
|
+
return (React.createElement(Error, { header: "Uploading theme files to the shop failed.\n" },
|
|
23
|
+
React.createElement(Text, null, "The rejected files have been removed locally. Please ensure that the files are not corrupted and that the file extensions are supported.")));
|
|
24
24
|
}
|
|
25
25
|
if (err?.message) {
|
|
26
26
|
return React.createElement(ValidationErrors, { errors: err.message });
|
|
@@ -3,43 +3,24 @@ 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
|
|
7
|
-
import difference from 'lodash/difference.js';
|
|
6
|
+
import { filterFiles, loadShoperIgnore } from '../../../utils/shoperignore/shoperignore_utils.js';
|
|
8
7
|
export class ThemeActionsUtils {
|
|
9
|
-
static
|
|
8
|
+
static getFilesGlobsThatMatchesActionName({ filesStructure, actionValue, actionType }) {
|
|
10
9
|
return Object.entries(filesStructure).reduce((acc, [filePath, fileStructureItem]) => {
|
|
11
10
|
if (fileStructureItem._links?.[actionType] && this._doesActionValueMatch(fileStructureItem._links[actionType], actionValue)) {
|
|
12
|
-
|
|
11
|
+
const normalizedPath = this._normalizeFileGlob(toUnixPath(filePath));
|
|
12
|
+
return [...acc, normalizedPath];
|
|
13
13
|
}
|
|
14
14
|
return acc;
|
|
15
15
|
}, []);
|
|
16
16
|
}
|
|
17
|
-
static async
|
|
18
|
-
return await globs(ThemeActionsUtils.getFilesGlobsThatMatchesAction({
|
|
19
|
-
actionType,
|
|
20
|
-
actionValue,
|
|
21
|
-
filesStructure
|
|
22
|
-
}), {
|
|
23
|
-
suppressErrors: true,
|
|
24
|
-
onlyFiles: true,
|
|
25
|
-
cwd: rootDir
|
|
26
|
-
});
|
|
27
|
-
}
|
|
28
|
-
static async getDeletedFilesThatMatchesAction({ actionType, actionValue, themeChecksums, filesStructure }) {
|
|
29
|
-
const deletedFiles = await themeChecksums.getDeletedFiles();
|
|
30
|
-
const globs = ThemeActionsUtils.getFilesGlobsThatMatchesAction({
|
|
31
|
-
actionType,
|
|
32
|
-
actionValue,
|
|
33
|
-
filesStructure
|
|
34
|
-
});
|
|
35
|
-
return deletedFiles.filter((path) => !looksLikeDirectory(path) && micromatch.isMatch(path, globs));
|
|
36
|
-
}
|
|
37
|
-
static async getFilesRecordsFromActionData({ themeRootDir, themeAction, filesStructure, themeChecksums }) {
|
|
17
|
+
static async getFilesRecordsFromActionData({ themeRootDir, themeAction, filesStructure }) {
|
|
38
18
|
const filesRecords = [];
|
|
39
|
-
|
|
19
|
+
// Load the ignore instance once before the loops to avoid reading .shoperignore multiple times
|
|
20
|
+
const ignoreInstance = await loadShoperIgnore(themeRootDir);
|
|
40
21
|
for (const [actionKey, actionData] of Object.entries(themeAction.data)) {
|
|
41
22
|
if (actionData.type === THEME_ACTION_DATA_TYPE.file) {
|
|
42
|
-
const filesGlobs = ThemeActionsUtils.
|
|
23
|
+
const filesGlobs = ThemeActionsUtils.getFilesGlobsThatMatchesActionName({
|
|
43
24
|
//TODO remove when backend fixed
|
|
44
25
|
actionType: 'push',
|
|
45
26
|
// actionType: themeAction.actionType,
|
|
@@ -47,27 +28,25 @@ export class ThemeActionsUtils {
|
|
|
47
28
|
filesStructure
|
|
48
29
|
});
|
|
49
30
|
for (const fileGlob of filesGlobs) {
|
|
50
|
-
const checksumMatchedFiles = micromatch(Object.keys(initialChecksums), [fileGlob]);
|
|
51
31
|
const files = await globs(fileGlob, {
|
|
52
32
|
suppressErrors: true,
|
|
53
33
|
onlyFiles: true,
|
|
54
34
|
cwd: themeRootDir
|
|
55
35
|
});
|
|
36
|
+
const filteredFiles = await filterFiles(files, themeRootDir, ignoreInstance);
|
|
56
37
|
let processedFileGlob = fileGlob;
|
|
57
38
|
if (looksLikeDirectory(fileGlob)) {
|
|
58
39
|
processedFileGlob = fileGlob.endsWith(THEME_PUSH_WILDCARD_GLOBS_FOR_FILES)
|
|
59
40
|
? fileGlob.slice(0, fileGlob.length - 2)
|
|
60
41
|
: fileGlob;
|
|
61
42
|
}
|
|
62
|
-
const
|
|
63
|
-
for (const filePath of [...files, ...deletedFiles]) {
|
|
43
|
+
for (const filePath of filteredFiles) {
|
|
64
44
|
filesRecords.push({
|
|
65
45
|
actionData,
|
|
66
46
|
actionKey,
|
|
67
47
|
path: filePath,
|
|
68
48
|
fileName: basename(filePath),
|
|
69
|
-
fileGlob: processedFileGlob
|
|
70
|
-
state: await themeChecksums.getFileState(filePath)
|
|
49
|
+
fileGlob: processedFileGlob
|
|
71
50
|
});
|
|
72
51
|
}
|
|
73
52
|
}
|
|
@@ -75,35 +54,6 @@ export class ThemeActionsUtils {
|
|
|
75
54
|
}
|
|
76
55
|
return filesRecords;
|
|
77
56
|
}
|
|
78
|
-
// static async getFilesRecordsFromFiles({
|
|
79
|
-
// files,
|
|
80
|
-
// themeRootDir,
|
|
81
|
-
// themeAction,
|
|
82
|
-
// filesStructure,
|
|
83
|
-
// themeChecksums
|
|
84
|
-
// }: {
|
|
85
|
-
// files: string[];
|
|
86
|
-
// themeRootDir: string;
|
|
87
|
-
// themeAction: TThemeAction;
|
|
88
|
-
// filesStructure: TThemeFilesStructure;
|
|
89
|
-
// themeChecksums: ThemeChecksums;
|
|
90
|
-
// }): Promise<TFileRecord[]> {
|
|
91
|
-
// const fileRecords: TFileRecord[] = [];
|
|
92
|
-
//
|
|
93
|
-
// for (const file of files) {
|
|
94
|
-
// const state = await themeChecksums.getFileState(file);
|
|
95
|
-
//
|
|
96
|
-
// if (state) {
|
|
97
|
-
// fileRecords.push({
|
|
98
|
-
// path: file,
|
|
99
|
-
// state,
|
|
100
|
-
// action: themeAction
|
|
101
|
-
// });
|
|
102
|
-
// }
|
|
103
|
-
// }
|
|
104
|
-
//
|
|
105
|
-
// return fileRecords;
|
|
106
|
-
// }
|
|
107
57
|
static _doesActionValueMatch(currentActionValue, valuesToMatch) {
|
|
108
58
|
if (typeof valuesToMatch === 'string') {
|
|
109
59
|
return currentActionValue === valuesToMatch || valuesToMatch === THEME_ALL_ACTIONS_NAME;
|
|
@@ -113,4 +63,10 @@ export class ThemeActionsUtils {
|
|
|
113
63
|
}
|
|
114
64
|
return false;
|
|
115
65
|
}
|
|
66
|
+
static _normalizeFileGlob(fileGlob) {
|
|
67
|
+
if (fileGlob.endsWith('/')) {
|
|
68
|
+
return `${fileGlob}${THEME_PUSH_WILDCARD_GLOBS_FOR_FILES}`;
|
|
69
|
+
}
|
|
70
|
+
return fileGlob;
|
|
71
|
+
}
|
|
116
72
|
}
|
|
@@ -8,9 +8,9 @@ import { FetchResources } from '../../../../class/fetch_resources/fetch_resource
|
|
|
8
8
|
import { jsonIndentTransform } from '../../../../../utils/stream_transforms/json_indent_transform.js';
|
|
9
9
|
import { JSON_FILE_INDENT } from '../../../../../cli/cli_constants.js';
|
|
10
10
|
import { AppError } from '../../../../../cli/utilities/features/logger/logs/app_error.js';
|
|
11
|
+
import { ThemeFilesStructureUtils } from '../../utils/files_structure/theme_files_structure_utils.js';
|
|
11
12
|
import { ThemeChecksums } from '../../../../class/checksums/theme_checksums.js';
|
|
12
13
|
import { ThemeMetaDataUtils } from '../../utils/meta_data/theme_meta_data_utils.js';
|
|
13
|
-
import { ThemeFilesUtils } from '../../utils/files/theme_files_utils.js';
|
|
14
14
|
export class ThemeFetchService {
|
|
15
15
|
#themeHttpApi;
|
|
16
16
|
#httpApi;
|
|
@@ -55,7 +55,7 @@ export class ThemeFetchService {
|
|
|
55
55
|
}
|
|
56
56
|
try {
|
|
57
57
|
this.#loggerApi.debug('Updating theme files structure.');
|
|
58
|
-
await
|
|
58
|
+
await ThemeFilesStructureUtils.updateFilesStructure(themeDir);
|
|
59
59
|
}
|
|
60
60
|
catch (error) {
|
|
61
61
|
throw new AppError({
|
|
@@ -66,6 +66,8 @@ export class ThemeFetchService {
|
|
|
66
66
|
}
|
|
67
67
|
this.#loggerApi.debug('Creating .gitignore file.');
|
|
68
68
|
await ThemeMetaDataUtils.createGitIgnoreFile(themeDir);
|
|
69
|
+
this.#loggerApi.debug('Creating .shoperignore file.');
|
|
70
|
+
await ThemeMetaDataUtils.createShoperIgnoreFile(themeDir);
|
|
69
71
|
this.#loggerApi.debug('Updating metadata file.');
|
|
70
72
|
await ThemeMetaDataUtils.updateMetadataFileWithWorkUrl(themeDir, credentials.shopUrl);
|
|
71
73
|
this.#loggerApi.debug('Updating theme checksums');
|
|
@@ -6,8 +6,8 @@ import { extractZip } from '../../../../../utils/zip/extract_zip_utils.js';
|
|
|
6
6
|
import { ThemeMetaDataUtils } from '../../utils/meta_data/theme_meta_data_utils.js';
|
|
7
7
|
import { ThemeInfoUtils } from '../../info/theme_info_utils.js';
|
|
8
8
|
import { ThemeChecksums } from '../../../../class/checksums/theme_checksums.js';
|
|
9
|
+
import { ThemeFilesStructureUtils } from '../../utils/files_structure/theme_files_structure_utils.js';
|
|
9
10
|
import { AppError } from '../../../../../cli/utilities/features/logger/logs/app_error.js';
|
|
10
|
-
import { ThemeFilesUtils } from '../../utils/files/theme_files_utils.js';
|
|
11
11
|
export class ThemeInitService {
|
|
12
12
|
#httpApi;
|
|
13
13
|
#loggerApi;
|
|
@@ -41,7 +41,7 @@ export class ThemeInitService {
|
|
|
41
41
|
this.#loggerApi.info('Theme archive extracted.');
|
|
42
42
|
try {
|
|
43
43
|
this.#loggerApi.debug('Updating theme files structure.');
|
|
44
|
-
await
|
|
44
|
+
await ThemeFilesStructureUtils.updateFilesStructure(themeDir);
|
|
45
45
|
}
|
|
46
46
|
catch (err) {
|
|
47
47
|
throw new AppError({
|
|
@@ -52,6 +52,8 @@ export class ThemeInitService {
|
|
|
52
52
|
}
|
|
53
53
|
this.#loggerApi.debug('Creating .gitignore file.');
|
|
54
54
|
await ThemeMetaDataUtils.createGitIgnoreFile(themeDir);
|
|
55
|
+
this.#loggerApi.debug('Creating .shoperignore file.');
|
|
56
|
+
await ThemeMetaDataUtils.createShoperIgnoreFile(themeDir);
|
|
55
57
|
this.#loggerApi.debug('Updating metadata file.');
|
|
56
58
|
await ThemeMetaDataUtils.updateMetadataFileWithWorkUrl(themeDir, credentials.shopUrl);
|
|
57
59
|
this.#loggerApi.debug('Updating theme checksums.');
|