@shoper/cli 0.9.0 → 0.9.2
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/README.md +11 -1
- package/build/theme/class/checksums/theme_checksums.js +2 -1
- package/build/theme/features/theme/actions/theme_actions_utils.js +5 -2
- package/build/theme/features/theme/fetch/service/theme_fetch_service.js +2 -0
- package/build/theme/features/theme/init/service/theme_init_service.js +2 -0
- package/build/theme/features/theme/push/theme_push_utils.js +7 -4
- package/build/theme/features/theme/utils/files/theme_files_utils.js +5 -2
- package/build/theme/features/theme/utils/meta_data/theme_meta_data_utils.js +28 -0
- package/build/theme/utils/directory_validator/directory_validator_utils.js +11 -4
- package/build/theme/utils/shoperignore/shoperignore_utils.js +37 -0
- package/package.json +3 -2
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)
|
|
@@ -154,7 +154,8 @@ export class ThemeChecksums {
|
|
|
154
154
|
join(SHOPER_THEME_METADATA_DIR, THEME_INITIAL_CHECKSUMS_FILE_NAME),
|
|
155
155
|
join(SHOPER_THEME_METADATA_DIR, THEME_INITIAL_CHECKSUMS_VERITY_FILE_NAME)
|
|
156
156
|
];
|
|
157
|
-
const
|
|
157
|
+
const allFiles = await ThemePushUtils.getAllFilesThatAreSendToRemote(this.#themeDir);
|
|
158
|
+
const filesToComputeChecksums = allFiles
|
|
158
159
|
.filter((path) => !filesToIgnoreInChecksums.some((ignorePath) => normalize(path) === ignorePath))
|
|
159
160
|
.map((relativePath) => join(this.#themeDir, relativePath));
|
|
160
161
|
this.#loggerApi?.debug('Computing checksums from file structure.', { details: { count: filesToComputeChecksums.length } });
|
|
@@ -5,6 +5,7 @@ import globs from 'fast-glob';
|
|
|
5
5
|
import { THEME_PUSH_WILDCARD_GLOBS_FOR_FILES } from '../push/service/theme_push_service_constants.js';
|
|
6
6
|
import micromatch from 'micromatch';
|
|
7
7
|
import difference from 'lodash/difference.js';
|
|
8
|
+
import { filterFiles, loadShoperIgnore } from '../../../utils/shoperignore/shoperignore_utils.js';
|
|
8
9
|
export class ThemeActionsUtils {
|
|
9
10
|
static getFilesGlobsThatMatchesAction({ filesStructure, actionValue, actionType }) {
|
|
10
11
|
return Object.entries(filesStructure).reduce((acc, [filePath, fileStructureItem]) => {
|
|
@@ -38,6 +39,7 @@ export class ThemeActionsUtils {
|
|
|
38
39
|
static async getFilesRecordsFromActionData({ themeRootDir, themeAction, filesStructure, themeChecksums }) {
|
|
39
40
|
const filesRecords = [];
|
|
40
41
|
const initialChecksums = await themeChecksums.getInitialChecksums();
|
|
42
|
+
const ignoreInstance = await loadShoperIgnore(themeRootDir);
|
|
41
43
|
for (const [actionKey, actionData] of Object.entries(themeAction.data)) {
|
|
42
44
|
if (actionData.type === THEME_ACTION_DATA_TYPE.file) {
|
|
43
45
|
const filesGlobs = ThemeActionsUtils.getFilesGlobsThatMatchesAction({
|
|
@@ -54,14 +56,15 @@ export class ThemeActionsUtils {
|
|
|
54
56
|
onlyFiles: true,
|
|
55
57
|
cwd: themeRootDir
|
|
56
58
|
});
|
|
59
|
+
const filteredFiles = await filterFiles(files, themeRootDir, ignoreInstance);
|
|
57
60
|
let processedFileGlob = fileGlob;
|
|
58
61
|
if (looksLikeDirectory(fileGlob)) {
|
|
59
62
|
processedFileGlob = fileGlob.endsWith(THEME_PUSH_WILDCARD_GLOBS_FOR_FILES)
|
|
60
63
|
? fileGlob.slice(0, fileGlob.length - 2)
|
|
61
64
|
: fileGlob;
|
|
62
65
|
}
|
|
63
|
-
const deletedFiles = difference(checksumMatchedFiles,
|
|
64
|
-
for (const filePath of [...
|
|
66
|
+
const deletedFiles = difference(checksumMatchedFiles, filteredFiles);
|
|
67
|
+
for (const filePath of [...filteredFiles, ...deletedFiles]) {
|
|
65
68
|
filesRecords.push({
|
|
66
69
|
actionData,
|
|
67
70
|
actionKey,
|
|
@@ -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');
|
|
@@ -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.');
|
|
@@ -3,6 +3,7 @@ import { AppError } from '../../../../cli/utilities/features/logger/logs/app_err
|
|
|
3
3
|
import { toUnixPath } from '../../../../utils/path_utils.js';
|
|
4
4
|
import { ThemeFilesUtils } from '../utils/files/theme_files_utils.js';
|
|
5
5
|
import { THEME_PUSH_WILDCARD_GLOBS_FOR_FILES } from './service/theme_push_service_constants.js';
|
|
6
|
+
import { filterFiles } from '../../../utils/shoperignore/shoperignore_utils.js';
|
|
6
7
|
export class ThemePushUtils {
|
|
7
8
|
static async getAllFilesThatAreSendToRemote(themeDir) {
|
|
8
9
|
const filesStructure = await ThemeFilesUtils.getThemeFilesStructure(themeDir);
|
|
@@ -17,11 +18,13 @@ export class ThemePushUtils {
|
|
|
17
18
|
.filter((path) => path !== '*');
|
|
18
19
|
//need unix styles globs
|
|
19
20
|
filesToArchive.push('styles/src/**/*');
|
|
20
|
-
|
|
21
|
-
suppressErrors: true,
|
|
21
|
+
const allFiles = await globs(filesToArchive, {
|
|
22
22
|
onlyFiles: true,
|
|
23
|
-
cwd: themeDir
|
|
24
|
-
|
|
23
|
+
cwd: themeDir,
|
|
24
|
+
suppressErrors: true
|
|
25
|
+
});
|
|
26
|
+
const filteredFiles = await filterFiles(allFiles, themeDir);
|
|
27
|
+
return filteredFiles.sort();
|
|
25
28
|
}
|
|
26
29
|
static _normalizeFileGlob(fileGlob) {
|
|
27
30
|
if (fileGlob.endsWith('/')) {
|
|
@@ -8,6 +8,7 @@ import path from 'node:path';
|
|
|
8
8
|
import { THEME_FILES_LIST_FILE_NAME } from '../../push/theme_push_constants.js';
|
|
9
9
|
import { THEME_ACTIONS_TYPES } from '../../actions/theme_actions_constants.js';
|
|
10
10
|
import { FILE_STATES } from '../../../../../utils/fs/fs_constants.js';
|
|
11
|
+
import { loadShoperIgnore } from '../../../../utils/shoperignore/shoperignore_utils.js';
|
|
11
12
|
export class ThemeFilesUtils {
|
|
12
13
|
static async getThemeFilesStructure(themeDirectory) {
|
|
13
14
|
const filesStructure = await readJSONFile(join(themeDirectory, SHOPER_THEME_METADATA_DIR, THEME_FILES_STRUCTURE_FILE_NAME));
|
|
@@ -29,10 +30,12 @@ export class ThemeFilesUtils {
|
|
|
29
30
|
}, {});
|
|
30
31
|
}
|
|
31
32
|
static async validateThemeDirectoryStructure({ checksums, permissions, rootDirectory }) {
|
|
33
|
+
const ignoreInstance = await loadShoperIgnore(rootDirectory);
|
|
32
34
|
return await validateDirectory({
|
|
33
35
|
permissions,
|
|
34
36
|
checksums,
|
|
35
|
-
rootDirectory
|
|
37
|
+
rootDirectory,
|
|
38
|
+
ignoreInstance
|
|
36
39
|
});
|
|
37
40
|
}
|
|
38
41
|
static async getThemeRootDirectories(themeDirectory) {
|
|
@@ -117,7 +120,7 @@ export class ThemeFilesUtils {
|
|
|
117
120
|
},
|
|
118
121
|
_links: { [THEME_ACTIONS_TYPES.push]: '*' }
|
|
119
122
|
};
|
|
120
|
-
['.gitignore'].forEach((fileName) => {
|
|
123
|
+
['.gitignore', '.shoperignore'].forEach((fileName) => {
|
|
121
124
|
fileStructure[fileName] = {
|
|
122
125
|
permissions: {
|
|
123
126
|
canAdd: true,
|
|
@@ -58,4 +58,32 @@ export class ThemeMetaDataUtils {
|
|
|
58
58
|
});
|
|
59
59
|
});
|
|
60
60
|
}
|
|
61
|
+
static async createShoperIgnoreFile(themeDir) {
|
|
62
|
+
return new Promise((resolve, reject) => {
|
|
63
|
+
const shoperIgnoreContent = `# .shoperignore
|
|
64
|
+
# Pliki i katalogi wymienione tutaj będą wykluczane z:
|
|
65
|
+
# - pakowania do archiwum podczas publikacji
|
|
66
|
+
# - uploadowania na serwer
|
|
67
|
+
# - weryfikacji checksum
|
|
68
|
+
|
|
69
|
+
# Przykłady:
|
|
70
|
+
# node_modules/
|
|
71
|
+
# .env
|
|
72
|
+
# .env.*
|
|
73
|
+
# *.log
|
|
74
|
+
# temp/
|
|
75
|
+
# .vscode/
|
|
76
|
+
# .idea/
|
|
77
|
+
`;
|
|
78
|
+
const writeStream = createWriteStream(join(themeDir, '.shoperignore'));
|
|
79
|
+
writeStream.write(shoperIgnoreContent);
|
|
80
|
+
writeStream.end();
|
|
81
|
+
writeStream.on('error', (err) => {
|
|
82
|
+
reject(err);
|
|
83
|
+
});
|
|
84
|
+
writeStream.on('finish', () => {
|
|
85
|
+
resolve();
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
}
|
|
61
89
|
}
|
|
@@ -5,7 +5,7 @@ import { computeFileChecksum } from '../../../utils/checksums/checksums_utils.js
|
|
|
5
5
|
import { DEFAULT_PERMISSION_FOR_ALL_FILES_KEY, DEFAULT_PERMISSION_FOR_DIRECTORY_KEY, FILES_MODIFICATION_TYPES, PERMISSION_KEY } from './directory_validator_constants.js';
|
|
6
6
|
import { CHECKSUM_KEY } from '../../../utils/checksums/checksums_utils_constants.js';
|
|
7
7
|
import _ from 'lodash';
|
|
8
|
-
export const validateDirectory = async ({ rootDirectory, permissions, checksums }) => {
|
|
8
|
+
export const validateDirectory = async ({ rootDirectory, permissions, checksums, ignoreInstance }) => {
|
|
9
9
|
const unpermittedActions = [];
|
|
10
10
|
await _checkPermissions({
|
|
11
11
|
permissions,
|
|
@@ -14,14 +14,15 @@ export const validateDirectory = async ({ rootDirectory, permissions, checksums
|
|
|
14
14
|
parts: [],
|
|
15
15
|
checksumsPart: checksums,
|
|
16
16
|
rootDirectory,
|
|
17
|
-
unpermittedActions
|
|
17
|
+
unpermittedActions,
|
|
18
|
+
ignoreInstance
|
|
18
19
|
});
|
|
19
20
|
return {
|
|
20
21
|
isValid: !unpermittedActions.length,
|
|
21
22
|
unpermittedActions
|
|
22
23
|
};
|
|
23
24
|
};
|
|
24
|
-
const _checkPermissions = async ({ permissionPart, permissionParts, parts = [], checksumsPart, rootDirectory, unpermittedActions, permissions }) => {
|
|
25
|
+
const _checkPermissions = async ({ permissionPart, permissionParts, parts = [], checksumsPart, rootDirectory, unpermittedActions, permissions, ignoreInstance }) => {
|
|
25
26
|
if (!permissionPart)
|
|
26
27
|
return;
|
|
27
28
|
//TODO required files in a custom module directory
|
|
@@ -40,6 +41,11 @@ const _checkPermissions = async ({ permissionPart, permissionParts, parts = [],
|
|
|
40
41
|
filesInsideCurrentDirectoryObject[path] = true;
|
|
41
42
|
});
|
|
42
43
|
for (const file of files) {
|
|
44
|
+
const relativePath = join(parentPath, file);
|
|
45
|
+
// Skip files that are in .shoperignore
|
|
46
|
+
if (ignoreInstance && ignoreInstance.ignores(relativePath)) {
|
|
47
|
+
continue;
|
|
48
|
+
}
|
|
43
49
|
const { permission, permissionKey } = _getPermission({
|
|
44
50
|
path: file,
|
|
45
51
|
fullPath: join(fullPath, file),
|
|
@@ -65,7 +71,8 @@ const _checkPermissions = async ({ permissionPart, permissionParts, parts = [],
|
|
|
65
71
|
checksumsPart: checksumsPart?.[file] ?? {},
|
|
66
72
|
rootDirectory,
|
|
67
73
|
unpermittedActions,
|
|
68
|
-
permissions
|
|
74
|
+
permissions,
|
|
75
|
+
ignoreInstance
|
|
69
76
|
});
|
|
70
77
|
}
|
|
71
78
|
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { readFile } from 'node:fs/promises';
|
|
2
|
+
import { join } from '../../../utils/path_utils.js';
|
|
3
|
+
import { fileExists } from '../../../utils/fs/fs_utils.js';
|
|
4
|
+
import ignoreModule from 'ignore';
|
|
5
|
+
const ignore = ignoreModule;
|
|
6
|
+
export const SHOPER_IGNORE_FILE_NAME = '.shoperignore';
|
|
7
|
+
export async function loadShoperIgnore(themeRootDir) {
|
|
8
|
+
const shoperIgnorePath = join(themeRootDir, SHOPER_IGNORE_FILE_NAME);
|
|
9
|
+
if (!(await fileExists(shoperIgnorePath))) {
|
|
10
|
+
return null;
|
|
11
|
+
}
|
|
12
|
+
try {
|
|
13
|
+
const content = await readFile(shoperIgnorePath, 'utf-8');
|
|
14
|
+
const ig = ignore();
|
|
15
|
+
ig.add('.shoperignore');
|
|
16
|
+
ig.add('.gitignore');
|
|
17
|
+
ig.add(content);
|
|
18
|
+
return ig;
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
export async function filterFiles(files, themeRootDir, ignoreInstance) {
|
|
25
|
+
const ig = ignoreInstance !== undefined ? ignoreInstance : await loadShoperIgnore(themeRootDir);
|
|
26
|
+
if (!ig) {
|
|
27
|
+
return files;
|
|
28
|
+
}
|
|
29
|
+
return files.filter((file) => !ig.ignores(file));
|
|
30
|
+
}
|
|
31
|
+
export async function isIgnored(filePath, themeRootDir, ignoreInstance) {
|
|
32
|
+
const ig = ignoreInstance !== undefined ? ignoreInstance : await loadShoperIgnore(themeRootDir);
|
|
33
|
+
if (!ig) {
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
36
|
+
return ig.ignores(filePath);
|
|
37
|
+
}
|
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.9.
|
|
5
|
+
"version": "0.9.2",
|
|
6
6
|
"description": "CLI tool for Shoper",
|
|
7
7
|
"author": "Joanna Firek",
|
|
8
8
|
"license": "MIT",
|
|
@@ -48,9 +48,11 @@
|
|
|
48
48
|
"chalk": "5.4.1",
|
|
49
49
|
"conf": "13.1.0",
|
|
50
50
|
"fast-glob": "3.3.3",
|
|
51
|
+
"figlet": "1.9.4",
|
|
51
52
|
"figures": "6.1.0",
|
|
52
53
|
"fs-extra": "11.3.0",
|
|
53
54
|
"fs-tree-diff": "2.0.1",
|
|
55
|
+
"ignore": "7.0.5",
|
|
54
56
|
"ink": "6.0.1",
|
|
55
57
|
"ink-link": "4.1.0",
|
|
56
58
|
"ink-gradient": "3.0.0",
|
|
@@ -77,7 +79,6 @@
|
|
|
77
79
|
"micromatch": "4.0.8",
|
|
78
80
|
"walk-sync": "3.0.0",
|
|
79
81
|
"yauzl": "3.2.0",
|
|
80
|
-
"figlet": "1.9.4",
|
|
81
82
|
"yazl": "3.3.1",
|
|
82
83
|
"puppeteer": "24.31.0",
|
|
83
84
|
"gradient-string": "3.0.0"
|