@shoper/cli 0.1.0-32 → 0.1.0-34

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.
Files changed (35) hide show
  1. package/build/cli/auth/tokens/cli_auth_tokens_initalizer.js +1 -1
  2. package/build/cli/core/cli_setup.js +3 -1
  3. package/build/cli/features/controls/controls_dto_to_ui_mappers.js +39 -0
  4. package/build/cli/features/controls/ui/controls_mappers.js +44 -0
  5. package/build/cli/features/controls/validators/greater_eq_than_validator.js +5 -0
  6. package/build/cli/features/controls/validators/length_validator.js +6 -0
  7. package/build/cli/features/controls/validators/required_validator.js +5 -0
  8. package/build/cli/features/controls/validators/validator_constants.js +13 -0
  9. package/build/cli/features/http_requester/http_requester_initializer.js +1 -1
  10. package/build/theme/class/fetch_resources/fetch_resources.js +1 -0
  11. package/build/theme/class/fetch_resources/fetch_resources_utils.js +4 -1
  12. package/build/theme/commands/publish/theme_publish_command.js +77 -0
  13. package/build/theme/commands/pull/theme_pull_command.js +6 -4
  14. package/build/theme/commands/theme_commands_constants.js +2 -1
  15. package/build/theme/features/theme/actions/theme_actions_initializer.js +1 -1
  16. package/build/theme/features/theme/actions/theme_actions_utils.js +18 -8
  17. package/build/theme/features/theme/fetch/service/theme_fetch_service.js +3 -2
  18. package/build/theme/features/theme/merge/service/theme_merge_service.js +12 -6
  19. package/build/theme/features/theme/push/service/theme_push_service.js +15 -4
  20. package/build/theme/features/theme/skinstore/api/theme_skinstore_api.js +3 -1
  21. package/build/theme/features/theme/skinstore/http/theme_skinstore_http_api.js +11 -1
  22. package/build/theme/features/theme/skinstore/service/theme_skinstore_service.js +29 -1
  23. package/build/theme/features/theme/skinstore/theme_publish_constants.js +2 -0
  24. package/build/theme/features/theme/skinstore/theme_skinstore_initialzier.js +20 -0
  25. package/build/theme/features/theme/utils/files_structure/theme_files_structure_utils.js +7 -1
  26. package/build/theme/features/theme/utils/resources/theme_resources_with_id_directory_utils.js +1 -1
  27. package/build/theme/features/theme/utils/theme_images_utils.js +1 -1
  28. package/build/theme/index.js +3 -1
  29. package/package.json +2 -2
  30. package/build/ui/form/controls_mappers.js +0 -39
  31. /package/build/cli/{features → class}/caches/cache_factory.js +0 -0
  32. /package/build/cli/{features → class}/caches/json_cache/json_cache.js +0 -0
  33. /package/build/cli/{features → class}/caches/memory_cache.js +0 -0
  34. /package/build/{ui/form → cli/features/controls/ui}/form.js +0 -0
  35. /package/build/{ui/form → cli/features/controls/ui}/form_constants.js +0 -0
@@ -3,7 +3,7 @@ import { CliAuthTokensApi } from './api/cli_auth_tokens_api.js';
3
3
  import { CLiAuthTokensService } from './service/cli_auth_tokens_service.js';
4
4
  import { CLI_AUTH_TOKENS_FEATURE_NAME, CLI_AUTH_TOKENS_FILE_NAME } from './cli_auth_tokens_constants.js';
5
5
  import { CLI_DATA_DIRECTORY_API_NAME } from '../../features/data_directory/cli_data_directory_constants.js';
6
- import { JsonCache } from '../../features/caches/json_cache/json_cache.js';
6
+ import { JsonCache } from '../../class/caches/json_cache/json_cache.js';
7
7
  // schema
8
8
  // {
9
9
  // default: string;
@@ -15,6 +15,7 @@ import { ThemeMergeInitializer } from '../../theme/features/theme/merge/theme_me
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
17
  import { ThemeDeleteInitializer } from '../../theme/features/theme/delete/theme_delete_initalizer.js';
18
+ import { ThemeSkinstoreInitializer } from '../../theme/features/theme/skinstore/theme_skinstore_initialzier.js';
18
19
  tmp.setGracefulCleanup();
