@shoper/cli 0.1.0-23 → 0.1.0-25

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.
@@ -14,6 +14,7 @@ import { ThemeFetchInitializer } from '../../theme/features/theme/fetch/theme_fe
14
14
  import { ThemeMergeInitializer } from '../../theme/features/theme/merge/theme_merge_initializer.js';
15
15
  import { ThemeActionsInitializer } from '../../theme/features/theme/actions/theme_actions_initializer.js';
16
16
  import { ThemePushInitializer } from '../../theme/features/theme/push/theme_push_initializer.js';
17
+ import { ThemeDeleteInitializer } from '../../theme/features/theme/delete/theme_delete_initalizer.js';
17
18
  tmp.setGracefulCleanup();
18
19
  export const cliSetup = async () => {
19
20
  //TODO jakis ładny komuniakt błedu
@@ -34,7 +35,8 @@ export const cliSetup = async () => {
34
35
  ThemeInitInitializer,
35
36
  ThemeMergeInitializer,
36
37
  ThemeFetchInitializer,
37
- ThemePushInitializer
38
+ ThemePushInitializer,
39
+ ThemeDeleteInitializer
38
40
  ],
39
41
  options: {
40
42
  featuresTracking: false,
@@ -0,0 +1,99 @@
1
+ import { Args } from '@oclif/core';
2
+ import { BaseThemeCommand } from '../../class/base_theme_command.js';
3
+ import { CLI_AUTH_API_NAME } from '../../../cli/auth/cli_auth_constants.js';
4
+ import { THEME_ACTION_NOT_FOUND_ERROR_CODE, THEME_ACTIONS_API_NAME, THEME_ACTIONS_TYPES } from '../../features/theme/actions/theme_actions_constants.js';
5
+ import { EXECUTION_CONTEXT_API_NAME } from '../../../cli/features/execution_context/execution_context_constants.js';
6
+ import { renderOnce } from '../../../ui/ui_utils.js';
7
+ import { MissingCredentialsError } from '../../../cli/commands/auth/ui/missing_credentials_error.js';
8
+ import React from 'react';
9
+ import { THEME_DELETE_API_NAME } from '../../features/theme/delete/theme_delete_constants.js';
10
+ import { UnpermittedCommandError } from '../ui/unpermitted_command_error.js';
11
+ import { Error } from '../../../ui/message_box/error.js';
12
+ import { Text } from '../../../ui/text.js';
13
+ import { promptInput } from '../../../ui/prompts/prompt_input.js';
14
+ import { Warning } from '../../../ui/message_box/warning.js';
15
+ import { Info } from '../../../ui/message_box/info.js';
16
+ export class ThemeDeleteCommand extends BaseThemeCommand {
17
+ static summary = 'Permanently deletes the specified theme from your store.';
18
+ 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).';
19
+ static examples = [
20
+ {
21
+ description: 'This will delete the theme with ID 123 permanently from your store. Make sure you have a backup if needed.',
22
+ command: '<%= config.bin %> <%= command.id %> 123'
23
+ }
24
+ ];
25
+ static hidden = true;
26
+ static args = {
27
+ id: Args.string({
28
+ description: 'Theme id',
29
+ name: 'id',
30
+ required: false,
31
+ type: 'string'
32
+ })
33
+ };
34
+ async run() {
35
+ const data = await this.parse(ThemeDeleteCommand);
36
+ const { args } = data;
37
+ const themeId = args.id;
38
+ const cliAuthApi = this.getApi(CLI_AUTH_API_NAME);
39
+ const executionContextApi = this.getApi(EXECUTION_CONTEXT_API_NAME);
40
+ const credentials = cliAuthApi.getCredentials();
41
+ if (!credentials) {
42
+ renderOnce(React.createElement(MissingCredentialsError, null));
43
+ return;
44
+ }
45
+ //
46
+ // if (!themeId) {
47
+ // renderOnce(<MissingThemeIdError />);
48
+ //
49
+ // return;
50
+ // }
51
+ // const executionContext = await executionContextApi.getExecutionContext();
52
+ //
53
+ // if (executionContext.type === EXECUTION_CONTEXTS.theme) {
54
+ // renderOnce(<ThemeDirectoryContextError />);
55
+ //
56
+ // return;
57
+ // }
58
+ renderOnce(React.createElement(Warning, { header: `Warning: This will permanently delete the theme {Theme_name} (ID: {theme_ID}) and cannot be undone.` }));
59
+ const { input: procced } = await promptInput('Proceed?');
60
+ if (!procced) {
61
+ renderOnce(React.createElement(Info, { header: "Theme deletion was cancelled." }));
62
+ return;
63
+ }
64
+ if (!themeId)
65
+ return;
66
+ const themeActionsApi = this.getApi(THEME_ACTIONS_API_NAME);
67
+ const deleteAction = themeActionsApi.getThemeAction({
68
+ actionType: THEME_ACTIONS_TYPES.delete,
69
+ themeId,
70
+ credentials
71
+ });
72
+ if (!deleteAction) {
73
+ renderOnce(React.createElement(UnpermittedCommandError, { themeId: themeId, commandName: "delete" }));
74
+ return;
75
+ }
76
+ const themeDeleteApi = this.getApi(THEME_DELETE_API_NAME);
77
+ try {
78
+ await themeDeleteApi.deleteTheme({
79
+ actionData: deleteAction.data,
80
+ credentials
81
+ });
82
+ }
83
+ catch (err) {
84
+ this._handleError(err, themeId);
85
+ return;
86
+ }
87
+ }
88
+ _handleError(err, themeId) {
89
+ if (err?.code === THEME_ACTION_NOT_FOUND_ERROR_CODE && themeId) {
90
+ renderOnce(React.createElement(UnpermittedCommandError, { themeId: themeId, commandName: "init" }));
91
+ }
92
+ if (err?.message) {
93
+ renderOnce(React.createElement(Error, null,
94
+ React.createElement(Text, null, err.message)));
95
+ return;
96
+ }
97
+ this.error(String(err));
98
+ }
99
+ }
@@ -78,6 +78,7 @@ export class ThemeInitCommand extends BaseThemeCommand {
78
78
  _handleError(err, themeId) {
79
79
  if (err?.code === THEME_ACTION_NOT_FOUND_ERROR_CODE && themeId) {
80
80
  renderOnce(React.createElement(UnpermittedCommandError, { themeId: themeId, commandName: "init" }));
81
+ return;
81
82
  }
82
83
  if (err?.message) {
83
84
  renderOnce(React.createElement(Error, null,
@@ -20,7 +20,6 @@ import { THEME_FILES_STRUCTURE_FILE_NAME } from '../../features/theme/theme_cons
20
20
  import { THEME_MERGE_API_NAME } from '../../features/theme/merge/theme_merge_constants.js';
21
21
  import { ThemePushSkipInfo } from './ui/theme_push_skip_into.js';
22
22
  import { ThemeUnpermittedActionsError } from './ui/theme_unpermitted_actions_error.js';
23
- import { ThemeValidationErrors } from './ui/theme_validation_errors.js';
24
23
  import { Error } from '../../../ui/message_box/error.js';
25
24
  import { Text } from '../../../ui/text.js';
26
25
  import { ThemeWorkUrlMismatch } from '../ui/theme_work_url_mismatch.js';
@@ -111,7 +110,7 @@ export class ThemePushCommand extends BaseThemeCommand {
111
110
  return;
112
111
  }
113
112
  if (err?.code === THEME_ARCHIVE_UPLOAD_ERROR) {
114
- renderOnce(React.createElement(ThemeValidationErrors, { errors: err?.details }));
113
+ // renderOnce(<ThemeValidationErrors errors={err?.details} />);
115
114
  return;
116
115
  }
117
116
  if (err?.code === THEME_WORK_URL_MISMATCH_ERROR) {
@@ -5,5 +5,6 @@ export const THEME_COMMANDS_NAME = {
5
5
  push: 'theme:push',
6
6
  showChanges: 'theme:show-changes',
7
7
  verify: 'theme:verify',
8
- info: 'theme:info'
8
+ info: 'theme:info',
9
+ delete: 'theme:delete'
9
10
  };
@@ -7,8 +7,7 @@ export const THEME_ACTIONS_TYPES = {
7
7
  pull: 'pull',
8
8
  delete: 'delete',
9
9
  publish_form: 'publish_form',
10
- publish: 'publish',
11
- info: 'info'
10
+ publish: 'publish'
12
11
  };
13
12
  export const THEME_ACTION_DATA_TYPE = {
14
13
  file: 'file',
@@ -0,0 +1,13 @@
1
+ import { FeatureApi } from '@dreamcommerce/star_core';
2
+ import { THEME_DELETE_API_NAME } from '../theme_delete_constants.js';
3
+ export class ThemeDeleteApi extends FeatureApi {
4
+ moduleName = THEME_DELETE_API_NAME;
5
+ #service;
6
+ constructor(service) {
7
+ super();
8
+ this.#service = service;
9
+ }
10
+ async deleteTheme(props) {
11
+ return this.#service.deleteTheme(props);
12
+ }
13
+ }
@@ -0,0 +1,17 @@
1
+ export class ThemeDeleteHttpApi {
2
+ #httpApi;
3
+ constructor(httpApi) {
4
+ this.#httpApi = httpApi;
5
+ }
6
+ deleteTheme({ shopUrl, actionData }) {
7
+ const { method, url } = actionData;
8
+ return this.#httpApi.fetch({
9
+ url: `${shopUrl}${url}`,
10
+ method,
11
+ sanitizeOptions: {
12
+ disable: true
13
+ },
14
+ isPrivate: true
15
+ });
16
+ }
17
+ }
@@ -0,0 +1,34 @@
1
+ import { STATUS_CODES } from '@dreamcommerce/star_core';
2
+ import { HttpErrorsFactory } from '../../../../../cli/class/errors/http/http_errors_factory.js';
3
+ import { DownloadFileErrorsFactory } from '../../../../../utils/download_file/download_file_errors_factory.js';
4
+ export class ThemeDeleteService {
5
+ #httpApi;
6
+ constructor(httpApi) {
7
+ this.#httpApi = httpApi;
8
+ }
9
+ async deleteTheme({ actionData, credentials }) {
10
+ try {
11
+ const { response } = this.#httpApi.deleteTheme({ actionData, shopUrl: credentials.shopUrl });
12
+ const { data, status } = await response;
13
+ if (status !== STATUS_CODES.ok)
14
+ return;
15
+ //TODO if is success remove theme directory
16
+ return data;
17
+ }
18
+ catch (err) {
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
+ if (err.response?.status !== 200) {
29
+ throw DownloadFileErrorsFactory.downloadError(err.response.status);
30
+ }
31
+ }
32
+ }
33
+ }
34
+ }
@@ -0,0 +1,2 @@
1
+ export const THEME_DELETE_FEATURE_NAME = 'ThemeDelete';
2
+ export const THEME_DELETE_API_NAME = 'ThemeDeleteApi';
@@ -0,0 +1,20 @@
1
+ import { FEATURE_CORES_TYPES, HTTP_REQUESTER_API_NAME, SyncFeatureInitializer } from '@dreamcommerce/star_core';
2
+ import { ThemeDeleteApi } from './api/theme_delete_api.js';
3
+ import { ThemeDeleteService } from './service/theme_delete_service.js';
4
+ import { ThemeDeleteHttpApi } from './http/theme_delete_http_api.js';
5
+ import { THEME_DELETE_FEATURE_NAME } from './theme_delete_constants.js';
6
+ export class ThemeDeleteInitializer extends SyncFeatureInitializer {
7
+ static featureName = THEME_DELETE_FEATURE_NAME;
8
+ init() {
9
+ const httpApi = this.getApiSync(HTTP_REQUESTER_API_NAME);
10
+ const service = new ThemeDeleteService(new ThemeDeleteHttpApi(httpApi));
11
+ return {
12
+ cores: [
13
+ {
14
+ type: FEATURE_CORES_TYPES.api,
15
+ instance: new ThemeDeleteApi(service)
16
+ }
17
+ ]
18
+ };
19
+ }
20
+ }
@@ -1,7 +1,7 @@
1
1
  import FSTree from 'fs-tree-diff';
2
2
  import { join } from '../../../../../utils/path_utils.js';
3
3
  import { computeFileChecksum } from '../../../../../utils/checksums/checksums_utils.js';
4
- import { copyFile, copyFileSync, fileExists, getAllDirectoriesNamesInside } from '../../../../../utils/fs/fs_utils.js';
4
+ import { copyFileSync, fileExists, getAllDirectoriesNamesInside } from '../../../../../utils/fs/fs_utils.js';
5
5
  import { ThemeChecksumsUtils } from '../../utils/checksums/theme_checksums_utils.js';
6
6
  import walkSync from 'walk-sync';
7
7
  import { SHOPER_THEME_METADATA_DIR } from '../../../../constants/directory_contstants.js';
@@ -9,7 +9,7 @@ export class ThemeMergeService {
9
9
  async applyChanges(fromTheme, toTheme, changes) {
10
10
  FSTree.applyPatch(fromTheme, toTheme, changes, {
11
11
  create(inputPath, outputPath) {
12
- copyFile(inputPath, outputPath);
12
+ copyFileSync(inputPath, outputPath);
13
13
  },
14
14
  change(inputPath, outputPath) {
15
15
  copyFileSync(inputPath, outputPath);
@@ -183,7 +183,7 @@ export class ThemePushService {
183
183
  return;
184
184
  const toUnixStyleFilesList = Object.entries(filesList).reduce((acc, [filePath, value]) => {
185
185
  const unixPath = toUnixPath(filePath);
186
- const finalPath = looksLikeDirectory(unixPath) ? join(unixPath, path.posix.sep) : unixPath;
186
+ const finalPath = looksLikeDirectory(unixPath) ? `${unixPath}${path.posix.sep}` : unixPath;
187
187
  return {
188
188
  ...acc,
189
189
  [finalPath]: value
@@ -25,7 +25,6 @@ export class ThemeChecksumsUtils {
25
25
  }
26
26
  static async createThemeChecksumsFiles(themeDir, checksums) {
27
27
  return new Promise((resolve, reject) => {
28
- // const sortedChecksums = _.pick(checksums, Object.keys(checksums).sort());
29
28
  const currentChecksumFilePath = this.getCurrentThemeChecksumsFilePath(themeDir);
30
29
  const initialChecksumFilePath = this.getInitialThemeChecksumsFilePath(themeDir);
31
30
  const checksumStream = createWriteStream(initialChecksumFilePath);
@@ -48,7 +47,6 @@ export class ThemeChecksumsUtils {
48
47
  }
49
48
  static async createThemeCurrentChecksumsFile(themeDir, checksums) {
50
49
  return new Promise((resolve, reject) => {
51
- // const sortedChecksums = _.pick(checksums, Object.keys(checksums).sort());
52
50
  const currentChecksumFilePath = this.getCurrentThemeChecksumsFilePath(themeDir);
53
51
  const checksumStream = createWriteStream(currentChecksumFilePath);
54
52
  checksumStream.write(JSON.stringify(checksums, null, JSON_FILE_INDENT));
@@ -19,7 +19,12 @@ export class ThemeMetaDataUtils {
19
19
  }
20
20
  static async updateThemeMetadata(themeDirectory, data) {
21
21
  const filePath = this.getThemeMetadataFilePath(themeDirectory);
22
- await writeJSONFile(filePath, data);
22
+ const newData = {
23
+ ...data,
24
+ skin_id: data.themeId
25
+ };
26
+ delete newData.themeId;
27
+ await writeJSONFile(filePath, newData);
23
28
  }
24
29
  static async getThemeWorkUrl(themeDirectory) {
25
30
  const metadataFilePath = this.getThemeMetadataFilePath(themeDirectory);
@@ -6,6 +6,7 @@ import { ThemePushCommand } from './commands/push/theme_push_command.js';
6
6
  import { ThemeShowChangesCommand } from './commands/theme_show_changes_command.js';
7
7
  import { ThemeVerifyCommand } from './commands/theme_verify_command.js';
8
8
  import { ThemeInfoCommand } from './commands/info/theme_info_command.js';
9
+ import { ThemeDeleteCommand } from './commands/delete/theme_delete_command.js';
9
10
  export const COMMANDS = {
10
11
  [THEME_COMMANDS_NAME.list]: ThemeListCommand,
11
12
  [THEME_COMMANDS_NAME.pull]: ThemePullCommand,
@@ -13,5 +14,6 @@ export const COMMANDS = {
13
14
  [THEME_COMMANDS_NAME.push]: ThemePushCommand,
14
15
  [THEME_COMMANDS_NAME.showChanges]: ThemeShowChangesCommand,
15
16
  [THEME_COMMANDS_NAME.verify]: ThemeVerifyCommand,
16
- [THEME_COMMANDS_NAME.info]: ThemeInfoCommand
17
+ [THEME_COMMANDS_NAME.info]: ThemeInfoCommand,
18
+ [THEME_COMMANDS_NAME.delete]: ThemeDeleteCommand
17
19
  };
@@ -41,6 +41,6 @@ export const downloadFile = async ({ dist, request }) => {
41
41
  throw DownloadFileErrorsFactory.downloadError(err.response.status);
42
42
  }
43
43
  }
44
+ throw DownloadFileErrorsFactory.downloadError('Unknown error');
44
45
  }
45
- throw DownloadFileErrorsFactory.downloadError('Unknown error');
46
46
  };
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.1.0-23",
5
+ "version": "0.1.0-25",
6
6
  "description": "CLI tool for Shoper",
7
7
  "author": "Joanna Firek",
8
8
  "license": "MIT",
@@ -1,6 +0,0 @@
1
- import React from 'react';
2
- import { Error } from '../../../../ui/message_box/error.js';
3
- import { Text } from '../../../../ui/text.js';
4
- export const ThemeValidationError = ({ messages, name }) => {
5
- return (React.createElement(Error, { header: name }, messages.map((message, index) => (React.createElement(Text, { key: index }, message)))));
6
- };
@@ -1,5 +0,0 @@
1
- import React from 'react';
2
- import { ThemeValidationError } from './theme_validation_error.js';
3
- export const ThemeValidationErrors = ({ errors }) => {
4
- return (React.createElement(React.Fragment, null, Object.entries(errors).map(([name, messages]) => (React.createElement(ThemeValidationError, { key: name, name: name, messages: messages })))));
5
- };