@shoper/cli 0.5.2 → 0.6.1
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/.docs/images/shoper-for-developers.svg +1 -0
- package/README.md +4 -2
- package/build/cli/auth/cli_auth_errors_factory.js +1 -1
- package/build/cli/auth/cli_auth_initializer.js +6 -1
- package/build/cli/auth/service/cli_auth_service.js +11 -2
- package/build/cli/auth/tokens/cli_auth_tokens_errors_factory.js +1 -1
- package/build/cli/auth/tokens/cli_auth_tokens_initalizer.js +6 -1
- package/build/cli/auth/tokens/service/cli_auth_tokens_service.js +8 -1
- package/build/cli/class/base_command.js +15 -1
- package/build/cli/class/cli_help.js +24 -0
- package/build/cli/class/errors/file_system_errors_factory.js +3 -3
- package/build/cli/class/errors/http/http_errors_factory.js +1 -1
- package/build/cli/commands/auth/cli_auth_add_token_command.js +5 -0
- package/build/cli/commands/auth/cli_auth_list_tokens_command.js +20 -11
- package/build/cli/commands/auth/cli_auth_logout_command.js +5 -0
- package/build/cli/commands/auth/cli_auth_remove_token_command.js +5 -0
- package/build/cli/commands/auth/cli_auth_switch_token_command.js +8 -0
- package/build/cli/commands/cli_update_command.js +5 -0
- package/build/cli/core/cli_setup.js +5 -13
- package/build/cli/features/execution_context/execution_context_service.js +2 -0
- package/build/cli/features/version/service/cli_version_service.js +2 -2
- package/build/cli/hooks/authorization/ensure_authorization_hook.js +9 -2
- package/build/cli/hooks/ensure_cli_initialized_hook.js +1 -6
- package/build/cli/hooks/ensure_logs_flushed_hook.js +7 -0
- package/build/cli/{features → utilities/features}/http_requester/http_client.js +2 -2
- package/build/cli/{features → utilities/features}/http_requester/http_requester_initializer.js +1 -1
- package/build/cli/utilities/features/logger/api/logger_api.js +28 -0
- package/build/cli/utilities/features/logger/logger_constants.js +7 -0
- package/build/cli/utilities/features/logger/logger_initializer.js +44 -0
- package/build/cli/utilities/features/logger/logs/app_error.js +14 -0
- package/build/cli/utilities/features/logger/logs/app_log.js +40 -0
- package/build/cli/{class/errors/app/app_error_constants.js → utilities/features/logger/logs/app_logs_constants.js} +1 -1
- package/build/cli/utilities/features/logger/service/logger_service.js +108 -0
- package/build/cli/utilities/features/logger/transports/log_object_map_transport.js +15 -0
- package/build/index.js +14 -2
- package/build/theme/class/archive/theme_archive.js +2 -1
- package/build/theme/class/archive/theme_archive_errors_factory.js +2 -2
- package/build/theme/class/checksums/theme_checksums.js +25 -5
- package/build/theme/class/checksums/theme_checksums_error_factory.js +3 -3
- package/build/theme/class/fetch_resources/fetch_resources.js +34 -4
- package/build/theme/class/fetch_resources/fetch_resources_errors_factory.js +1 -1
- package/build/theme/commands/delete/theme_delete_command.js +3 -2
- package/build/theme/commands/info/theme_info_command.js +3 -0
- package/build/theme/commands/init/theme_init_command.js +10 -1
- package/build/theme/commands/list/theme_list_command.js +22 -8
- package/build/theme/commands/publish/theme_publish_command.js +5 -1
- package/build/theme/commands/pull/theme_pull_command.js +17 -5
- package/build/theme/commands/push/theme_push_command.js +7 -1
- package/build/theme/commands/theme_verify_command.js +6 -1
- package/build/theme/commands/ui/theme_error.js +1 -1
- package/build/theme/features/theme/actions/service/theme_actions_service.js +42 -2
- package/build/theme/features/theme/actions/theme_actions_initializer.js +3 -1
- package/build/theme/features/theme/delete/service/theme_delete_service.js +12 -1
- package/build/theme/features/theme/delete/theme_delete_initalizer.js +6 -1
- package/build/theme/features/theme/fetch/service/theme_fetch_service.js +35 -6
- package/build/theme/features/theme/fetch/theme_fetch_initializer.js +3 -0
- package/build/theme/features/theme/init/service/theme_init_service.js +32 -6
- package/build/theme/features/theme/init/theme_init_initializer.js +4 -1
- package/build/theme/features/theme/merge/service/theme_merge_service.js +38 -4
- package/build/theme/features/theme/merge/theme_merge_initializer.js +3 -1
- package/build/theme/features/theme/push/service/theme_push_service.js +38 -10
- package/build/theme/features/theme/push/theme_push_errors_factory.js +2 -2
- package/build/theme/features/theme/push/theme_push_initializer.js +3 -1
- package/build/theme/features/theme/push/theme_push_utils.js +1 -1
- package/build/theme/features/theme/utils/files_structure/theme_file_structure_errors_factory.js +2 -3
- package/build/theme/features/theme/utils/hidden_directory/hidden_directory_utils.js +5 -2
- package/build/theme/features/theme/utils/meta_data/theme_meta_data_error_factory.js +1 -1
- package/build/theme/features/theme/utils/resources/theme_resources_with_id_directory_utils.js +5 -2
- package/build/theme/features/theme/verify/theme_verify_initializer.js +4 -3
- package/build/theme/features/theme/verify/verify/theme_verify_service.js +20 -2
- package/build/theme/features/theme/watch/api/theme_watch_api.js +11 -0
- package/build/theme/features/theme/watch/service/theme_watch_service.js +48 -0
- package/build/theme/features/theme/watch/theme_watch_constants.js +2 -0
- package/build/theme/features/theme/watch/theme_watch_initializer.js +20 -0
- package/build/theme/features/themes/list/services/themes_list_service.js +36 -5
- package/build/theme/features/themes/list/themes_list_initializer.js +3 -1
- package/build/theme/hooks/ensure_theme_meta_data_untouched_hook.js +4 -2
- package/build/theme/hooks/theme_checksums/ensure_theme_current_checksums_up_to_date_hook.js +7 -2
- package/build/theme/hooks/themes_actions/ensure_themes_actions_hook.js +2 -3
- package/build/ui/gradient.js +2 -0
- package/build/ui/hooks/stream_hook.js +26 -0
- package/build/utils/download_file/download_file_errors_factory.js +1 -1
- package/build/utils/download_file/download_file_utils.js +19 -1
- package/build/utils/fs/errors/stream_read_error.js +7 -5
- package/build/utils/fs/errors/stream_write_error.js +7 -5
- package/build/utils/get_api.js +9 -0
- package/build/utils/use_api.js +5 -0
- package/build/utils/zip/create_zip_utils.js +21 -9
- package/build/utils/zip/errors/create_zip_error.js +7 -5
- package/build/utils/zip/errors/open_zip_error.js +7 -5
- package/build/utils/zip/extract_zip_utils.js +90 -15
- package/oclif.config.js +3 -1
- package/package.json +15 -5
- package/build/cli/class/errors/app/app_error.js +0 -17
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { FeatureApi } from '@dreamcommerce/star_core';
|
|
2
|
+
import { LOGGER_API_NAME } from '../logger_constants.js';
|
|
3
|
+
export class LoggerApi extends FeatureApi {
|
|
4
|
+
moduleName = LOGGER_API_NAME;
|
|
5
|
+
#service;
|
|
6
|
+
constructor(loggerService) {
|
|
7
|
+
super();
|
|
8
|
+
this.#service = loggerService;
|
|
9
|
+
}
|
|
10
|
+
debug(message, props) {
|
|
11
|
+
this.#service.debug(message, props);
|
|
12
|
+
}
|
|
13
|
+
info(message, props) {
|
|
14
|
+
this.#service.info(message, props);
|
|
15
|
+
}
|
|
16
|
+
warn(message, props) {
|
|
17
|
+
this.#service.warn(message, props);
|
|
18
|
+
}
|
|
19
|
+
error(message, props) {
|
|
20
|
+
this.#service.error(message, props);
|
|
21
|
+
}
|
|
22
|
+
fatal(message, props) {
|
|
23
|
+
this.#service.fatal(message, props);
|
|
24
|
+
}
|
|
25
|
+
async flush() {
|
|
26
|
+
await this.#service.flush();
|
|
27
|
+
}
|
|
28
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { AsyncFeatureInitializer, FEATURE_CORES_TYPES } from '@dreamcommerce/star_core';
|
|
2
|
+
import { LoggerService } from './service/logger_service.js';
|
|
3
|
+
import { LoggerApi } from './api/logger_api.js';
|
|
4
|
+
import { EXECUTION_CONTEXT_API_NAME, EXECUTION_CONTEXTS } from '../../../features/execution_context/execution_context_constants.js';
|
|
5
|
+
import { LOGGER_DEBUG_MODES, LOGGER_FEATURE_NAME } from './logger_constants.js';
|
|
6
|
+
import { CLI_VERSION_API_NAME } from '../../../features/version/cli_version_constants.js';
|
|
7
|
+
import { join } from '../../../../utils/path_utils.js';
|
|
8
|
+
export class LoggerInitializer extends AsyncFeatureInitializer {
|
|
9
|
+
static featureName = LOGGER_FEATURE_NAME;
|
|
10
|
+
async init() {
|
|
11
|
+
const executionContextApi = this.getApiSync(EXECUTION_CONTEXT_API_NAME);
|
|
12
|
+
const cliVersionApi = this.getApiSync(CLI_VERSION_API_NAME);
|
|
13
|
+
const executionContext = await executionContextApi.getExecutionContext();
|
|
14
|
+
const service = new LoggerService({
|
|
15
|
+
executionContext,
|
|
16
|
+
cliVersion: await cliVersionApi.getGloballyInstalledVersion(),
|
|
17
|
+
debugMode: this._getDebugMode(executionContext.command),
|
|
18
|
+
logFile: this.getLogFilePath(executionContext)
|
|
19
|
+
});
|
|
20
|
+
return {
|
|
21
|
+
cores: [
|
|
22
|
+
{
|
|
23
|
+
type: FEATURE_CORES_TYPES.api,
|
|
24
|
+
instance: new LoggerApi(service)
|
|
25
|
+
}
|
|
26
|
+
]
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
_getDebugMode(command) {
|
|
30
|
+
if (command.includes('--vv'))
|
|
31
|
+
return LOGGER_DEBUG_MODES.detailed;
|
|
32
|
+
if (command.includes('--v'))
|
|
33
|
+
return LOGGER_DEBUG_MODES.standard;
|
|
34
|
+
return LOGGER_DEBUG_MODES.none;
|
|
35
|
+
}
|
|
36
|
+
getLogFilePath(executionContext) {
|
|
37
|
+
const logFileName = 'shoper_cli_debug_log.json';
|
|
38
|
+
if (executionContext.command.includes('--log-file')) {
|
|
39
|
+
return executionContext.type === EXECUTION_CONTEXTS.theme
|
|
40
|
+
? join(executionContext.themeRootDir, logFileName)
|
|
41
|
+
: join(process.cwd(), logFileName);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { AppLog } from './app_log.js';
|
|
2
|
+
import { APP_LOGS_TYPES } from './app_logs_constants.js';
|
|
3
|
+
export class AppError extends AppLog {
|
|
4
|
+
constructor({ details, message, level = APP_LOGS_TYPES.error, tags, error, code }) {
|
|
5
|
+
super({ message, level, tags, details });
|
|
6
|
+
this.tags = {
|
|
7
|
+
...tags,
|
|
8
|
+
code
|
|
9
|
+
};
|
|
10
|
+
this.level = level;
|
|
11
|
+
if (error?.stack)
|
|
12
|
+
this.stacktrace = error.stack;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
export class AppLog {
|
|
2
|
+
level;
|
|
3
|
+
message;
|
|
4
|
+
user;
|
|
5
|
+
/**
|
|
6
|
+
* Example:
|
|
7
|
+
* {
|
|
8
|
+
* userId: 123,
|
|
9
|
+
* shopId: 456,
|
|
10
|
+
* }
|
|
11
|
+
*/
|
|
12
|
+
stacktrace;
|
|
13
|
+
// jakie wartosci dopuszczamy w value tagu?
|
|
14
|
+
tags;
|
|
15
|
+
/**
|
|
16
|
+
* Example:
|
|
17
|
+
* {
|
|
18
|
+
* // always added tags:
|
|
19
|
+
* environment: 'MacOS',
|
|
20
|
+
* appVersion: '1.0.0',
|
|
21
|
+
* nodeVersion: 'v16.0.0',
|
|
22
|
+
* traceId: 'unique-trace-id',
|
|
23
|
+
* command: 'theme init 2'
|
|
24
|
+
* // other tags not always added, spefic to the log, EXAMPLE ONLY added quickly:
|
|
25
|
+
* errorCode: 'E_THEME_NOT_FOUND',
|
|
26
|
+
* filePath: '/path/to/file',
|
|
27
|
+
* responseTimeMs: 250,
|
|
28
|
+
* }
|
|
29
|
+
*/
|
|
30
|
+
details;
|
|
31
|
+
// user timestamp in ISO format or server timestamp added when proxied to elastic?
|
|
32
|
+
timestamp = new Date().toISOString();
|
|
33
|
+
constructor({ details, message, level, tags, stacktrace }) {
|
|
34
|
+
this.details = details;
|
|
35
|
+
this.message = message;
|
|
36
|
+
this.level = level;
|
|
37
|
+
this.tags = tags;
|
|
38
|
+
this.stacktrace = stacktrace;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import pino from 'pino';
|
|
2
|
+
import os from 'node:os';
|
|
3
|
+
import { v4 as uuid } from 'uuid';
|
|
4
|
+
import { LOGGER_DEBUG_MODES } from '../logger_constants.js';
|
|
5
|
+
export class LoggerService {
|
|
6
|
+
#logger;
|
|
7
|
+
#transport;
|
|
8
|
+
#traceId = uuid();
|
|
9
|
+
#executionContext;
|
|
10
|
+
#cliVersion;
|
|
11
|
+
constructor({ executionContext, cliVersion, debugMode, logFile }) {
|
|
12
|
+
this.#executionContext = executionContext;
|
|
13
|
+
this.#cliVersion = cliVersion;
|
|
14
|
+
const { logger, transport } = this._getLoggerInstance(debugMode, logFile);
|
|
15
|
+
this.#logger = logger;
|
|
16
|
+
this.#transport = transport;
|
|
17
|
+
}
|
|
18
|
+
_getLoggerInstance(debugMode, logFile) {
|
|
19
|
+
const targets = [];
|
|
20
|
+
if (logFile) {
|
|
21
|
+
targets.push({
|
|
22
|
+
target: 'pino/file',
|
|
23
|
+
level: 'debug',
|
|
24
|
+
options: {
|
|
25
|
+
destination: logFile,
|
|
26
|
+
mkdir: true,
|
|
27
|
+
append: false
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
const prettyTransport = {
|
|
32
|
+
target: 'pino-pretty',
|
|
33
|
+
options: {
|
|
34
|
+
messageKey: 'message',
|
|
35
|
+
colorize: true,
|
|
36
|
+
translateTime: 'SYS:standard',
|
|
37
|
+
ignore: debugMode === LOGGER_DEBUG_MODES.standard
|
|
38
|
+
? 'pid,hostname,message,traceId,cliVersion,nodeVersion,environment,command,details'
|
|
39
|
+
: ''
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
/*
|
|
43
|
+
* To bd potem zastąpine customowym transportem który wysyła logi do backendu
|
|
44
|
+
*/
|
|
45
|
+
// const logObjectMapTransport = {
|
|
46
|
+
// target: '../transports/log_object_map_transport.js',
|
|
47
|
+
// options: {
|
|
48
|
+
// traceId: this.#traceId
|
|
49
|
+
// }
|
|
50
|
+
// };
|
|
51
|
+
// TODO docelowo logObjectMapTransport bd wysyłał logi do serwera jezeli bedzie gdzies error a apce
|
|
52
|
+
// targets: debugMode !== LOGGER_DEBUG_MODES.none ? [prettyTransport, logObjectMapTransport] : [logObjectMapTransport]
|
|
53
|
+
if (debugMode !== LOGGER_DEBUG_MODES.none) {
|
|
54
|
+
targets.push(prettyTransport);
|
|
55
|
+
}
|
|
56
|
+
const transport = targets.length
|
|
57
|
+
? pino.transport({
|
|
58
|
+
targets
|
|
59
|
+
})
|
|
60
|
+
: undefined;
|
|
61
|
+
/**
|
|
62
|
+
* Przy dodawaniu logowania na backend trzeba to usunac
|
|
63
|
+
*/
|
|
64
|
+
if (!transport)
|
|
65
|
+
return {};
|
|
66
|
+
const logger = pino({
|
|
67
|
+
level: 'debug',
|
|
68
|
+
base: {
|
|
69
|
+
pid: process.pid,
|
|
70
|
+
hostname: os.hostname(),
|
|
71
|
+
cliVersion: this.#cliVersion,
|
|
72
|
+
nodeVersion: process.version,
|
|
73
|
+
environment: os.type(),
|
|
74
|
+
command: this.#executionContext.command.slice(2).join(' '),
|
|
75
|
+
traceId: this.#traceId
|
|
76
|
+
},
|
|
77
|
+
timestamp: pino.stdTimeFunctions.isoTime,
|
|
78
|
+
messageKey: 'message'
|
|
79
|
+
}, transport);
|
|
80
|
+
return { logger, transport };
|
|
81
|
+
}
|
|
82
|
+
debug(message, props) {
|
|
83
|
+
this.#logger?.debug(props || {}, message);
|
|
84
|
+
}
|
|
85
|
+
info(message, props) {
|
|
86
|
+
this.#logger?.info(props || {}, message);
|
|
87
|
+
}
|
|
88
|
+
warn(message, props) {
|
|
89
|
+
this.#logger?.warn(props || {}, message);
|
|
90
|
+
}
|
|
91
|
+
error(message, props) {
|
|
92
|
+
this.#logger?.error(props || {}, message);
|
|
93
|
+
}
|
|
94
|
+
fatal(message, props) {
|
|
95
|
+
this.#logger?.fatal(props || {}, message);
|
|
96
|
+
}
|
|
97
|
+
async flush() {
|
|
98
|
+
if (!this.#transport || this.#transport?.destroyed) {
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
return new Promise((resolve) => {
|
|
102
|
+
// The 'close' event is emitted when the stream has been fully closed.
|
|
103
|
+
this.#transport?.on('close', resolve);
|
|
104
|
+
this.#logger?.flush(); // Synchronously flush buffered logs
|
|
105
|
+
this.#transport?.end(); // End the stream, which will trigger 'close'
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { Writable } from 'node:stream';
|
|
2
|
+
export default async function (opts) {
|
|
3
|
+
return new Writable({
|
|
4
|
+
objectMode: true,
|
|
5
|
+
write(chunk, enc, cb) {
|
|
6
|
+
const mapped = mapLog(chunk);
|
|
7
|
+
cb();
|
|
8
|
+
return mapped;
|
|
9
|
+
}
|
|
10
|
+
});
|
|
11
|
+
}
|
|
12
|
+
function mapLog(obj) {
|
|
13
|
+
//TODO map for backend structure
|
|
14
|
+
return obj;
|
|
15
|
+
}
|
package/build/index.js
CHANGED
|
@@ -9,6 +9,9 @@ import { CliAuthRemoveTokenCommand } from './cli/commands/auth/cli_auth_remove_t
|
|
|
9
9
|
import { CliAuthSwitchTokenCommand } from './cli/commands/auth/cli_auth_switch_token_command.js';
|
|
10
10
|
import { CliUIDumpCommand } from './cli/commands/cli_ui_dump_command.js';
|
|
11
11
|
import { CliAuthLogoutCommand } from './cli/commands/auth/cli_auth_logout_command.js';
|
|
12
|
+
import { getApiSync } from './utils/get_api.js';
|
|
13
|
+
import { LOGGER_API_NAME } from './cli/utilities/features/logger/logger_constants.js';
|
|
14
|
+
import './cli/class/cli_help.js';
|
|
12
15
|
//TODO
|
|
13
16
|
//@ts-ignore
|
|
14
17
|
if (typeof global.crypto !== 'object') {
|
|
@@ -24,12 +27,21 @@ function getRandomValues(array) {
|
|
|
24
27
|
return crypto.webcrypto.getRandomValues(array);
|
|
25
28
|
}
|
|
26
29
|
process.on('uncaughtException', (err) => {
|
|
27
|
-
//TODO handle error
|
|
28
30
|
console.error('Uncaught Exception:', err);
|
|
31
|
+
const loggerApi = getApiSync(LOGGER_API_NAME);
|
|
32
|
+
if (loggerApi)
|
|
33
|
+
loggerApi.fatal('Uncaught Exception', { error: err });
|
|
29
34
|
process.exit(1);
|
|
30
35
|
});
|
|
31
|
-
process.on('unhandledRejection', (reason
|
|
36
|
+
process.on('unhandledRejection', (reason) => {
|
|
32
37
|
console.error('Unhandled Rejection:', reason);
|
|
38
|
+
const loggerApi = getApiSync(LOGGER_API_NAME);
|
|
39
|
+
if (loggerApi)
|
|
40
|
+
loggerApi.fatal('Unhandled Rejection', {
|
|
41
|
+
details: {
|
|
42
|
+
reason
|
|
43
|
+
}
|
|
44
|
+
});
|
|
33
45
|
process.exit(1);
|
|
34
46
|
});
|
|
35
47
|
const signals = ['SIGINT', 'SIGTERM', 'SIGQUIT'];
|
|
@@ -11,7 +11,7 @@ export class ThemeArchive {
|
|
|
11
11
|
constructor(themeRootDir) {
|
|
12
12
|
this.#themeRootDir = themeRootDir;
|
|
13
13
|
}
|
|
14
|
-
async createFullArchive({ dist, actionValue, actionType }) {
|
|
14
|
+
async createFullArchive({ dist, actionValue, actionType, logger }) {
|
|
15
15
|
const filesStructure = await ThemeFilesStructureUtils.getThemeFilesStructure(this.#themeRootDir);
|
|
16
16
|
if (!filesStructure)
|
|
17
17
|
throw ThemeFileStructureErrorsFactory.createNoFilesStructureError();
|
|
@@ -28,6 +28,7 @@ export class ThemeArchive {
|
|
|
28
28
|
await this._formatJsonFiles(this.#themeRootDir, filesInThemeDirectory);
|
|
29
29
|
return createZip({
|
|
30
30
|
files: filesInThemeDirectory,
|
|
31
|
+
logger,
|
|
31
32
|
baseDir: this.#themeRootDir,
|
|
32
33
|
dist
|
|
33
34
|
});
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { AppError } from '../../../cli/
|
|
1
|
+
import { AppError } from '../../../cli/utilities/features/logger/logs/app_error.js';
|
|
2
2
|
export class ThemeArchiveErrorsFactory {
|
|
3
3
|
static createErrorWhileCreatingThemeArchive(error) {
|
|
4
4
|
return new AppError({
|
|
5
5
|
code: 'theme_archive.error_creating_archive',
|
|
6
6
|
message: `Error while creating theme archive`,
|
|
7
7
|
level: 'error',
|
|
8
|
-
|
|
8
|
+
error
|
|
9
9
|
});
|
|
10
10
|
}
|
|
11
11
|
}
|
|
@@ -12,14 +12,16 @@ import { SHOPER_THEME_METADATA_DIR } from '../../constants/directory_contstants.
|
|
|
12
12
|
import { THEME_CURRENT_CHECKSUMS_FILE_NAME, THEME_CURRENT_CHECKSUMS_VERITY_FILE_NAME, THEME_INITIAL_CHECKSUMS_FILE_NAME, THEME_INITIAL_CHECKSUMS_VERITY_FILE_NAME } from '../../features/theme/theme_constants.js';
|
|
13
13
|
export class ThemeChecksums {
|
|
14
14
|
#themeDir;
|
|
15
|
+
#loggerApi;
|
|
15
16
|
#currentChecksumsFilePath;
|
|
16
17
|
#currentChecksumsVerificationFilePath;
|
|
17
18
|
#initialChecksumsFilePath;
|
|
18
19
|
#initialChecksumsVerificationFilePath;
|
|
19
20
|
#initialChecksums;
|
|
20
21
|
#currentChecksums;
|
|
21
|
-
constructor(themeDir) {
|
|
22
|
+
constructor({ loggerApi, themeDir }) {
|
|
22
23
|
this.#themeDir = themeDir;
|
|
24
|
+
this.#loggerApi = loggerApi;
|
|
23
25
|
this.#currentChecksumsFilePath = ThemeChecksumsUtils.getCurrentThemeChecksumsFilePath(themeDir);
|
|
24
26
|
this.#currentChecksumsVerificationFilePath = ThemeChecksumsUtils.getCurrentThemeChecksumsVerificationFilePath(themeDir);
|
|
25
27
|
this.#initialChecksumsFilePath = ThemeChecksumsUtils.getInitialThemeChecksumsFilePath(themeDir);
|
|
@@ -83,6 +85,7 @@ export class ThemeChecksums {
|
|
|
83
85
|
return initialChecksum === initialChecksumVerify;
|
|
84
86
|
}
|
|
85
87
|
async updateAllChecksums() {
|
|
88
|
+
this.#loggerApi?.debug('Updating all checksums.');
|
|
86
89
|
const checksums = await this.computeThemeChecksums();
|
|
87
90
|
return new Promise((resolve, reject) => {
|
|
88
91
|
const currentChecksumFilePath = this.#currentChecksumsFilePath;
|
|
@@ -92,20 +95,24 @@ export class ThemeChecksums {
|
|
|
92
95
|
checksumStream.end();
|
|
93
96
|
checksumStream
|
|
94
97
|
.on('finish', async () => {
|
|
98
|
+
this.#loggerApi?.debug('Finished writing initial checksums file. Creating verification files.');
|
|
95
99
|
const initialChecksumVerifyFilePath = this.#initialChecksumsVerificationFilePath;
|
|
96
100
|
const currentChecksumVerifyFilePath = this.#currentChecksumsVerificationFilePath;
|
|
97
101
|
await this._createThemeChecksumVerifyFile(initialChecksumFilePath, initialChecksumVerifyFilePath);
|
|
98
102
|
await copyFile(initialChecksumFilePath, currentChecksumFilePath);
|
|
99
103
|
await copyFile(initialChecksumVerifyFilePath, currentChecksumVerifyFilePath);
|
|
104
|
+
this.#loggerApi?.debug('All checksums and verification files updated.');
|
|
100
105
|
resolve();
|
|
101
106
|
})
|
|
102
107
|
.on('error', async (err) => {
|
|
108
|
+
this.#loggerApi?.debug('Error writing checksums file. Cleaning up.', { details: { file: initialChecksumFilePath } });
|
|
103
109
|
await removeFile(initialChecksumFilePath, { force: true });
|
|
104
|
-
reject(ThemeChecksumsErrorFactory.createThemeChecksumError(err
|
|
110
|
+
reject(ThemeChecksumsErrorFactory.createThemeChecksumError(err));
|
|
105
111
|
});
|
|
106
112
|
});
|
|
107
113
|
}
|
|
108
114
|
async updateCurrentChecksums() {
|
|
115
|
+
this.#loggerApi?.debug('Updating current checksums.');
|
|
109
116
|
const checksums = await this.computeThemeChecksums();
|
|
110
117
|
return new Promise((resolve, reject) => {
|
|
111
118
|
const currentChecksumFilePath = this.#currentChecksumsFilePath;
|
|
@@ -114,17 +121,23 @@ export class ThemeChecksums {
|
|
|
114
121
|
checksumStream.end();
|
|
115
122
|
checksumStream
|
|
116
123
|
.on('finish', async () => {
|
|
124
|
+
this.#loggerApi?.debug('Finished writing current checksums file. Creating verification file.');
|
|
117
125
|
const currentChecksumVerifyFilePath = this.#currentChecksumsVerificationFilePath;
|
|
118
126
|
await this._createThemeChecksumVerifyFile(currentChecksumFilePath, currentChecksumVerifyFilePath);
|
|
127
|
+
this.#loggerApi?.debug('Current checksums and verification file updated.');
|
|
119
128
|
resolve();
|
|
120
129
|
})
|
|
121
130
|
.on('error', async (err) => {
|
|
131
|
+
this.#loggerApi?.debug('Error writing current checksums file. Cleaning up.', {
|
|
132
|
+
details: { file: currentChecksumFilePath }
|
|
133
|
+
});
|
|
122
134
|
await removeFile(currentChecksumFilePath, { force: true });
|
|
123
|
-
reject(ThemeChecksumsErrorFactory.createThemeChecksumError(err
|
|
135
|
+
reject(ThemeChecksumsErrorFactory.createThemeChecksumError(err));
|
|
124
136
|
});
|
|
125
137
|
});
|
|
126
138
|
}
|
|
127
139
|
async computeThemeChecksums() {
|
|
140
|
+
this.#loggerApi?.debug('Computing theme checksums.');
|
|
128
141
|
const filesToIgnoreInChecksums = [
|
|
129
142
|
join(SHOPER_THEME_METADATA_DIR, THEME_CURRENT_CHECKSUMS_FILE_NAME),
|
|
130
143
|
join(SHOPER_THEME_METADATA_DIR, THEME_CURRENT_CHECKSUMS_VERITY_FILE_NAME),
|
|
@@ -134,9 +147,12 @@ export class ThemeChecksums {
|
|
|
134
147
|
const filesToComputeChecksums = (await ThemePushUtils.getAllFilesThatAreSendToRemote(this.#themeDir))
|
|
135
148
|
.filter((path) => !filesToIgnoreInChecksums.some((ignorePath) => normalize(path) === ignorePath))
|
|
136
149
|
.map((relativePath) => join(this.#themeDir, relativePath));
|
|
150
|
+
this.#loggerApi?.debug('Computing checksums from file structure.', { details: { count: filesToComputeChecksums.length } });
|
|
137
151
|
const { filesChecksumsInDirectories, filesChecksums } = await computeChecksumsFromFilesStructure(filesToComputeChecksums, this.#themeDir);
|
|
138
152
|
const directoriesChecksums = computeDirectoriesChecksums(filesChecksumsInDirectories);
|
|
139
|
-
|
|
153
|
+
const allChecksums = { ...filesChecksums, ...directoriesChecksums };
|
|
154
|
+
this.#loggerApi?.debug('Finished computing theme checksums.', { details: { count: Object.keys(allChecksums).length } });
|
|
155
|
+
return allChecksums;
|
|
140
156
|
}
|
|
141
157
|
async _getInitialChecksumsVerification() {
|
|
142
158
|
return await readJSONFile(this.#initialChecksumsVerificationFilePath);
|
|
@@ -157,12 +173,16 @@ export class ThemeChecksums {
|
|
|
157
173
|
return mapKeysPathsToWindowPlatform(checksums);
|
|
158
174
|
}
|
|
159
175
|
async _createThemeChecksumVerifyFile(checksumFilePath, checksumVerifyFullPath) {
|
|
176
|
+
this.#loggerApi?.debug('Creating checksum verification file.', { details: { path: checksumVerifyFullPath } });
|
|
160
177
|
const checksumVerifyStream = createWriteStream(checksumVerifyFullPath);
|
|
161
178
|
const checksumVerify = await computeFileChecksum(checksumFilePath);
|
|
162
179
|
checksumVerifyStream
|
|
163
180
|
.on('error', async (err) => {
|
|
181
|
+
this.#loggerApi?.debug('Error creating checksum verification file. Cleaning up.', {
|
|
182
|
+
details: { file: checksumFilePath }
|
|
183
|
+
});
|
|
164
184
|
await removeFile(checksumFilePath, { force: true });
|
|
165
|
-
throw ThemeChecksumsErrorFactory.createThemeChecksumError(err
|
|
185
|
+
throw ThemeChecksumsErrorFactory.createThemeChecksumError(err);
|
|
166
186
|
})
|
|
167
187
|
.write(JSON.stringify(checksumVerify));
|
|
168
188
|
checksumVerifyStream.end();
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { AppError } from '../../../cli/
|
|
1
|
+
import { AppError } from '../../../cli/utilities/features/logger/logs/app_error.js';
|
|
2
2
|
export class ThemeChecksumsErrorFactory {
|
|
3
|
-
static createThemeChecksumError(
|
|
3
|
+
static createThemeChecksumError(error) {
|
|
4
4
|
return new AppError({
|
|
5
5
|
message: 'Theme checksum error',
|
|
6
6
|
code: 'theme_checksum_error',
|
|
7
|
-
|
|
7
|
+
error
|
|
8
8
|
});
|
|
9
9
|
}
|
|
10
10
|
}
|
|
@@ -12,16 +12,23 @@ import { move } from 'fs-extra';
|
|
|
12
12
|
export class FetchResources {
|
|
13
13
|
#urlsAttempts = {};
|
|
14
14
|
#httpApi;
|
|
15
|
-
|
|
15
|
+
#loggerApi;
|
|
16
|
+
constructor({ httpApi, loggerApi }) {
|
|
16
17
|
this.#httpApi = httpApi;
|
|
18
|
+
this.#loggerApi = loggerApi;
|
|
17
19
|
}
|
|
18
20
|
async fetchResources({ dist, resourcesPart, parts, shopUrl }) {
|
|
21
|
+
this.#loggerApi.debug('Traversing resources part.', { details: { parts } });
|
|
19
22
|
if (Array.isArray(resourcesPart)) {
|
|
20
23
|
for (let i = 0; i < resourcesPart.length; i++) {
|
|
21
24
|
const resource = resourcesPart[i];
|
|
22
25
|
if (isResourceObject(resource)) {
|
|
26
|
+
const resourceUrl = isRelativeUrl(resource.url) ? `${shopUrl}${resource.url}` : resource.url;
|
|
27
|
+
this.#loggerApi.debug('Found resource object. Fetching resource.', {
|
|
28
|
+
details: { url: resourceUrl, parts: [...parts, i] }
|
|
29
|
+
});
|
|
23
30
|
await this._fetchResource({
|
|
24
|
-
url:
|
|
31
|
+
url: resourceUrl,
|
|
25
32
|
dist,
|
|
26
33
|
parts: [...parts, i],
|
|
27
34
|
shopUrl,
|
|
@@ -40,8 +47,12 @@ export class FetchResources {
|
|
|
40
47
|
}
|
|
41
48
|
if (typeof resourcesPart === 'object') {
|
|
42
49
|
if (isResourceObject(resourcesPart)) {
|
|
50
|
+
const resourceUrl = isRelativeUrl(resourcesPart.url) ? `${shopUrl}${resourcesPart.url}` : resourcesPart.url;
|
|
51
|
+
this.#loggerApi.debug('Found resource object. Fetching resource.', {
|
|
52
|
+
details: { url: resourceUrl, parts }
|
|
53
|
+
});
|
|
43
54
|
await this._fetchResource({
|
|
44
|
-
url:
|
|
55
|
+
url: resourceUrl,
|
|
45
56
|
dist,
|
|
46
57
|
parts,
|
|
47
58
|
shopUrl,
|
|
@@ -61,6 +72,7 @@ export class FetchResources {
|
|
|
61
72
|
return;
|
|
62
73
|
}
|
|
63
74
|
async _getResourcesFileContent(resourcePath) {
|
|
75
|
+
this.#loggerApi.debug('Reading and mapping resources file content.', { details: { resourcePath } });
|
|
64
76
|
return mapResourcesToTree(await readJSONFile(resourcePath));
|
|
65
77
|
}
|
|
66
78
|
_getResourcesPart(resources, parts) {
|
|
@@ -68,28 +80,42 @@ export class FetchResources {
|
|
|
68
80
|
}
|
|
69
81
|
async _fetchResource({ url, shopUrl, parts, dist, isPrivate }) {
|
|
70
82
|
this.#urlsAttempts[url] = (this.#urlsAttempts[url] ?? 0) + 1;
|
|
83
|
+
this.#loggerApi.debug('Fetching resource.', {
|
|
84
|
+
details: { url, attempt: this.#urlsAttempts[url], parts, isPrivate }
|
|
85
|
+
});
|
|
71
86
|
if (this.#urlsAttempts[url] > MAX_REQUEST_FOR_RESOURCES)
|
|
72
87
|
throw FetchResourcesErrorsFactory.createErrorFetchingResource(shopUrl, [
|
|
73
88
|
`Failed to fetch ${url} after 3 attempts, for: ${parts.join('')}`
|
|
74
89
|
]);
|
|
75
90
|
const { path: tmpDir } = await tmp.dir({ unsafeCleanup: true, prefix: 'theme_resources_tmp' });
|
|
91
|
+
this.#loggerApi.debug('Created temporary directory for resource fetch.', { details: { tmpDir, url } });
|
|
76
92
|
try {
|
|
93
|
+
this.#loggerApi.debug('Downloading resource file.', { details: { url, dist: tmpDir } });
|
|
77
94
|
const { filename, ext, basename } = await downloadFile({
|
|
78
95
|
dist: tmpDir,
|
|
96
|
+
logger: this.#loggerApi,
|
|
79
97
|
request: this._getRequest({ url, isPrivate }).response
|
|
80
98
|
});
|
|
99
|
+
this.#loggerApi.debug('Resource file downloaded.', { details: { url, filename } });
|
|
81
100
|
if (ext === '.zip') {
|
|
101
|
+
this.#loggerApi.debug('Resource is a zip archive. Extracting.', { details: { source: filename, dist: basename } });
|
|
82
102
|
await extractZip({
|
|
103
|
+
logger: this.#loggerApi,
|
|
83
104
|
source: join(tmpDir, filename),
|
|
84
105
|
dist: join(tmpDir, basename)
|
|
85
106
|
});
|
|
107
|
+
this.#loggerApi.debug('Zip archive extracted.', { details: { filename } });
|
|
86
108
|
if (!(await fileExists(getResourcesPath(join(tmpDir, basename)))))
|
|
87
109
|
return;
|
|
110
|
+
this.#loggerApi.debug('Copying extracted files.', {
|
|
111
|
+
details: { from: join(tmpDir, basename), to: dist }
|
|
112
|
+
});
|
|
88
113
|
await copyFile(join(tmpDir, basename), dist, {
|
|
89
114
|
recursive: true,
|
|
90
115
|
force: true
|
|
91
116
|
});
|
|
92
117
|
const newResources = await this._getResourcesFileContent(getResourcesPath(join(tmpDir, basename)));
|
|
118
|
+
this.#loggerApi.debug('Recursively fetching resources from extracted archive.', { details: { parts } });
|
|
93
119
|
await this.fetchResources({
|
|
94
120
|
dist,
|
|
95
121
|
resourcesPart: this._getResourcesPart(newResources, parts),
|
|
@@ -98,7 +124,11 @@ export class FetchResources {
|
|
|
98
124
|
});
|
|
99
125
|
}
|
|
100
126
|
else {
|
|
101
|
-
|
|
127
|
+
const destinationPath = join(dist, this._getFilepathFromParts(parts), filename);
|
|
128
|
+
this.#loggerApi.debug('Moving downloaded file.', {
|
|
129
|
+
details: { from: join(tmpDir, filename), to: destinationPath }
|
|
130
|
+
});
|
|
131
|
+
await move(join(tmpDir, filename), destinationPath, { overwrite: true });
|
|
102
132
|
}
|
|
103
133
|
}
|
|
104
134
|
catch (err) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AppError } from '../../../cli/
|
|
1
|
+
import { AppError } from '../../../cli/utilities/features/logger/logs/app_error.js';
|
|
2
2
|
export class FetchResourcesErrorsFactory {
|
|
3
3
|
static createErrorFetchingResource(shopUrl, messages) {
|
|
4
4
|
return new AppError({
|
|
@@ -16,6 +16,7 @@ import { ThemeDeletedSuccessfully } from './ui/theme_deleted_successfully.js';
|
|
|
16
16
|
import { ThemeDeletionWarning } from './ui/theme_deletion_warning.js';
|
|
17
17
|
import { promptConfirmation } from '../../../ui/prompts/prompt_confirmation.js';
|
|
18
18
|
import { ThemeError } from '../ui/theme_error.js';
|
|
19
|
+
import { LOGGER_API_NAME } from '../../../cli/utilities/features/logger/logger_constants.js';
|
|
19
20
|
export class ThemeDeleteCommand extends BaseThemeCommand {
|
|
20
21
|
static summary = 'Permanently deletes the specified theme from your store.';
|
|
21
22
|
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).';
|
|
@@ -37,6 +38,7 @@ export class ThemeDeleteCommand extends BaseThemeCommand {
|
|
|
37
38
|
const themeId = this.args.id;
|
|
38
39
|
const cliAuthApi = this.getApi(CLI_AUTH_API_NAME);
|
|
39
40
|
const executionContextApi = this.getApi(EXECUTION_CONTEXT_API_NAME);
|
|
41
|
+
const loggerApi = this.getApi(LOGGER_API_NAME);
|
|
40
42
|
const credentials = cliAuthApi.getCredentials();
|
|
41
43
|
if (!credentials) {
|
|
42
44
|
renderOnce(React.createElement(MissingCredentialsError, null));
|
|
@@ -86,11 +88,10 @@ export class ThemeDeleteCommand extends BaseThemeCommand {
|
|
|
86
88
|
if (!resp?.isSuccess)
|
|
87
89
|
return;
|
|
88
90
|
renderOnce(React.createElement(ThemeDeletedSuccessfully, null));
|
|
89
|
-
return;
|
|
90
91
|
}
|
|
91
92
|
catch (err) {
|
|
93
|
+
loggerApi.error('Error executing theme delete command', { error: err });
|
|
92
94
|
renderOnce(React.createElement(ThemeError, { err: err, executionContext: executionContext }));
|
|
93
|
-
return;
|
|
94
95
|
}
|
|
95
96
|
}
|
|
96
97
|
}
|
|
@@ -15,6 +15,7 @@ import { ThemeInfoCommandUtils } from './theme_info_command_utils.js';
|
|
|
15
15
|
import { InvalidThemeIdError } from '../ui/invalid_theme_id.js';
|
|
16
16
|
import { Box } from '../../../ui/box.js';
|
|
17
17
|
import { ThemeError } from '../ui/theme_error.js';
|
|
18
|
+
import { LOGGER_API_NAME } from '../../../cli/utilities/features/logger/logger_constants.js';
|
|
18
19
|
export class ThemeInfoCommand extends BaseThemeCommand {
|
|
19
20
|
static summary = 'View details about the current theme.';
|
|
20
21
|
static description = 'Displays key information about the theme you’re working on — including its ID, name, description (if provided), and status.\n\nYou can run this command from a specific theme directory (ID not needed) or outside any theme directory (theme ID required).';
|
|
@@ -36,6 +37,7 @@ export class ThemeInfoCommand extends BaseThemeCommand {
|
|
|
36
37
|
const cliAuthApi = this.getApi(CLI_AUTH_API_NAME);
|
|
37
38
|
const executionContextApi = this.getApi(EXECUTION_CONTEXT_API_NAME);
|
|
38
39
|
const themesListApi = this.getApi(THEMES_LIST_API_NAME);
|
|
40
|
+
const loggerApi = this.getApi(LOGGER_API_NAME);
|
|
39
41
|
const credentials = cliAuthApi.getCredentials();
|
|
40
42
|
if (!credentials) {
|
|
41
43
|
renderOnce(React.createElement(MissingCredentialsError, null));
|
|
@@ -70,6 +72,7 @@ export class ThemeInfoCommand extends BaseThemeCommand {
|
|
|
70
72
|
this._displayThemeInfo(theme);
|
|
71
73
|
}
|
|
72
74
|
catch (err) {
|
|
75
|
+
loggerApi.error('An error occurred while fetching theme info.', { error: err });
|
|
73
76
|
renderOnce(React.createElement(ThemeError, { err: err, executionContext: executionContext }));
|
|
74
77
|
}
|
|
75
78
|
}
|