19
20
  export const cliSetup = async () => {
20
21
  //TODO jakis ładny komuniakt błedu
@@ -36,7 +37,8 @@ export const cliSetup = async () => {
36
37
  ThemeMergeInitializer,
37
38
  ThemeFetchInitializer,
38
39
  ThemePushInitializer,
39
- ThemeDeleteInitializer
40
+ ThemeDeleteInitializer,
41
+ ThemeSkinstoreInitializer
40
42
  ],
41
43
  options: {
42
44
  featuresTracking: false,
@@ -0,0 +1,39 @@
1
+ import { CONTROLS_TYPES } from './ui/form_constants.js';
2
+ export const toTextControl = ({ validators, label, isRequired, defaultValue, name }) => {
3
+ return {
4
+ type: CONTROLS_TYPES.text,
5
+ name,
6
+ label,
7
+ isRequired,
8
+ defaultValue,
9
+ validators
10
+ };
11
+ };
12
+ export const toNumberControl = ({ validators, label, isRequired, name }) => {
13
+ return {
14
+ type: 'number',
15
+ name,
16
+ label,
17
+ isRequired,
18
+ validators
19
+ };
20
+ };
21
+ export const toSelectControl = ({ label, isRequired, name, options }) => {
22
+ return {
23
+ type: 'list',
24
+ name,
25
+ label,
26
+ isRequired,
27
+ options: options.selectOptions.map(({ label, key }) => ({ label, value: key }))
28
+ };
29
+ };
30
+ export const toMultiSelectControl = ({ label, isRequired, name, options }) => {
31
+ //TODO multiple checkboxes
32
+ return {
33
+ type: 'checkbox',
34
+ name,
35
+ label,
36
+ isRequired,
37
+ options: options.selectOptions.map(({ label, key }) => ({ label, value: key }))
38
+ };
39
+ };
@@ -0,0 +1,44 @@
1
+ export const toTextControl = ({ validators, label, isRequired, defaultValue, name }) => {
2
+ return {
3
+ type: 'input',
4
+ name,
5
+ message: isRequired ? `${label} (required)` : label,
6
+ default: defaultValue,
7
+ validate: toInquirerValidate(validators)
8
+ };
9
+ };
10
+ export const toNumberControl = ({ validators, label, isRequired, defaultValue, name }) => {
11
+ return {
12
+ type: 'number',
13
+ name,
14
+ message: isRequired ? `${label} (required)` : label,
15
+ default: defaultValue,
16
+ validate: toInquirerValidate(validators)
17
+ };
18
+ };
19
+ export const toSelectControl = ({ validators, label, isRequired, defaultValue, name, options }) => {
20
+ return {
21
+ type: 'list',
22
+ name,
23
+ message: isRequired ? `${label} (required)` : label,
24
+ choices: options,
25
+ default: defaultValue,
26
+ validate: toInquirerValidate(validators)
27
+ };
28
+ };
29
+ export const toMultiSelectControl = ({ validators, label, isRequired, defaultValue, name, options }) => {
30
+ //TODO multiple checkboxes
31
+ return {
32
+ type: 'checkbox',
33
+ name,
34
+ message: isRequired ? `${label} (required)` : label,
35
+ choices: options,
36
+ default: defaultValue,
37
+ validate: toInquirerValidate(validators)
38
+ };
39
+ };
40
+ export const toInquirerValidate = (validators) => {
41
+ return (value) => {
42
+ return validators?.find((v) => !v.isValid(value))?.getErrorMessage() ?? true;
43
+ };
44
+ };
@@ -0,0 +1,5 @@
1
+ export class GreaterEqThanValidator {
2
+ isValid(value, { min }) {
3
+ return value >= min;
4
+ }
5
+ }
@@ -0,0 +1,6 @@
1
+ export class LengthValidator {
2
+ isValid(value, { min, max }) {
3
+ const length = value.length;
4
+ return length >= min && length <= max;
5
+ }
6
+ }
@@ -0,0 +1,5 @@
1
+ export class RequiredValidator {
2
+ isValid(value) {
3
+ return value !== null && value !== undefined && value !== '';
4
+ }
5
+ }
@@ -0,0 +1,13 @@
1
+ import { RequiredValidator } from './required_validator.js';
2
+ import { GreaterEqThanValidator } from './greater_eq_than_validator.js';
3
+ import { LengthValidator } from './length_validator.js';
4
+ export const VALIDATORS_TYPES = {
5
+ required: 'required',
6
+ greaterEqThan: 'greaterEqThan',
7
+ length: 'length'
8
+ };
9
+ export const VALIDATOR_TYPE_TO_VALIDATOR = {
10
+ [VALIDATORS_TYPES.required]: new RequiredValidator(),
11
+ [VALIDATORS_TYPES.greaterEqThan]: new GreaterEqThanValidator(),
12
+ [VALIDATORS_TYPES.length]: new LengthValidator()
13
+ };
@@ -1,6 +1,6 @@
1
1
  import { CACHE_TYPES, CacheService, DEFAULT_REQUESTER_CACHE_NAMESPACE, FEATURE_CORES_TYPES, HTTP_REQUESTER_FEATURE_NAME, HTTPRequesterApi, HTTPRequesterBalancer, REQUEST_SOURCE, Requester, RequesterCacheServiceKeySerializer, SANITIZER_API_NAME, StrategiesContainer, SyncFeatureInitializer } from '@dreamcommerce/star_core';
2
2
  import { HttpClient } from './http_client.js';
3
- import { CacheFactory } from '../caches/cache_factory.js';
3
+ import { CacheFactory } from '../../class/caches/cache_factory.js';
4
4
  export class HTTPRequesterInitializer extends SyncFeatureInitializer {
5
5
  static featureName = HTTP_REQUESTER_FEATURE_NAME;
6
6
  init() {
@@ -58,6 +58,7 @@ export class FetchResources {
58
58
  });
59
59
  }));
