@shoper/cli 0.1.0-7 → 0.1.0-9

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 (132) hide show
  1. package/build/cli/auth/api/cli_auth_api.js +15 -2
  2. package/build/cli/auth/cli_auth_constants.js +6 -0
  3. package/build/cli/auth/cli_auth_errors_factory.js +10 -0
  4. package/build/cli/auth/cli_auth_initializer.js +19 -0
  5. package/build/cli/auth/cli_auth_utils.js +22 -0
  6. package/build/cli/auth/model/cli_credentials.js +10 -0
  7. package/build/cli/auth/service/cli_auth_service.js +34 -0
  8. package/build/cli/auth/tokens/api/cli_auth_tokens_api.js +43 -1
  9. package/build/cli/auth/tokens/cli_auth_tokens_constants.js +6 -0
  10. package/build/cli/auth/tokens/cli_auth_tokens_errors_factory.js +10 -0
  11. package/build/cli/auth/tokens/cli_auth_tokens_initalizer.js +35 -1
  12. package/build/cli/auth/tokens/cli_auth_tokens_utils.js +26 -0
  13. package/build/cli/auth/tokens/service/cli_auth_tokens_service.js +84 -4
  14. package/build/cli/auth/tokens/service/cli_auth_tokens_service_constants.js +2 -0
  15. package/build/cli/class/base_cli_command.js +3 -0
  16. package/build/cli/class/errors/app_error/app_error.js +17 -0
  17. package/build/cli/class/errors/app_error/app_error_constants.js +7 -0
  18. package/build/cli/class/errors/file_system_errors_factory.js +26 -0
  19. package/build/cli/class/errors/http_errors_factory.js +21 -0
  20. package/build/cli/cli_constants.js +1 -0
  21. package/build/cli/commands/auth/cli_auth_add_token_command.js +32 -0
  22. package/build/cli/commands/auth/cli_auth_list_tokens_command.js +17 -0
  23. package/build/cli/commands/auth/cli_auth_remove_token_command.js +37 -0
  24. package/build/cli/commands/auth/cli_auth_switch_token_command.js +36 -0
  25. package/build/cli/commands/cli_update_command.js +7 -3
  26. package/build/cli/commands/commands_constants.js +5 -1
  27. package/build/cli/commands/files_diff_command.js +174 -0
  28. package/build/cli/commands/utils/prompt_for_token_utils.js +25 -0
  29. package/build/cli/core/cli_setup.js +20 -7
  30. package/build/cli/features/caches/json_cache/json_cache.js +50 -0
  31. package/build/cli/features/caches/memory_cache.js +6 -0
  32. package/build/cli/features/data_directory/cli_data_directory_constants.js +0 -1
  33. package/build/cli/features/data_directory/cli_data_directory_utils.js +3 -4
  34. package/build/cli/features/data_directory/service/cli_data_directory_service.js +2 -2
  35. package/build/cli/features/execution_context/execution_context_service.js +3 -5
  36. package/build/cli/features/http_requester/http_client.js +32 -1
  37. package/build/cli/features/version/service/cli_version_service.js +2 -4
  38. package/build/cli/hooks/authorization/ensure_authorization_hook.js +11 -6
  39. package/build/cli/hooks/authorization/ensure_authorization_hook_constants.js +8 -1
  40. package/build/index.js +34 -1
  41. package/build/theme/class/fetch_resources/fetch_resources.js +127 -0
  42. package/build/theme/class/fetch_resources/fetch_resources_constants.js +2 -0
  43. package/build/theme/class/fetch_resources/fetch_resources_errors_factory.js +11 -0
  44. package/build/theme/class/fetch_resources/fetch_resources_utils.js +35 -0
  45. package/build/theme/commands/theme_commands_constants.js +8 -0
  46. package/build/theme/commands/theme_init_command.js +53 -0
  47. package/build/theme/commands/theme_list_command.js +16 -0
  48. package/build/theme/commands/theme_pull_command.js +126 -0
  49. package/build/theme/commands/theme_push_command.js +65 -0
  50. package/build/theme/commands/theme_show_changes_command.js +60 -0
  51. package/build/theme/commands/theme_verify_command.js +43 -0
  52. package/build/theme/features/theme/actions/api/theme_actions_api.js +19 -0
  53. package/build/theme/features/theme/actions/service/theme_actions_service.js +92 -0
  54. package/build/theme/features/theme/actions/service/theme_actions_service_constants.js +2 -0
  55. package/build/theme/features/theme/actions/theme_actions_constants.js +15 -0
  56. package/build/theme/features/theme/actions/theme_actions_initializer.js +27 -0
  57. package/build/theme/features/theme/actions/theme_actions_utils.js +8 -0
  58. package/build/theme/features/theme/directory/theme_directory_utils.js +61 -14
  59. package/build/theme/features/theme/fetch/api/theme_fetch_api.js +16 -0
  60. package/build/theme/features/theme/fetch/http/theme_fetch_http_api.js +18 -0
  61. package/build/theme/features/theme/fetch/service/theme_fetch_service.js +113 -0
  62. package/build/theme/features/theme/fetch/theme_fetch_constants.js +7 -0
  63. package/build/theme/features/theme/fetch/theme_fetch_initializer.js +23 -0
  64. package/build/theme/features/theme/init/api/theme_init_api.js +13 -0
  65. package/build/theme/features/theme/init/http/theme_init_http_api.js +18 -0
  66. package/build/theme/features/theme/init/service/theme_init_service.js +28 -0
  67. package/build/theme/features/theme/init/theme_init_constants.js +2 -0
  68. package/build/theme/features/theme/init/theme_init_initializer.js +22 -0
  69. package/build/theme/features/theme/merge/api/theme_merge_api.js +28 -0
  70. package/build/theme/features/theme/merge/service/theme_merge_service.js +56 -0
  71. package/build/theme/features/theme/merge/theme_merge_constants.js +9 -0
  72. package/build/theme/features/theme/merge/theme_merge_initializer.js +18 -0
  73. package/build/theme/features/theme/publish/theme_publish_constants.js +2 -0
  74. package/build/theme/features/theme/publish/theme_publish_utils.js +7 -0
  75. package/build/theme/features/theme/push/api/theme_push_api.js +13 -0
  76. package/build/theme/features/theme/push/http_api/theme_push_http_api.js +21 -0
  77. package/build/theme/features/theme/push/service/theme_push_service.js +200 -0
  78. package/build/theme/features/theme/push/service/theme_push_service_constants.js +1 -0
  79. package/build/theme/features/theme/push/theme_push_constants.js +4 -0
  80. package/build/theme/features/theme/push/theme_push_errors_factory.js +41 -0
  81. package/build/theme/features/theme/push/theme_push_initializer.js +27 -0
  82. package/build/theme/features/theme/theme_constants.js +6 -0
  83. package/build/theme/features/theme/utils/checksums/theme_checksums_error_factory.js +10 -0
  84. package/build/theme/features/theme/utils/checksums/theme_checksums_utils.js +94 -0
  85. package/build/theme/features/theme/utils/directories/theme_resources_with_id_directory_utils.js +80 -0
  86. package/build/theme/features/theme/utils/theme_images_utils.js +30 -0
  87. package/build/theme/features/themes/list/api/themes_list_api.js +13 -0
  88. package/build/theme/features/themes/{http/shoper_themes_http_api.js → list/http/themes_list_http_api.js} +3 -2
  89. package/build/theme/features/themes/{model/theme_metadata.js → list/model/theme_list_metadata.js} +3 -1
  90. package/build/theme/features/themes/list/services/themes_list_service.js +37 -0
  91. package/build/theme/features/themes/list/themes_list_constants.js +2 -0
  92. package/build/theme/features/themes/list/themes_list_initializer.js +20 -0
  93. package/build/theme/hooks/ensure_theme_meta_data_untouched.js +17 -0
  94. package/build/theme/hooks/theme_checksums/ensure_theme_current_checksums_up_to_date.js +21 -0
  95. package/build/theme/hooks/theme_checksums/ensure_theme_current_checksums_up_to_date_constants.js +7 -0
  96. package/build/theme/hooks/themes_actions/ensure_themes_actions_hook.js +45 -0
  97. package/build/theme/hooks/themes_actions/ensure_themes_actions_hook_constants.js +7 -0
  98. package/build/theme/index.js +13 -4
  99. package/build/theme/utils/directory_validator/directory_validator_constants.js +13 -0
  100. package/build/theme/utils/directory_validator/directory_validator_utils.js +185 -0
  101. package/build/utils/checksums/checksums_utils.js +95 -0
  102. package/build/utils/checksums/checksums_utils_constants.js +1 -0
  103. package/build/utils/date_utils.js +3 -0
  104. package/build/utils/download_file/download_file_errors_factory.js +9 -0
  105. package/build/utils/download_file/download_file_utils.js +46 -0
  106. package/build/utils/fs/errors/stream_read_error.js +11 -0
  107. package/build/utils/fs/errors/stream_write_error.js +11 -0
  108. package/build/utils/fs/fs_utils.js +155 -0
  109. package/build/utils/http_utils.js +10 -0
  110. package/build/utils/path_utils.js +37 -0
  111. package/build/utils/stream_transforms/json_indent_transform.js +21 -0
  112. package/build/utils/url_utils.js +9 -0
  113. package/build/utils/zip/create_zip_utils.js +80 -0
  114. package/build/utils/zip/errors/create_zip_error.js +11 -0
  115. package/build/utils/zip/errors/open_zip_error.js +11 -0
  116. package/build/utils/zip/extract_zip_utils.js +104 -0
  117. package/oclif.config.js +4 -2
  118. package/package.json +36 -7
  119. package/build/cli/hooks/migration/migration_hook.js +0 -4
  120. package/build/theme/commands/list_command.js +0 -12
  121. package/build/theme/commands/pull_command.js +0 -24
  122. package/build/theme/features/theme/directory/theme_directories_utils.js +0 -10
  123. package/build/theme/features/theme/pull/api/shoper_theme_pull_api.js +0 -9
  124. package/build/theme/features/theme/pull/http/shoper_theme_pull_http_api.js +0 -14
  125. package/build/theme/features/theme/pull/service/shoper_theme_pull_service.js +0 -68
  126. package/build/theme/features/theme/pull/shoper_theme_pull_initializer.js +0 -22
  127. package/build/theme/features/themes/api/shoper_themes_api.js +0 -14
  128. package/build/theme/features/themes/services/shoper_themes_service.js +0 -15
  129. package/build/theme/features/themes/shoper_themes_constants.js +0 -2
  130. package/build/theme/features/themes/shoper_themes_initalizer.js +0 -20
  131. package/build/utils/fs.js +0 -44
  132. package/build/utils/path.js +0 -13
