@shoper/cli 0.7.1-1 → 0.8.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 (52) hide show
  1. package/README.md +11 -1
  2. package/build/cli/class/errors/http/http_errors_factory.js +1 -1
  3. package/build/cli/core/cli_setup.js +6 -6
  4. package/build/cli/index.js +0 -1
  5. package/build/cli/utilities/features/logger/logs/app_logs_constants.js +1 -2
  6. package/build/index.js +2 -2
  7. package/build/theme/class/archive/theme_archive.js +47 -1
  8. package/build/theme/{features/theme/utils/archive/theme_archive_utils_errors_factory.js → class/archive/theme_archive_errors_factory.js} +1 -1
  9. package/build/theme/class/checksums/theme_checksums.js +11 -51
  10. package/build/theme/commands/pull/theme_pull_command.js +3 -4
  11. package/build/theme/commands/push/theme_push_command.js +8 -17
  12. package/build/theme/commands/theme_commands_constants.js +1 -2
  13. package/build/theme/commands/theme_verify_command.js +3 -3
  14. package/build/theme/commands/ui/theme_error.js +6 -6
  15. package/build/theme/features/theme/actions/theme_actions_constants.js +1 -2
  16. package/build/theme/features/theme/actions/theme_actions_utils.js +9 -60
  17. package/build/theme/features/theme/fetch/service/theme_fetch_service.js +4 -2
  18. package/build/theme/features/theme/init/service/theme_init_service.js +4 -2
  19. package/build/theme/features/theme/merge/service/theme_merge_service.js +2 -2
  20. package/build/theme/features/theme/push/api/theme_push_api.js +2 -2
  21. package/build/theme/features/theme/push/service/theme_push_service.js +33 -93
  22. package/build/theme/features/theme/push/theme_push_utils.js +9 -6
  23. package/build/theme/features/theme/utils/files_structure/theme_file_structure_errors_factory.js +9 -1
  24. package/build/theme/features/theme/utils/{files/theme_files_utils.js → files_structure/theme_files_structure_utils.js} +28 -36
  25. package/build/theme/features/theme/utils/meta_data/theme_meta_data_utils.js +28 -0
  26. package/build/theme/features/theme/verify/verify/theme_verify_service.js +12 -19
  27. package/build/theme/index.js +2 -6
  28. package/build/theme/utils/directory_validator/directory_validator_utils.js +11 -4
  29. package/build/theme/utils/shoperignore/shoperignore_utils.js +36 -0
  30. package/build/ui/ui_dump/ui_dump.js +4 -9
  31. package/build/utils/array_utils.js +0 -3
  32. package/build/utils/fs/fs_utils.js +1 -1
  33. package/package.json +6 -10
  34. package/build/theme/class/browser/browser.js +0 -108
  35. package/build/theme/commands/watch/theme_watch_command.js +0 -88
  36. package/build/theme/commands/watch/theme_watch_constants.js +0 -21
  37. package/build/theme/commands/watch/theme_watch_utils.js +0 -32
  38. package/build/theme/commands/watch/theme_watching_info.js +0 -14
  39. package/build/theme/commands/watch/watch.js +0 -55
  40. package/build/theme/features/theme/push/service/theme_push_service_types.js +0 -1
  41. package/build/theme/features/theme/utils/archive/theme_archive_utils.js +0 -26
  42. package/build/theme/features/theme/utils/files/them_files_constants.js +0 -1
  43. package/build/theme/features/theme/watch/api/theme_watch_api.js +0 -19
  44. package/build/theme/features/theme/watch/service/theme_watch_service.js +0 -167
  45. package/build/theme/features/theme/watch/theme_watch_constants.js +0 -4
  46. package/build/theme/features/theme/watch/theme_watch_initializer.js +0 -22
  47. package/build/ui/command_input/command_input.js +0 -25
  48. package/build/ui/logs/log_entry.js +0 -12
  49. package/build/ui/logs/logs_constants.js +0 -20
  50. package/build/ui/logs/logs_list.js +0 -18
  51. package/build/ui/logs/use_logs.js +0 -23
  52. package/build/utils/fs/fs_constants.js +0 -6
package/README.md CHANGED
@@ -2,4 +2,14 @@
2
2
 
3
3
  # Shoper CLI
4
4
 
5
- Learn more in the [commands docs](https://storefront.developers.shoper.pl/cli/)
5
+ Learn more in the [commands docs](https://storefront.developers.shoper.pl/cli/)
6
+
7
+ ## Features
8
+
9
+ ### .shoperignore
10
+
11
+ Plik `.shoperignore` pozwala na wykluczenie określonych plików i katalogów z pakowania, uploadowania oraz weryfikacji checksum.
12
+
13
+ Używa tej samej składni co `.gitignore`. Plik jest automatycznie tworzony podczas inicjalizacji lub pobierania motywu.
14
+
15
+ Więcej informacji: [SHOPERIGNORE.md](docs/SHOPERIGNORE.md)
@@ -15,7 +15,7 @@ export class HttpErrorsFactory {
15
15
  }
16
16
  static createNotFoundError() {
17
17
  return new AppError({
18
- message: '404 Not found',
18
+ message: 'Not found',
19
19
  code: HTTP_NOT_FOUND_ERROR_CODE
20
20
  });
21
21
  }
@@ -29,7 +29,7 @@ export const cliSetup = async () => {
29
29
  LoggerInitializer,
30
30
  CliAuthTokensInitializer,
31
31
  CliAuthInitializer,
32
- ...getInitializersBasedOnCommand()
32
+ ...getCommandBaseInitializers()
33
33
  ],
34
34
  options: {
35
35
  featuresTracking: false,
@@ -37,15 +37,15 @@ export const cliSetup = async () => {
37
37
  }
38
38
  });
39
39
  };
