@shoper/cli 0.1.0-7 → 0.1.0-8
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/auth/api/cli_auth_api.js +15 -2
- package/build/cli/auth/cli_auth_constants.js +6 -0
- package/build/cli/auth/cli_auth_errors_factory.js +10 -0
- package/build/cli/auth/cli_auth_initializer.js +19 -0
- package/build/cli/auth/cli_auth_utils.js +22 -0
- package/build/cli/auth/model/cli_credentials.js +10 -0
- package/build/cli/auth/service/cli_auth_service.js +34 -0
- package/build/cli/auth/tokens/api/cli_auth_tokens_api.js +43 -1
- package/build/cli/auth/tokens/cli_auth_tokens_constants.js +6 -0
- package/build/cli/auth/tokens/cli_auth_tokens_errors_factory.js +10 -0
- package/build/cli/auth/tokens/cli_auth_tokens_initalizer.js +35 -1
- package/build/cli/auth/tokens/cli_auth_tokens_utils.js +26 -0
- package/build/cli/auth/tokens/service/cli_auth_tokens_service.js +84 -4
- package/build/cli/auth/tokens/service/cli_auth_tokens_service_constants.js +2 -0
- package/build/cli/class/base_cli_command.js +3 -0
- package/build/cli/class/errors/app_error/app_error.js +17 -0
- package/build/cli/class/errors/app_error/app_error_constants.js +7 -0
- package/build/cli/class/errors/file_system_errors_factory.js +26 -0
- package/build/cli/class/errors/http_errors_factory.js +21 -0
- package/build/cli/cli_constants.js +1 -0
- package/build/cli/commands/auth/cli_auth_add_token_command.js +32 -0
- package/build/cli/commands/auth/cli_auth_list_tokens_command.js +17 -0
- package/build/cli/commands/auth/cli_auth_remove_token_command.js +37 -0
- package/build/cli/commands/auth/cli_auth_switch_token_command.js +36 -0
- package/build/cli/commands/cli_update_command.js +7 -3
- package/build/cli/commands/commands_constants.js +5 -1
- package/build/cli/commands/files_diff_command.js +174 -0
- package/build/cli/commands/utils/prompt_for_token_utils.js +25 -0
- package/build/cli/core/cli_setup.js +20 -7
- package/build/cli/features/caches/json_cache/json_cache.js +50 -0
- package/build/cli/features/caches/memory_cache.js +6 -0
- package/build/cli/features/data_directory/cli_data_directory_constants.js +0 -1
- package/build/cli/features/data_directory/cli_data_directory_utils.js +3 -4
- package/build/cli/features/data_directory/service/cli_data_directory_service.js +2 -2
- package/build/cli/features/execution_context/execution_context_service.js +3 -5
- package/build/cli/features/http_requester/http_client.js +32 -1
- package/build/cli/features/version/service/cli_version_service.js +2 -4
- package/build/cli/hooks/authorization/ensure_authorization_hook.js +11 -6
- package/build/cli/hooks/authorization/ensure_authorization_hook_constants.js +8 -1
- package/build/index.js +34 -1
- package/build/theme/class/fetch_resources/fetch_resources.js +126 -0
- package/build/theme/class/fetch_resources/fetch_resources_constants.js +2 -0
- package/build/theme/class/fetch_resources/fetch_resources_errors_factory.js +11 -0
- package/build/theme/class/fetch_resources/fetch_resources_utils.js +35 -0
- package/build/theme/commands/theme_commands_constants.js +8 -0
- package/build/theme/commands/theme_init_command.js +53 -0
- package/build/theme/commands/theme_list_command.js +16 -0
- package/build/theme/commands/theme_pull_command.js +124 -0
- package/build/theme/commands/theme_push_command.js +65 -0
- package/build/theme/commands/theme_show_changes_command.js +60 -0
- package/build/theme/commands/theme_verify_command.js +43 -0
- package/build/theme/features/theme/actions/api/theme_actions_api.js +19 -0
- package/build/theme/features/theme/actions/service/theme_actions_service.js +92 -0
- package/build/theme/features/theme/actions/service/theme_actions_service_constants.js +2 -0
- package/build/theme/features/theme/actions/theme_actions_constants.js +15 -0
- package/build/theme/features/theme/actions/theme_actions_initializer.js +27 -0
- package/build/theme/features/theme/actions/theme_actions_utils.js +8 -0
- package/build/theme/features/theme/directory/theme_directory_utils.js +61 -14
- package/build/theme/features/theme/fetch/api/theme_fetch_api.js +16 -0
- package/build/theme/features/theme/fetch/http/theme_fetch_http_api.js +18 -0
- package/build/theme/features/theme/fetch/service/theme_fetch_service.js +113 -0
- package/build/theme/features/theme/fetch/theme_fetch_constants.js +7 -0
- package/build/theme/features/theme/fetch/theme_fetch_initializer.js +23 -0
- package/build/theme/features/theme/init/api/theme_init_api.js +13 -0
- package/build/theme/features/theme/init/http/theme_init_http_api.js +18 -0
- package/build/theme/features/theme/init/service/theme_init_service.js +28 -0
- package/build/theme/features/theme/init/theme_init_constants.js +2 -0
- package/build/theme/features/theme/init/theme_init_initializer.js +22 -0
- package/build/theme/features/theme/merge/api/theme_merge_api.js +28 -0
- package/build/theme/features/theme/merge/service/theme_merge_service.js +56 -0
- package/build/theme/features/theme/merge/theme_merge_constants.js +9 -0
- package/build/theme/features/theme/merge/theme_merge_initializer.js +18 -0
- package/build/theme/features/theme/publish/theme_publish_constants.js +2 -0
- package/build/theme/features/theme/publish/theme_publish_utils.js +7 -0
- package/build/theme/features/theme/push/api/theme_push_api.js +13 -0
- package/build/theme/features/theme/push/http_api/theme_push_http_api.js +21 -0
- package/build/theme/features/theme/push/service/theme_push_service.js +194 -0
- package/build/theme/features/theme/push/service/theme_push_service_constants.js +1 -0
- package/build/theme/features/theme/push/theme_push_constants.js +4 -0
- package/build/theme/features/theme/push/theme_push_errors_factory.js +41 -0
- package/build/theme/features/theme/push/theme_push_initializer.js +27 -0
- package/build/theme/features/theme/theme_constants.js +6 -0
- package/build/theme/features/theme/utils/checksums/theme_checksums_error_factory.js +10 -0
- package/build/theme/features/theme/utils/checksums/theme_checksums_utils.js +94 -0
- package/build/theme/features/theme/utils/directories/theme_resources_with_id_directory_utils.js +80 -0
- package/build/theme/features/theme/utils/theme_images_utils.js +31 -0
- package/build/theme/features/themes/list/api/themes_list_api.js +13 -0
- package/build/theme/features/themes/{http/shoper_themes_http_api.js → list/http/themes_list_http_api.js} +3 -2
- package/build/theme/features/themes/{model/theme_metadata.js → list/model/theme_list_metadata.js} +3 -1
- package/build/theme/features/themes/list/services/themes_list_service.js +37 -0
- package/build/theme/features/themes/list/themes_list_constants.js +2 -0
- package/build/theme/features/themes/list/themes_list_initializer.js +20 -0
- package/build/theme/hooks/ensure_theme_meta_data_untouched.js +17 -0
- package/build/theme/hooks/theme_checksums/ensure_theme_current_checksums_up_to_date.js +21 -0
- package/build/theme/hooks/theme_checksums/ensure_theme_current_checksums_up_to_date_constants.js +7 -0
- package/build/theme/hooks/themes_actions/ensure_themes_actions_hook.js +45 -0
- package/build/theme/hooks/themes_actions/ensure_themes_actions_hook_constants.js +7 -0
- package/build/theme/index.js +13 -4
- package/build/theme/utils/directory_validator/directory_validator_constants.js +13 -0
- package/build/theme/utils/directory_validator/directory_validator_utils.js +187 -0
- package/build/utils/checksums/checksums_utils.js +95 -0
- package/build/utils/checksums/checksums_utils_constants.js +1 -0
- package/build/utils/date_utils.js +3 -0
- package/build/utils/download_file/download_file_errors_factory.js +9 -0
- package/build/utils/download_file/download_file_utils.js +46 -0
- package/build/utils/fs/errors/stream_read_error.js +11 -0
- package/build/utils/fs/errors/stream_write_error.js +11 -0
- package/build/utils/fs/fs_utils.js +152 -0
- package/build/utils/http_utils.js +10 -0
- package/build/utils/path_utils.js +34 -0
- package/build/utils/stream_transforms/json_indent_transform.js +21 -0
- package/build/utils/url_utils.js +9 -0
- package/build/utils/zip/create_zip_utils.js +80 -0
- package/build/utils/zip/errors/create_zip_error.js +11 -0
- package/build/utils/zip/errors/open_zip_error.js +11 -0
- package/build/utils/zip/extract_zip_utils.js +104 -0
- package/oclif.config.js +4 -2
- package/package.json +34 -7
- package/build/cli/hooks/migration/migration_hook.js +0 -4
- package/build/theme/commands/list_command.js +0 -12
- package/build/theme/commands/pull_command.js +0 -24
- package/build/theme/features/theme/directory/theme_directories_utils.js +0 -10
- package/build/theme/features/theme/pull/api/shoper_theme_pull_api.js +0 -9
- package/build/theme/features/theme/pull/http/shoper_theme_pull_http_api.js +0 -14
- package/build/theme/features/theme/pull/service/shoper_theme_pull_service.js +0 -68
- package/build/theme/features/theme/pull/shoper_theme_pull_initializer.js +0 -22
- package/build/theme/features/themes/api/shoper_themes_api.js +0 -14
- package/build/theme/features/themes/services/shoper_themes_service.js +0 -15
- package/build/theme/features/themes/shoper_themes_constants.js +0 -2
- package/build/theme/features/themes/shoper_themes_initalizer.js +0 -20
- package/build/utils/fs.js +0 -44
- package/build/utils/path.js +0 -13
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
import tmp from 'tmp-promise';
|
|
2
|
+
import { THEME_ACTION_DATA_TYPE, THEME_ACTIONS_TYPES } from '../../actions/theme_actions_constants.js';
|
|
3
|
+
import { createZip } from '../../../../../utils/zip/create_zip_utils.js';
|
|
4
|
+
import { basename, dirname, extname, join, looksLikeDirectory } from '../../../../../utils/path_utils.js';
|
|
5
|
+
import { createReadStream } from 'node:fs';
|
|
6
|
+
import { v4 as uuid } from 'uuid';
|
|
7
|
+
import globs from 'fast-glob';
|
|
8
|
+
import { THEME_WILDCARD_ACTION_NAME } from '../../actions/service/theme_actions_service_constants.js';
|
|
9
|
+
import { getFilesGlobsThatMatchesActionName } from '../../actions/theme_actions_utils.js';
|
|
10
|
+
import { THEME_FILES_LIST_FILE_NAME, THEME_MODULE_SETTINGS_FILE_NAME } from '../theme_push_constants.js';
|
|
11
|
+
import { THEME_PUSH_WILDCARD_GLOBS_FOR_FILES } from './theme_push_service_constants.js';
|
|
12
|
+
import { ThemePushErrorsFactory } from '../theme_push_errors_factory.js';
|
|
13
|
+
import { ThemeImagesUtils } from '../../utils/theme_images_utils.js';
|
|
14
|
+
import { ThemePublishUtils } from '../../publish/theme_publish_utils.js';
|
|
15
|
+
import { formatJSONFile, removeFile, writeJSONFile } from '../../../../../utils/fs/fs_utils.js';
|
|
16
|
+
import { computeChecksumsFromSource } from '../../../../../utils/checksums/checksums_utils.js';
|
|
17
|
+
import { ThemeChecksumsUtils } from '../../utils/checksums/theme_checksums_utils.js';
|
|
18
|
+
import { MODULES_DIRECTORY_NAME } from '../../theme_constants.js';
|
|
19
|
+
export class ThemePushService {
|
|
20
|
+
#themePushHttpApi;
|
|
21
|
+
#themeMergeApi;
|
|
22
|
+
#themeFetchApi;
|
|
23
|
+
constructor({ themePushHttpApi, themeMergeApi, themeFetchApi }) {
|
|
24
|
+
this.#themePushHttpApi = themePushHttpApi;
|
|
25
|
+
this.#themeMergeApi = themeMergeApi;
|
|
26
|
+
this.#themeFetchApi = themeFetchApi;
|
|
27
|
+
}
|
|
28
|
+
async push({ pushAction, filesStructure, credentials, executionContext }) {
|
|
29
|
+
const { path: tmpDir } = await tmp.dir({ unsafeCleanup: true });
|
|
30
|
+
if (await this.#themeMergeApi.hasFileBeenCreated(ThemePublishUtils.getSkinStoreSettingsFilePath(executionContext.themeRootDir), executionContext))
|
|
31
|
+
throw ThemePushErrorsFactory.createErrorWhilePushingUnpublishedThemeWithSkinstoreData(credentials.shopUrl);
|
|
32
|
+
const { uploadData, localFiles } = await this._getActionDataForFilesToUpload({
|
|
33
|
+
pushAction,
|
|
34
|
+
filesStructure,
|
|
35
|
+
executionContext
|
|
36
|
+
});
|
|
37
|
+
console.log('uploadData', uploadData);
|
|
38
|
+
if (uploadData.length) {
|
|
39
|
+
await this._uploadThemeFiles({
|
|
40
|
+
filesToUpload: uploadData,
|
|
41
|
+
actionData: pushAction.data[THEME_WILDCARD_ACTION_NAME],
|
|
42
|
+
credentials,
|
|
43
|
+
localFiles,
|
|
44
|
+
themeRootDir: executionContext.themeRootDir
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
await this._createAFilesListFile(executionContext.themeRootDir, localFiles);
|
|
49
|
+
}
|
|
50
|
+
const themeArchivePath = join(tmpDir, `${uuid()}.zip`);
|
|
51
|
+
try {
|
|
52
|
+
await this._createThemeArchive({
|
|
53
|
+
themeRootDir: executionContext.themeRootDir,
|
|
54
|
+
filesToArchive: [
|
|
55
|
+
...getFilesGlobsThatMatchesActionName(THEME_ACTIONS_TYPES.push, THEME_WILDCARD_ACTION_NAME, filesStructure),
|
|
56
|
+
THEME_FILES_LIST_FILE_NAME
|
|
57
|
+
],
|
|
58
|
+
dist: themeArchivePath
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
catch (err) {
|
|
62
|
+
throw ThemePushErrorsFactory.createErrorWhileCreatingThemeArchive(credentials.shopUrl, err);
|
|
63
|
+
}
|
|
64
|
+
const { resources, modules } = await this._uploadThemeArchive({
|
|
65
|
+
themeArchivePath,
|
|
66
|
+
credentials,
|
|
67
|
+
pushAction
|
|
68
|
+
});
|
|
69
|
+
if (modules)
|
|
70
|
+
await this._updateDataForNewCreatedModules({ modules, themeRootDir: executionContext.themeRootDir });
|
|
71
|
+
console.log('resources', resources);
|
|
72
|
+
if (resources)
|
|
73
|
+
await this.#themeFetchApi.fetchResources(credentials.shopUrl, executionContext.themeRootDir, resources);
|
|
74
|
+
await removeFile(join(executionContext.themeRootDir, THEME_FILES_LIST_FILE_NAME));
|
|
75
|
+
const checksums = await computeChecksumsFromSource(executionContext.themeRootDir);
|
|
76
|
+
await ThemeChecksumsUtils.createThemeChecksumsFiles(executionContext.themeRootDir, checksums);
|
|
77
|
+
}
|
|
78
|
+
async _createThemeArchive({ themeRootDir, filesToArchive, dist }) {
|
|
79
|
+
const filesInThemeDirectory = await globs(filesToArchive, {
|
|
80
|
+
suppressErrors: true,
|
|
81
|
+
onlyFiles: true,
|
|
82
|
+
cwd: themeRootDir
|
|
83
|
+
});
|
|
84
|
+
await this._formatJsonFiles(themeRootDir, filesInThemeDirectory);
|
|
85
|
+
return createZip({
|
|
86
|
+
files: filesInThemeDirectory,
|
|
87
|
+
baseDir: themeRootDir,
|
|
88
|
+
dist
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
async _uploadThemeArchive({ themeArchivePath, credentials, pushAction }) {
|
|
92
|
+
try {
|
|
93
|
+
const request = this.#themePushHttpApi.pushThemeData({
|
|
94
|
+
actionData: pushAction.data[THEME_WILDCARD_ACTION_NAME],
|
|
95
|
+
stream: createReadStream(themeArchivePath),
|
|
96
|
+
shopUrl: credentials.shopUrl
|
|
97
|
+
});
|
|
98
|
+
const response = await request.response;
|
|
99
|
+
if (response.status !== 200 || !response.data.isSuccess)
|
|
100
|
+
throw response;
|
|
101
|
+
return response.data;
|
|
102
|
+
}
|
|
103
|
+
catch (err) {
|
|
104
|
+
console.log('error', err.response.data.messages);
|
|
105
|
+
throw ThemePushErrorsFactory.createErrorWhileUploadingTheme(credentials.shopUrl, err.response.data.messages);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
async _formatJsonFiles(themeRootDir, filesToUpload) {
|
|
109
|
+
await Promise.all(filesToUpload
|
|
110
|
+
.filter((path) => extname(path).toLowerCase() === '.json')
|
|
111
|
+
.map((jsonFile) => formatJSONFile(join(themeRootDir, jsonFile))));
|
|
112
|
+
}
|
|
113
|
+
async _uploadThemeFiles({ filesToUpload, themeRootDir, localFiles, credentials }) {
|
|
114
|
+
try {
|
|
115
|
+
const uploadedImageData = await this._uploadFiles(filesToUpload, credentials);
|
|
116
|
+
const newFilesList = ThemeImagesUtils.updateOriginalFilenameToUploadedFilename(localFiles, uploadedImageData);
|
|
117
|
+
if (uploadedImageData.length)
|
|
118
|
+
await ThemeImagesUtils.renameOriginalFilenameToUploadedFilename(themeRootDir, uploadedImageData);
|
|
119
|
+
await this._createAFilesListFile(themeRootDir, newFilesList);
|
|
120
|
+
}
|
|
121
|
+
catch (err) {
|
|
122
|
+
throw ThemePushErrorsFactory.createErrorWhileUploadingThemeFiles(credentials.shopUrl, err);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
async _uploadFiles(uploadData, credentials) {
|
|
126
|
+
const uploadedImageData = [];
|
|
127
|
+
await Promise.all(uploadData.map(({ actionData, path }) => this.#themePushHttpApi
|
|
128
|
+
.pushThemeData({
|
|
129
|
+
actionData,
|
|
130
|
+
stream: createReadStream(path),
|
|
131
|
+
shopUrl: credentials.shopUrl
|
|
132
|
+
})
|
|
133
|
+
.response.then((response) => {
|
|
134
|
+
if (response.status !== 200 || !response.data.isSuccess)
|
|
135
|
+
throw ThemePushErrorsFactory.createErrorWhileUploadingThemeFiles(credentials.shopUrl, response.data?.messages ?? []);
|
|
136
|
+
uploadedImageData.push({
|
|
137
|
+
location: dirname(path),
|
|
138
|
+
originalFilename: basename(path),
|
|
139
|
+
uploadedFilename: response.data.imageId
|
|
140
|
+
});
|
|
141
|
+
})));
|
|
142
|
+
return uploadedImageData;
|
|
143
|
+
}
|
|
144
|
+
async _getActionDataForFilesToUpload({ pushAction, filesStructure, executionContext }) {
|
|
145
|
+
const uploadData = [];
|
|
146
|
+
const localFiles = {};
|
|
147
|
+
for (const [actionKey, actionData] of Object.entries(pushAction.data)) {
|
|
148
|
+
if (actionData.type === THEME_ACTION_DATA_TYPE.file) {
|
|
149
|
+
const filesGlobs = getFilesGlobsThatMatchesActionName(THEME_ACTIONS_TYPES.push, actionKey, filesStructure);
|
|
150
|
+
for (const fileGlob of filesGlobs) {
|
|
151
|
+
const files = await globs(fileGlob, {
|
|
152
|
+
suppressErrors: true,
|
|
153
|
+
onlyFiles: true,
|
|
154
|
+
cwd: executionContext.themeRootDir
|
|
155
|
+
});
|
|
156
|
+
if (looksLikeDirectory(fileGlob)) {
|
|
157
|
+
const processedFileGlob = fileGlob.endsWith(THEME_PUSH_WILDCARD_GLOBS_FOR_FILES)
|
|
158
|
+
? fileGlob.slice(0, fileGlob.length - 2)
|
|
159
|
+
: fileGlob;
|
|
160
|
+
localFiles[processedFileGlob] = files.length ? files.map((filePath) => basename(filePath)) : [];
|
|
161
|
+
}
|
|
162
|
+
else {
|
|
163
|
+
localFiles[fileGlob] = files.length ? basename(files[0]) : null;
|
|
164
|
+
}
|
|
165
|
+
for (const filePath of files) {
|
|
166
|
+
if ((await this.#themeMergeApi.hasFileBeenCreated(filePath, executionContext)) ||
|
|
167
|
+
(await this.#themeMergeApi.hasFileBeenModified(filePath, executionContext))) {
|
|
168
|
+
uploadData.push({
|
|
169
|
+
actionData,
|
|
170
|
+
actionKey,
|
|
171
|
+
path: filePath
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
return {
|
|
179
|
+
uploadData,
|
|
180
|
+
localFiles
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
async _updateDataForNewCreatedModules({ modules, themeRootDir }) {
|
|
184
|
+
for (const [moduleDirectoryName, metaData] of Object.entries(modules)) {
|
|
185
|
+
await writeJSONFile(join(themeRootDir, MODULES_DIRECTORY_NAME, moduleDirectoryName, THEME_MODULE_SETTINGS_FILE_NAME), JSON.parse(metaData[THEME_MODULE_SETTINGS_FILE_NAME]));
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
async _createAFilesListFile(themeRootDir, filesList) {
|
|
189
|
+
console.log('filesList', filesList);
|
|
190
|
+
//TODO replace pathSeparator to unix style if platform windows
|
|
191
|
+
if (Object.keys(filesList))
|
|
192
|
+
await writeJSONFile(join(themeRootDir, THEME_FILES_LIST_FILE_NAME), filesList);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const THEME_PUSH_WILDCARD_GLOBS_FOR_FILES = '*';
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { AppError } from '../../../../cli/class/errors/app_error/app_error.js';
|
|
2
|
+
export class ThemePushErrorsFactory {
|
|
3
|
+
static createErrorWhileUploadingThemeFiles(shopUrl, messages) {
|
|
4
|
+
return new AppError({
|
|
5
|
+
code: 'theme.push.error_uploading_theme_files',
|
|
6
|
+
message: `Error while uploading theme files to shop "${shopUrl}"`,
|
|
7
|
+
level: 'error',
|
|
8
|
+
details: messages
|
|
9
|
+
});
|
|
10
|
+
}
|
|
11
|
+
static createErrorWhileCreatingThemeArchive(shopUrl, error) {
|
|
12
|
+
return new AppError({
|
|
13
|
+
code: 'theme.push.error_creating_theme_archive',
|
|
14
|
+
message: `Error while creating theme archive for shop "${shopUrl}"`,
|
|
15
|
+
level: 'error',
|
|
16
|
+
stack: error.stack
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
static createErrorWhilePushingUnpublishedThemeWithSkinstoreData(shopUrl) {
|
|
20
|
+
return new AppError({
|
|
21
|
+
code: 'theme.push.error_pushing_unpublished_theme_with_skinstore_data',
|
|
22
|
+
message: `Error while pushing unpublished theme with skinstore data to shop "${shopUrl}"`,
|
|
23
|
+
level: 'error'
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
static createGeneralError() {
|
|
27
|
+
return new AppError({
|
|
28
|
+
code: 'theme.push.general_error',
|
|
29
|
+
message: 'Something went wrong while pushing theme, try again later. ',
|
|
30
|
+
level: 'error'
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
static createErrorWhileUploadingTheme(shopUrl, messages) {
|
|
34
|
+
return new AppError({
|
|
35
|
+
code: 'theme.push.error_uploading_theme',
|
|
36
|
+
message: `Error while uploading theme to shop "${shopUrl}"`,
|
|
37
|
+
level: 'error',
|
|
38
|
+
details: messages
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { FEATURE_CORES_TYPES, HTTP_REQUESTER_API_NAME, SyncFeatureInitializer } from '@dreamcommerce/star_core';
|
|
2
|
+
import { ThemePushService } from './service/theme_push_service.js';
|
|
3
|
+
import { THEME_PUSH_FEATURE_NAME } from './theme_push_constants.js';
|
|
4
|
+
import { ThemePushApi } from './api/theme_push_api.js';
|
|
5
|
+
import { ThemePushHttpApi } from './http_api/theme_push_http_api.js';
|
|
6
|
+
import { THEME_MERGE_API_NAME } from '../merge/theme_merge_constants.js';
|
|
7
|
+
import { THEME_FETCH_API_NAME } from '../fetch/theme_fetch_constants.js';
|
|
8
|
+
export class ThemePushInitializer extends SyncFeatureInitializer {
|
|
9
|
+
static featureName = THEME_PUSH_FEATURE_NAME;
|
|
10
|
+
init() {
|
|
11
|
+
const httpApi = this.getApiSync(HTTP_REQUESTER_API_NAME);
|
|
12
|
+
const themeMergeApi = this.getApiSync(THEME_MERGE_API_NAME);
|
|
13
|
+
const service = new ThemePushService({
|
|
14
|
+
themePushHttpApi: new ThemePushHttpApi(httpApi),
|
|
15
|
+
themeMergeApi,
|
|
16
|
+
themeFetchApi: this.getApiSync(THEME_FETCH_API_NAME)
|
|
17
|
+
});
|
|
18
|
+
return {
|
|
19
|
+
cores: [
|
|
20
|
+
{
|
|
21
|
+
type: FEATURE_CORES_TYPES.api,
|
|
22
|
+
instance: new ThemePushApi(service)
|
|
23
|
+
}
|
|
24
|
+
]
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export const THEME_CURRENT_CHECKSUMS_FILE_NAME = 'current_checksum.json';
|
|
2
|
+
export const THEME_INITIAL_CHECKSUMS_FILE_NAME = 'initial_checksum.json';
|
|
3
|
+
export const THEME_CURRENT_CHECKSUMS_VERITY_FILE_NAME = 'current_checksums_verify.json';
|
|
4
|
+
export const THEME_INITIAL_CHECKSUMS_VERITY_FILE_NAME = 'initial_checksums_verify.json';
|
|
5
|
+
export const THEME_FILES_STRUCTURE_FILE_NAME = 'filesStructure.json';
|
|
6
|
+
export const MODULES_DIRECTORY_NAME = 'modules';
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { AppError } from '../../../../../cli/class/errors/app_error/app_error.js';
|
|
2
|
+
export class ThemeChecksumsErrorFactory {
|
|
3
|
+
static createThemeChecksumError(stack) {
|
|
4
|
+
return new AppError({
|
|
5
|
+
message: 'Theme checksum error',
|
|
6
|
+
code: 'theme_checksum_error',
|
|
7
|
+
stack
|
|
8
|
+
});
|
|
9
|
+
}
|
|
10
|
+
}
|
|
@@ -0,0 +1,94 @@
|
|
|
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 '../../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 { ThemeDirectoryUtils } from '../../directory/theme_directory_utils.js';
|
|
7
|
+
import { computeFileChecksum } from '../../../../../utils/checksums/checksums_utils.js';
|
|
8
|
+
import { JSON_FILE_INDENT } from '../../../../../cli/cli_constants.js';
|
|
9
|
+
export class ThemeChecksumsUtils {
|
|
10
|
+
static getCurrentThemeChecksumsFilePath(themeDir) {
|
|
11
|
+
return join(ThemeDirectoryUtils.getThemeMetaDataDirPath(themeDir), THEME_CURRENT_CHECKSUMS_FILE_NAME);
|
|
12
|
+
}
|
|
13
|
+
static getInitialThemeChecksumsFilePath(themeDir) {
|
|
14
|
+
return join(ThemeDirectoryUtils.getThemeMetaDataDirPath(themeDir), THEME_INITIAL_CHECKSUMS_FILE_NAME);
|
|
15
|
+
}
|
|
16
|
+
static getCurrentThemeChecksumsVerificationFilePath(themeDir) {
|
|
17
|
+
return join(ThemeDirectoryUtils.getThemeMetaDataDirPath(themeDir), THEME_CURRENT_CHECKSUMS_VERITY_FILE_NAME);
|
|
18
|
+
}
|
|
19
|
+
static getInitialThemeChecksumsVerificationFilePath(themeDir) {
|
|
20
|
+
return join(ThemeDirectoryUtils.getThemeMetaDataDirPath(themeDir), THEME_INITIAL_CHECKSUMS_VERITY_FILE_NAME);
|
|
21
|
+
}
|
|
22
|
+
static async createThemeChecksumsFiles(themeDir, checksums) {
|
|
23
|
+
return new Promise((resolve, reject) => {
|
|
24
|
+
const currentChecksumFilePath = this.getCurrentThemeChecksumsFilePath(themeDir);
|
|
25
|
+
const initialChecksumFilePath = this.getInitialThemeChecksumsFilePath(themeDir);
|
|
26
|
+
const checksumStream = createWriteStream(initialChecksumFilePath);
|
|
27
|
+
checksumStream.write(JSON.stringify(checksums, null, JSON_FILE_INDENT));
|
|
28
|
+
checksumStream.end();
|
|
29
|
+
checksumStream
|
|
30
|
+
.on('finish', async () => {
|
|
31
|
+
const initialChecksumVerifyFilePath = this.getInitialThemeChecksumsVerificationFilePath(themeDir);
|
|
32
|
+
const currentChecksumVerifyFilePath = this.getCurrentThemeChecksumsVerificationFilePath(themeDir);
|
|
33
|
+
await this.createThemeChecksumVerifyFile(initialChecksumFilePath, initialChecksumVerifyFilePath);
|
|
34
|
+
await copyFile(initialChecksumFilePath, currentChecksumFilePath);
|
|
35
|
+
await copyFile(initialChecksumVerifyFilePath, currentChecksumVerifyFilePath);
|
|
36
|
+
resolve();
|
|
37
|
+
})
|
|
38
|
+
.on('error', async (err) => {
|
|
39
|
+
await removeFile(initialChecksumFilePath, { force: true });
|
|
40
|
+
reject(ThemeChecksumsErrorFactory.createThemeChecksumError(err.stack));
|
|
41
|
+
});
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
static async createThemeCurrentChecksumsFile(themeDir, checksums) {
|
|
45
|
+
return new Promise((resolve, reject) => {
|
|
46
|
+
const currentChecksumFilePath = this.getCurrentThemeChecksumsFilePath(themeDir);
|
|
47
|
+
const checksumStream = createWriteStream(currentChecksumFilePath);
|
|
48
|
+
checksumStream.write(JSON.stringify(checksums, null, JSON_FILE_INDENT));
|
|
49
|
+
checksumStream.end();
|
|
50
|
+
checksumStream
|
|
51
|
+
.on('finish', async () => {
|
|
52
|
+
const currentChecksumVerifyFilePath = this.getCurrentThemeChecksumsVerificationFilePath(themeDir);
|
|
53
|
+
await this.createThemeChecksumVerifyFile(currentChecksumFilePath, currentChecksumVerifyFilePath);
|
|
54
|
+
resolve();
|
|
55
|
+
})
|
|
56
|
+
.on('error', async (err) => {
|
|
57
|
+
await removeFile(currentChecksumFilePath, { force: true });
|
|
58
|
+
reject(ThemeChecksumsErrorFactory.createThemeChecksumError(err.stack));
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
static async createThemeChecksumVerifyFile(checksumFilePath, checksumVerifyFullPath) {
|
|
63
|
+
const checksumVerifyStream = createWriteStream(checksumVerifyFullPath);
|
|
64
|
+
const checksumVerify = await computeFileChecksum(checksumFilePath);
|
|
65
|
+
checksumVerifyStream
|
|
66
|
+
.on('error', async (err) => {
|
|
67
|
+
await removeFile(checksumFilePath, { force: true });
|
|
68
|
+
throw ThemeChecksumsErrorFactory.createThemeChecksumError(err.stack);
|
|
69
|
+
})
|
|
70
|
+
.write(JSON.stringify(checksumVerify));
|
|
71
|
+
checksumVerifyStream.end();
|
|
72
|
+
}
|
|
73
|
+
static async getThemeCurrentChecksums(themeDir) {
|
|
74
|
+
const checksumFilePath = this.getCurrentThemeChecksumsFilePath(themeDir);
|
|
75
|
+
return await readJSONFile(checksumFilePath);
|
|
76
|
+
}
|
|
77
|
+
static async getThemeInitialChecksums(themeDir) {
|
|
78
|
+
const checksumFilePath = this.getInitialThemeChecksumsFilePath(themeDir);
|
|
79
|
+
return await readJSONFile(checksumFilePath);
|
|
80
|
+
}
|
|
81
|
+
static async verifyThemeChecksums(themeDir) {
|
|
82
|
+
const initialChecksumFilePath = this.getInitialThemeChecksumsFilePath(themeDir);
|
|
83
|
+
const initialChecksumVerifyFilePath = this.getInitialThemeChecksumsVerificationFilePath(themeDir);
|
|
84
|
+
const initialChecksum = await computeFileChecksum(initialChecksumFilePath);
|
|
85
|
+
const initialChecksumVerify = await readJSONFile(initialChecksumVerifyFilePath);
|
|
86
|
+
return initialChecksum === initialChecksumVerify;
|
|
87
|
+
}
|
|
88
|
+
static async getThemeCurrentChecksumsVerification(themeDir) {
|
|
89
|
+
return await readJSONFile(this.getCurrentThemeChecksumsVerificationFilePath(themeDir));
|
|
90
|
+
}
|
|
91
|
+
static async getInitialChecksumsVerification(themeDir) {
|
|
92
|
+
return await readJSONFile(this.getInitialThemeChecksumsVerificationFilePath(themeDir));
|
|
93
|
+
}
|
|
94
|
+
}
|
package/build/theme/features/theme/utils/directories/theme_resources_with_id_directory_utils.js
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import FSTree from 'fs-tree-diff';
|
|
2
|
+
import walkSync from 'walk-sync';
|
|
3
|
+
import { THEME_FILES_OPERATIONS } from '../../merge/theme_merge_constants.js';
|
|
4
|
+
import { fileExists, readJSONFile, renameFile } from '../../../../../utils/fs/fs_utils.js';
|
|
5
|
+
import { join } from '../../../../../utils/path_utils.js';
|
|
6
|
+
import fs from 'node:fs';
|
|
7
|
+
import fsPromises from 'node:fs/promises';
|
|
8
|
+
import { computeChecksumsFromSource } from '../../../../../utils/checksums/checksums_utils.js';
|
|
9
|
+
import { ThemeChecksumsUtils } from '../checksums/theme_checksums_utils.js';
|
|
10
|
+
export class ThemeResourcesWithIdDirectoryUtils {
|
|
11
|
+
static async updateDirectoryNamesOfResourcesWithIdAccordingToLocalThemeNames(localResourcesDir, localThemeDir, remoteResourcesDir, remoteThemeDir) {
|
|
12
|
+
const localThemeTree = new FSTree({
|
|
13
|
+
entries: walkSync.entries(localResourcesDir, {
|
|
14
|
+
globs: ['*/'],
|
|
15
|
+
ignore: ['*/**']
|
|
16
|
+
})
|
|
17
|
+
});
|
|
18
|
+
const remoteThemeTree = new FSTree({
|
|
19
|
+
entries: walkSync.entries(remoteResourcesDir, {
|
|
20
|
+
globs: ['*/'],
|
|
21
|
+
ignore: ['*/**']
|
|
22
|
+
})
|
|
23
|
+
});
|
|
24
|
+
const localResourceIdToPathMap = await this._getFilePathToResourceIdMap(localThemeTree.entries);
|
|
25
|
+
const remoteResourceIdToPathMap = await this._getResourceIdToFilePathMap(remoteThemeTree.entries);
|
|
26
|
+
const directoriesCreatedLocally = localThemeTree
|
|
27
|
+
.calculatePatch(remoteThemeTree)
|
|
28
|
+
.filter(([operation]) => operation === THEME_FILES_OPERATIONS.rmdir);
|
|
29
|
+
for (const operation of directoriesCreatedLocally) {
|
|
30
|
+
const resourceDirectoryRelativePath = operation[1];
|
|
31
|
+
if (!resourceDirectoryRelativePath)
|
|
32
|
+
continue;
|
|
33
|
+
const resourceId = localResourceIdToPathMap.get(resourceDirectoryRelativePath);
|
|
34
|
+
if (!resourceId)
|
|
35
|
+
continue;
|
|
36
|
+
const remoteResourceDirectoryRelativePath = remoteResourceIdToPathMap.get(resourceId);
|
|
37
|
+
if (!remoteResourceDirectoryRelativePath)
|
|
38
|
+
continue;
|
|
39
|
+
await renameFile(join(remoteResourcesDir, remoteResourceDirectoryRelativePath), join(remoteResourcesDir, resourceDirectoryRelativePath));
|
|
40
|
+
const resultPath = join(remoteResourcesDir, resourceDirectoryRelativePath);
|
|
41
|
+
const stat = fs.lstatSync(resultPath);
|
|
42
|
+
console.log('stat', stat.isDirectory());
|
|
43
|
+
if (stat.isSymbolicLink()) {
|
|
44
|
+
console.warn(`[WARN] Renamed path became a symlink: ${resultPath}`);
|
|
45
|
+
console.warn(`Points to: ${await fsPromises.readlink(resultPath)}`);
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* If these become performance bottlenecks, we can consider computing checksums only for the changed directories.
|
|
49
|
+
*/
|
|
50
|
+
const checksums = await computeChecksumsFromSource(remoteThemeDir);
|
|
51
|
+
await ThemeChecksumsUtils.createThemeChecksumsFiles(remoteThemeDir, checksums);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
static async _getResourceIdToFilePathMap(entries) {
|
|
55
|
+
const map = new Map();
|
|
56
|
+
for (const entry of entries) {
|
|
57
|
+
const settingsPaths = join(entry.basePath, entry.relativePath, 'settings.json');
|
|
58
|
+
if (!(await fileExists(settingsPaths)))
|
|
59
|
+
continue;
|
|
60
|
+
const settings = await readJSONFile(settingsPaths);
|
|
61
|
+
if (settings.id === undefined)
|
|
62
|
+
continue;
|
|
63
|
+
map.set(settings.id, entry.relativePath);
|
|
64
|
+
}
|
|
65
|
+
return map;
|
|
66
|
+
}
|
|
67
|
+
static async _getFilePathToResourceIdMap(entries) {
|
|
68
|
+
const map = new Map();
|
|
69
|
+
for (const entry of entries) {
|
|
70
|
+
const settingsPaths = join(entry.basePath, entry.relativePath, 'settings.json');
|
|
71
|
+
if (!(await fileExists(settingsPaths)))
|
|
72
|
+
continue;
|
|
73
|
+
const settings = await readJSONFile(settingsPaths);
|
|
74
|
+
if (settings.id === undefined)
|
|
75
|
+
continue;
|
|
76
|
+
map.set(entry.relativePath, settings.id);
|
|
77
|
+
}
|
|
78
|
+
return map;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { join } from '../../../../utils/path_utils.js';
|
|
2
|
+
import { renameFile } from '../../../../utils/fs/fs_utils.js';
|
|
3
|
+
export class ThemeImagesUtils {
|
|
4
|
+
static updateOriginalFilenameToUploadedFilename(filesList, uploadedImageData) {
|
|
5
|
+
const newFilesList = { ...filesList };
|
|
6
|
+
uploadedImageData
|
|
7
|
+
.filter(({ uploadedFilename }) => Boolean(uploadedFilename))
|
|
8
|
+
.forEach(({ originalFilename, uploadedFilename, location }) => {
|
|
9
|
+
if (typeof newFilesList[location] === 'string' && uploadedFilename) {
|
|
10
|
+
newFilesList[location] = uploadedFilename;
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
13
|
+
if (Array.isArray(newFilesList[location]) && uploadedFilename) {
|
|
14
|
+
const indexOfOriginalFilename = newFilesList[location].indexOf(originalFilename);
|
|
15
|
+
if (indexOfOriginalFilename === -1 && !uploadedFilename)
|
|
16
|
+
return;
|
|
17
|
+
newFilesList[location].splice(indexOfOriginalFilename, 1, uploadedFilename);
|
|
18
|
+
}
|
|
19
|
+
});
|
|
20
|
+
return newFilesList;
|
|
21
|
+
}
|
|
22
|
+
static async renameOriginalFilenameToUploadedFilename(themeRootDir, uploadedImagesData) {
|
|
23
|
+
await Promise.all(uploadedImagesData
|
|
24
|
+
.filter(({ originalFilename }) => Boolean(originalFilename))
|
|
25
|
+
.map(({ uploadedFilename, originalFilename, location }) => {
|
|
26
|
+
const originalFilePath = join(themeRootDir, location, originalFilename);
|
|
27
|
+
const newFilePath = join(themeRootDir, location, uploadedFilename ?? originalFilename);
|
|
28
|
+
return renameFile(originalFilePath, newFilePath);
|
|
29
|
+
}));
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { FeatureApi } from '@dreamcommerce/star_core';
|
|
2
|
+
import { THEMES_LIST_API_NAME } from '../themes_list_constants.js';
|
|
3
|
+
export class ThemesListApi extends FeatureApi {
|
|
4
|
+
moduleName = THEMES_LIST_API_NAME;
|
|
5
|
+
#service;
|
|
6
|
+
constructor(service) {
|
|
7
|
+
super();
|
|
8
|
+
this.#service = service;
|
|
9
|
+
}
|
|
10
|
+
async getThemes(credentials) {
|
|
11
|
+
return this.#service.getThemes(credentials);
|
|
12
|
+
}
|
|
13
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { REQUEST_TYPES } from '@dreamcommerce/star_core';
|
|
2
|
-
export class
|
|
2
|
+
export class ThemesListHttpApi {
|
|
3
3
|
#httpApi;
|
|
4
4
|
constructor(httpApi) {
|
|
5
5
|
this.#httpApi = httpApi;
|
|
@@ -10,7 +10,8 @@ export class ShoperThemesHttpApi {
|
|
|
10
10
|
method: REQUEST_TYPES.get,
|
|
11
11
|
sanitizeOptions: {
|
|
12
12
|
disable: true
|
|
13
|
-
}
|
|
13
|
+
},
|
|
14
|
+
isPrivate: true
|
|
14
15
|
});
|
|
15
16
|
}
|
|
16
17
|
}
|
package/build/theme/features/themes/{model/theme_metadata.js → list/model/theme_list_metadata.js}
RENAMED
|
@@ -4,11 +4,13 @@ export class ThemeMetaData {
|
|
|
4
4
|
description;
|
|
5
5
|
isActive;
|
|
6
6
|
canEdit;
|
|
7
|
-
|
|
7
|
+
links;
|
|
8
|
+
constructor({ canEdit, skinId, description, isActive, title, links }) {
|
|
8
9
|
this.skinId = skinId;
|
|
9
10
|
this.title = title;
|
|
10
11
|
this.description = description;
|
|
11
12
|
this.isActive = isActive;
|
|
12
13
|
this.canEdit = canEdit;
|
|
14
|
+
this.links = links;
|
|
13
15
|
}
|
|
14
16
|
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { ThemeMetaData } from '../model/theme_list_metadata.js';
|
|
2
|
+
import { STATUS_CODES } from '@dreamcommerce/star_core';
|
|
3
|
+
import { HttpErrorsFactory } from '../../../../../cli/class/errors/http_errors_factory.js';
|
|
4
|
+
import { DownloadFileErrorsFactory } from '../../../../../utils/download_file/download_file_errors_factory.js';
|
|
5
|
+
export class ThemesListService {
|
|
6
|
+
httpApi;
|
|
7
|
+
constructor(httpApi) {
|
|
8
|
+
this.httpApi = httpApi;
|
|
9
|
+
}
|
|
10
|
+
async getThemes({ shopUrl }) {
|
|
11
|
+
try {
|
|
12
|
+
const { response } = this.httpApi.getThemes(shopUrl);
|
|
13
|
+
const { data, status } = await response;
|
|
14
|
+
if (status !== STATUS_CODES.ok)
|
|
15
|
+
return;
|
|
16
|
+
//TODO baseHttpApi + model mapper
|
|
17
|
+
return data.map(({ _links, ...rest }) => new ThemeMetaData({
|
|
18
|
+
...rest,
|
|
19
|
+
links: _links
|
|
20
|
+
}));
|
|
21
|
+
}
|
|
22
|
+
catch (err) {
|
|
23
|
+
switch (err.response?.status) {
|
|
24
|
+
case 403:
|
|
25
|
+
throw HttpErrorsFactory.createForbiddenError();
|
|
26
|
+
case 401:
|
|
27
|
+
throw HttpErrorsFactory.createUnauthorizedError();
|
|
28
|
+
case 404:
|
|
29
|
+
throw HttpErrorsFactory.createNotFoundError();
|
|
30
|
+
default:
|
|
31
|
+
if (err.response?.status !== 200) {
|
|
32
|
+
throw DownloadFileErrorsFactory.downloadError(err.response.status);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { ThemesListService } from './services/themes_list_service.js';
|
|
2
|
+
import { ThemesListApi } from './api/themes_list_api.js';
|
|
3
|
+
import { FEATURE_CORES_TYPES, HTTP_REQUESTER_API_NAME, SyncFeatureInitializer } from '@dreamcommerce/star_core';
|
|
4
|
+
import { THEMES_LIST_FEATURE_NAME } from './themes_list_constants.js';
|
|
5
|
+
import { ThemesListHttpApi } from './http/themes_list_http_api.js';
|
|
6
|
+
export class ThemesListInitializer extends SyncFeatureInitializer {
|
|
7
|
+
static featureName = THEMES_LIST_FEATURE_NAME;
|
|
8
|
+
init() {
|
|
9
|
+
const httpApi = this.getApiSync(HTTP_REQUESTER_API_NAME);
|
|
10
|
+
const service = new ThemesListService(new ThemesListHttpApi(httpApi));
|
|
11
|
+
return {
|
|
12
|
+
cores: [
|
|
13
|
+
{
|
|
14
|
+
type: FEATURE_CORES_TYPES.api,
|
|
15
|
+
instance: new ThemesListApi(service)
|
|
16
|
+
}
|
|
17
|
+
]
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { useApi } from '../../cli/hooks/ensure_cli_initialized_hook.js';
|
|
2
|
+
import { EXECUTION_CONTEXT_API_NAME, EXECUTION_CONTEXTS } from '../../cli/features/execution_context/execution_context_constants.js';
|
|
3
|
+
import { ThemeDirectoryUtils } from '../features/theme/directory/theme_directory_utils.js';
|
|
4
|
+
const ensureThemeMetaDataUntouched = async () => {
|
|
5
|
+
const executionContextApi = useApi(EXECUTION_CONTEXT_API_NAME);
|
|
6
|
+
const executionContext = await executionContextApi.getExecutionContext();
|
|
7
|
+
if (executionContext.type !== EXECUTION_CONTEXTS.theme)
|
|
8
|
+
return;
|
|
9
|
+
try {
|
|
10
|
+
await ThemeDirectoryUtils.ensureFilesInsideThemeMetaDataDirectoryUntouched(executionContext.themeRootDir);
|
|
11
|
+
}
|
|
12
|
+
catch (err) {
|
|
13
|
+
console.error(err);
|
|
14
|
+
process.exit(1);
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
export default ensureThemeMetaDataUntouched;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { EXECUTION_CONTEXT_API_NAME, EXECUTION_CONTEXTS } from '../../../cli/features/execution_context/execution_context_constants.js';
|
|
2
|
+
import { useApi } from '../../../cli/hooks/ensure_cli_initialized_hook.js';
|
|
3
|
+
import { ThemeChecksumsUtils } from '../../features/theme/utils/checksums/theme_checksums_utils.js';
|
|
4
|
+
import { computeChecksumsFromSource } from '../../../utils/checksums/checksums_utils.js';
|
|
5
|
+
import { THEME_COMMANDS_THAT_REQUIRES_UP_TO_DATE_CHECKSUMS } from './ensure_theme_current_checksums_up_to_date_constants.js';
|
|
6
|
+
export const ensureThemeChecksumsUpToDate = async ({ Command }) => {
|
|
7
|
+
console.log('Command', Command.id);
|
|
8
|
+
if (!THEME_COMMANDS_THAT_REQUIRES_UP_TO_DATE_CHECKSUMS.includes(Command.id))
|
|
9
|
+
return;
|
|
10
|
+
const executionContextApi = useApi(EXECUTION_CONTEXT_API_NAME);
|
|
11
|
+
const executionContext = await executionContextApi.getExecutionContext();
|
|
12
|
+
if (executionContext.type !== EXECUTION_CONTEXTS.theme)
|
|
13
|
+
return;
|
|
14
|
+
/**
|
|
15
|
+
* Naive solution, recalculate checksums every time a command based on checksums calculation is executed;
|
|
16
|
+
* If performance becomes an issue, we can implement a more sophisticated solution, eg. recalculate checksums only when files in the theme directory have changed.
|
|
17
|
+
*/
|
|
18
|
+
const checksums = await computeChecksumsFromSource(executionContext.themeRootDir);
|
|
19
|
+
await ThemeChecksumsUtils.createThemeCurrentChecksumsFile(executionContext.themeRootDir, checksums);
|
|
20
|
+
};
|
|
21
|
+
export default ensureThemeChecksumsUpToDate;
|