60
60
  }
61
+ return;
61
62
  }
62
63
  async _getResourcesFileContent(resourcePath) {
63
64
  return mapResourcesToTree(await readJSONFile(resourcePath));
@@ -1,7 +1,7 @@
1
1
  import { join } from '../../../utils/path_utils.js';
2
2
  import { RESOURCES_FILE_NAME } from './fetch_resources_constants.js';
3
3
  import { SHOPER_THEME_METADATA_DIR } from '../../constants/directory_contstants.js';
4
- import { fileExists, readJSONFile } from '../../../utils/fs/fs_utils.js';
4
+ import { fileExists, readJSONFile, removeFiles } from '../../../utils/fs/fs_utils.js';
5
5
  export const isResourceObject = (resource) => {
6
6
  return typeof resource === 'object' && resource !== null && 'url' in resource && typeof resource.url === 'string';
7
7
  };
@@ -15,6 +15,9 @@ export const getResources = async (root) => {
15
15
  }
16
16
  return await readJSONFile(resourcesPath);
17
17
  };
18
+ export const removeOldResources = async (rootDir, resources) => {
19
+ await removeFiles(Object.keys(resources).map((path) => join(rootDir, path)));
20
+ };
18
21
  export const mapResourcesToTree = (resources, separator = '/') => {
19
22
  const tree = {};
20
23
  Object.entries(resources).forEach(([path, value]) => {
@@ -0,0 +1,77 @@
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 } from '../../features/theme/actions/theme_actions_constants.js';
5
+ import { EXECUTION_CONTEXT_API_NAME, EXECUTION_CONTEXTS } 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 { UnpermittedCommandError } from '../ui/unpermitted_command_error.js';
10
+ import { Error } from '../../../ui/message_box/error.js';
11
+ import { Text } from '../../../ui/text.js';
12
+ import { MissingThemeIdError } from '../ui/missing_theme_id_error.js';
13
+ import { THEME_SKINSTORE_API_NAME } from '../../features/theme/skinstore/theme_publish_constants.js';
14
+ export class ThemePublishCommand extends BaseThemeCommand {
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).';
17
+ static examples = [
18
+ {
19
+ description: 'This will delete the theme with ID 123 permanently from your store. Make sure you have a backup if needed.',
20
+ command: '<%= config.bin %> <%= command.id %> 123'
21
+ }
22
+ ];
23
+ static hidden = true;
24
+ static args = {
25
+ id: Args.string({
26
+ description: 'Theme id',
27
+ name: 'id',
28
+ required: false,
29
+ type: 'string'
30
+ })
31
+ };
32
+ async run() {
33
+ const data = await this.parse(ThemePublishCommand);
34
+ const { args } = data;
35
+ const themeId = args.id;
36
+ const cliAuthApi = this.getApi(CLI_AUTH_API_NAME);
37
+ const executionContextApi = this.getApi(EXECUTION_CONTEXT_API_NAME);
38
+ const credentials = cliAuthApi.getCredentials();
39
+ if (!credentials) {
40
+ renderOnce(React.createElement(MissingCredentialsError, null));
41
+ return;
42
+ }
43
+ const executionContext = await executionContextApi.getExecutionContext();
44
+ try {
45
+ let _themeId = themeId;
46
+ if (executionContext.type !== EXECUTION_CONTEXTS.theme && !_themeId) {
47
+ return;
48
+ }
49
+ if (executionContext.type === EXECUTION_CONTEXTS.theme) {
50
+ _themeId = _themeId ?? executionContext.themeId;
51
+ }
52
+ if (!_themeId) {
53
+ renderOnce(React.createElement(MissingThemeIdError, null));
54
+ return;
55
+ }
56
+ const themeSkinstoreApi = this.getApi(THEME_SKINSTORE_API_NAME);
57
+ const formData = await themeSkinstoreApi.getPublishFormData();
58
+ console.log('formData', formData);
59
+ }
60
+ catch (err) {
61
+ this._handleError(err, themeId);
62
+ return;
63
+ }
64
+ }
65
+ _handleError(err, themeId) {
66
+ if (err?.code === THEME_ACTION_NOT_FOUND_ERROR_CODE && themeId) {
67
+ renderOnce(React.createElement(UnpermittedCommandError, { themeId: themeId, commandName: "delete" }));
68
+ return;
69
+ }
70
+ if (err?.message) {
71
+ renderOnce(React.createElement(Error, null,
72
+ React.createElement(Text, null, err.message)));
73
+ return;
74
+ }
75
+ this.error(String(err));
76
+ }
77
+ }
@@ -171,11 +171,13 @@ export class ThemePullCommand extends BaseThemeCommand {
171
171
  dist: tmpDir
172
172
  }
173
173
  });