40
- const getInitializersBasedOnCommand = () => {
41
- if (isCommandWithTopic(process.argv[2]))
42
- return getInitializersBasedOnCommandWithTopic();
40
+ const getCommandBaseInitializers = () => {
41
+ if (isCommandsTopic(process.argv[2]))
42
+ return getCommandWithTopicBaseInitializers();
43
43
  return [];
44
44
  };
45
- const isCommandWithTopic = (arg) => {
45
+ const isCommandsTopic = (arg) => {
46
46
  return arg === THEME_TOPIC_NAME || arg === CLI_AUTH_TOPIC_NAME;
47
47
  };
48
- const getInitializersBasedOnCommandWithTopic = () => {
48
+ const getCommandWithTopicBaseInitializers = () => {
49
49
  const topic = process.argv[2];
50
50
  const command = process.argv[3];
51
51
  switch (topic) {
@@ -6,7 +6,6 @@ export const runCLI = async () => {
6
6
  await execute({ dir: import.meta.url });
7
7
  }
8
8
  catch (error) {
9
- console.log('Error executing CLI command:', error);
10
9
  // TODO handle error
11
10
  }
12
11
  };
@@ -3,6 +3,5 @@ export const APP_LOGS_TYPES = {
3
3
  error: 'error',
4
4
  warn: 'warn',
5
5
  info: 'info',
6
- debug: 'debug',
7
- success: 'success'
6
+ debug: 'debug'
8
7
  };
package/build/index.js CHANGED
@@ -31,7 +31,7 @@ process.on('uncaughtException', (err) => {
31
31
  const loggerApi = getApiSync(LOGGER_API_NAME);
32
32
  if (loggerApi)
33
33
  loggerApi.fatal('Uncaught Exception', { error: err });
34
- // process.exit(1);
34
+ process.exit(1);
35
35
  });
36
36
  process.on('unhandledRejection', (reason) => {
37
37
  console.error('Unhandled Rejection:', reason);
@@ -42,7 +42,7 @@ process.on('unhandledRejection', (reason) => {
42
42
  reason
43
43
  }
44
44
  });
45
- // process.exit(1);
45
+ process.exit(1);
46
46
  });
47
47
  const signals = ['SIGINT', 'SIGTERM', 'SIGQUIT'];
48
48
  signals.forEach((signal) => {
@@ -1 +1,47 @@
1
- export {};
1
+ import globs from 'fast-glob';
2
+ import { createZip } from '../../../utils/zip/create_zip_utils.js';
3
+ import { ThemeFilesStructureUtils } from '../../features/theme/utils/files_structure/theme_files_structure_utils.js';
4
+ import { extname, join } from '../../../utils/path_utils.js';
5
+ import { formatJSONFile } from '../../../utils/fs/fs_utils.js';
6
+ import { ThemeArchiveErrorsFactory } from './theme_archive_errors_factory.js';
7
+ import { ThemeFileStructureErrorsFactory } from '../../features/theme/utils/files_structure/theme_file_structure_errors_factory.js';
8
+ import { ThemeActionsUtils } from '../../features/theme/actions/theme_actions_utils.js';
9
+ import { filterFiles } from '../../utils/shoperignore/shoperignore_utils.js';
10
+ export class ThemeArchive {
11
+ #themeRootDir;
12
+ constructor(themeRootDir) {
13
+ this.#themeRootDir = themeRootDir;
14
+ }
15
+ async createFullArchive({ dist, actionValue, actionType, logger }) {
16
+ const filesStructure = await ThemeFilesStructureUtils.getThemeFilesStructure(this.#themeRootDir);
17
+ if (!filesStructure)
18
+ throw ThemeFileStructureErrorsFactory.createNoFilesStructureError();
19
+ try {
20
+ const filesInThemeDirectory = await globs(ThemeActionsUtils.getFilesGlobsThatMatchesActionName({
21
+ actionType,
22
+ actionValue,
23
+ filesStructure
24
+ }), {
25
+ suppressErrors: true,
26
+ onlyFiles: true,
27
+ cwd: this.#themeRootDir
28
+ });
29
+ const filteredFiles = await filterFiles(filesInThemeDirectory, this.#themeRootDir);
30
+ await this._formatJsonFiles(this.#themeRootDir, filteredFiles);
31
+ await createZip({
32
+ baseDir: this.#themeRootDir,
33
+ dist,
34
+ files: filteredFiles,
35
+ logger
36
+ });
37
+ }
38
+ catch (err) {
39
+ throw ThemeArchiveErrorsFactory.createErrorWhileCreatingThemeArchive(err);
40
+ }
41
+ }
42
+ async _formatJsonFiles(themeRootDir, filesToUpload) {
43
+ await Promise.all(filesToUpload
44
+ .filter((path) => extname(path).toLowerCase() === '.json')
45
+ .map((jsonFile) => formatJSONFile(join(themeRootDir, jsonFile))));
46
+ }
47
+ }
@@ -1,4 +1,4 @@
1
- import { AppError } from '../../../../../cli/utilities/features/logger/logs/app_error.js';
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({
@@ -10,7 +10,6 @@ import { normalize } from 'node:path';
10
10
  import { ThemePushUtils } from '../../features/theme/push/theme_push_utils.js';
11
11
  import { SHOPER_THEME_METADATA_DIR } from '../../constants/directory_contstants.js';
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
- import { FILE_STATES } from '../../../utils/fs/fs_constants.js';
14
13
  export class ThemeChecksums {
15
14
  #themeDir;
16
15
  #loggerApi;
@@ -34,6 +33,12 @@ export class ThemeChecksums {
34
33
  this.#initialChecksums = await this._getChecksums(this.#initialChecksumsFilePath);
35
34
  return this.#initialChecksums;
36
35
  }
36
+ getInitialChecksumsSync() {
37
+ if (this.#initialChecksums)
38
+ return this.#initialChecksums;
39
+ this.#initialChecksums = this._getChecksumsSync(this.#initialChecksumsFilePath);
40
+ return this.#initialChecksums;
41
+ }
37
42
  async getCurrentChecksums() {
38
43
  if (this.#currentChecksums)
39
44
  return this.#currentChecksums;
@@ -67,22 +72,10 @@ export class ThemeChecksums {
67
72
  const initialChecksum = await this.getInitialChecksumFromPath(path);
68
73
  return !initialChecksum && (await fileExists(join(this.#themeDir, path)));
69
74
  }
70
- async getFileState(path) {
71
- const initialChecksum = await this.getInitialChecksumFromPath(path);
75
+ async hasThemeFileBeenModified(path) {
72
76
  const currentChecksum = await this.getCurrentChecksumFromPath(path);
73
- const fileExistsInFs = await fileExists(join(this.#themeDir, path));
74
- if (!initialChecksum && fileExistsInFs) {
75
- return FILE_STATES.created;
76
- }
77
- else if (initialChecksum && !fileExistsInFs) {
78
- return FILE_STATES.deleted;
79
- }
80
- else if (initialChecksum && currentChecksum && initialChecksum !== currentChecksum) {
81
- return FILE_STATES.modified;
82
- }
83
- else {
84
- return FILE_STATES.unchanged;
85
- }
77
+ const initialChecksum = await this.getInitialChecksumFromPath(path);
78
+ return !!currentChecksum && !!initialChecksum && currentChecksum !== initialChecksum;
86
79
  }
87
80
  async verify() {
88
81
  const initialChecksumFilePath = this.#initialChecksumsFilePath;
@@ -94,8 +87,6 @@ export class ThemeChecksums {
94
87
  async updateAllChecksums() {
95
88
  this.#loggerApi?.debug('Updating all checksums.');
96
89
  const checksums = await this.computeThemeChecksums();
97
- this.#initialChecksums = checksums;
98
- this.#currentChecksums = checksums;
99
90
  return new Promise((resolve, reject) => {
100
91
  const currentChecksumFilePath = this.#currentChecksumsFilePath;
101
92
  const initialChecksumFilePath = this.#initialChecksumsFilePath;
@@ -123,7 +114,6 @@ export class ThemeChecksums {
123
114
  async updateCurrentChecksums() {
124
115
  this.#loggerApi?.debug('Updating current checksums.');
125
116
  const checksums = await this.computeThemeChecksums();
126
- this.#currentChecksums = checksums;
127
117
  return new Promise((resolve, reject) => {
128
118
  const currentChecksumFilePath = this.#currentChecksumsFilePath;
129
119
  const checksumStream = createWriteStream(currentChecksumFilePath);
@@ -154,7 +144,8 @@ export class ThemeChecksums {
154
144
  join(SHOPER_THEME_METADATA_DIR, THEME_INITIAL_CHECKSUMS_FILE_NAME),
155
145
  join(SHOPER_THEME_METADATA_DIR, THEME_INITIAL_CHECKSUMS_VERITY_FILE_NAME)
156
146
  ];
157
- const filesToComputeChecksums = (await ThemePushUtils.getAllFilesThatAreSendToRemote(this.#themeDir))
147
+ const allFiles = await ThemePushUtils.getAllFilesThatAreSendToRemote(this.#themeDir);
148
+ const filesToComputeChecksums = allFiles
158
149
  .filter((path) => !filesToIgnoreInChecksums.some((ignorePath) => normalize(path) === ignorePath))
159
150
  .map((relativePath) => join(this.#themeDir, relativePath));
160
151
  this.#loggerApi?.debug('Computing checksums from file structure.', { details: { count: filesToComputeChecksums.length } });
@@ -164,37 +155,6 @@ export class ThemeChecksums {
164
155
  this.#loggerApi?.debug('Finished computing theme checksums.', { details: { count: Object.keys(allChecksums).length } });
165
156
  return allChecksums;
166
157
  }
167
- async groupFilesByStatus(files) {
168
- const initialChecksums = await this.getInitialChecksums();
169
- const currentChecksums = await this.getCurrentChecksums();
170
- return files.reduce((acc, filePath) => {
171
- const initialChecksum = initialChecksums[toCurrentPlatformPath(filePath)];
172
- const currentChecksum = currentChecksums[toCurrentPlatformPath(filePath)];
173
- if (!initialChecksum) {
174
- acc.created.push(filePath);
175
- }
176
- else if (currentChecksum && initialChecksum !== currentChecksum) {
177
- acc.modified.push(filePath);
178
- }
179
- else if (initialChecksum && !currentChecksum) {
180
- acc.deleted.push(filePath);
181
- }
182
- else {
183
- acc.unchanged.push(filePath);
184
- }
185
- return acc;
186
- }, {
187
- created: [],
188
- modified: [],
189
- unchanged: [],
190
- deleted: []
191
- });
192
- }
193
- async getDeletedFiles() {
194
- const initialChecksums = await this.getInitialChecksums();
195
- const currentChecksums = await this.getCurrentChecksums();
196
- return Object.keys(initialChecksums).filter((filePath) => !currentChecksums[filePath]);
197
- }
198
158
  async _getInitialChecksumsVerification() {
199
159
  return await readJSONFile(this.#initialChecksumsVerificationFilePath);
200
160
  }
@@ -24,9 +24,8 @@ import { Info } from '../../../ui/message_box/info.js';
24
24
  import { directoryExists } from '../../../utils/fs/fs_utils.js';
25
25
  import { ThemeMetaDataUtils } from '../../features/theme/utils/meta_data/theme_meta_data_utils.js';
26
26
  import { ThemeChecksums } from '../../class/checksums/theme_checksums.js';
27
- import { LOGGER_API_NAME } from '../../../cli/utilities/features/logger/logger_constants.js';
28
27
  import { ThemeError } from '../ui/theme_error.js';
29
- import { MODULES_DIRECTORY_NAME } from '../../features/theme/theme_constants.js';
28
+ import { LOGGER_API_NAME } from '../../../cli/utilities/features/logger/logger_constants.js';
30
29
  //TODO jak jest error w pullu wowczas usuwamy docelowo pociagniety skin
31
30
  export class ThemePullCommand extends BaseThemeCommand {
32
31
  static summary = 'Downloads the current version of your theme from the store and overwrites your local theme files.';
@@ -161,9 +160,9 @@ export class ThemePullCommand extends BaseThemeCommand {
161
160
  dist: tmpDir
162
161
  }
163
162
  });
164
- const localModulesPath = join(executionContext.themeRootDir, MODULES_DIRECTORY_NAME);
163
+ const localModulesPath = join(executionContext.themeRootDir, 'modules');
165
164
  const fetchedThemePath = join(tmpDir, name);
166
- const fetchedModulesPath = join(fetchedThemePath, MODULES_DIRECTORY_NAME);
165
+ const fetchedModulesPath = join(fetchedThemePath, 'modules');
167
166
  if ((await directoryExists(localModulesPath)) && (await directoryExists(fetchedModulesPath)))
168
167
  await ThemeResourcesWithIdDirectoryUtils.updateDirectoryNamesOfResourcesWithIdAccordingToLocalThemeNames(localModulesPath, fetchedModulesPath, fetchedThemePath, loggerApi);
169
168
  this.#spinner.stop();
@@ -4,6 +4,8 @@ import { EXECUTION_CONTEXT_API_NAME, EXECUTION_CONTEXTS } from '../../../cli/fea
4
4
  import { THEME_ACTIONS_API_NAME, THEME_ACTIONS_TYPES } from '../../features/theme/actions/theme_actions_constants.js';
5
5
  import { THEME_PUSH_API_NAME } from '../../features/theme/push/theme_push_constants.js';
6
6
  import { ThemeMetaDataUtils } from '../../features/theme/utils/meta_data/theme_meta_data_utils.js';
7
+ import { mapChecksumToTree } from '../../../utils/checksums/checksums_utils.js';
8
+ import { mapToPermissionsTree } from '../../utils/directory_validator/directory_validator_utils.js';
7
9
  import { renderOnce } from '../../../ui/ui_utils.js';
8
10
  import { UnpermittedCommandError } from '../ui/unpermitted_command_error.js';
9
11
  import React from 'react';
@@ -12,7 +14,7 @@ import { MissingCredentialsError } from '../../../cli/commands/auth/ui/missing_c
12
14
  import { ThemePushedSuccess } from './ui/theme_pushed_success.js';
13
15
  import ora from 'ora';
14
16
  import { ThemePushSkipInfo } from './ui/theme_push_skip_into.js';
15
- import { ThemeFilesUtils } from '../../features/theme/utils/files/theme_files_utils.js';
17
+ import { ThemeFilesStructureUtils } from '../../features/theme/utils/files_structure/theme_files_structure_utils.js';
16
18
  import { ThemeInfoUtils } from '../../features/theme/info/theme_info_utils.js';
17
19
  import { ThemeChecksums } from '../../class/checksums/theme_checksums.js';
18
20
  import { ThemeFilesUpload } from '../../class/files_upload/theme_files_upload.js';
@@ -22,8 +24,6 @@ import { MissingThemeFiles } from '../ui/missing_theme_files.js';
22
24
  import { SHOPER_THEME_METADATA_DIR } from '../../constants/directory_contstants.js';
23
25
  import { THEME_FILES_STRUCTURE_FILE_NAME } from '../../features/theme/theme_constants.js';
24
26
  import { ThemeError } from '../ui/theme_error.js';
25
- import { mapToPermissionsTree } from '../../utils/directory_validator/directory_validator_utils.js';
26
- import { mapChecksumToTree } from '../../../utils/checksums/checksums_utils.js';
27
27
  import { ThemeUnpermittedActionsError } from './ui/theme_unpermitted_actions_error.js';
28
28
  import { LOGGER_API_NAME } from '../../../cli/utilities/features/logger/logger_constants.js';
29
29
  export class ThemePushCommand extends BaseThemeCommand {
@@ -68,7 +68,7 @@ export class ThemePushCommand extends BaseThemeCommand {
68
68
  return;
69
69
  }
70
70
  const initialChecksums = await themeChecksums.getInitialChecksums();
71
- const permissions = await ThemeFilesUtils.getFilesPermissions(executionContext.themeRootDir);
71
+ const permissions = await ThemeFilesStructureUtils.getFilesPermissions(executionContext.themeRootDir);
72
72
  const themeFilesUploadApi = new ThemeFilesUpload({
73
73
  themeRootDir: executionContext.themeRootDir,
74
74
  credentials,
@@ -80,7 +80,7 @@ export class ThemePushCommand extends BaseThemeCommand {
80
80
  renderOnce(React.createElement(ThemePushSkipInfo, null));
81
81
  return;
82
82
  }
83
- const validationResult = await ThemeFilesUtils.validateThemeDirectoryStructure({
83
+ const validationResult = await ThemeFilesStructureUtils.validateThemeDirectoryStructure({
84
84
  //TDO przeniesc do theme checksums
85
85
  checksums: mapChecksumToTree(initialChecksums),
86
86
  permissions: mapToPermissionsTree(permissions),
@@ -92,25 +92,16 @@ export class ThemePushCommand extends BaseThemeCommand {
92
92
  renderOnce(React.createElement(ThemeUnpermittedActionsError, { unpermittedActions: validationResult.unpermittedActions }));
93
93
  return;
94
94
  }
95
- const filesStructure = await ThemeFilesUtils.getThemeFilesStructure(executionContext.themeRootDir);
95
+ const filesStructure = await ThemeFilesStructureUtils.getThemeFilesStructure(executionContext.themeRootDir);
96
96
  if (!filesStructure) {
97
97
  renderOnce(React.createElement(MissingThemeFiles, { files: `${SHOPER_THEME_METADATA_DIR}/${THEME_FILES_STRUCTURE_FILE_NAME}` }));
98
98
  return;
99
99
  }
100
100
  spinner = ora('Pushing theme...').start();
101
- const partialPushAction = themeActionsApi.getThemeAction({
102
- actionType: THEME_ACTIONS_TYPES.partialPush,
103
- themeId: executionContext.themeId,
104
- credentials
105
- });
106
- if (!pushAction) {
107
- renderOnce(React.createElement(UnpermittedCommandError, { themeId: executionContext.themeId, commandName: "push" }));
108
- return;
109
- }
110
- await themePushApi.partialPush({
101
+ await themePushApi.push({
111
102
  credentials,
112
103
  filesStructure,
113
- action: partialPushAction,
104
+ pushAction,
114
105
  themeChecksums,
115
106
  executionContext,
116
107
  themeFilesUploadApi
@@ -7,6 +7,5 @@ export const THEME_COMMANDS_NAME = {
7
7
  verify: `${THEME_TOPIC_NAME}:verify`,
8
8
  info: `${THEME_TOPIC_NAME}:info`,
9
9
  delete: `${THEME_TOPIC_NAME}:delete`,
10
- publish: `${THEME_TOPIC_NAME}:publish`,
11
- watch: `${THEME_TOPIC_NAME}:watch`
10
+ publish: `${THEME_TOPIC_NAME}:publish`
12
11
  };
@@ -1,6 +1,7 @@
1
1
  import { BaseThemeCommand } from '../class/base_theme_command.js';
2
2
  import { CLI_AUTH_API_NAME } from '../../cli/auth/cli_auth_constants.js';
3
3
  import { EXECUTION_CONTEXT_API_NAME, EXECUTION_CONTEXTS } from '../../cli/features/execution_context/execution_context_constants.js';
4
+ import { ThemeFilesStructureUtils } from '../features/theme/utils/files_structure/theme_files_structure_utils.js';
4
5
  import { ThemeChecksums } from '../class/checksums/theme_checksums.js';
5
6
  import { THEME_VERIFY_API_NAME } from '../features/theme/verify/theme_verify_constants.js';
6
7
  import { THEME_ACTIONS_API_NAME, THEME_ACTIONS_TYPES } from '../features/theme/actions/theme_actions_constants.js';
@@ -17,7 +18,6 @@ import { MissingCredentialsError } from '../../cli/commands/auth/ui/missing_cred
17
18
  import { OutsideOfThemeDirectoryContextError } from './ui/ouside_of_theme_directory_context_error.js';
18
19
  import ora from 'ora';
19
20
  import { ThemeError } from './ui/theme_error.js';
20
- import { ThemeFilesUtils } from '../features/theme/utils/files/theme_files_utils.js';
21
21
  import { LOGGER_API_NAME } from '../../cli/utilities/features/logger/logger_constants.js';
22
22
  export class ThemeVerifyCommand extends BaseThemeCommand {
23
23
  static description = 'Verify theme files structure';
@@ -39,7 +39,7 @@ export class ThemeVerifyCommand extends BaseThemeCommand {
39
39
  themeDir: executionContext.themeRootDir,
40
40
  loggerApi
41
41
  });
42
- const permissions = await ThemeFilesUtils.getFilesPermissions(executionContext.themeRootDir);
42
+ const permissions = await ThemeFilesStructureUtils.getFilesPermissions(executionContext.themeRootDir);
43
43
  if (!themeChecksums || !permissions)
44
44
  this.error('Theme checksums or permissions not found. Please ensure you are in the correct theme directory.');
45
45
  let spinner;
@@ -56,7 +56,7 @@ export class ThemeVerifyCommand extends BaseThemeCommand {
56
56
  themeId: executionContext.themeId,
57
57
  credentials
58
58
  });
59
- const filesStructure = await ThemeFilesUtils.getThemeFilesStructure(executionContext.themeRootDir);
59
+ const filesStructure = await ThemeFilesStructureUtils.getThemeFilesStructure(executionContext.themeRootDir);
60
60
  if (!filesStructure) {
61
61
  renderOnce(React.createElement(MissingThemeFiles, { files: `${SHOPER_THEME_METADATA_DIR}/${THEME_FILES_STRUCTURE_FILE_NAME}` }));
62
62
  return;
@@ -9,18 +9,18 @@ import React from 'react';
9
9
  import { ThemeWorkUrlMismatch } from './theme_work_url_mismatch.js';
10
10
  import { EXECUTION_CONTEXTS } from '../../../cli/features/execution_context/execution_context_constants.js';
11
11
  export const ThemeError = ({ err, executionContext }) => {
12
- if (err?.tags?.code === THEME_ACTION_NOT_FOUND_ERROR_CODE && executionContext?.type === EXECUTION_CONTEXTS.theme) {
12
+ if (err?.code === THEME_ACTION_NOT_FOUND_ERROR_CODE && executionContext?.type === EXECUTION_CONTEXTS.theme) {
13
13
  return (React.createElement(UnpermittedCommandError, { themeId: executionContext.themeId, commandName: "verify" }));
14
14
  }
15
- if (err?.tags?.code === THEME_ARCHIVE_UPLOAD_ERROR) {
15
+ if (err?.code === THEME_ARCHIVE_UPLOAD_ERROR) {
16
16
  return React.createElement(ValidationErrors, { errors: err?.details });
17
17
  }
18
- if (err?.tags?.code === THEME_WORK_URL_MISMATCH_ERROR) {
18
+ if (err?.code === THEME_WORK_URL_MISMATCH_ERROR) {
19
19
  return React.createElement(ThemeWorkUrlMismatch, { command: "verify" });
20
20
  }
21
- if (err?.tags?.code === THEME_FILES_UPLOAD_ERROR) {
22
- return (React.createElement(Error, { header: "Uploading theme files to the shop failed." },
23
- React.createElement(Text, null, "Please ensure that the files are not corrupted and that the file extensions are supported or file size is not too large.")));
21
+ if (err?.code === THEME_FILES_UPLOAD_ERROR) {
22
+ return (React.createElement(Error, { header: "Uploading theme files to the shop failed.\n" },
23
+ React.createElement(Text, null, "The rejected files have been removed locally. Please ensure that the files are not corrupted and that the file extensions are supported.")));
24
24
  }
25
25
  if (err?.message) {
26
26
  return React.createElement(ValidationErrors, { errors: err.message });
@@ -8,8 +8,7 @@ export const THEME_ACTIONS_TYPES = {
8
8
  delete: 'delete',
9
9
  publishForm: 'publish_form',
10
10
  publish: 'publish',
11
- verify: 'verify',
12
- partialPush: 'partial_push'
11
+ verify: 'verify'
13
12
  };
14
13
  export const THEME_ACTION_DATA_TYPE = {
15
14
  file: 'file',
@@ -3,10 +3,9 @@ import { basename, looksLikeDirectory, toUnixPath } from '../../../../utils/path
3
3
  import { THEME_ACTION_DATA_TYPE } from './theme_actions_constants.js';
4
4
  import globs from 'fast-glob';
5
5
  import { THEME_PUSH_WILDCARD_GLOBS_FOR_FILES } from '../push/service/theme_push_service_constants.js';
6
- import micromatch from 'micromatch';
7
- import difference from 'lodash/difference.js';
6
+ import { filterFiles, loadShoperIgnore } from '../../../utils/shoperignore/shoperignore_utils.js';
8
7
  export class ThemeActionsUtils {
9
- static getFilesGlobsThatMatchesAction({ filesStructure, actionValue, actionType }) {
8
+ static getFilesGlobsThatMatchesActionName({ filesStructure, actionValue, actionType }) {
10
9
  return Object.entries(filesStructure).reduce((acc, [filePath, fileStructureItem]) => {
11
10
  if (fileStructureItem._links?.[actionType] && this._doesActionValueMatch(fileStructureItem._links[actionType], actionValue)) {
12
11
  return [...acc, toUnixPath(filePath)];
@@ -14,32 +13,13 @@ export class ThemeActionsUtils {
14
13
  return acc;
15
14
  }, []);
16
15
  }
17
- static async getFilesThatMatchesAction({ actionType, actionValue, filesStructure, rootDir }) {
18
- return await globs(ThemeActionsUtils.getFilesGlobsThatMatchesAction({
19
- actionType,
20
- actionValue,
21
- filesStructure
22
- }), {
23
- suppressErrors: true,
24
- onlyFiles: true,
25
- cwd: rootDir
26
- });
27
- }
28
- static async getDeletedFilesThatMatchesAction({ actionType, actionValue, themeChecksums, filesStructure }) {
29
- const deletedFiles = await themeChecksums.getDeletedFiles();
30
- const globs = ThemeActionsUtils.getFilesGlobsThatMatchesAction({
31
- actionType,
32
- actionValue,
33
- filesStructure
34
- });
35
- return deletedFiles.filter((path) => !looksLikeDirectory(path) && micromatch.isMatch(path, globs));
36
- }
37
- static async getFilesRecordsFromActionData({ themeRootDir, themeAction, filesStructure, themeChecksums }) {
16
+ static async getFilesRecordsFromActionData({ themeRootDir, themeAction, filesStructure }) {
38
17
  const filesRecords = [];
39
- const initialChecksums = await themeChecksums.getInitialChecksums();
18
+ // Load the ignore instance once before the loops to avoid reading .shoperignore multiple times
19
+ const ignoreInstance = await loadShoperIgnore(themeRootDir);
40
20
  for (const [actionKey, actionData] of Object.entries(themeAction.data)) {
41
21
  if (actionData.type === THEME_ACTION_DATA_TYPE.file) {
42
- const filesGlobs = ThemeActionsUtils.getFilesGlobsThatMatchesAction({
22
+ const filesGlobs = ThemeActionsUtils.getFilesGlobsThatMatchesActionName({
43
23
  //TODO remove when backend fixed
44
24
  actionType: 'push',
45
25
  // actionType: themeAction.actionType,
@@ -47,27 +27,25 @@ export class ThemeActionsUtils {
47
27
  filesStructure
48
28
  });
49
29
  for (const fileGlob of filesGlobs) {
50
- const checksumMatchedFiles = micromatch(Object.keys(initialChecksums), [fileGlob]);
51
30
  const files = await globs(fileGlob, {
52
31
  suppressErrors: true,
53
32
  onlyFiles: true,
54
33
  cwd: themeRootDir
55
34
  });
35
+ const filteredFiles = await filterFiles(files, themeRootDir, ignoreInstance);
56
36
  let processedFileGlob = fileGlob;
57
37
  if (looksLikeDirectory(fileGlob)) {
58
38
  processedFileGlob = fileGlob.endsWith(THEME_PUSH_WILDCARD_GLOBS_FOR_FILES)
59
39
  ? fileGlob.slice(0, fileGlob.length - 2)
60
40
  : fileGlob;
61
41
  }
62
- const deletedFiles = difference(checksumMatchedFiles, files);
63
- for (const filePath of [...files, ...deletedFiles]) {
42
+ for (const filePath of filteredFiles) {
64
43
  filesRecords.push({
65
44
  actionData,
66
45
  actionKey,
67
46
  path: filePath,
68
47
  fileName: basename(filePath),
69
- fileGlob: processedFileGlob,
70
- state: await themeChecksums.getFileState(filePath)
48
+ fileGlob: processedFileGlob
71
49
  });
72
50
  }
73
51
  }
@@ -75,35 +53,6 @@ export class ThemeActionsUtils {
75
53
  }
76
54
  return filesRecords;
77
55
  }
78
- // static async getFilesRecordsFromFiles({
79
- // files,
80
- // themeRootDir,
81
- // themeAction,
82
- // filesStructure,
83
- // themeChecksums
84
- // }: {
85
- // files: string[];
86
- // themeRootDir: string;
87
- // themeAction: TThemeAction;
88
- // filesStructure: TThemeFilesStructure;
89
- // themeChecksums: ThemeChecksums;
90
- // }): Promise<TFileRecord[]> {
91
- // const fileRecords: TFileRecord[] = [];
92
- //
93
- // for (const file of files) {
94
- // const state = await themeChecksums.getFileState(file);
95
- //
96
- // if (state) {
97
- // fileRecords.push({
98
- // path: file,
99
- // state,
100
- // action: themeAction
101
- // });
102
- // }
103
- // }
104
- //
105
- // return fileRecords;
106
- // }
107
56
  static _doesActionValueMatch(currentActionValue, valuesToMatch) {
108
57
  if (typeof valuesToMatch === 'string') {
109
58
  return currentActionValue === valuesToMatch || valuesToMatch === THEME_ALL_ACTIONS_NAME;
@@ -8,9 +8,9 @@ import { FetchResources } from '../../../../class/fetch_resources/fetch_resource
8
8
  import { jsonIndentTransform } from '../../../../../utils/stream_transforms/json_indent_transform.js';
9
9
  import { JSON_FILE_INDENT } from '../../../../../cli/cli_constants.js';
10
10
  import { AppError } from '../../../../../cli/utilities/features/logger/logs/app_error.js';
11
+ import { ThemeFilesStructureUtils } from '../../utils/files_structure/theme_files_structure_utils.js';
11
12
  import { ThemeChecksums } from '../../../../class/checksums/theme_checksums.js';
12
13
  import { ThemeMetaDataUtils } from '../../utils/meta_data/theme_meta_data_utils.js';
13
- import { ThemeFilesUtils } from '../../utils/files/theme_files_utils.js';
14
14
  export class ThemeFetchService {
15
15
  #themeHttpApi;
16
16
  #httpApi;
@@ -55,7 +55,7 @@ export class ThemeFetchService {
55
55
  }
56
56
  try {
57
57
  this.#loggerApi.debug('Updating theme files structure.');
58
- await ThemeFilesUtils.updateFilesStructure(themeDir);
58
+ await ThemeFilesStructureUtils.updateFilesStructure(themeDir);
59
59
  }
60
60
  catch (error) {
61
61
  throw new AppError({
@@ -66,6 +66,8 @@ export class ThemeFetchService {
66
66
  }
67
67
  this.#loggerApi.debug('Creating .gitignore file.');
68
68
  await ThemeMetaDataUtils.createGitIgnoreFile(themeDir);
69
+ this.#loggerApi.debug('Creating .shoperignore file.');
70
+ await ThemeMetaDataUtils.createShoperIgnoreFile(themeDir);
69
71
  this.#loggerApi.debug('Updating metadata file.');
70
72
  await ThemeMetaDataUtils.updateMetadataFileWithWorkUrl(themeDir, credentials.shopUrl);
71
73
  this.#loggerApi.debug('Updating theme checksums');
@@ -6,8 +6,8 @@ import { extractZip } from '../../../../../utils/zip/extract_zip_utils.js';
6
6
  import { ThemeMetaDataUtils } from '../../utils/meta_data/theme_meta_data_utils.js';
7
7
  import { ThemeInfoUtils } from '../../info/theme_info_utils.js';
8
8
  import { ThemeChecksums } from '../../../../class/checksums/theme_checksums.js';
9
+ import { ThemeFilesStructureUtils } from '../../utils/files_structure/theme_files_structure_utils.js';
9
10
  import { AppError } from '../../../../../cli/utilities/features/logger/logs/app_error.js';
10
- import { ThemeFilesUtils } from '../../utils/files/theme_files_utils.js';
11
11
  export class ThemeInitService {
12
12
  #httpApi;
13
13
  #loggerApi;
@@ -41,7 +41,7 @@ export class ThemeInitService {
41
41
  this.#loggerApi.info('Theme archive extracted.');
42
42
  try {
43
43
  this.#loggerApi.debug('Updating theme files structure.');
44
- await ThemeFilesUtils.updateFilesStructure(themeDir);
44
+ await ThemeFilesStructureUtils.updateFilesStructure(themeDir);
45
45
  }
46
46
  catch (err) {
47
47
  throw new AppError({
@@ -52,6 +52,8 @@ export class ThemeInitService {
52
52
  }
53
53
  this.#loggerApi.debug('Creating .gitignore file.');
54
54
  await ThemeMetaDataUtils.createGitIgnoreFile(themeDir);
55
+ this.#loggerApi.debug('Creating .shoperignore file.');
56
+ await ThemeMetaDataUtils.createShoperIgnoreFile(themeDir);
55
57
  this.#loggerApi.debug('Updating metadata file.');
56
58
  await ThemeMetaDataUtils.updateMetadataFileWithWorkUrl(themeDir, credentials.shopUrl);
57
59
  this.#loggerApi.debug('Updating theme checksums.');
@@ -2,9 +2,9 @@ import FSTree from 'fs-tree-diff';
2
2
  import { copyFileSync, getAllDirectoriesNamesInside } from '../../../../../utils/fs/fs_utils.js';
3
3
  import walkSync from 'walk-sync';
4
4
  import { ThemeChecksums } from '../../../../class/checksums/theme_checksums.js';
5
+ import { ThemeFilesStructureUtils } from '../../utils/files_structure/theme_files_structure_utils.js';
5
6
  import { SHOPER_THEME_METADATA_DIR } from '../../../../constants/directory_contstants.js';
6
7
  import { join, platformSeparator } from '../../../../../utils/path_utils.js';
7
- import { ThemeFilesUtils } from '../../utils/files/theme_files_utils.js';
8
8
  export class ThemeMergeService {
9
9
  #loggerApi;
10
10
  constructor({ loggerApi }) {
@@ -73,7 +73,7 @@ export class ThemeMergeService {
73
73
  return patch;
74
74
  }
75
75
  async _getUserRootDirectories(themeDir) {
76
- const filesStructure = await ThemeFilesUtils.getThemeRootDirectories(themeDir);
76
+ const filesStructure = await ThemeFilesStructureUtils.getThemeRootDirectories(themeDir);
77
77
  return (await getAllDirectoriesNamesInside(themeDir, {
78
78
  recursive: false,
79
79
  hidden: true
@@ -7,7 +7,7 @@ export class ThemePushApi extends FeatureApi {
7
7
  super();
8
8
  this.#service = service;
9
9
  }
10
- async partialPush(props) {
11
- return this.#service.partialPush(props);
10
+ async push(props) {
11
+ return this.#service.push(props);
12
12
  }
13
13
  }