@shoper/cli 0.2.0 → 0.2.1-0

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 (40) hide show
  1. package/build/cli/class/base_command.js +14 -0
  2. package/build/cli/commands/auth/cli_auth_remove_token_command.js +1 -3
  3. package/build/cli/commands/auth/cli_auth_switch_token_command.js +1 -2
  4. package/build/cli/commands/commands_constants.js +5 -4
  5. package/build/cli/core/cli_setup.js +33 -18
  6. package/build/cli/hooks/authorization/ensure_authorization_hook_constants.js +0 -1
  7. package/build/theme/class/archive/theme_archive.js +44 -0
  8. package/build/theme/class/archive/theme_archive_errors_factory.js +11 -0
  9. package/build/theme/class/checksums/theme_checksums.js +3 -7
  10. package/build/theme/class/files_upload/theme_files_upload.js +61 -0
  11. package/build/theme/class/files_upload/theme_files_upload_http_api.js +23 -0
  12. package/build/theme/commands/delete/theme_delete_command.js +4 -18
  13. package/build/theme/commands/info/theme_info_command.js +3 -18
  14. package/build/theme/commands/init/theme_init_command.js +4 -17
  15. package/build/theme/commands/publish/theme_publish_command.js +4 -20
  16. package/build/theme/commands/pull/theme_pull_command.js +6 -26
  17. package/build/theme/commands/push/theme_push_command.js +17 -39
  18. package/build/theme/commands/theme_commands_constants.js +9 -9
  19. package/build/theme/commands/theme_verify_command.js +59 -18
  20. package/build/theme/commands/ui/theme_error.js +29 -0
  21. package/build/theme/features/theme/actions/theme_actions_constants.js +2 -1
  22. package/build/theme/features/theme/actions/theme_actions_utils.js +41 -1
  23. package/build/theme/features/theme/init/theme_init_initializer.js +3 -3
  24. package/build/theme/features/theme/push/service/theme_push_service.js +42 -164
  25. package/build/theme/features/theme/push/theme_push_initializer.js +1 -4
  26. package/build/theme/features/theme/utils/files_structure/theme_file_structure_errors_factory.js +10 -0
  27. package/build/theme/features/theme/utils/files_structure/theme_files_structure_utils.js +38 -2
  28. package/build/theme/features/theme/utils/theme_images_utils.js +0 -18
  29. package/build/theme/features/theme/verify/api/theme_verify_api.js +13 -0
  30. package/build/theme/features/theme/verify/http/theme_verify_http_api.js +30 -0
  31. package/build/theme/features/theme/verify/theme_verify_constants.js +2 -0
  32. package/build/theme/features/theme/verify/theme_verify_initializer.js +19 -0
  33. package/build/theme/features/theme/verify/verify/theme_verify_service.js +55 -0
  34. package/build/theme/hooks/theme_checksums/ensure_theme_current_checksums_up_to_date_constants.js +0 -1
  35. package/build/theme/hooks/themes_actions/ensure_themes_actions_hook_constants.js +1 -2
  36. package/build/theme/index.js +23 -3
  37. package/oclif.config.js +1 -1
  38. package/package.json +2 -2
  39. package/build/cli/commands/files_diff_command.js +0 -174
  40. package/build/theme/commands/theme_show_changes_command.js +0 -61
@@ -1,9 +1,11 @@
1
- import { readJSONFile, writeJSONFile } from '../../../../../utils/fs/fs_utils.js';
2
- import { join, looksLikeDirectory, mapKeysPathsToWindowPlatform } from '../../../../../utils/path_utils.js';
1
+ import { readJSONFile, removeFile, writeJSONFile } from '../../../../../utils/fs/fs_utils.js';
2
+ import { join, looksLikeDirectory, mapKeysPathsToWindowPlatform, toUnixPath } from '../../../../../utils/path_utils.js';
3
3
  import { SHOPER_THEME_METADATA_DIR } from '../../../../constants/directory_contstants.js';
4
4
  import { THEME_FILES_STRUCTURE_FILE_NAME } from '../../theme_constants.js';
5
5
  import { isWindowsOs } from '../../../../../utils/platform_utils.js';
6
6
  import { validateDirectory } from '../../../../utils/directory_validator/directory_validator_utils.js';