@@ -1,6 +1,15 @@
1
- import { closestFile, fileExists, readFile } from '../../../../utils/fs.js';
1
+ import { closestFile, fileExists, getAllFilesNamesInside, readJSONFile, writeJSONFile } from '../../../../utils/fs/fs_utils.js';
2
2
  import { SHOPER_THEME_METADATA_DIR } from '../../../constants/directory_contstants.js';
3
- import { join } from '../../../../utils/path.js';
3
+ import { join } from '../../../../utils/path_utils.js';
4
+ import { validateDirectory } from '../../../utils/directory_validator/directory_validator_utils.js';
5
+ import { THEME_CURRENT_CHECKSUMS_FILE_NAME, THEME_CURRENT_CHECKSUMS_VERITY_FILE_NAME, THEME_FILES_STRUCTURE_FILE_NAME, THEME_INITIAL_CHECKSUMS_FILE_NAME, THEME_INITIAL_CHECKSUMS_VERITY_FILE_NAME } from '../theme_constants.js';
6
+ import { computeFileChecksum } from '../../../../utils/checksums/checksums_utils.js';
7
+ import { ThemeChecksumsUtils } from '../utils/checksums/theme_checksums_utils.js';
8
+ /**
9
+ * jezli modzisz lokalnie
10
+ * robisz pull, ale na remove nic ise nie zmienia
11
+ * tow nie pownno byc pytania
12
+ */
4
13
  export class ThemeDirectoryUtils {
5
14
  static async closestThemeRootDirectory(path) {
6
15
  return closestFile(SHOPER_THEME_METADATA_DIR, path);
@@ -8,22 +17,60 @@ export class ThemeDirectoryUtils {
8
17
  static async isThemeRootDirectory(path) {
9
18
  return fileExists(join(path, SHOPER_THEME_METADATA_DIR));
10
19
  }
11
- static getThemeMetaDataDir(themeDirectory) {
20
+ static getThemeMetaDataDirPath(themeDirectory) {
12
21
  return join(themeDirectory, SHOPER_THEME_METADATA_DIR);
13
22
  }
14
23
  static async getThemeDirectoryMetadata(themeDirectory) {
15
- const filePath = join(this.getThemeMetaDataDir(themeDirectory), 'metadata.json');
16
- const metadataFile = await readFile(filePath, {
17
- encoding: 'utf-8',
18
- flag: 'r'
19
- });
20
- if (typeof metadataFile !== 'string')
21
- throw 'Metadata file is not a string';
22
- const metadataFileContent = JSON.parse(metadataFile);
24
+ const filePath = join(this.getThemeMetaDataDirPath(themeDirectory), 'metadata.json');
25
+ const metadataFileContent = await readJSONFile(filePath);
23
26
  return {
24
- themeId: metadataFileContent.skin_id,
25
- //TODO
26
- shopUrl: 'https://shoper.docker.shoper.tech'
27
+ themeId: String(metadataFileContent.skin_id)
27
28
  };
28
29
  }
30
+ static async getThemeFilesStructure(themeDirectory) {
31
+ return await readJSONFile(join(themeDirectory, SHOPER_THEME_METADATA_DIR, THEME_FILES_STRUCTURE_FILE_NAME));
32
+ }
33
+ static async writeThemeFilesStructure(themeDirectory, filesStructure) {
34
+ const filePath = join(themeDirectory, SHOPER_THEME_METADATA_DIR, THEME_FILES_STRUCTURE_FILE_NAME);
35
+ await writeJSONFile(filePath, filesStructure);
36
+ }
37
+ static async getFilesPermissions(themeDirectory) {
38
+ //TODO convert paths sepearator to os
39
+ const fileStructure = await ThemeDirectoryUtils.getThemeFilesStructure(themeDirectory);
40
+ return Object.entries(fileStructure).reduce((acc, [key, value]) => {
41
+ return {
42
+ ...acc,
43
+ [key]: value.permissions
44
+ };
45
+ }, {});
46
+ }
47
+ static async validateThemeDirectoryStructure({ checksums, permissions, rootDirectory }) {
48
+ return await validateDirectory({
49
+ permissions,
50
+ checksums,
51
+ rootDirectory
52
+ });
53
+ }
54
+ static async ensureFilesInsideThemeMetaDataDirectoryUntouched(themeDirectory) {
55
+ const themeMetadataPath = this.getThemeMetaDataDirPath(themeDirectory);
56
+ const checksums = await ThemeChecksumsUtils.getThemeCurrentChecksums(themeDirectory);
57
+ if (!checksums)
58
+ throw new Error(`Checksums file not found in theme metadata directory: ${themeMetadataPath}`);
59
+ if (!(await ThemeChecksumsUtils.verifyThemeChecksums(themeDirectory)))
60
+ throw new Error('Theme checksum file is not valid');
61
+ const filesNames = (await getAllFilesNamesInside(themeMetadataPath)).filter((fileName) => fileName !== THEME_CURRENT_CHECKSUMS_FILE_NAME &&
62
+ fileName !== THEME_CURRENT_CHECKSUMS_VERITY_FILE_NAME &&
63
+ fileName !== THEME_INITIAL_CHECKSUMS_FILE_NAME &&
64
+ fileName !== THEME_INITIAL_CHECKSUMS_VERITY_FILE_NAME);
65
+ for (const fileName of filesNames) {
66
+ if (fileName === THEME_CURRENT_CHECKSUMS_FILE_NAME)
67
+ continue;
68
+ const fileFullPath = join(themeMetadataPath, fileName);
69
+ const fileRelativePath = join(SHOPER_THEME_METADATA_DIR, fileName);
70
+ const currentChecksum = await computeFileChecksum(fileFullPath);
71
+ if (currentChecksum !== checksums[fileRelativePath]) {
72
+ throw new Error(`File ${fileName} inside theme metadata directory (${themeMetadataPath}) has been modified. You cannot modify files inside .shoper directory`);
73
+ }
74
+ }
75
+ }
29
76
  }
@@ -0,0 +1,16 @@
1
+ import { FeatureApi } from '@dreamcommerce/star_core';
2
+ import { THEME_FETCH_API_NAME } from '../theme_fetch_constants.js';
3
+ export class ThemeFetchApi extends FeatureApi {
4
+ moduleName = THEME_FETCH_API_NAME;
5
+ #themeFetchService;
6
+ constructor(themeFetchService) {
7
+ super();
8
+ this.#themeFetchService = themeFetchService;
9
+ }
10
+ async fetchTheme(props) {
11
+ return this.#themeFetchService.fetchTheme(props);
12
+ }
13
+ async fetchResources(shopUrl, dist, resources) {
14
+ return this.#themeFetchService.fetchResources(shopUrl, dist, resources);
15
+ }
16
+ }
@@ -0,0 +1,18 @@
1
+ export class ThemeFetchHttpApi {
2
+ #httpApi;
3
+ constructor(httpApi) {
4
+ this.#httpApi = httpApi;
5
+ }
6
+ fetchTheme(shopUrl, { method, url }) {
7
+ return this.#httpApi.fetch({
8
+ url: `${shopUrl}${url}`,
9
+ method,
10
+ //@ts-ignore
11
+ responseType: 'stream',
12
+ sanitizeOptions: {
13
+ disable: true
14
+ },
15
+ isPrivate: true
16
+ });
17
+ }
18
+ }
@@ -0,0 +1,113 @@
1
+ import tmp from 'tmp-promise';
2
+ import { THEME_FETCH_TYPES } from '../theme_fetch_constants.js';
3
+ import { downloadFile } from '../../../../../utils/download_file/download_file_utils.js';
4
+ import { extractZip } from '../../../../../utils/zip/extract_zip_utils.js';
5
+ import { join } from '../../../../../utils/path_utils.js';
6
+ import { SHOPER_THEME_METADATA_DIR } from '../../../../constants/directory_contstants.js';
7
+ import { getResources, mapResourcesToTree } from '../../../../class/fetch_resources/fetch_resources_utils.js';
8
+ import { FetchResources } from '../../../../class/fetch_resources/fetch_resources.js';
9
+ import { jsonIndentTransform } from '../../../../../utils/stream_transforms/json_indent_transform.js';
10
+ import { ThemeDirectoryUtils } from '../../directory/theme_directory_utils.js';
11
+ 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';
12
+ import { THEME_FILES_LIST_FILE_NAME } from '../../push/theme_push_constants.js';
13
+ import { JSON_FILE_INDENT } from '../../../../../cli/cli_constants.js';
14
+ import { computeChecksumsFromSource } from '../../../../../utils/checksums/checksums_utils.js';
15
+ import { ThemeChecksumsUtils } from '../../utils/checksums/theme_checksums_utils.js';
16
+ import { createWriteStream } from 'node:fs';
17
+ import { AppError } from '../../../../../cli/class/errors/app_error/app_error.js';
18
+ export class ThemeFetchService {
19
+ #themeHttpApi;
20
+ #httpApi;
21
+ constructor({ themeHttpApi, httpApi }) {
22
+ this.#themeHttpApi = themeHttpApi;
23
+ this.#httpApi = httpApi;
24
+ }
25
+ async fetchTheme({ credentials, action, config }) {
26
+ const { dist } = config;
27
+ const { path: tmpDir } = await tmp.dir({ unsafeCleanup: true });
28
+ const fetchType = config?.fetchType ?? THEME_FETCH_TYPES.full;
29
+ const { shopUrl } = credentials;
30
+ //TODO think global mechanism to handle when user ctr+c during download
31
+ const { basename, filename } = await downloadFile({
32
+ dist: tmpDir,
33
+ request: this.#themeHttpApi.fetchTheme(shopUrl, action.data[fetchType]).response
34
+ });
35
+ const themeDir = join(dist, basename);
36
+ await extractZip({
37
+ source: join(tmpDir, filename),
38
+ dist: themeDir,
39
+ getTransforms: (fileName) => (fileName.endsWith('.json') ? [jsonIndentTransform(JSON_FILE_INDENT)] : [])
40
+ });
41
+ if (fetchType === THEME_FETCH_TYPES.full) {
42
+ const resources = await getResources(themeDir);
43
+ await this.fetchResources(shopUrl, themeDir, resources);
44
+ }
45
+ try {
46
+ await this._updateFilesStructure(themeDir);
47
+ }
48
+ catch (err) {
49
+ throw new AppError({
50
+ message: `Failed to update files structure in theme directory: ${themeDir}`,
51
+ code: 'theme_files_structure_update_error',
52
+ stack: err.stack
53
+ });
54
+ }
55
+ await this._createGitIgnoreFile(themeDir);
56
+ const checksums = await computeChecksumsFromSource(themeDir);
57
+ await ThemeChecksumsUtils.createThemeChecksumsFiles(themeDir, checksums);
58
+ return {
59
+ name: basename
60
+ };
61
+ }
62
+ async _updateFilesStructure(themeDir) {
63
+ const fileStructure = await ThemeDirectoryUtils.getThemeFilesStructure(themeDir);
64
+ const checksumsFiles = [
65
+ `${SHOPER_THEME_METADATA_DIR}/${THEME_INITIAL_CHECKSUMS_FILE_NAME}`,
66
+ `${SHOPER_THEME_METADATA_DIR}/${THEME_INITIAL_CHECKSUMS_VERITY_FILE_NAME}`,
67
+ `${SHOPER_THEME_METADATA_DIR}/${THEME_CURRENT_CHECKSUMS_FILE_NAME}`,
68
+ `${SHOPER_THEME_METADATA_DIR}/${THEME_CURRENT_CHECKSUMS_VERITY_FILE_NAME}`
69
+ ];
70
+ checksumsFiles.forEach((file) => {
71
+ fileStructure[file] = {
72
+ permissions: {
73
+ canAdd: true,
74
+ canEdit: true,
75
+ canDelete: false
76
+ }
77
+ };
78
+ });
79
+ [THEME_FILES_LIST_FILE_NAME, '.gitignore'].forEach((fileName) => {
80
+ fileStructure[fileName] = {
81
+ permissions: {
82
+ canAdd: true,
83
+ canEdit: true,
84
+ canDelete: true
85
+ }
86
+ };
87
+ });
88
+ await ThemeDirectoryUtils.writeThemeFilesStructure(themeDir, fileStructure);
89
+ }
90
+ async fetchResources(shopUrl, dist, resources) {
91
+ const resourcesTree = mapResourcesToTree(resources);
92
+ await Promise.all(Object.keys(resourcesTree).map((resource) => new FetchResources(this.#httpApi).fetchResources({
93
+ resourcesPart: resourcesTree[resource],
94
+ dist,
95
+ parts: [resource],
96
+ shopUrl
97
+ })));
98
+ }
99
+ _createGitIgnoreFile(themeDir) {
100
+ return new Promise((resolve, reject) => {
101
+ const gitIgnoreContent = `styles/src/`;
102
+ const writeStream = createWriteStream(join(themeDir, '.gitignore'));
103
+ writeStream.write(gitIgnoreContent);
104
+ writeStream.end();
105
+ writeStream.on('error', (err) => {
106
+ reject(err);
107
+ });
108
+ writeStream.on('finish', () => {
109
+ resolve();
110
+ });
111
+ });
112
+ }
113
+ }
@@ -0,0 +1,7 @@
1
+ export const THEME_FETCH_FEATURE_NAME = 'ThemeFetch';
2
+ export const THEME_FETCH_API_NAME = 'ThemeFetchApi';
3
+ export const THEME_FETCH_TYPES = {
4
+ full: 'full',
5
+ minimal: 'minimal'
6
+ };
7
+ export const SKINSTORE_RESOURCE_NAME = 'skinstore/';
@@ -0,0 +1,23 @@
1
+ import { ThemeFetchHttpApi } from './http/theme_fetch_http_api.js';
2
+ import { ThemeFetchApi } from './api/theme_fetch_api.js';
3
+ import { FEATURE_CORES_TYPES, HTTP_REQUESTER_API_NAME, SyncFeatureInitializer } from '@dreamcommerce/star_core';
4
+ import { THEME_FETCH_FEATURE_NAME } from './theme_fetch_constants.js';
5
+ import { ThemeFetchService } from './service/theme_fetch_service.js';
6
+ export class ThemeFetchInitializer extends SyncFeatureInitializer {
7
+ static featureName = THEME_FETCH_FEATURE_NAME;
8
+ init() {
9
+ const httpApi = this.getApiSync(HTTP_REQUESTER_API_NAME);
10
+ const themeFetchService = new ThemeFetchService({
11
+ themeHttpApi: new ThemeFetchHttpApi(httpApi),
12
+ httpApi
13
+ });
14
+ return {
15
+ cores: [
16
+ {
17
+ type: FEATURE_CORES_TYPES.api,
18
+ instance: new ThemeFetchApi(themeFetchService)
19
+ }
20
+ ]
21
+ };
22
+ }
23
+ }
@@ -0,0 +1,13 @@
1
+ import { FeatureApi } from '@dreamcommerce/star_core';
2
+ import { THEME_INIT_API_NAME } from '../theme_init_constants.js';
3
+ export class ThemeInitApi extends FeatureApi {
4
+ moduleName = THEME_INIT_API_NAME;
5
+ #service;
6
+ constructor(service) {
7
+ super();
8
+ this.#service = service;
9
+ }
10
+ async initTheme(props) {
11
+ return this.#service.initTheme(props);
12
+ }
13
+ }
@@ -0,0 +1,18 @@
1
+ export class ThemeInitHttpApi {
2
+ #httpApi;
3
+ constructor(httpApi) {
4
+ this.#httpApi = httpApi;
5
+ }
6
+ initTheme(shopUrl, { url, method }) {
7
+ return this.#httpApi.fetch({
8
+ url: `${shopUrl}${url}`,
9
+ method,
10
+ //@ts-ignore
11
+ responseType: 'stream',
12
+ sanitizeOptions: {
13
+ disable: true
14
+ },
15
+ isPrivate: true
16
+ });
17
+ }
18
+ }
@@ -0,0 +1,28 @@
1
+ import { join } from '../../../../../utils/path_utils.js';
2
+ import process from 'process';
3
+ import tmp from 'tmp-promise';
4
+ import { downloadFile } from '../../../../../utils/download_file/download_file_utils.js';
5
+ import { extractZip } from '../../../../../utils/zip/extract_zip_utils.js';
6
+ import { ThemeChecksumsUtils } from '../../utils/checksums/theme_checksums_utils.js';
7
+ import { computeChecksumsFromSource } from '../../../../../utils/checksums/checksums_utils.js';
8
+ export class ThemeInitService {
9
+ #httpApi;
10
+ constructor({ httpApi }) {
11
+ this.#httpApi = httpApi;
12
+ }
13
+ async initTheme({ action, credentials }) {
14
+ const { path: tmpDir } = await tmp.dir({ unsafeCleanup: true });
15
+ //TODO think global mechanism to handle when user ctr+c during download
16
+ const { basename, filename } = await downloadFile({
17
+ dist: tmpDir,
18
+ request: this.#httpApi.initTheme(credentials.shopUrl, action.data).response
19
+ });
20
+ const distDir = join(process.cwd(), basename);
21
+ await extractZip({
22
+ source: join(tmpDir, filename),
23
+ dist: distDir
24
+ });
25
+ const checksums = await computeChecksumsFromSource(distDir);
26
+ await ThemeChecksumsUtils.createThemeChecksumsFiles(join(process.cwd(), basename), checksums);
27
+ }
28
+ }
@@ -0,0 +1,2 @@
1
+ export const THEME_INIT_FEATURE_NAME = 'ThemeInit';
2
+ export const THEME_INIT_API_NAME = 'ThemeInitApi';
@@ -0,0 +1,22 @@
1
+ import { AsyncFeatureInitializer, FEATURE_CORES_TYPES, HTTP_REQUESTER_API_NAME } from '@dreamcommerce/star_core';
2
+ import { ThemeInitHttpApi } from './http/theme_init_http_api.js';
3
+ import { ThemeInitApi } from './api/theme_init_api.js';
4
+ import { ThemeInitService } from './service/theme_init_service.js';
5
+ import { THEME_INIT_FEATURE_NAME } from './theme_init_constants.js';
6
+ export class ThemeInitInitializer extends AsyncFeatureInitializer {
7
+ static featureName = THEME_INIT_FEATURE_NAME;
8
+ async init() {
9
+ const httpApi = this.getApiSync(HTTP_REQUESTER_API_NAME);
10
+ const service = new ThemeInitService({
11
+ httpApi: new ThemeInitHttpApi(httpApi)
12
+ });
13
+ return {
14
+ cores: [
15
+ {
16
+ type: FEATURE_CORES_TYPES.api,
17
+ instance: new ThemeInitApi(service)
18
+ }
19
+ ]
20
+ };
21
+ }
22
+ }
@@ -0,0 +1,28 @@
1
+ import { THEME_MERGE_API_NAME } from '../theme_merge_constants.js';
2
+ import { FeatureApi } from '@dreamcommerce/star_core';
3
+ export class ThemeMergeApi extends FeatureApi {
4
+ moduleName = THEME_MERGE_API_NAME;
5
+ #service;
6
+ constructor(service) {
7
+ super();
8
+ this.#service = service;
9
+ }
10
+ hasThemeBeenModified(themeRootDir) {
11
+ return this.#service.hasThemeBeenModified(themeRootDir);
12
+ }
13
+ getChangesBetweenThemes(theme1, theme2) {
14
+ return this.#service.getChangesBetweenThemes(theme1, theme2);
15
+ }
16
+ applyChanges(fromTheme, toTheme, changes) {
17
+ return this.#service.applyChanges(fromTheme, toTheme, changes);
18
+ }
19
+ hasFileBeenCreated(path, executionContext) {
20
+ return this.#service.hasFileBeenCreated(path, executionContext);
21
+ }
22
+ hasFileBeenRemoved(path, executionContext) {
23
+ return this.#service.hasFileBeenRemoved(path, executionContext);
24
+ }
25
+ hasFileBeenModified(path, executionContext) {
26
+ return this.#service.hasFileBeenModified(path, executionContext);
27
+ }
28
+ }
@@ -0,0 +1,56 @@
1
+ import FSTree from 'fs-tree-diff';
2
+ import { join } from '../../../../../utils/path_utils.js';
3
+ import { computeChecksumsFromSource, computeFileChecksum } from '../../../../../utils/checksums/checksums_utils.js';
4
+ import { copyFile, copyFileSync, fileExists } from '../../../../../utils/fs/fs_utils.js';
5
+ import { ThemeChecksumsUtils } from '../../utils/checksums/theme_checksums_utils.js';
6
+ import walkSync from 'walk-sync';
7
+ import { SHOPER_THEME_METADATA_DIR } from '../../../../constants/directory_contstants.js';
8
+ export class ThemeMergeService {
9
+ async applyChanges(fromTheme, toTheme, changes) {
10
+ FSTree.applyPatch(fromTheme, toTheme, changes, {
11
+ create(inputPath, outputPath) {
12
+ copyFile(inputPath, outputPath);
13
+ },
14
+ change(inputPath, outputPath) {
15
+ copyFileSync(inputPath, outputPath);
16
+ }
17
+ });
18
+ const checksums = await computeChecksumsFromSource(toTheme);
19
+ await ThemeChecksumsUtils.createThemeChecksumsFiles(toTheme, checksums);
20
+ }
21
+ async getChangesBetweenThemes(theme1, theme2) {
22
+ const theme1Tree = new FSTree({
23
+ entries: walkSync.entries(theme1)
24
+ });
25
+ const theme2Tree = new FSTree({
26
+ entries: walkSync.entries(theme2)
27
+ });
28
+ const theme1Checksums = await ThemeChecksumsUtils.getThemeCurrentChecksums(theme1);
29
+ const theme2Checksums = await ThemeChecksumsUtils.getThemeCurrentChecksums(theme2);
30
+ return theme2Tree
31
+ .calculatePatch(theme1Tree, (entryA, entryB) => {
32
+ if (entryA.isDirectory() && entryB.isDirectory())
33
+ return true;
34
+ return theme1Checksums[entryA.relativePath] === theme2Checksums[entryB.relativePath];
35
+ })
36
+ .filter(([, name]) => !name.startsWith(SHOPER_THEME_METADATA_DIR));
37
+ }
38
+ async hasThemeBeenModified(themeRootDir) {
39
+ const initialChecksums = await ThemeChecksumsUtils.getInitialChecksumsVerification(themeRootDir);
40
+ const currentChecksums = await ThemeChecksumsUtils.getThemeCurrentChecksumsVerification(themeRootDir);
41
+ return initialChecksums !== currentChecksums;
42
+ }
43
+ async hasFileBeenCreated(path, executionContext) {
44
+ const checksums = await ThemeChecksumsUtils.getThemeCurrentChecksums(executionContext.themeRootDir);
45
+ return checksums[path] === undefined && (await fileExists(join(executionContext.themeRootDir, path)));
46
+ }
47
+ async hasFileBeenRemoved(path, executionContext) {
48
+ const checksums = await ThemeChecksumsUtils.getThemeCurrentChecksums(executionContext.themeRootDir);
49
+ return !(await fileExists(join(executionContext.themeRootDir, path))) && !!checksums[path];
50
+ }
51
+ async hasFileBeenModified(path, executionContext) {
52
+ const checksums = await ThemeChecksumsUtils.getThemeCurrentChecksums(executionContext.themeRootDir);
53
+ const currentChecksum = await computeFileChecksum(join(executionContext.themeRootDir, path));
54
+ return !!checksums[path] && checksums[path] !== currentChecksum;
55
+ }
56
+ }
@@ -0,0 +1,9 @@
1
+ export const THEME_MERGE_FEATURE_NAME = 'themeMerge';
2
+ export const THEME_MERGE_API_NAME = 'themeMergeApi';
3
+ export const THEME_FILES_OPERATIONS = {
4
+ unlink: 'unlink',
5
+ rmdir: 'rmdir',
6
+ mkdir: 'mkdir',
7
+ create: 'create',
8
+ change: 'change'
9
+ };
@@ -0,0 +1,18 @@
1
+ import { FEATURE_CORES_TYPES, SyncFeatureInitializer } from '@dreamcommerce/star_core';
2
+ import { ThemeMergeApi } from './api/theme_merge_api.js';
3
+ import { ThemeMergeService } from './service/theme_merge_service.js';
4
+ import { THEME_MERGE_FEATURE_NAME } from './theme_merge_constants.js';
5
+ export class ThemeMergeInitializer extends SyncFeatureInitializer {
6
+ static featureName = THEME_MERGE_FEATURE_NAME;
7
+ init() {
8
+ const themeMergeService = new ThemeMergeService();
9
+ return {
10
+ cores: [
11
+ {
12
+ type: FEATURE_CORES_TYPES.api,
13
+ instance: new ThemeMergeApi(themeMergeService)
14
+ }
15
+ ]
16
+ };
17
+ }
18
+ }
@@ -0,0 +1,2 @@
1
+ export const THEME_SKINSTORE_LOCATION = 'skinstore';
2
+ export const THEME_SKINSTORE_SETTINGS_FILE_NAME = 'settings.json';
@@ -0,0 +1,7 @@
1
+ import { join } from '../../../../utils/path_utils.js';
2
+ import { THEME_SKINSTORE_LOCATION, THEME_SKINSTORE_SETTINGS_FILE_NAME } from './theme_publish_constants.js';
3
+ export class ThemePublishUtils {
4
+ static getSkinStoreSettingsFilePath(themeRootDir) {
5
+ return join(themeRootDir, THEME_SKINSTORE_LOCATION, THEME_SKINSTORE_SETTINGS_FILE_NAME);
6
+ }
7
+ }
@@ -0,0 +1,13 @@
1
+ import { FeatureApi } from '@dreamcommerce/star_core';
2
+ import { THEME_PUSH_API_NAME } from '../theme_push_constants.js';
3
+ export class ThemePushApi extends FeatureApi {
4
+ moduleName = THEME_PUSH_API_NAME;
5
+ #service;
6
+ constructor(service) {
7
+ super();
8
+ this.#service = service;
9
+ }
10
+ async push(props) {
11
+ return this.#service.push(props);
12
+ }
13
+ }
@@ -0,0 +1,21 @@
1
+ export class ThemePushHttpApi {
2
+ #httpApi;
3
+ constructor(httpApi) {
4
+ this.#httpApi = httpApi;
5
+ }
6
+ pushThemeData({ actionData, shopUrl, stream }) {
7
+ const { method, url } = actionData;
8
+ return this.#httpApi.fetch({
9
+ url: `${shopUrl}${url}`,
10
+ method,
11
+ data: stream,
12
+ sanitizeOptions: {
13
+ disable: true
14
+ },
15
+ headers: {
16
+ 'Content-Type': 'application/octet-stream'
17
+ },
18
+ isPrivate: true
19
+ });
20
+ }
21
+ }