@shoper/cli 0.9.4-3 → 0.9.4-7
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/theme/commands/publish/theme_publish_command.js +18 -201
- package/build/theme/features/theme/skinstore/api/theme_skinstore_api.js +4 -4
- package/build/theme/features/theme/skinstore/http/theme_skinstore_http_api.js +0 -26
- package/build/theme/features/theme/skinstore/service/theme_skinstore_service.js +12 -64
- package/build/theme/features/theme/skinstore/theme_publish_constants.js +0 -4
- package/build/theme/features/theme/skinstore/theme_skinstore_initialzier.js +1 -6
- package/build/theme/features/theme/utils/files/theme_files_utils.js +12 -1
- package/build/theme/index.js +0 -2
- package/build/utils/fs/fs_utils.js +1 -1
- package/build/utils/path_utils.js +14 -5
- package/package.json +3 -3
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Args
|
|
1
|
+
import { Args } from '@oclif/core';
|
|
2
2
|
import { BaseThemeCommand } from '../../class/base_theme_command.js';
|
|
3
3
|
import { CLI_AUTH_API_NAME } from '../../../cli/auth/cli_auth_constants.js';
|
|
4
4
|
import { THEME_ACTIONS_API_NAME, THEME_ACTIONS_TYPES } from '../../features/theme/actions/theme_actions_constants.js';
|
|
@@ -7,41 +7,20 @@ import { renderOnce } from '../../../ui/ui_utils.js';
|
|
|
7
7
|
import { MissingCredentialsError } from '../../../cli/commands/auth/ui/missing_credentials_error.js';
|
|
8
8
|
import React from 'react';
|
|
9
9
|
import { MissingThemeIdError } from '../ui/missing_theme_id_error.js';
|
|
10
|
-
import { THEME_SKINSTORE_API_NAME
|
|
10
|
+
import { THEME_SKINSTORE_API_NAME } from '../../features/theme/skinstore/theme_publish_constants.js';
|
|
11
11
|
import { Form } from '../../../cli/features/controls/ui/form.js';
|
|
12
|
-
import { toInquirerControls } from '../../../cli/features/controls/ui/controls_mappers.js';
|
|
13
12
|
import { ThemeError } from '../ui/theme_error.js';
|
|
14
13
|
import { LOGGER_API_NAME } from '../../../cli/utilities/features/logger/logger_constants.js';
|
|
15
|
-
import { Text } from '../../../ui/text.js';
|
|
16
|
-
import { Box } from '../../../ui/box.js';
|
|
17
|
-
import { Success } from '../../../ui/message_box/success.js';
|
|
18
|
-
import { Info } from '../../../ui/message_box/info.js';
|
|
19
|
-
import { Error as ErrorBox } from '../../../ui/message_box/error.js';
|
|
20
|
-
import { UnpermittedCommandError } from '../ui/unpermitted_command_error.js';
|
|
21
|
-
import { promptConfirmation } from '../../../ui/prompts/prompt_confirmation.js';
|
|
22
|
-
import fs from 'fs';
|
|
23
|
-
import path from 'path';
|
|
24
14
|
export class ThemePublishCommand extends BaseThemeCommand {
|
|
25
|
-
static summary = '
|
|
26
|
-
static description = '
|
|
15
|
+
static summary = 'Permanently deletes the specified theme from your store.';
|
|
16
|
+
static description = 'This action cannot be undone, so make sure you really want to remove this theme.\n\nYou can run this command from a specific theme directory (ID not needed) or outside any theme directory (theme ID required).';
|
|
27
17
|
static examples = [
|
|
28
18
|
{
|
|
29
|
-
description: '
|
|
30
|
-
command: '<%= config.bin %> <%= command.id %>'
|
|
31
|
-
},
|
|
32
|
-
{
|
|
33
|
-
description: 'Publish specific theme by ID',
|
|
19
|
+
description: 'This will delete the theme with ID 123 permanently from your store. Make sure you have a backup if needed.',
|
|
34
20
|
command: '<%= config.bin %> <%= command.id %> 123'
|
|
35
|
-
},
|
|
36
|
-
{
|
|
37
|
-
description: 'Publish as test release',
|
|
38
|
-
command: '<%= config.bin %> <%= command.id %> --release test'
|
|
39
|
-
},
|
|
40
|
-
{
|
|
41
|
-
description: 'Publish with screenshots from directory',
|
|
42
|
-
command: '<%= config.bin %> <%= command.id %> --images ./screenshots'
|
|
43
21
|
}
|
|
44
22
|
];
|
|
23
|
+
static hidden = true;
|
|
45
24
|
static args = {
|
|
46
25
|
id: Args.string({
|
|
47
26
|
description: 'Theme id',
|
|
@@ -50,18 +29,6 @@ export class ThemePublishCommand extends BaseThemeCommand {
|
|
|
50
29
|
type: 'string'
|
|
51
30
|
})
|
|
52
31
|
};
|
|
53
|
-
static flags = {
|
|
54
|
-
release: Flags.string({
|
|
55
|
-
char: 'r',
|
|
56
|
-
description: 'Release type',
|
|
57
|
-
options: ['test', 'stable'],
|
|
58
|
-
default: 'stable'
|
|
59
|
-
}),
|
|
60
|
-
images: Flags.string({
|
|
61
|
-
char: 'i',
|
|
62
|
-
description: 'Path to directory with screenshots'
|
|
63
|
-
})
|
|
64
|
-
};
|
|
65
32
|
async run() {
|
|
66
33
|
const themeId = this.args.id;
|
|
67
34
|
const cliAuthApi = this.getApi(CLI_AUTH_API_NAME);
|
|
@@ -76,59 +43,32 @@ export class ThemePublishCommand extends BaseThemeCommand {
|
|
|
76
43
|
try {
|
|
77
44
|
let _themeId = themeId;
|
|
78
45
|
if (executionContext.type !== EXECUTION_CONTEXTS.theme && !_themeId) {
|
|
79
|
-
renderOnce(React.createElement(MissingThemeIdError, null,
|
|
80
|
-
React.createElement(Box, { flexDirection: "column", gap: 1 },
|
|
81
|
-
React.createElement(Text, null, "Usage: shoper theme publish [ID]"),
|
|
82
|
-
React.createElement(Text, null, "Please run this command inside a theme directory or provide a theme ID."))));
|
|
83
46
|
return;
|
|
84
47
|
}
|
|
85
|
-
if (executionContext.type === EXECUTION_CONTEXTS.theme)
|
|
48
|
+
if (executionContext.type === EXECUTION_CONTEXTS.theme) {
|
|
86
49
|
_themeId = _themeId ?? executionContext.themeId;
|
|
50
|
+
}
|
|
87
51
|
if (!_themeId) {
|
|
88
52
|
renderOnce(React.createElement(MissingThemeIdError, null));
|
|
89
53
|
return;
|
|
90
54
|
}
|
|
91
55
|
const themeActionsApi = this.getApi(THEME_ACTIONS_API_NAME);
|
|
92
56
|
const themeSkinstoreApi = this.getApi(THEME_SKINSTORE_API_NAME);
|
|
93
|
-
const
|
|
94
|
-
actionType: THEME_ACTIONS_TYPES.
|
|
57
|
+
const pushAction = themeActionsApi.getThemeAction({
|
|
58
|
+
actionType: THEME_ACTIONS_TYPES.publishForm,
|
|
95
59
|
themeId: _themeId,
|
|
96
60
|
credentials
|
|
97
61
|
});
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
return;
|
|
101
|
-
}
|
|
102
|
-
const publishFormAction = themeActionsApi.getThemeAction({
|
|
103
|
-
actionType: THEME_ACTIONS_TYPES.publishForm,
|
|
104
|
-
themeId: _themeId,
|
|
62
|
+
const controls = await themeSkinstoreApi.getPublishFormData({
|
|
63
|
+
actionData: pushAction.data,
|
|
105
64
|
credentials
|
|
106
65
|
});
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
publishAction,
|
|
114
|
-
credentials,
|
|
115
|
-
releaseType,
|
|
116
|
-
themeId: _themeId,
|
|
117
|
-
loggerApi
|
|
118
|
-
});
|
|
119
|
-
}
|
|
120
|
-
else {
|
|
121
|
-
await this.#handleFirstPublish({
|
|
122
|
-
themeSkinstoreApi,
|
|
123
|
-
themeActionsApi,
|
|
124
|
-
publishFormAction,
|
|
125
|
-
publishAction,
|
|
126
|
-
credentials,
|
|
127
|
-
releaseType,
|
|
128
|
-
themeId: _themeId,
|
|
129
|
-
loggerApi
|
|
130
|
-
});
|
|
131
|
-
}
|
|
66
|
+
Form({
|
|
67
|
+
controls,
|
|
68
|
+
onSubmit: (formData) => {
|
|
69
|
+
console.log('formData', formData);
|
|
70
|
+
}
|
|
71
|
+
});
|
|
132
72
|
}
|
|
133
73
|
catch (err) {
|
|
134
74
|
loggerApi.error('Theme publish command error:', {
|
|
@@ -137,127 +77,4 @@ export class ThemePublishCommand extends BaseThemeCommand {
|
|
|
137
77
|
renderOnce(React.createElement(ThemeError, { err: err, executionContext: executionContext }));
|
|
138
78
|
}
|
|
139
79
|
}
|
|
140
|
-
async #handleUpgrade({ themeSkinstoreApi, themeActionsApi, publishAction, credentials, releaseType, themeId, loggerApi }) {
|
|
141
|
-
renderOnce(React.createElement(Info, { header: `Upgrading published theme (ID: ${themeId})` },
|
|
142
|
-
React.createElement(Text, null,
|
|
143
|
-
"Release type: ",
|
|
144
|
-
this.flags.release)));
|
|
145
|
-
const { proceed } = await promptConfirmation('Proceed with upgrade?');
|
|
146
|
-
if (!proceed) {
|
|
147
|
-
renderOnce(React.createElement(Info, { header: "Theme publish was cancelled." }));
|
|
148
|
-
return;
|
|
149
|
-
}
|
|
150
|
-
const result = await themeSkinstoreApi.publishTheme({
|
|
151
|
-
actionData: publishAction.data,
|
|
152
|
-
credentials,
|
|
153
|
-
payload: { release: releaseType }
|
|
154
|
-
});
|
|
155
|
-
if (result?.isSuccess) {
|
|
156
|
-
themeActionsApi.removeThemeActions({ themeId, credentials });
|
|
157
|
-
renderOnce(React.createElement(Success, { header: "Theme upgraded successfully!" },
|
|
158
|
-
React.createElement(Text, null,
|
|
159
|
-
"Release type: ",
|
|
160
|
-
this.flags.release)));
|
|
161
|
-
}
|
|
162
|
-
else {
|
|
163
|
-
this.#renderPublishErrors(result?.messages);
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
async #handleFirstPublish({ themeSkinstoreApi, themeActionsApi, publishFormAction, publishAction, credentials, releaseType, themeId, loggerApi }) {
|
|
167
|
-
const controls = await themeSkinstoreApi.getPublishFormData({
|
|
168
|
-
actionData: publishFormAction.data,
|
|
169
|
-
credentials
|
|
170
|
-
});
|
|
171
|
-
if (!controls || controls.length === 0) {
|
|
172
|
-
renderOnce(React.createElement(ErrorBox, { header: "Could not fetch publish form." },
|
|
173
|
-
React.createElement(Text, null, "Check if SkinStore SDK feature is enabled for your store.")));
|
|
174
|
-
return;
|
|
175
|
-
}
|
|
176
|
-
const inquirerControls = toInquirerControls(controls.filter((c) => c.name !== 'release'));
|
|
177
|
-
const formData = await new Promise((resolve) => {
|
|
178
|
-
Form({
|
|
179
|
-
controls: inquirerControls,
|
|
180
|
-
onSubmit: resolve
|
|
181
|
-
});
|
|
182
|
-
});
|
|
183
|
-
let uploadedImageIds = [];
|
|
184
|
-
if (this.flags.images) {
|
|
185
|
-
uploadedImageIds = await this.#uploadImages({
|
|
186
|
-
themeSkinstoreApi,
|
|
187
|
-
credentials,
|
|
188
|
-
imagesDir: this.flags.images,
|
|
189
|
-
skinId: themeId,
|
|
190
|
-
loggerApi
|
|
191
|
-
});
|
|
192
|
-
}
|
|
193
|
-
const payload = {
|
|
194
|
-
name: formData.name,
|
|
195
|
-
desc: formData.desc ?? '',
|
|
196
|
-
description: formData.description,
|
|
197
|
-
categories: Array.isArray(formData.categories) ? formData.categories : [formData.categories],
|
|
198
|
-
colors: Array.isArray(formData.colors) ? formData.colors : [formData.colors],
|
|
199
|
-
price: Number(formData.price) || 0,
|
|
200
|
-
release: releaseType,
|
|
201
|
-
...(uploadedImageIds.length > 0 ? { filesList: uploadedImageIds } : {})
|
|
202
|
-
};
|
|
203
|
-
const result = await themeSkinstoreApi.publishTheme({
|
|
204
|
-
actionData: publishAction.data,
|
|
205
|
-
credentials,
|
|
206
|
-
payload
|
|
207
|
-
});
|
|
208
|
-
if (result?.isSuccess) {
|
|
209
|
-
themeActionsApi.removeThemeActions({ themeId, credentials });
|
|
210
|
-
renderOnce(React.createElement(Success, { header: "Theme published successfully!" },
|
|
211
|
-
React.createElement(Text, null, "Your theme is now available on SkinStore.")));
|
|
212
|
-
}
|
|
213
|
-
else {
|
|
214
|
-
this.#renderPublishErrors(result?.messages);
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
async #uploadImages({ themeSkinstoreApi, credentials, imagesDir, skinId, loggerApi }) {
|
|
218
|
-
const imageIds = [];
|
|
219
|
-
if (!fs.existsSync(imagesDir)) {
|
|
220
|
-
renderOnce(React.createElement(ErrorBox, { header: "Images directory not found." },
|
|
221
|
-
React.createElement(Text, null,
|
|
222
|
-
"Path: ",
|
|
223
|
-
imagesDir)));
|
|
224
|
-
return imageIds;
|
|
225
|
-
}
|
|
226
|
-
const files = fs.readdirSync(imagesDir).filter((file) => {
|
|
227
|
-
const ext = path.extname(file).toLowerCase().replace('.', '');
|
|
228
|
-
return IMAGE_ALLOWED_EXTENSIONS.includes(ext);
|
|
229
|
-
});
|
|
230
|
-
for (const file of files) {
|
|
231
|
-
const filePath = path.join(imagesDir, file);
|
|
232
|
-
const stat = fs.statSync(filePath);
|
|
233
|
-
if (stat.size > IMAGE_MAX_SIZE) {
|
|
234
|
-
loggerApi.warn(`Skipping ${file}: exceeds ${IMAGE_MAX_SIZE} bytes limit`);
|
|
235
|
-
continue;
|
|
236
|
-
}
|
|
237
|
-
const uploadUrl = `/webapi/cli/themes/${skinId}/skinstore/files`;
|
|
238
|
-
const result = await themeSkinstoreApi.uploadImage({
|
|
239
|
-
credentials,
|
|
240
|
-
uploadUrl,
|
|
241
|
-
imagePath: filePath
|
|
242
|
-
});
|
|
243
|
-
if (result?.isSuccess && result?.imageId) {
|
|
244
|
-
imageIds.push(result.imageId);
|
|
245
|
-
}
|
|
246
|
-
else {
|
|
247
|
-
loggerApi.warn(`Failed to upload ${file}`, { details: result?.messages });
|
|
248
|
-
}
|
|
249
|
-
}
|
|
250
|
-
return imageIds;
|
|
251
|
-
}
|
|
252
|
-
#renderPublishErrors(messages) {
|
|
253
|
-
if (!messages) {
|
|
254
|
-
renderOnce(React.createElement(ErrorBox, { header: "Publishing failed." },
|
|
255
|
-
React.createElement(Text, null, "An unknown error occurred.")));
|
|
256
|
-
return;
|
|
257
|
-
}
|
|
258
|
-
renderOnce(React.createElement(ErrorBox, { header: "Publishing failed. Validation errors:" }, Object.entries(messages).map(([field, msg]) => (React.createElement(Text, { key: field },
|
|
259
|
-
field,
|
|
260
|
-
": ",
|
|
261
|
-
typeof msg === 'string' ? msg : Object.values(msg).join(', '))))));
|
|
262
|
-
}
|
|
263
80
|
}
|
|
@@ -10,10 +10,10 @@ export class ThemeSkinstoreApi extends FeatureApi {
|
|
|
10
10
|
async getPublishFormData(props) {
|
|
11
11
|
return this.#service.getPublishFormData(props);
|
|
12
12
|
}
|
|
13
|
-
async
|
|
14
|
-
return this.#service.
|
|
13
|
+
async publish(themeId) {
|
|
14
|
+
// return this.#service.publish(themeId);
|
|
15
15
|
}
|
|
16
|
-
async
|
|
17
|
-
return this.#service.
|
|
16
|
+
async update() {
|
|
17
|
+
// return this.#service.update();
|
|
18
18
|
}
|
|
19
19
|
}
|
|
@@ -14,30 +14,4 @@ export class ThemeSkinstoreHttpApi {
|
|
|
14
14
|
isPrivate: true
|
|
15
15
|
});
|
|
16
16
|
}
|
|
17
|
-
publishTheme({ actionData, shopUrl, payload }) {
|
|
18
|
-
const { method, url } = actionData;
|
|
19
|
-
return this.#httpApi.fetch({
|
|
20
|
-
url: `${shopUrl}${url}`,
|
|
21
|
-
method,
|
|
22
|
-
data: payload,
|
|
23
|
-
sanitizeOptions: {
|
|
24
|
-
disable: true
|
|
25
|
-
},
|
|
26
|
-
isPrivate: true
|
|
27
|
-
});
|
|
28
|
-
}
|
|
29
|
-
uploadImage({ shopUrl, url, imageBuffer }) {
|
|
30
|
-
return this.#httpApi.fetch({
|
|
31
|
-
url: `${shopUrl}${url}`,
|
|
32
|
-
method: 'post',
|
|
33
|
-
data: imageBuffer,
|
|
34
|
-
headers: {
|
|
35
|
-
'Content-Type': 'application/octet-stream'
|
|
36
|
-
},
|
|
37
|
-
sanitizeOptions: {
|
|
38
|
-
disable: true
|
|
39
|
-
},
|
|
40
|
-
isPrivate: true
|
|
41
|
-
});
|
|
42
|
-
}
|
|
43
17
|
}
|
|
@@ -2,18 +2,13 @@ import { STATUS_CODES } from '@dreamcommerce/star_core';
|
|
|
2
2
|
import { HttpErrorsFactory } from '../../../../../cli/class/errors/http/http_errors_factory.js';
|
|
3
3
|
import { DownloadFileErrorsFactory } from '../../../../../utils/download_file/download_file_errors_factory.js';
|
|
4
4
|
import { toControls } from '../../../../../cli/features/controls/controls_dto_mappers.js';
|
|
5
|
-
import fs from 'fs';
|
|
6
|
-
const PUBLISH_RESPONSE_STATUSES = [STATUS_CODES.ok, STATUS_CODES.badRequest];
|
|
7
5
|
export class ThemeSkinstoreService {
|
|
8
6
|
#httpApi;
|
|
9
|
-
|
|
10
|
-
constructor({ httpApi, logger }) {
|
|
7
|
+
constructor(httpApi) {
|
|
11
8
|
this.#httpApi = httpApi;
|
|
12
|
-
this.#logger = logger;
|
|
13
9
|
}
|
|
14
10
|
async getPublishFormData({ credentials, actionData }) {
|
|
15
11
|
try {
|
|
16
|
-
this.#logger.info('Fetching publish form data');
|
|
17
12
|
const { response: request } = this.#httpApi.getPublishFormData({ actionData, shopUrl: credentials.shopUrl });
|
|
18
13
|
const response = await request;
|
|
19
14
|
if (response?.status !== STATUS_CODES.ok)
|
|
@@ -21,64 +16,17 @@ export class ThemeSkinstoreService {
|
|
|
21
16
|
return response?.data ? toControls(response.data) : [];
|
|
22
17
|
}
|
|
23
18
|
catch (err) {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
shopUrl: credentials.shopUrl,
|
|
36
|
-
payload
|
|
37
|
-
});
|
|
38
|
-
const response = await request;
|
|
39
|
-
if (!PUBLISH_RESPONSE_STATUSES.includes(response?.status))
|
|
40
|
-
return;
|
|
41
|
-
this.#logger.info('Successfully published theme');
|
|
42
|
-
return response?.data;
|
|
43
|
-
}
|
|
44
|
-
catch (err) {
|
|
45
|
-
this.#logger.error('Error publishing theme', { error: err });
|
|
46
|
-
this.#handleHttpError(err);
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
async uploadImage({ credentials, uploadUrl, imagePath }) {
|
|
50
|
-
try {
|
|
51
|
-
this.#logger.info('Uploading image', {
|
|
52
|
-
details: { uploadUrl, imagePath }
|
|
53
|
-
});
|
|
54
|
-
const imageBuffer = fs.readFileSync(imagePath);
|
|
55
|
-
const { response: request } = this.#httpApi.uploadImage({
|
|
56
|
-
shopUrl: credentials.shopUrl,
|
|
57
|
-
url: uploadUrl,
|
|
58
|
-
imageBuffer
|
|
59
|
-
});
|
|
60
|
-
const response = await request;
|
|
61
|
-
if (response?.status !== STATUS_CODES.ok)
|
|
62
|
-
return;
|
|
63
|
-
this.#logger.info('Successfully uploaded image');
|
|
64
|
-
return response?.data;
|
|
65
|
-
}
|
|
66
|
-
catch (err) {
|
|
67
|
-
this.#logger.error('Error uploading image', { error: err });
|
|
68
|
-
this.#handleHttpError(err);
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
#handleHttpError(err) {
|
|
72
|
-
//TODO to basic class
|
|
73
|
-
switch (err.response?.status) {
|
|
74
|
-
case 403:
|
|
75
|
-
throw HttpErrorsFactory.createForbiddenError();
|
|
76
|
-
case 401:
|
|
77
|
-
throw HttpErrorsFactory.createUnauthorizedError();
|
|
78
|
-
case 404:
|
|
79
|
-
throw HttpErrorsFactory.createNotFoundError();
|
|
80
|
-
default:
|
|
81
|
-
throw DownloadFileErrorsFactory.downloadError(err.response?.status);
|
|
19
|
+
//TODO to basic class
|
|
20
|
+
switch (err.response?.status) {
|
|
21
|
+
case 403:
|
|
22
|
+
throw HttpErrorsFactory.createForbiddenError();
|
|
23
|
+
case 401:
|
|
24
|
+
throw HttpErrorsFactory.createUnauthorizedError();
|
|
25
|
+
case 404:
|
|
26
|
+
throw HttpErrorsFactory.createNotFoundError();
|
|
27
|
+
default:
|
|
28
|
+
throw DownloadFileErrorsFactory.downloadError(err.response.status);
|
|
29
|
+
}
|
|
82
30
|
}
|
|
83
31
|
}
|
|
84
32
|
}
|
|
@@ -2,7 +2,3 @@ export const THEME_SKINSTORE_LOCATION = 'skinstore';
|
|
|
2
2
|
export const THEME_SKINSTORE_SETTINGS_FILE_NAME = 'settings.json';
|
|
3
3
|
export const THEME_SKINSTORE_API_NAME = 'ThemeSkinstoreApi';
|
|
4
4
|
export const THEME_SKINSTORE_FEATURE_NAME = 'ThemeSkinstore';
|
|
5
|
-
export const RELEASE_TYPE_TEST = 0;
|
|
6
|
-
export const RELEASE_TYPE_STABLE = 1;
|
|
7
|
-
export const IMAGE_MAX_SIZE = 5242880;
|
|
8
|
-
export const IMAGE_ALLOWED_EXTENSIONS = ['gif', 'jpeg', 'jpg', 'png', 'webp'];
|
|
@@ -3,16 +3,11 @@ import { THEME_SKINSTORE_FEATURE_NAME } from './theme_publish_constants.js';
|
|
|
3
3
|
import { ThemeSkinstoreService } from './service/theme_skinstore_service.js';
|
|
4
4
|
import { ThemeSkinstoreHttpApi } from './http/theme_skinstore_http_api.js';
|
|
5
5
|
import { ThemeSkinstoreApi } from './api/theme_skinstore_api.js';
|
|
6
|
-
import { LOGGER_API_NAME } from '../../../../cli/utilities/features/logger/logger_constants.js';
|
|
7
6
|
export class ThemeSkinstoreInitializer extends SyncFeatureInitializer {
|
|
8
7
|
static featureName = THEME_SKINSTORE_FEATURE_NAME;
|
|
9
8
|
init() {
|
|
10
9
|
const httpApi = this.getApiSync(HTTP_REQUESTER_API_NAME);
|
|
11
|
-
const
|
|
12
|
-
const service = new ThemeSkinstoreService({
|
|
13
|
-
httpApi: new ThemeSkinstoreHttpApi(httpApi),
|
|
14
|
-
logger: loggerApi
|
|
15
|
-
});
|
|
10
|
+
const service = new ThemeSkinstoreService(new ThemeSkinstoreHttpApi(httpApi));
|
|
16
11
|
return {
|
|
17
12
|
cores: [
|
|
18
13
|
{
|
|
@@ -18,7 +18,18 @@ export class ThemeFilesUtils {
|
|
|
18
18
|
}
|
|
19
19
|
static async writeThemeFilesStructure(themeDirectory, filesStructure) {
|
|
20
20
|
const filePath = join(themeDirectory, SHOPER_THEME_METADATA_DIR, THEME_FILES_STRUCTURE_FILE_NAME);
|
|
21
|
-
|
|
21
|
+
const normalizedStructure = {};
|
|
22
|
+
for (const [key, value] of Object.entries(filesStructure)) {
|
|
23
|
+
let unixKey = toUnixPath(key);
|
|
24
|
+
if (!unixKey.endsWith('/')) {
|
|
25
|
+
const fullPath = join(themeDirectory, unixKey);
|
|
26
|
+
if ((await fileExists(fullPath)) && (await isDirectory(fullPath))) {
|
|
27
|
+
unixKey = `${unixKey}/`;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
normalizedStructure[unixKey] = value;
|
|
31
|
+
}
|
|
32
|
+
await writeJSONFile(filePath, normalizedStructure);
|
|
22
33
|
}
|
|
23
34
|
static async getFilesPermissions(themeDirectory) {
|
|
24
35
|
const fileStructure = await ThemeFilesUtils.getThemeFilesStructure(themeDirectory);
|
package/build/theme/index.js
CHANGED
|
@@ -17,7 +17,6 @@ import { ThemeDeleteInitializer } from './features/theme/delete/theme_delete_ini
|
|
|
17
17
|
import { ThemeActionsInitializer } from './features/theme/actions/theme_actions_initializer.js';
|
|
18
18
|
import { ThemeWatchCommand } from './commands/watch/theme_watch_command.js';
|
|
19
19
|
import { ThemeWatchInitializer } from './features/theme/watch/theme_watch_initializer.js';
|
|
20
|
-
import { ThemeSkinstoreInitializer } from './features/theme/skinstore/theme_skinstore_initialzier.js';
|
|
21
20
|
export const COMMANDS = {
|
|
22
21
|
[THEME_COMMANDS_NAME.list]: ThemeListCommand,
|
|
23
22
|
[THEME_COMMANDS_NAME.pull]: ThemePullCommand,
|
|
@@ -35,7 +34,6 @@ export const COMMAND_TO_FEATURES_MAP = {
|
|
|
35
34
|
[THEME_COMMANDS_NAME.push]: [ThemeFetchInitializer, ThemePushInitializer],
|
|
36
35
|
[THEME_COMMANDS_NAME.verify]: ThemeVerifyInitializer,
|
|
37
36
|
[THEME_COMMANDS_NAME.delete]: ThemeDeleteInitializer,
|
|
38
|
-
[THEME_COMMANDS_NAME.publish]: ThemeSkinstoreInitializer,
|
|
39
37
|
[THEME_COMMANDS_NAME.watch]: [ThemeFetchInitializer, ThemePushInitializer, ThemeWatchInitializer]
|
|
40
38
|
};
|
|
41
39
|
export const getThemeInitializersForCommand = (commandName) => {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import fsPromises from 'node:fs/promises';
|
|
2
2
|
import fs, { createReadStream, readFileSync } from 'node:fs';
|
|
3
|
-
|
|
3
|
+
const isHiddenFile = (filename) => /(?:^|[/\\])\./.test(filename);
|
|
4
4
|
import process from 'node:process';
|
|
5
5
|
import { basename, join, resolve } from '../path_utils.js';
|
|
6
6
|
import { parse as parsePath } from 'node:path';
|
|
@@ -21,22 +21,31 @@ export const hasParentDirectory = (path) => {
|
|
|
21
21
|
};
|
|
22
22
|
export const looksLikeDirectory = (path) => {
|
|
23
23
|
const normalized = normalize(path);
|
|
24
|
-
if (normalized.endsWith(sep))
|
|
24
|
+
if (normalized.endsWith(sep) || path.endsWith('/') || path.endsWith('\\'))
|
|
25
25
|
return true;
|
|
26
26
|
const base = basename(normalized);
|
|
27
27
|
const isHidden = base.startsWith('.') && !base.slice(1).includes('.');
|
|
28
28
|
const hasExtension = base.slice(1).includes('.');
|
|
29
29
|
if (isHidden) {
|
|
30
|
-
|
|
31
|
-
|
|
30
|
+
try {
|
|
31
|
+
const stat = fs.lstatSync(normalized);
|
|
32
|
+
return stat.isDirectory();
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
32
37
|
}
|
|
33
38
|
return isHidden || !hasExtension;
|
|
34
39
|
};
|
|
35
40
|
export const toUnixPath = (filePath) => {
|
|
36
|
-
|
|
41
|
+
const endsWithSep = filePath.endsWith('/') || filePath.endsWith('\\');
|
|
42
|
+
const result = normalize(filePath).split(sep).join(path.posix.sep);
|
|
43
|
+
return endsWithSep && !result.endsWith('/') ? `${result}/` : result;
|
|
37
44
|
};
|
|
38
45
|
export const toWinowsPath = (filePath) => {
|
|
39
|
-
|
|
46
|
+
const endsWithSep = filePath.endsWith('/') || filePath.endsWith('\\');
|
|
47
|
+
const result = normalize(filePath).split(sep).join(path.win32.sep);
|
|
48
|
+
return endsWithSep && !result.endsWith('\\') ? `${result}\\` : result;
|
|
40
49
|
};
|
|
41
50
|
export const isUnixPath = (path) => {
|
|
42
51
|
return !path.includes('\\') && (path.startsWith('/') || path.includes('/'));
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@shoper/cli",
|
|
3
3
|
"packageManager": "yarn@3.2.0",
|
|
4
4
|
"sideEffects": false,
|
|
5
|
-
"version": "0.9.4-
|
|
5
|
+
"version": "0.9.4-7",
|
|
6
6
|
"description": "CLI tool for Shoper",
|
|
7
7
|
"author": "Joanna Firek",
|
|
8
8
|
"license": "MIT",
|
|
@@ -48,16 +48,17 @@
|
|
|
48
48
|
"chalk": "5.4.1",
|
|
49
49
|
"conf": "13.1.0",
|
|
50
50
|
"fast-glob": "3.3.3",
|
|
51
|
+
"figlet": "1.9.4",
|
|
51
52
|
"figures": "6.1.0",
|
|
52
53
|
"fs-extra": "11.3.0",
|
|
53
54
|
"fs-tree-diff": "2.0.1",
|
|
55
|
+
"ignore": "7.0.5",
|
|
54
56
|
"ink": "6.0.1",
|
|
55
57
|
"ink-link": "4.1.0",
|
|
56
58
|
"ink-gradient": "3.0.0",
|
|
57
59
|
"ink-text-input": "6.0.0",
|
|
58
60
|
"inquirer": "12.5.2",
|
|
59
61
|
"inquirer-select-line": "1.1.3",
|
|
60
|
-
"is-hidden-file": "1.1.2",
|
|
61
62
|
"jsonwebtoken": "9.0.2",
|
|
62
63
|
"klaw": "4.1.0",
|
|
63
64
|
"lodash": "4.17.21",
|
|
@@ -77,7 +78,6 @@
|
|
|
77
78
|
"micromatch": "4.0.8",
|
|
78
79
|
"walk-sync": "3.0.0",
|
|
79
80
|
"yauzl": "3.2.0",
|
|
80
|
-
"figlet": "1.9.4",
|
|
81
81
|
"yazl": "3.3.1",
|
|
82
82
|
"puppeteer": "24.31.0",
|
|
83
83
|
"gradient-string": "3.0.0"
|