7
+ import path from 'node:path';
8
+ import { THEME_FILES_LIST_FILE_NAME } from '../../push/theme_push_constants.js';
7
9
  export class ThemeFilesStructureUtils {
8
10
  static async getThemeFilesStructure(themeDirectory) {
9
11
  const filesStructure = await readJSONFile(join(themeDirectory, SHOPER_THEME_METADATA_DIR, THEME_FILES_STRUCTURE_FILE_NAME));
@@ -37,4 +39,38 @@ export class ThemeFilesStructureUtils {
37
39
  return looksLikeDirectory(path);
38
40
  });
39
41
  }
42
+ static mapFilesRecordsToFilesList(filesRecords, localFileNameToUploaded = {}) {
43
+ return filesRecords.reduce((acc, { fileGlob, fileName }) => {
44
+ const name = localFileNameToUploaded[fileName] ?? fileName;
45
+ if (looksLikeDirectory(fileGlob)) {
46
+ const existingFiles = acc[fileGlob] || [];
47
+ return {
48
+ ...acc,
49
+ [fileGlob]: [...existingFiles, name]
50
+ };
51
+ }
52
+ else {
53
+ return {
54
+ ...acc,
55
+ [fileGlob]: name
56
+ };
57
+ }
58
+ }, {});
59
+ }
60
+ static async createAFilesListFile(themeRootDir, filesList) {
61
+ if (!filesList || !Object.keys(filesList).length)
62
+ return;
63
+ const toUnixStyleFilesList = Object.entries(filesList).reduce((acc, [filePath, value]) => {
64
+ const unixPath = toUnixPath(filePath);
65
+ const finalPath = looksLikeDirectory(unixPath) ? `${unixPath}${path.posix.sep}` : unixPath;
66
+ return {
67
+ ...acc,
68
+ [finalPath]: value
69
+ };
70
+ }, {});
71
+ await writeJSONFile(join(themeRootDir, THEME_FILES_LIST_FILE_NAME), toUnixStyleFilesList);
72
+ }
73
+ static async removeAFilesListFile(themeRootDir) {
74
+ await removeFile(join(themeRootDir, THEME_FILES_LIST_FILE_NAME));
75
+ }
40
76
  }
@@ -1,24 +1,6 @@
1
1
  import { join } from '../../../../utils/path_utils.js';
2
2
  import { removeFile } from '../../../../utils/fs/fs_utils.js';
