@shoper/cli 0.1.0-7 → 0.1.0-8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/cli/auth/api/cli_auth_api.js +15 -2
- package/build/cli/auth/cli_auth_constants.js +6 -0
- package/build/cli/auth/cli_auth_errors_factory.js +10 -0
- package/build/cli/auth/cli_auth_initializer.js +19 -0
- package/build/cli/auth/cli_auth_utils.js +22 -0
- package/build/cli/auth/model/cli_credentials.js +10 -0
- package/build/cli/auth/service/cli_auth_service.js +34 -0
- package/build/cli/auth/tokens/api/cli_auth_tokens_api.js +43 -1
- package/build/cli/auth/tokens/cli_auth_tokens_constants.js +6 -0
- package/build/cli/auth/tokens/cli_auth_tokens_errors_factory.js +10 -0
- package/build/cli/auth/tokens/cli_auth_tokens_initalizer.js +35 -1
- package/build/cli/auth/tokens/cli_auth_tokens_utils.js +26 -0
- package/build/cli/auth/tokens/service/cli_auth_tokens_service.js +84 -4
- package/build/cli/auth/tokens/service/cli_auth_tokens_service_constants.js +2 -0
- package/build/cli/class/base_cli_command.js +3 -0
- package/build/cli/class/errors/app_error/app_error.js +17 -0
- package/build/cli/class/errors/app_error/app_error_constants.js +7 -0
- package/build/cli/class/errors/file_system_errors_factory.js +26 -0
- package/build/cli/class/errors/http_errors_factory.js +21 -0
- package/build/cli/cli_constants.js +1 -0
- package/build/cli/commands/auth/cli_auth_add_token_command.js +32 -0
- package/build/cli/commands/auth/cli_auth_list_tokens_command.js +17 -0
- package/build/cli/commands/auth/cli_auth_remove_token_command.js +37 -0
- package/build/cli/commands/auth/cli_auth_switch_token_command.js +36 -0
- package/build/cli/commands/cli_update_command.js +7 -3
- package/build/cli/commands/commands_constants.js +5 -1
- package/build/cli/commands/files_diff_command.js +174 -0
- package/build/cli/commands/utils/prompt_for_token_utils.js +25 -0
- package/build/cli/core/cli_setup.js +20 -7
- package/build/cli/features/caches/json_cache/json_cache.js +50 -0
- package/build/cli/features/caches/memory_cache.js +6 -0
- package/build/cli/features/data_directory/cli_data_directory_constants.js +0 -1
- package/build/cli/features/data_directory/cli_data_directory_utils.js +3 -4
- package/build/cli/features/data_directory/service/cli_data_directory_service.js +2 -2
- package/build/cli/features/execution_context/execution_context_service.js +3 -5
- package/build/cli/features/http_requester/http_client.js +32 -1
- package/build/cli/features/version/service/cli_version_service.js +2 -4
- package/build/cli/hooks/authorization/ensure_authorization_hook.js +11 -6
- package/build/cli/hooks/authorization/ensure_authorization_hook_constants.js +8 -1
- package/build/index.js +34 -1
- package/build/theme/class/fetch_resources/fetch_resources.js +126 -0
- package/build/theme/class/fetch_resources/fetch_resources_constants.js +2 -0
- package/build/theme/class/fetch_resources/fetch_resources_errors_factory.js +11 -0
- package/build/theme/class/fetch_resources/fetch_resources_utils.js +35 -0
- package/build/theme/commands/theme_commands_constants.js +8 -0
- package/build/theme/commands/theme_init_command.js +53 -0
- package/build/theme/commands/theme_list_command.js +16 -0
- package/build/theme/commands/theme_pull_command.js +124 -0
- package/build/theme/commands/theme_push_command.js +65 -0
- package/build/theme/commands/theme_show_changes_command.js +60 -0
- package/build/theme/commands/theme_verify_command.js +43 -0
- package/build/theme/features/theme/actions/api/theme_actions_api.js +19 -0
- package/build/theme/features/theme/actions/service/theme_actions_service.js +92 -0
- package/build/theme/features/theme/actions/service/theme_actions_service_constants.js +2 -0
- package/build/theme/features/theme/actions/theme_actions_constants.js +15 -0
- package/build/theme/features/theme/actions/theme_actions_initializer.js +27 -0
- package/build/theme/features/theme/actions/theme_actions_utils.js +8 -0
- package/build/theme/features/theme/directory/theme_directory_utils.js +61 -14
- package/build/theme/features/theme/fetch/api/theme_fetch_api.js +16 -0
- package/build/theme/features/theme/fetch/http/theme_fetch_http_api.js +18 -0
- package/build/theme/features/theme/fetch/service/theme_fetch_service.js +113 -0
- package/build/theme/features/theme/fetch/theme_fetch_constants.js +7 -0
- package/build/theme/features/theme/fetch/theme_fetch_initializer.js +23 -0
- package/build/theme/features/theme/init/api/theme_init_api.js +13 -0
- package/build/theme/features/theme/init/http/theme_init_http_api.js +18 -0
- package/build/theme/features/theme/init/service/theme_init_service.js +28 -0
- package/build/theme/features/theme/init/theme_init_constants.js +2 -0
- package/build/theme/features/theme/init/theme_init_initializer.js +22 -0
- package/build/theme/features/theme/merge/api/theme_merge_api.js +28 -0
- package/build/theme/features/theme/merge/service/theme_merge_service.js +56 -0
- package/build/theme/features/theme/merge/theme_merge_constants.js +9 -0
- package/build/theme/features/theme/merge/theme_merge_initializer.js +18 -0
- package/build/theme/features/theme/publish/theme_publish_constants.js +2 -0
- package/build/theme/features/theme/publish/theme_publish_utils.js +7 -0
- package/build/theme/features/theme/push/api/theme_push_api.js +13 -0
- package/build/theme/features/theme/push/http_api/theme_push_http_api.js +21 -0
- package/build/theme/features/theme/push/service/theme_push_service.js +194 -0
- package/build/theme/features/theme/push/service/theme_push_service_constants.js +1 -0
- package/build/theme/features/theme/push/theme_push_constants.js +4 -0
- package/build/theme/features/theme/push/theme_push_errors_factory.js +41 -0
- package/build/theme/features/theme/push/theme_push_initializer.js +27 -0
- package/build/theme/features/theme/theme_constants.js +6 -0
- package/build/theme/features/theme/utils/checksums/theme_checksums_error_factory.js +10 -0
- package/build/theme/features/theme/utils/checksums/theme_checksums_utils.js +94 -0
- package/build/theme/features/theme/utils/directories/theme_resources_with_id_directory_utils.js +80 -0
- package/build/theme/features/theme/utils/theme_images_utils.js +31 -0
- package/build/theme/features/themes/list/api/themes_list_api.js +13 -0
- package/build/theme/features/themes/{http/shoper_themes_http_api.js → list/http/themes_list_http_api.js} +3 -2
- package/build/theme/features/themes/{model/theme_metadata.js → list/model/theme_list_metadata.js} +3 -1
- package/build/theme/features/themes/list/services/themes_list_service.js +37 -0
- package/build/theme/features/themes/list/themes_list_constants.js +2 -0
- package/build/theme/features/themes/list/themes_list_initializer.js +20 -0
- package/build/theme/hooks/ensure_theme_meta_data_untouched.js +17 -0
- package/build/theme/hooks/theme_checksums/ensure_theme_current_checksums_up_to_date.js +21 -0
- package/build/theme/hooks/theme_checksums/ensure_theme_current_checksums_up_to_date_constants.js +7 -0
- package/build/theme/hooks/themes_actions/ensure_themes_actions_hook.js +45 -0
- package/build/theme/hooks/themes_actions/ensure_themes_actions_hook_constants.js +7 -0
- package/build/theme/index.js +13 -4
- package/build/theme/utils/directory_validator/directory_validator_constants.js +13 -0
- package/build/theme/utils/directory_validator/directory_validator_utils.js +187 -0
- package/build/utils/checksums/checksums_utils.js +95 -0
- package/build/utils/checksums/checksums_utils_constants.js +1 -0
- package/build/utils/date_utils.js +3 -0
- package/build/utils/download_file/download_file_errors_factory.js +9 -0
- package/build/utils/download_file/download_file_utils.js +46 -0
- package/build/utils/fs/errors/stream_read_error.js +11 -0
- package/build/utils/fs/errors/stream_write_error.js +11 -0
- package/build/utils/fs/fs_utils.js +152 -0
- package/build/utils/http_utils.js +10 -0
- package/build/utils/path_utils.js +34 -0
- package/build/utils/stream_transforms/json_indent_transform.js +21 -0
- package/build/utils/url_utils.js +9 -0
- package/build/utils/zip/create_zip_utils.js +80 -0
- package/build/utils/zip/errors/create_zip_error.js +11 -0
- package/build/utils/zip/errors/open_zip_error.js +11 -0
- package/build/utils/zip/extract_zip_utils.js +104 -0
- package/oclif.config.js +4 -2
- package/package.json +34 -7
- package/build/cli/hooks/migration/migration_hook.js +0 -4
- package/build/theme/commands/list_command.js +0 -12
- package/build/theme/commands/pull_command.js +0 -24
- package/build/theme/features/theme/directory/theme_directories_utils.js +0 -10
- package/build/theme/features/theme/pull/api/shoper_theme_pull_api.js +0 -9
- package/build/theme/features/theme/pull/http/shoper_theme_pull_http_api.js +0 -14
- package/build/theme/features/theme/pull/service/shoper_theme_pull_service.js +0 -68
- package/build/theme/features/theme/pull/shoper_theme_pull_initializer.js +0 -22
- package/build/theme/features/themes/api/shoper_themes_api.js +0 -14
- package/build/theme/features/themes/services/shoper_themes_service.js +0 -15
- package/build/theme/features/themes/shoper_themes_constants.js +0 -2
- package/build/theme/features/themes/shoper_themes_initalizer.js +0 -20
- package/build/utils/fs.js +0 -44
- package/build/utils/path.js +0 -13
|
@@ -1,6 +1,15 @@
|
|
|
1
|
-
import { closestFile, fileExists,
|
|
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/
|
|
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
|
|
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.
|
|
16
|
-
const
|
|
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,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,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,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,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
|
+
}
|