174
- const themeModulesPath = join(executionContext.themeRootDir, 'modules');
175
- if (await directoryExists(themeModulesPath))
176
- await ThemeResourcesWithIdDirectoryUtils.updateDirectoryNamesOfResourcesWithIdAccordingToLocalThemeNames(themeModulesPath, join(tmpDir, name, 'modules'), join(tmpDir, name));
174
+ const localModulesPath = join(executionContext.themeRootDir, 'modules');
175
+ const fetchedThemePath = join(tmpDir, name);
176
+ const fetchedModulesPath = join(fetchedThemePath, 'modules');
177
+ if ((await directoryExists(localModulesPath)) && (await directoryExists(fetchedModulesPath)))
178
+ await ThemeResourcesWithIdDirectoryUtils.updateDirectoryNamesOfResourcesWithIdAccordingToLocalThemeNames(localModulesPath, fetchedModulesPath, fetchedThemePath);
177
179
  this.#spinner.stop();
178
- const changes = await themeMergeApi.getChangesBetweenThemes(join(tmpDir, name), executionContext.themeRootDir);
180
+ const changes = await themeMergeApi.getChangesBetweenThemes(fetchedThemePath, executionContext.themeRootDir);
179
181
  const themeChecksums = new ThemeChecksums(executionContext.themeRootDir);