3
3
  export class ThemeImagesUtils {
4
- static updateOriginalFilenameToUploadedFilename(filesList, uploadedImageData) {
5
- const newFilesList = { ...filesList };
6
- uploadedImageData
7
- .filter(({ uploadedFilename }) => Boolean(uploadedFilename))
8
- .forEach(({ originalFilename, uploadedFilename, location }) => {
9
- if (typeof newFilesList[location] === 'string' && uploadedFilename) {
10
- newFilesList[location] = uploadedFilename;
11
- return;
12
- }
13
- if (Array.isArray(newFilesList[location]) && uploadedFilename) {
14
- const indexOfOriginalFilename = newFilesList[location].indexOf(originalFilename);
15
- if (indexOfOriginalFilename === -1 && !uploadedFilename)
16
- return;
17
- newFilesList[location].splice(indexOfOriginalFilename, 1, uploadedFilename);
18
- }
19
- });
20
- return newFilesList;
21
- }
22
4
  static async removeUploadedOriginalFiles(themeRootDir, uploadedImagesData) {
23
5
  await Promise.all(uploadedImagesData
24
6
  .filter(({ originalFilename, uploadedFilename }) => Boolean(originalFilename) && Boolean(uploadedFilename))
@@ -0,0 +1,13 @@
1
+ import { FeatureApi } from '@dreamcommerce/star_core';
2
+ import { THEME_VERIFY_API_NAME } from '../theme_verify_constants.js';
3
+ export class ThemeVerifyApi extends FeatureApi {
4
+ moduleName = THEME_VERIFY_API_NAME;
5
+ #service;
6
+ constructor(service) {
7
+ super();
8
+ this.#service = service;
9
+ }
10
+ async verifyTheme(props) {
11
+ return this.#service.verifyTheme(props);
12
+ }
13
+ }
@@ -0,0 +1,30 @@
1
+ export class ThemeVerifyHttpApi {
2
+ #httpApi;
3
+ constructor(httpApi) {
4
+ this.#httpApi = httpApi;
5
+ }
6
+ verifyTheme(props) {
7
+ return this._verifyThemeData(props);
8
+ }
9
+ verifySkinstoreFiles(props) {
10
+ return this._verifyThemeData(props);
11
+ }
12
+ verifyThumbnail(props) {
13
+ return this._verifyThemeData(props);
14
+ }
15
+ _verifyThemeData({ actionData, shopUrl, stream }) {
16
+ const { method, url } = actionData;
17
+ return this.#httpApi.fetch({
18
+ url: `${shopUrl}${url}`,
19
+ method,
20
+ data: stream,
21
+ sanitizeOptions: {
22
+ disable: true
23
+ },
24
+ headers: {
25
+ 'Content-Type': 'application/octet-stream'
26
+ },
27
+ isPrivate: true
28
+ });
29
+ }
30
+ }
@@ -0,0 +1,2 @@
1
+ export const THEME_VERIFY_API_NAME = 'ThemeVerifyApi';
2
+ export const THEME_VERIFY_FEATURE_NAME = 'ThemeVerify';
@@ -0,0 +1,19 @@
1
+ import { FEATURE_CORES_TYPES, HTTP_REQUESTER_API_NAME, SyncFeatureInitializer } from '@dreamcommerce/star_core';
2
+ import { ThemeVerifyApi } from './api/theme_verify_api.js';
3
+ import { THEME_VERIFY_FEATURE_NAME } from './theme_verify_constants.js';
4
+ import { ThemeVerifyService } from './verify/theme_verify_service.js';
5
+ export class ThemeVerifyInitializer extends SyncFeatureInitializer {
6
+ static featureName = THEME_VERIFY_FEATURE_NAME;
7
+ init() {
8
+ const httpApi = this.getApiSync(HTTP_REQUESTER_API_NAME);
9
+ const service = new ThemeVerifyService();
10
+ return {
11
+ cores: [
12
+ {
13
+ type: FEATURE_CORES_TYPES.api,
14
+ instance: new ThemeVerifyApi(service)
15
+ }
16
+ ]
17
+ };
18
+ }
19
+ }
@@ -0,0 +1,55 @@
1
+ import tmp from 'tmp-promise';
2
+ import { ThemePublishUtils } from '../../skinstore/theme_publish_utils.js';
3
+ import { ThemePushErrorsFactory } from '../../push/theme_push_errors_factory.js';
4
+ import { join } from '../../../../../utils/path_utils.js';
5
+ import { v4 as uuid } from 'uuid';
6
+ import { ThemeArchive } from '../../../../class/archive/theme_archive.js';
7
+ import { THEME_WILDCARD_ACTION_NAME } from '../../actions/service/theme_actions_service_constants.js';
8
+ import { THEME_ACTIONS_TYPES } from '../../actions/theme_actions_constants.js';
9
+ import { ThemeFilesStructureUtils } from '../../utils/files_structure/theme_files_structure_utils.js';
10
+ import { ThemeActionsUtils } from '../../actions/theme_actions_utils.js';
11
+ import { ArrayUtils } from '@dreamcommerce/utilities';
12
+ export class ThemeVerifyService {
13
+ async verifyTheme({ verifyAction, executionContext, credentials, themeChecksums, filesStructure, themeFilesUploadApi }) {
14
+ const { path: tmpDir } = await tmp.dir({ unsafeCleanup: true });
15
+ const themeRootDir = executionContext.themeRootDir;
16
+ if (await themeChecksums.hasThemeFileBeenCreated(ThemePublishUtils.getSkinStoreSettingsFilePath(themeRootDir)))
17
+ throw ThemePushErrorsFactory.createErrorWhilePushingUnpublishedThemeWithSkinstoreData(credentials.shopUrl);
18
+ try {
19
+ //TODO to do api?
20
+ const filesRecords = await ThemeActionsUtils.getFilesRecordsFromActionData({
21
+ themeRootDir,
22
+ themeAction: verifyAction,
23
+ filesStructure
24
+ });
25
+ const filesToVerify = await ArrayUtils.asyncFilter(filesRecords, async ({ path }) => (await themeChecksums.hasThemeFileBeenModified(path)) || !(await themeChecksums.hasThemeFileBeenCreated(path)));
26
+ if (filesToVerify.length) {
27
+ const { rejectedImageData } = await themeFilesUploadApi.uploadFiles(filesToVerify);
28
+ if (rejectedImageData.length)
29
+ return {
30
+ isSuccess: false,
31
+ messages: rejectedImageData.flatMap((file) => file.messages)
32
+ };
33
+ }
34
+ await this._createFilesList(themeRootDir, filesToVerify);
35
+ const themeArchivePath = join(tmpDir, `${uuid()}.zip`);
36
+ await new ThemeArchive(themeRootDir).createFullArchive({
37
+ dist: themeArchivePath,
38
+ actionValue: THEME_WILDCARD_ACTION_NAME,
39
+ actionType: THEME_ACTIONS_TYPES.push
40
+ });
41
+ const { isSuccess, messages } = await themeFilesUploadApi.uploadArchive({
42
+ action: verifyAction,
43
+ themeArchivePath,
44
+ credentials
45
+ });
46
+ return { isSuccess, messages };
47
+ }
48
+ finally {
49
+ await ThemeFilesStructureUtils.removeAFilesListFile(themeRootDir);
50
+ }
51
+ }
52
+ async _createFilesList(themeRootDir, filesRecords) {
53
+ await ThemeFilesStructureUtils.createAFilesListFile(themeRootDir, ThemeFilesStructureUtils.mapFilesRecordsToFilesList(filesRecords));
54
+ }
55
+ }
@@ -1,7 +1,6 @@
1
1
  import { THEME_COMMANDS_NAME } from '../../commands/theme_commands_constants.js';
2
2
  export const THEME_COMMANDS_THAT_REQUIRES_UP_TO_DATE_CHECKSUMS = [
3
3
  THEME_COMMANDS_NAME.push,
4
- THEME_COMMANDS_NAME.showChanges,
5
4
  THEME_COMMANDS_NAME.pull,
6
5
  THEME_COMMANDS_NAME.verify
7
6
  ];
@@ -3,6 +3,5 @@ export const THEME_COMMANDS_THAT_REQUIRED_ACTIONS_LIST = [
3
3
  THEME_COMMANDS_NAME.pull,
4
4
  THEME_COMMANDS_NAME.init,
5
5
  THEME_COMMANDS_NAME.push,
6
- THEME_COMMANDS_NAME.delete,
7
- THEME_COMMANDS_NAME.showChanges
6
+ THEME_COMMANDS_NAME.delete
8
7
  ];
@@ -1,21 +1,41 @@
1
1
  import { ThemeListCommand } from './commands/list/theme_list_command.js';
2
2
  import { ThemePullCommand } from './commands/pull/theme_pull_command.js';
3
3
  import { ThemeInitCommand } from './commands/init/theme_init_command.js';
4
- import { THEME_COMMANDS_NAME } from './commands/theme_commands_constants.js';
4
+ import { THEME_COMMANDS_NAME, THEME_TOPIC_NAME } from './commands/theme_commands_constants.js';
5
5
  import { ThemePushCommand } from './commands/push/theme_push_command.js';
6
- import { ThemeShowChangesCommand } from './commands/theme_show_changes_command.js';
7
6
  import { ThemeVerifyCommand } from './commands/theme_verify_command.js';
8
7
  import { ThemeInfoCommand } from './commands/info/theme_info_command.js';
9
8
  import { ThemeDeleteCommand } from './commands/delete/theme_delete_command.js';
10
9
  import { ThemePublishCommand } from './commands/publish/theme_publish_command.js';
10
+ import { ThemesListInitializer } from './features/themes/list/themes_list_initializer.js';
11
+ import { ThemeMergeInitializer } from './features/theme/merge/theme_merge_initializer.js';
12
+ import { ThemeFetchInitializer } from './features/theme/fetch/theme_fetch_initializer.js';
13
+ import { ThemeInitInitializer } from './features/theme/init/theme_init_initializer.js';
14
+ import { ThemePushInitializer } from './features/theme/push/theme_push_initializer.js';
15
+ import { ThemeVerifyInitializer } from './features/theme/verify/theme_verify_initializer.js';
16
+ import { ThemeDeleteInitializer } from './features/theme/delete/theme_delete_initalizer.js';
17
+ import { ThemeActionsInitializer } from './features/theme/actions/theme_actions_initializer.js';
11
18
  export const COMMANDS = {
12
19
  [THEME_COMMANDS_NAME.list]: ThemeListCommand,
13
20
  [THEME_COMMANDS_NAME.pull]: ThemePullCommand,
14
21
  [THEME_COMMANDS_NAME.init]: ThemeInitCommand,
15
22
  [THEME_COMMANDS_NAME.push]: ThemePushCommand,
16
- [THEME_COMMANDS_NAME.showChanges]: ThemeShowChangesCommand,
17
23
  [THEME_COMMANDS_NAME.verify]: ThemeVerifyCommand,
18
24
  [THEME_COMMANDS_NAME.info]: ThemeInfoCommand,
19
25
  [THEME_COMMANDS_NAME.delete]: ThemeDeleteCommand,
20
26
  [THEME_COMMANDS_NAME.publish]: ThemePublishCommand
21
27
  };
28
+ export const COMMAND_TO_FEATURES_MAP = {
29
+ [THEME_COMMANDS_NAME.pull]: [ThemeMergeInitializer, ThemeFetchInitializer],
30
+ [THEME_COMMANDS_NAME.init]: ThemeInitInitializer,
31
+ [THEME_COMMANDS_NAME.push]: [ThemeFetchInitializer, ThemePushInitializer],
32
+ [THEME_COMMANDS_NAME.verify]: ThemeVerifyInitializer,
33
+ [THEME_COMMANDS_NAME.delete]: ThemeDeleteInitializer
34
+ };
35
+ export const getThemeInitializersForCommand = (commandName) => {
36
+ if (!commandName)
37
+ return [];
38
+ const alwaysIncludedInitializers = [ThemesListInitializer, ThemeActionsInitializer];
39
+ const initializers = COMMAND_TO_FEATURES_MAP[`${THEME_TOPIC_NAME}:${commandName}`] ?? [];
40
+ return [...alwaysIncludedInitializers, ...(Array.isArray(initializers) ? initializers : [initializers])];
41
+ };
package/oclif.config.js CHANGED
@@ -2,7 +2,7 @@ export default {
2
2
  bin: 'shoper',
3
3
  scope: 'shoper',
4
4
  dirname: 'shoper_cli',
5
- plugins: ['@oclif/plugin-help', '@oclif/plugin-not-found', '@oclif/plugin-warn-if-update-available', '@oclif/plugin-version'],
5
+ plugins: ['@oclif/plugin-help', '@oclif/plugin-warn-if-update-available', '@oclif/plugin-version'],
6
6
  'warn-if-update-available': {
7
7
  frequency: 1,
8
8
  frequencyUnit: 'days',
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@shoper/cli",
3
3
  "packageManager": "yarn@3.2.0",
4
4
  "sideEffects": false,
5
- "version": "0.2.0",
5
+ "version": "0.2.1-0",
6
6
  "description": "CLI tool for Shoper",
7
7
  "author": "Joanna Firek",
8
8
  "license": "MIT",
@@ -31,10 +31,10 @@
31
31
  },
32
32
  "dependencies": {
33
33
  "@dreamcommerce/star_core": "1.8.5",
34
+ "@dreamcommerce/utilities": "1.20.1",
34
35
  "@oclif/core": "4.2.10",
35
36
  "@oclif/plugin-autocomplete": "3.2.27",
36
37
  "@oclif/plugin-help": "6.2.27",
37
- "@oclif/plugin-not-found": "3.2.49",
38
38
  "@oclif/plugin-version": "2.2.27",
39
39
  "@oclif/plugin-warn-if-update-available": "3.1.38",
40
40
  "axios": "1.8.4",
@@ -1,174 +0,0 @@
1
- //@ts-nocheck
2
- import { BaseCliCommand } from '../class/base_cli_command.js';
3
- import * as readline from 'node:readline/promises';
4
- import * as fs from 'node:fs';
5
- import { Args } from '@oclif/core';
6
- import process from 'node:process';
7
- import * as path from 'node:path';
8
- import { diffWords } from 'diff';
9
- export class CliFilesDiffCommand extends BaseCliCommand {
10
- static summary = 'Differentiate between two files';
11
- static args = {
12
- file1: Args.string({
13
- name: 'file1',
14
- required: true,
15
- description: 'First file to compare',
16
- type: 'string'
17
- }),
18
- file2: Args.string({
19
- name: 'file2',
20
- required: true,
21
- description: 'Second file to compare',
22
- type: 'string'
23
- })
24
- };
25
- async run() {
26
- const { args } = await this.parse(CliFilesDiffCommand);
27
- const inputAPath = args.file1;
28
- const inputBPath = args.file2;
29
- return new Promise((resolve, reject) => {
30
- Promise.all([readLines(inputAPath), readLines(inputBPath)]).then((result) => {
31
- const [aLines, bLines] = result;
32
- // console.log('aLines', aLines);
33
- // console.log('bLines', bLines);
34
- merge(aLines, bLines);
35
- resolve();
36
- });
37
- // aStream.on('line', (input) => {
38
- // console.log('aStream line: ', input);
39
- // });
40
- //
41
- // bStream.on('line', (input) => {
42
- // console.log('bStream line: ', input);
43
- // });
44
- });
45
- }
46
- }
47
- const readLines = async (filePath) => {
48
- return new Promise((resolve, reject) => {
49
- const lines = [];
50
- const inputStream = readline.createInterface({
51
- input: fs.createReadStream(path.resolve(process.cwd(), filePath)),
52
- crlfDelay: Infinity
53
- });
54
- inputStream.on('line', (line) => {
55
- lines.push(line);
56
- });
57
- inputStream.on('close', () => {
58
- resolve(lines);
59
- });
60
- inputStream.on('error', (err) => {
61
- reject(err);
62
- });
63
- });
64
- };
65
- const merge = (aLines, bLines) => {
66
- let hasConflict = false;
67
- const maxLength = Math.max(aLines.length, bLines.length);
68
- const outputPath = path.resolve(process.cwd(), 'merge.txt');
69
- const outStream = fs.createWriteStream(outputPath, { encoding: 'utf8' });
70
- for (let i = 0; i < maxLength; i++) {
71
- const a = aLines[i] ?? '';
72
- const b = bLines[i] ?? '';
73
- if (a === b) {
74
- outStream.write(a + '\n');
75
- }
76
- else {
77
- hasConflict = true;
78
- console.log('⚠️ Conflict detected:');
79
- console.log('a:', a);
80
- console.log('b:', b);
81
- const { aFormatted, bFormatted } = formatWordDiff(a, b);
82
- console.log('aFormatted', aFormatted);
83
- console.log('bFormatted', bFormatted);
84
- outStream.write('<<<<<<< mine\n');
85
- outStream.write(aFormatted + '\n');
86
- outStream.write('=======\n');
87
- outStream.write(bFormatted + '\n');
88
- outStream.write('>>>>>>> theirs\n');
89
- }
90
- }
91
- outStream.end();
92
- outStream.on('finish', () => {
93
- if (hasConflict) {
94
- console.log('⚠️ Conflicts detected. Check', outputPath);
95
- }
96
- else {
97
- console.log('✅ Merged with no conflicts. Output written to', outputPath);
98
- }
99
- });
100
- };
101
- function formatWordDiff(aLine, bLine) {
102
- const changes = diffWords(aLine, bLine);
103
- const aFormatted = changes.map((part) => (part.added ? '' : part.value)).join('');
104
- const bFormatted = changes.map((part) => (part.removed ? '' : part.value)).join('');
105
- return {
106
- aFormatted: aFormatted.trimEnd(),
107
- bFormatted: bFormatted.trimEnd()
108
- };
109
- }
110
- // const fs = require('fs');
111
- // const path = require('path');
112
- // const readline = require('readline');
113
- // const Diff = require('diff');
114
- //
115
- // async function mergeWithCommentsStream(fileAPath, fileBPath, outputPath) {
116
- // const textA = await fs.promises.readFile(fileAPath, 'utf8');
117
- // const textB = await fs.promises.readFile(fileBPath, 'utf8');
118
- //
119
- // const diffs = Diff.diffLines(textA, textB);
120
- // const outputStream = fs.createWriteStream(outputPath, { encoding: 'utf8' });
121
- //
122
- // let currentState = 'equal'; // 'equal' | 'added' | 'removed'
123
- //
124
- // for (const part of diffs) {
125
- // const lines = part.value.endsWith('\n') ? part.value.slice(0, -1).split('\n') : part.value.split('\n');
126
- //
127
- // if (part.added) {
128
- // if (currentState !== 'added') {
129
- // outputStream.write('added >>>>>\n');
130
- // currentState = 'added';
131
- // }
132
- // for (const line of lines) {
133
- // outputStream.write(line + '\n');
134
- // }
135
- // } else if (part.removed) {
136
- // if (currentState !== 'removed') {
137
- // outputStream.write('removed >>>>>\n');
138
- // currentState = 'removed';
139
- // }
140
- // for (const line of lines) {
141
- // outputStream.write(line + '\n');
142
- // }
143
- // } else {
144
- // // Close any previous block
145
- // if (currentState === 'added') {
146
- // outputStream.write('end-added >>>\n');
147
- // } else if (currentState === 'removed') {
148
- // outputStream.write('end-removed >>>\n');
149
- // }
150
- // currentState = 'equal';
151
- //
152
- // for (const line of lines) {
153
- // outputStream.write(line + '\n');
154
- // }
155
- // }
156
- // }
157
- //
158
- // // Final cleanup in case the last block was added/removed
159
- // if (currentState === 'added') {
160
- // outputStream.write('end-added >>>\n');
161
- // } else if (currentState === 'removed') {
162
- // outputStream.write('end-removed >>>\n');
163
- // }
164
- //
165
- // outputStream.end();
166
- // console.log(`✅ Merge complete. Output written to: ${outputPath}`);
167
- // }
168
- //
169
- // // Example usage
170
- // const fileA = path.resolve('versionA.txt');
171
- // const fileB = path.resolve('versionB.txt');
172
- // const outputFile = path.resolve('mergedWithComments.txt');
173
- //
174
- // mergeWithCommentsStream(fileA, fileB, outputFile);
@@ -1,61 +0,0 @@
1
- import { BaseThemeCommand } from '../class/base_theme_command.js';
2
- import { CLI_AUTH_API_NAME } from '../../cli/auth/cli_auth_constants.js';
3
- import { THEME_MERGE_API_NAME } from '../features/theme/merge/theme_merge_constants.js';
4
- import { EXECUTION_CONTEXT_API_NAME, EXECUTION_CONTEXTS } from '../../cli/features/execution_context/execution_context_constants.js';
5
- import { THEME_FETCH_API_NAME, THEME_FETCH_TYPES } from '../features/theme/fetch/theme_fetch_constants.js';
6
- import tmp from 'tmp-promise';
7
- import { THEME_ACTIONS_API_NAME, THEME_ACTIONS_TYPES } from '../features/theme/actions/theme_actions_constants.js';
8
- import { join } from '../../utils/path_utils.js';
9
- import ora from 'ora';
10
- export class ThemeShowChangesCommand extends BaseThemeCommand {
11
- static description = 'Show local theme changes';
12
- static hidden = true;
13
- async run() {
14
- const cliAuthApi = this.getApi(CLI_AUTH_API_NAME);
15
- const credentials = cliAuthApi.getCredentials();
16
- if (!credentials)
17
- this.error('Credentials not found. Please authorize first.');
18
- const executionContextApi = this.getApi(EXECUTION_CONTEXT_API_NAME);
19
- const themeMergeApi = this.getApi(THEME_MERGE_API_NAME);
20
- const themeFetchApi = this.getApi(THEME_FETCH_API_NAME);
21
- const themeActionsApi = this.getApi(THEME_ACTIONS_API_NAME);
22
- const executionContext = await executionContextApi.getExecutionContext();
23
- if (executionContext.type !== EXECUTION_CONTEXTS.theme)
24
- this.error('You cannot run this command outside theme context.');
25
- const { path: tmpDir } = await tmp.dir({ unsafeCleanup: true });
26
- const pullAction = themeActionsApi.getThemeAction({
27
- actionType: THEME_ACTIONS_TYPES.pull,
28
- themeId: executionContext.themeId,
29
- credentials
30
- });
31
- const spinner = ora('Pulling theme...').start();
32
- try {
33
- spinner.stopAndPersist({
34
- symbol: '\u2713',
35
- text: 'Calculating theme changes...'
36
- });
37
- const { name } = await themeFetchApi.fetchTheme({
38
- credentials,
39
- action: pullAction,
40
- config: {
41
- fetchType: THEME_FETCH_TYPES.full,
42
- dist: tmpDir
43
- }
44
- });
45
- const changes = await themeMergeApi.getChangesBetweenThemes(executionContext.themeRootDir, join(tmpDir, name));
46
- spinner.clear();
47
- if (!changes.length) {
48
- this.log('No changes found in your theme.');
49
- return;
50
- }
51
- this.log('Changes:\n');
52
- changes.forEach(([action, name]) => {
53
- console.log(name, ' - ', action);
54
- });
55
- }
56
- catch (err) {
57
- //TODO
58
- this.error(`Failed to fetch theme changes: ${err instanceof Error ? err.message : 'Unknown error'}`);
59
- }
60
- }
61
- }