@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.
@@ -1,4 +1,4 @@
1
- import { Args, Flags } from '@oclif/core';
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, RELEASE_TYPE_TEST, RELEASE_TYPE_STABLE, IMAGE_MAX_SIZE, IMAGE_ALLOWED_EXTENSIONS } from '../../features/theme/skinstore/theme_publish_constants.js';
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 = 'Publish theme to SkinStore.';
26
- static description = 'Publishes a theme to the Shoper SkinStore marketplace.\n\nFor first-time publication, you will be prompted to fill in theme details.\nFor already published themes, this performs an upgrade (new version).';
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: 'Publish current theme (from theme directory)',
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 publishAction = themeActionsApi.getThemeAction({
94
- actionType: THEME_ACTIONS_TYPES.publish,
57
+ const pushAction = themeActionsApi.getThemeAction({
58
+ actionType: THEME_ACTIONS_TYPES.publishForm,
95
59
  themeId: _themeId,
96
60
  credentials
97
61
  });
98
- if (!publishAction) {
99
- renderOnce(React.createElement(UnpermittedCommandError, { themeId: _themeId, commandName: "publish" }));
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
- const releaseType = this.flags.release === 'test' ? RELEASE_TYPE_TEST : RELEASE_TYPE_STABLE;
108
- const isUpgrade = !publishFormAction;
109
- if (isUpgrade) {
110
- await this.#handleUpgrade({
111
- themeSkinstoreApi,
112
- themeActionsApi,
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 publishTheme(props) {
14
- return this.#service.publishTheme(props);
13
+ async publish(themeId) {
14
+ // return this.#service.publish(themeId);
15
15
  }
16
- async uploadImage(props) {
17
- return this.#service.uploadImage(props);
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
- #logger;
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
- this.#logger.error('Error fetching publish form data', { error: err });
25
- this.#handleHttpError(err);
26
- }
27
- }
28
- async publishTheme({ credentials, actionData, payload }) {
29
- try {
30
- this.#logger.info('Publishing theme', {
31
- details: { actionData }
32
- });
33
- const { response: request } = this.#httpApi.publishTheme({
34
- actionData,
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 loggerApi = this.getApiSync(LOGGER_API_NAME);
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
- await writeJSONFile(filePath, filesStructure);
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);
@@ -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
- import { isHiddenFile } from 'is-hidden-file';
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
- const stat = fs.lstatSync(path);
31
- return stat.isDirectory();
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
- return normalize(filePath).split(sep).join(path.posix.sep);
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
- return normalize(filePath).split(sep).join(path.win32.sep);
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-3",
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"