180
182
  if (changes.length && (await themeChecksums.hasThemeBeenModified())) {
181
183
  renderOnce(React.createElement(ThemePullUnpublishedChangesWarning, { changes: changes }));
@@ -6,5 +6,6 @@ export const THEME_COMMANDS_NAME = {
6
6
  showChanges: 'theme:show-changes',
7
7
  verify: 'theme:verify',
8
8
  info: 'theme:info',
9
- delete: 'theme:delete'
9
+ delete: 'theme:delete',
10
+ publish: 'theme:publish'
10
11
  };
@@ -1,6 +1,6 @@
1
1
  import { AsyncFeatureInitializer, FEATURE_CORES_TYPES } from '@dreamcommerce/star_core';
2
2
  import { THEMES_LIST_API_NAME } from '../../themes/list/themes_list_constants.js';
3
- import { JsonCache } from '../../../../cli/features/caches/json_cache/json_cache.js';
3
+ import { JsonCache } from '../../../../cli/class/caches/json_cache/json_cache.js';
4
4
  import { CLI_DATA_DIRECTORY_API_NAME } from '../../../../cli/features/data_directory/cli_data_directory_constants.js';
5
5
  import { THEME_ACTIONS_FEATURE_NAME, THEMES_ACTIONS_FILE_NAME } from './theme_actions_constants.js';
6
6
  import { ThemeActionsApi } from './api/theme_actions_api.js';
@@ -1,11 +1,21 @@
1
1
  import { THEME_ALL_ACTIONS_NAME } from './service/theme_actions_service_constants.js';
2
2
  import { toUnixPath } from '../../../../utils/path_utils.js';
3
- export const getFilesGlobsThatMatchesActionName = (actionType, actionValue, filesStructure) => {
4
- return Object.entries(filesStructure).reduce((acc, [filePath, fileStructureItem]) => {
5
- if (fileStructureItem._links?.[actionType] &&
6
- (fileStructureItem._links[actionType] === actionValue || actionValue === THEME_ALL_ACTIONS_NAME)) {
7
- return [...acc, toUnixPath(filePath)];
3
+ export class ThemeActionsUtils {
4
+ static getFilesGlobsThatMatchesActionName({ filesStructure, actionValue, actionType }) {
5
+ return Object.entries(filesStructure).reduce((acc, [filePath, fileStructureItem]) => {
6
+ if (fileStructureItem._links?.[actionType] && this._doesActionValueMatch(fileStructureItem._links[actionType], actionValue)) {
7
+ return [...acc, toUnixPath(filePath)];
8
+ }
9
+ return acc;
10
+ }, []);
11
+ }
12
+ static _doesActionValueMatch(currentActionValue, valuesToMatch) {
13
+ if (typeof valuesToMatch === 'string') {
14
+ return currentActionValue === valuesToMatch || valuesToMatch === THEME_ALL_ACTIONS_NAME;
8
15
  }
9
- return acc;
10
- }, []);
11
- };
16
+ if (Array.isArray(valuesToMatch)) {
17
+ return valuesToMatch.includes(currentActionValue) || valuesToMatch.includes(THEME_ALL_ACTIONS_NAME);
18
+ }
19
+ return false;
20
+ }
21
+ }
@@ -4,7 +4,7 @@ import { downloadFile } from '../../../../../utils/download_file/download_file_u
4
4
  import { extractZip } from '../../../../../utils/zip/extract_zip_utils.js';
5
5
  import { join } from '../../../../../utils/path_utils.js';
6
6
  import { SHOPER_THEME_METADATA_DIR } from '../../../../constants/directory_contstants.js';
7
- import { getResources, mapResourcesToTree } from '../../../../class/fetch_resources/fetch_resources_utils.js';
7
+ import { getResources, mapResourcesToTree, removeOldResources } from '../../../../class/fetch_resources/fetch_resources_utils.js';
8
8
  import { FetchResources } from '../../../../class/fetch_resources/fetch_resources.js';
9
9
  import { jsonIndentTransform } from '../../../../../utils/stream_transforms/json_indent_transform.js';
10
10
  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';
@@ -15,7 +15,7 @@ import { AppError } from '../../../../../cli/class/errors/app/app_error.js';
15
15
  import { THEME_ACTIONS_TYPES } from '../../actions/theme_actions_constants.js';
16
16
  import { ThemeFilesStructureUtils } from '../../utils/files_structure/theme_files_structure_utils.js';
17
17
  import { ThemeMetaDataUtils } from '../../utils/meta_data/theme_meta_data_utils.js';
18
- import { ThemeChecksums } from '../../../../../theme/class/checksums/theme_checksums.js';
18
+ import { ThemeChecksums } from '../../../../class/checksums/theme_checksums.js';
19
19
  export class ThemeFetchService {
20
20
  #themeHttpApi;
21
21
  #httpApi;
@@ -107,6 +107,7 @@ export class ThemeFetchService {
107
107
  });
108
108
  }
109
109
  async fetchResources(shopUrl, dist, resources) {
110
+ await removeOldResources(dist, resources);
110
111
  const resourcesTree = mapResourcesToTree(resources);
111
112
  await Promise.all(Object.keys(resourcesTree).map((resource) => new FetchResources(this.#httpApi).fetchResources({
112
113
  resourcesPart: resourcesTree[resource],
@@ -1,8 +1,10 @@
1
1
  import FSTree from 'fs-tree-diff';
2
2
  import { copyFileSync, getAllDirectoriesNamesInside } from '../../../../../utils/fs/fs_utils.js';
3
3
  import walkSync from 'walk-sync';
4
- import { SHOPER_THEME_METADATA_DIR } from '../../../../constants/directory_contstants.js';
5
4
  import { ThemeChecksums } from '../../../../class/checksums/theme_checksums.js';
5
+ import { ThemeFilesStructureUtils } from '../../utils/files_structure/theme_files_structure_utils.js';
6
+ import { SHOPER_THEME_METADATA_DIR } from '../../../../constants/directory_contstants.js';
7
+ import { join, platformSeparator } from '../../../../../utils/path_utils.js';
6
8
  export class ThemeMergeService {
7
9
  async applyChanges(fromTheme, toTheme, changes) {
8
10
  FSTree.applyPatch(fromTheme, toTheme, changes, {
@@ -24,10 +26,7 @@ export class ThemeMergeService {
24
26
  });
25
27
  const theme1Checksums = new ThemeChecksums(theme1);
26
28
  const theme2Checksums = new ThemeChecksums(theme2);
27
- const rootDirectories = await getAllDirectoriesNamesInside(theme1, {
28
- recursive: false,
29
- hidden: true
30
- });
29
+ const userDirectories = await this._getUserRootDirectories(theme2);
31
30
  return theme2Tree
32
31
  .calculatePatch(theme1Tree, (entryA, entryB) => {
33
32
  if (entryA.isDirectory() && entryB.isDirectory())
@@ -36,7 +35,14 @@ export class ThemeMergeService {
36
35
  theme2Checksums.getCurrentChecksumFromPathSync(entryB.relativePath));
37
36
  })
38
37
  .filter(([_, name]) => {
39
- return !name.startsWith(SHOPER_THEME_METADATA_DIR) && rootDirectories.some((rootDir) => name.startsWith(rootDir));
38
+ return !name.startsWith(SHOPER_THEME_METADATA_DIR) && !userDirectories.some((userDir) => name.startsWith(userDir));
40
39
  });
41
40
  }
41
+ async _getUserRootDirectories(themeDir) {
42
+ const filesStructure = await ThemeFilesStructureUtils.getThemeRootDirectories(themeDir);
43
+ return (await getAllDirectoriesNamesInside(themeDir, {
44
+ recursive: false,
45
+ hidden: true
46
+ })).filter((directory) => !filesStructure.includes(join(directory, platformSeparator)));
47
+ }
42
48
  }
@@ -6,7 +6,7 @@ import { createReadStream } from 'node:fs';
6
6
  import { v4 as uuid } from 'uuid';
7
7
  import globs from 'fast-glob';
8
8
  import { THEME_WILDCARD_ACTION_NAME } from '../../actions/service/theme_actions_service_constants.js';
9
- import { getFilesGlobsThatMatchesActionName } from '../../actions/theme_actions_utils.js';
9
+ import { ThemeActionsUtils } from '../../actions/theme_actions_utils.js';
10
10
  import { THEME_FILES_LIST_FILE_NAME, THEME_MODULE_SETTINGS_FILE_NAME } from '../theme_push_constants.js';
11
11
  import { THEME_PUSH_WILDCARD_GLOBS_FOR_FILES } from './theme_push_service_constants.js';
12
12
  import { ThemePushErrorsFactory } from '../theme_push_errors_factory.js';
@@ -15,6 +15,7 @@ import { ThemePublishUtils } from '../../skinstore/theme_publish_utils.js';
15
15
  import { formatJSONFile, removeFile, writeJSONFile } from '../../../../../utils/fs/fs_utils.js';
16
16
  import { MODULES_DIRECTORY_NAME } from '../../theme_constants.js';
17
17
  import path from 'node:path';
18
+ import { removeOldResources } from '../../../../class/fetch_resources/fetch_resources_utils.js';
18
19
  export class ThemePushService {
19
20
  #themePushHttpApi;
20
21
  #themeFetchApi;
@@ -47,7 +48,11 @@ export class ThemePushService {
47
48
  try {
48
49
  await this._createThemeArchive({
49
50
  themeRootDir: executionContext.themeRootDir,
50
- filesToArchive: getFilesGlobsThatMatchesActionName(THEME_ACTIONS_TYPES.push, THEME_WILDCARD_ACTION_NAME, filesStructure),
51
+ filesToArchive: ThemeActionsUtils.getFilesGlobsThatMatchesActionName({
52
+ actionType: THEME_ACTIONS_TYPES.push,
53
+ actionValue: THEME_WILDCARD_ACTION_NAME,
54
+ filesStructure
55
+ }),
51
56
  dist: themeArchivePath
52
57
  });
53
58
  }
@@ -61,8 +66,10 @@ export class ThemePushService {
61
66
  });
62
67
  if (modules)
63
68
  await this._updateDataForNewCreatedModules({ modules, themeRootDir: executionContext.themeRootDir });
64
- if (resources)
69
+ if (resources) {
70
+ await removeOldResources(executionContext.themeRootDir, resources);
65
71
  await this.#themeFetchApi.fetchResources(credentials.shopUrl, executionContext.themeRootDir, resources);
72
+ }
66
73
  await removeFile(join(executionContext.themeRootDir, THEME_FILES_LIST_FILE_NAME));
67
74
  await themeChecksums.updateAllChecksums();
68
75
  }
@@ -150,7 +157,11 @@ export class ThemePushService {
150
157
  const localFiles = {};
151
158
  for (const [actionKey, actionData] of Object.entries(pushAction.data)) {
152
159
  if (actionData.type === THEME_ACTION_DATA_TYPE.file) {
153
- const filesGlobs = getFilesGlobsThatMatchesActionName(THEME_ACTIONS_TYPES.push, actionKey, filesStructure);
160
+ const filesGlobs = ThemeActionsUtils.getFilesGlobsThatMatchesActionName({
161
+ actionType: THEME_ACTIONS_TYPES.push,
162
+ actionValue: actionKey,
163
+ filesStructure
164
+ });
154
165
  for (const fileGlob of filesGlobs) {
155
166
  const files = await globs(fileGlob, {
156
167
  suppressErrors: true,
@@ -1,12 +1,14 @@
1
1
  import { FeatureApi } from '@dreamcommerce/star_core';
2
+ import { THEME_SKINSTORE_API_NAME } from '../theme_publish_constants.js';
2
3
  export class ThemeSkinstoreApi extends FeatureApi {
4
+ moduleName = THEME_SKINSTORE_API_NAME;
3
5
  #service;
4
6
  constructor(service) {
5
7
  super();
6
8
  this.#service = service;
7
9
  }
8
10
  async getPublishFormData() {
9
- return this.#service.getPublishFormData();
11
+ // return this.#service.getPublishFormData();
10
12
  }
11
13
  async publish(themeId) {
12
14
  // return this.#service.publish(themeId);
@@ -3,5 +3,15 @@ export class ThemeSkinstoreHttpApi {
3
3
  constructor(httpApi) {
4
4
  this.#httpApi = httpApi;
5
5
  }
6
- async getPublishFormData() { }
6
+ getPublishFormData({ actionData, shopUrl }) {
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
+ }
7
17
  }
@@ -1,3 +1,31 @@
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';
1
4
  export class ThemeSkinstoreService {
2
- async getPublishFormData() { }
5
+ #httpApi;
6
+ constructor(httpApi) {
7
+ this.#httpApi = httpApi;
8
+ }
9
+ async getPublishFormData({ credentials, actionData }) {
10
+ try {
11
+ const { response: request } = this.#httpApi.getPublishFormData({ actionData, shopUrl: credentials.shopUrl });
12
+ const response = await request;
13
+ if (response?.status !== STATUS_CODES.ok)
14
+ return;
15
+ return response?.data;
16
+ }
17
+ catch (err) {
18
+ //TODO to basic class
19
+ switch (err.response?.status) {
20
+ case 403:
21
+ throw HttpErrorsFactory.createForbiddenError();
22
+ case 401:
23
+ throw HttpErrorsFactory.createUnauthorizedError();
24
+ case 404:
25
+ throw HttpErrorsFactory.createNotFoundError();
26
+ default:
27
+ throw DownloadFileErrorsFactory.downloadError(err.response.status);
28
+ }
29
+ }
30
+ }
3
31
  }
@@ -1,2 +1,4 @@
1
1
  export const THEME_SKINSTORE_LOCATION = 'skinstore';
2
2
  export const THEME_SKINSTORE_SETTINGS_FILE_NAME = 'settings.json';
3
+ export const THEME_SKINSTORE_API_NAME = 'ThemeSkinstoreApi';
4
+ export const THEME_SKINSTORE_FEATURE_NAME = 'ThemeSkinstore';
@@ -0,0 +1,20 @@
1
+ import { FEATURE_CORES_TYPES, HTTP_REQUESTER_API_NAME, SyncFeatureInitializer } from '@dreamcommerce/star_core';
2
+ import { THEME_SKINSTORE_FEATURE_NAME } from './theme_publish_constants.js';
3
+ import { ThemeSkinstoreService } from './service/theme_skinstore_service.js';
4
+ import { ThemeSkinstoreHttpApi } from './http/theme_skinstore_http_api.js';
5
+ import { ThemeSkinstoreApi } from './api/theme_skinstore_api.js';
6
+ export class ThemeSkinstoreInitializer extends SyncFeatureInitializer {
7
+ static featureName = THEME_SKINSTORE_FEATURE_NAME;
8
+ init() {
9
+ const httpApi = this.getApiSync(HTTP_REQUESTER_API_NAME);
10
+ const service = new ThemeSkinstoreService(new ThemeSkinstoreHttpApi(httpApi));
11
+ return {
12
+ cores: [
13
+ {
14
+ type: FEATURE_CORES_TYPES.api,
15
+ instance: new ThemeSkinstoreApi(service)
16
+ }
17
+ ]
18
+ };
19
+ }
20
+ }
@@ -1,5 +1,5 @@
1
1
  import { readJSONFile, writeJSONFile } from '../../../../../utils/fs/fs_utils.js';
2
- import { join, mapKeysPathsToWindowPlatform } from '../../../../../utils/path_utils.js';
2
+ import { join, looksLikeDirectory, mapKeysPathsToWindowPlatform } from '../../../../../utils/path_utils.js';
3
3
  import { SHOPER_THEME_METADATA_DIR } from '../../../../constants/directory_contstants.js';
4
4
  import { THEME_FILES_STRUCTURE_FILE_NAME } from '../../theme_constants.js';
5
5
  import { isWindowsOs } from '../../../../../utils/platform_utils.js';
@@ -31,4 +31,10 @@ export class ThemeFilesStructureUtils {
31
31
  rootDirectory
32
32
  });
33
33
  }
34
+ static async getThemeRootDirectories(themeDirectory) {
35
+ const filesStructure = await ThemeFilesStructureUtils.getThemeFilesStructure(themeDirectory);
36
+ return Object.keys(filesStructure).filter((path) => {
37
+ return looksLikeDirectory(path);
38
+ });
39
+ }
34
40
  }
@@ -3,7 +3,7 @@ import walkSync from 'walk-sync';
3
3
  import { THEME_FILES_OPERATIONS } from '../../merge/theme_merge_constants.js';
4
4
  import { fileExists, readJSONFile, renameFile } from '../../../../../utils/fs/fs_utils.js';
5
5
  import { join } from '../../../../../utils/path_utils.js';
6
- import { ThemeChecksums } from '../../../../../theme/class/checksums/theme_checksums.js';
6
+ import { ThemeChecksums } from '../../../../class/checksums/theme_checksums.js';
7
7
  export class ThemeResourcesWithIdDirectoryUtils {
8
8
  static async updateDirectoryNamesOfResourcesWithIdAccordingToLocalThemeNames(localResourcesDir, remoteResourcesDir, remoteThemeDir) {
9
9
  const localThemeTree = new FSTree({
@@ -21,7 +21,7 @@ export class ThemeImagesUtils {
21
21
  }
22
22
  static async removeUploadedOriginalFiles(themeRootDir, uploadedImagesData) {
23
23
  await Promise.all(uploadedImagesData
24
- .filter(({ originalFilename }) => Boolean(originalFilename))
24
+ .filter(({ originalFilename, uploadedFilename }) => Boolean(originalFilename) && Boolean(uploadedFilename))
25
25
  .map(({ originalFilename, location }) => {
26
26
  const originalFilePath = join(themeRootDir, location, originalFilename);
27
27
  return removeFile(originalFilePath);
@@ -7,6 +7,7 @@ import { ThemeShowChangesCommand } from './commands/theme_show_changes_command.j
7
7
  import { ThemeVerifyCommand } from './commands/theme_verify_command.js';
8
8
  import { ThemeInfoCommand } from './commands/info/theme_info_command.js';
9
9
  import { ThemeDeleteCommand } from './commands/delete/theme_delete_command.js';
10
+ import { ThemePublishCommand } from './commands/publish/theme_publish_command.js';
10
11
  export const COMMANDS = {
11
12
  [THEME_COMMANDS_NAME.list]: ThemeListCommand,
12
13
  [THEME_COMMANDS_NAME.pull]: ThemePullCommand,
@@ -15,5 +16,6 @@ export const COMMANDS = {
15
16
  [THEME_COMMANDS_NAME.showChanges]: ThemeShowChangesCommand,
16
17
  [THEME_COMMANDS_NAME.verify]: ThemeVerifyCommand,
17
18
  [THEME_COMMANDS_NAME.info]: ThemeInfoCommand,
18
- [THEME_COMMANDS_NAME.delete]: ThemeDeleteCommand
19
+ [THEME_COMMANDS_NAME.delete]: ThemeDeleteCommand,
20
+ [THEME_COMMANDS_NAME.publish]: ThemePublishCommand
19
21
  };
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-32",
5
+ "version": "0.1.0-34",
6
6
  "description": "CLI tool for Shoper",
7
7
  "author": "Joanna Firek",
8
8
  "license": "MIT",
@@ -30,7 +30,7 @@
30
30
  "deploy:beta": "npm run build && npm version prerelease --no-git-tag-version && npm publish --tag beta --access public"
31
31
  },
32
32
  "dependencies": {
33
- "@dreamcommerce/star_core": "1.8.2-3",
33
+ "@dreamcommerce/star_core": "1.8.5",
34
34
  "@oclif/core": "4.2.10",
35
35
  "@oclif/plugin-autocomplete": "3.2.27",
36
36
  "@oclif/plugin-help": "6.2.27",
@@ -1,39 +0,0 @@
1
- export const toTextControl = ({ type, validate, label, isRequired, defaultValue, name }) => {
2
- return {
3
- type: 'input',
4
- name,
5
- message: isRequired ? `${label} (required)` : label,
6
- default: defaultValue,
7
- validate
8
- };
9
- };
10
- export const toNumberControl = ({ type, validate, label, isRequired, defaultValue, name }) => {
11
- return {
12
- type: 'number',
13
- name,
14
- message: isRequired ? `${label} (required)` : label,
15
- default: defaultValue,
16
- validate
17
- };
18
- };
19
- export const toSelectControl = ({ type, validate, label, isRequired, defaultValue, name, choices }) => {
20
- return {
21
- type: 'list',
22
- name,
23
- message: isRequired ? `${label} (required)` : label,
24
- choices,
25
- default: defaultValue,
26
- validate
27
- };
28
- };
29
- export const toMultiSelectControl = ({ type, validate, label, isRequired, defaultValue, name, choices }) => {
30
- //TODO multiple checkboxes
31
- return {
32
- type: 'checkbox',
33
- name,
34
- message: isRequired ? `${label} (required)` : label,
35
- choices,
36
- default: defaultValue,
37
- validate
38
- };
39
- };