@bigcommerce/stencil-cli 8.2.0 → 8.4.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.
- package/bin/stencil-bundle.js +7 -18
- package/bin/stencil-debug.js +5 -9
- package/bin/stencil-download.js +7 -19
- package/bin/stencil-init.js +4 -8
- package/bin/stencil-pull.js +5 -11
- package/bin/stencil-push.js +5 -8
- package/bin/stencil-release.js +5 -10
- package/bin/stencil-scss-autofix.js +6 -11
- package/bin/stencil-start.js +6 -9
- package/bin/stencil.js +2 -3
- package/constants.js +9 -9
- package/lib/BuildConfigManager.js +25 -23
- package/lib/Cycles.js +3 -12
- package/lib/ScssValidator.js +5 -9
- package/lib/StencilCLISettings.js +1 -3
- package/lib/StencilConfigManager.js +6 -22
- package/lib/StencilDebug.js +7 -10
- package/lib/archiveManager.js +7 -23
- package/lib/bundle-validator.js +28 -64
- package/lib/cliCommon.js +11 -21
- package/lib/commander.js +2 -3
- package/lib/content-api-client.js +5 -13
- package/lib/css/compile.js +5 -11
- package/lib/graphql/query.js +5 -8
- package/lib/lang/validator.js +5 -11
- package/lib/lang-assembler.js +4 -10
- package/lib/lang-helper.js +3 -6
- package/lib/nodeSass/AutoFixer.js +10 -19
- package/lib/nodeSass/BaseFixer.js +5 -7
- package/lib/nodeSass/BaseRulesFixer.js +3 -8
- package/lib/nodeSass/CommaRemovalFixer.js +3 -8
- package/lib/nodeSass/ConditionalImportFixer.js +3 -9
- package/lib/nodeSass/UndefinedVariableFixer.js +3 -6
- package/lib/parse-json.js +10 -9
- package/lib/regions.js +3 -23
- package/lib/release/questions.js +3 -13
- package/lib/release/release.js +16 -55
- package/lib/spinner.js +1 -2
- package/lib/stencil-bundle.js +19 -78
- package/lib/stencil-download.js +5 -6
- package/lib/stencil-download.utils.js +5 -18
- package/lib/stencil-init.js +8 -24
- package/lib/stencil-pull.js +4 -5
- package/lib/stencil-pull.utils.js +5 -28
- package/lib/stencil-push.js +3 -4
- package/lib/stencil-push.utils.js +12 -102
- package/lib/stencil-start.js +19 -48
- package/lib/store-settings-api-client.js +4 -7
- package/lib/template-assembler.js +6 -45
- package/lib/theme-api-client.js +7 -30
- package/lib/theme-config.js +16 -59
- package/lib/utils/NetworkUtils.js +6 -16
- package/lib/utils/asyncUtils.js +2 -3
- package/lib/utils/frontmatter.js +4 -5
- package/lib/utils/fsUtils.js +6 -10
- package/lib/validator/schema-translations.js +5 -7
- package/package.json +12 -9
- package/server/config.js +3 -7
- package/server/index.js +29 -29
- package/server/lib/page-type-util.js +2 -5
- package/server/lib/show-logo.js +3 -3
- package/server/lib/utils.js +6 -11
- package/server/manifest.js +7 -11
- package/server/plugins/renderer/renderer.module.js +38 -100
- package/server/plugins/renderer/responses/index.js +7 -4
- package/server/plugins/renderer/responses/pencil-response.js +4 -32
- package/server/plugins/renderer/responses/raw-response.js +6 -14
- package/server/plugins/renderer/responses/redirect-response.js +1 -4
- package/server/plugins/router/router.module.js +9 -14
- package/server/plugins/theme-assets/theme-assets.module.js +15 -27
- package/lib/css-assembler.js +0 -99
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
const { THEME_PATH, API_HOST } = require('../constants');
|
|
1
|
+
import 'colors';
|
|
2
|
+
import fsModule from 'fs';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
import fsUtilsModule from './utils/fsUtils.js';
|
|
5
|
+
import { THEME_PATH, API_HOST } from '../constants.js';
|
|
7
6
|
|
|
8
7
|
class StencilConfigManager {
|
|
9
8
|
constructor({
|
|
@@ -15,13 +14,11 @@ class StencilConfigManager {
|
|
|
15
14
|
this.oldConfigFileName = '.stencil';
|
|
16
15
|
this.configFileName = 'config.stencil.json';
|
|
17
16
|
this.secretsFileName = 'secrets.stencil.json';
|
|
18
|
-
|
|
19
17
|
this.themePath = themePath;
|
|
20
18
|
this.oldConfigPath = path.join(themePath, this.oldConfigFileName);
|
|
21
19
|
this.configPath = path.join(themePath, this.configFileName);
|
|
22
20
|
this.secretsPath = path.join(themePath, this.secretsFileName);
|
|
23
21
|
this.secretFieldsSet = new Set(['accessToken', 'githubToken']);
|
|
24
|
-
|
|
25
22
|
this._fs = fs;
|
|
26
23
|
this._fsUtils = fsUtils;
|
|
27
24
|
this._logger = logger;
|
|
@@ -46,21 +43,17 @@ class StencilConfigManager {
|
|
|
46
43
|
await this._migrateOldConfig(parsedConfig);
|
|
47
44
|
return this._validateStencilConfig(parsedConfig, ignoreMissingFields);
|
|
48
45
|
}
|
|
49
|
-
|
|
50
46
|
const generalConfig = this._fs.existsSync(this.configPath)
|
|
51
47
|
? await this._fsUtils.parseJsonFile(this.configPath)
|
|
52
48
|
: null;
|
|
53
49
|
const secretsConfig = await this._getSecretsConfig(generalConfig);
|
|
54
|
-
|
|
55
50
|
if (generalConfig || secretsConfig) {
|
|
56
51
|
const parsedConfig = { ...generalConfig, ...secretsConfig };
|
|
57
52
|
return this._validateStencilConfig(parsedConfig, ignoreMissingFields);
|
|
58
53
|
}
|
|
59
|
-
|
|
60
54
|
if (ignoreFileNotExists) {
|
|
61
55
|
return null;
|
|
62
56
|
}
|
|
63
|
-
|
|
64
57
|
throw new Error('Please run'.red + ' $ stencil init'.cyan + ' first.'.red);
|
|
65
58
|
}
|
|
66
59
|
|
|
@@ -69,7 +62,6 @@ class StencilConfigManager {
|
|
|
69
62
|
*/
|
|
70
63
|
async save(config) {
|
|
71
64
|
const { generalConfig, secretsConfig } = this._splitStencilConfig(config);
|
|
72
|
-
|
|
73
65
|
await this._fs.promises.writeFile(this.configPath, JSON.stringify(generalConfig, null, 2));
|
|
74
66
|
await this._fs.promises.writeFile(this.secretsPath, JSON.stringify(secretsConfig, null, 2));
|
|
75
67
|
}
|
|
@@ -100,12 +92,10 @@ class StencilConfigManager {
|
|
|
100
92
|
async _getSecretsConfig(generalConfig) {
|
|
101
93
|
if (generalConfig && generalConfig.secretsFileName) {
|
|
102
94
|
const secretsPath = path.join(this.themePath, generalConfig.secretsFileName);
|
|
103
|
-
|
|
104
95
|
if (this._fs.existsSync(secretsPath)) {
|
|
105
96
|
return this._fsUtils.parseJsonFile(secretsPath);
|
|
106
97
|
}
|
|
107
98
|
}
|
|
108
|
-
|
|
109
99
|
return this._fs.existsSync(this.secretsPath)
|
|
110
100
|
? this._fsUtils.parseJsonFile(this.secretsPath)
|
|
111
101
|
: null;
|
|
@@ -119,7 +109,6 @@ class StencilConfigManager {
|
|
|
119
109
|
*/
|
|
120
110
|
_validateStencilConfig(configFile, ignoreMissingFields) {
|
|
121
111
|
const config = configFile;
|
|
122
|
-
|
|
123
112
|
if (!ignoreMissingFields && (!config.normalStoreUrl || !config.customLayouts)) {
|
|
124
113
|
throw new Error(
|
|
125
114
|
'Error: Your stencil config is outdated. Please run'.red +
|
|
@@ -127,13 +116,11 @@ class StencilConfigManager {
|
|
|
127
116
|
' again.'.red,
|
|
128
117
|
);
|
|
129
118
|
}
|
|
130
|
-
|
|
131
119
|
if (!config.apiHost) {
|
|
132
120
|
config.apiHost = API_HOST;
|
|
133
121
|
} else {
|
|
134
122
|
this._logger.log(`API host set in config file: ${config.apiHost}`);
|
|
135
123
|
}
|
|
136
|
-
|
|
137
124
|
return config;
|
|
138
125
|
}
|
|
139
126
|
|
|
@@ -146,10 +133,8 @@ class StencilConfigManager {
|
|
|
146
133
|
`Detected a deprecated ${this.oldConfigFileName.cyan} file.\n` +
|
|
147
134
|
`It will be replaced with ${this.configFileName.cyan} and ${this.secretsFileName.cyan}\n`,
|
|
148
135
|
);
|
|
149
|
-
|
|
150
136
|
await this.save(config);
|
|
151
137
|
await this._fs.promises.unlink(this.oldConfigPath);
|
|
152
|
-
|
|
153
138
|
this._logger.log(
|
|
154
139
|
`The deprecated ${this.oldConfigFileName.cyan} file was successfully replaced.\n` +
|
|
155
140
|
`Make sure to add ${this.secretsFileName.cyan} to .gitignore.\n` +
|
|
@@ -157,5 +142,4 @@ class StencilConfigManager {
|
|
|
157
142
|
);
|
|
158
143
|
}
|
|
159
144
|
}
|
|
160
|
-
|
|
161
|
-
module.exports = StencilConfigManager;
|
|
145
|
+
export default StencilConfigManager;
|
package/lib/StencilDebug.js
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
const StencilConfigManager = require('./StencilConfigManager');
|
|
1
|
+
import fsModule from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import osModule from 'os';
|
|
4
|
+
import { PACKAGE_INFO, THEME_PATH } from '../constants.js';
|
|
5
|
+
import ThemeConfig from './theme-config.js';
|
|
6
|
+
import StencilConfigManager from './StencilConfigManager.js';
|
|
8
7
|
|
|
9
8
|
class StencilDebug {
|
|
10
9
|
constructor({
|
|
@@ -55,7 +54,6 @@ class StencilDebug {
|
|
|
55
54
|
|
|
56
55
|
async getThemeInfo() {
|
|
57
56
|
this.checkExecutableLocation(this._themeConfig);
|
|
58
|
-
|
|
59
57
|
const rawConfig = await this._themeConfig.getRawConfig();
|
|
60
58
|
const {
|
|
61
59
|
name,
|
|
@@ -109,5 +107,4 @@ class StencilDebug {
|
|
|
109
107
|
}
|
|
110
108
|
}
|
|
111
109
|
}
|
|
112
|
-
|
|
113
|
-
module.exports = StencilDebug;
|
|
110
|
+
export default StencilDebug;
|
package/lib/archiveManager.js
CHANGED
|
@@ -1,15 +1,10 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
const fs = require('fs');
|
|
7
|
-
const path = require('path');
|
|
8
|
-
const { promisify } = require('util');
|
|
9
|
-
const stream = require('stream');
|
|
1
|
+
import yauzl from 'yauzl';
|
|
2
|
+
import fs from 'fs';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
import { promisify } from 'util';
|
|
5
|
+
import stream from 'stream';
|
|
10
6
|
|
|
11
7
|
const pipeline = promisify(stream.pipeline);
|
|
12
|
-
|
|
13
8
|
/**
|
|
14
9
|
* @param {object} options
|
|
15
10
|
* @param {string} options.zipPath
|
|
@@ -20,9 +15,7 @@ const pipeline = promisify(stream.pipeline);
|
|
|
20
15
|
*/
|
|
21
16
|
async function extractZipFiles({ zipPath, fileToExtract, exclude = [], outputNames = {} }) {
|
|
22
17
|
let foundMatch = false;
|
|
23
|
-
|
|
24
18
|
const zipFile = await promisify(yauzl.open)(zipPath, { lazyEntries: true });
|
|
25
|
-
|
|
26
19
|
await new Promise((resolve, reject) => {
|
|
27
20
|
zipFile.on('entry', async (entry) => {
|
|
28
21
|
try {
|
|
@@ -33,50 +26,41 @@ async function extractZipFiles({ zipPath, fileToExtract, exclude = [], outputNam
|
|
|
33
26
|
exclude.length &&
|
|
34
27
|
// startsWith used to exclude files from the specified directories
|
|
35
28
|
exclude.some((excludeItem) => entry.fileName.startsWith(excludeItem));
|
|
36
|
-
|
|
37
29
|
if (isDirectory || isNotTheSearchedFile || isExcluded) {
|
|
38
30
|
zipFile.readEntry();
|
|
39
31
|
return;
|
|
40
32
|
}
|
|
41
|
-
|
|
42
33
|
const outputFilePath = outputNames[entry.fileName] || entry.fileName;
|
|
43
34
|
const outputDir = path.parse(outputFilePath).dir;
|
|
44
35
|
// Create a directory if the parent directory does not exists
|
|
45
36
|
if (outputDir && !fs.existsSync(outputDir)) {
|
|
46
37
|
await fs.promises.mkdir(outputDir, { recursive: true });
|
|
47
38
|
}
|
|
48
|
-
|
|
49
39
|
const readableStream = await promisify(zipFile.openReadStream.bind(zipFile))(entry);
|
|
50
40
|
const writeStream = fs.createWriteStream(outputFilePath, { flag: 'w+' });
|
|
51
41
|
await pipeline(readableStream, writeStream);
|
|
52
|
-
|
|
53
42
|
if (foundMatch) {
|
|
54
43
|
zipFile.close();
|
|
55
44
|
resolve();
|
|
56
45
|
return;
|
|
57
46
|
}
|
|
58
|
-
|
|
59
47
|
zipFile.readEntry();
|
|
60
48
|
} catch (err) {
|
|
61
49
|
reject(err);
|
|
62
50
|
}
|
|
63
51
|
});
|
|
64
|
-
|
|
65
52
|
zipFile.readEntry();
|
|
66
|
-
|
|
67
53
|
zipFile.once('end', () => {
|
|
68
54
|
zipFile.close();
|
|
69
|
-
|
|
70
55
|
if (!foundMatch && fileToExtract) {
|
|
71
56
|
reject(new Error(`${fileToExtract} not found`));
|
|
72
57
|
return;
|
|
73
58
|
}
|
|
74
|
-
|
|
75
59
|
resolve();
|
|
76
60
|
});
|
|
77
61
|
});
|
|
78
62
|
}
|
|
79
|
-
|
|
80
|
-
|
|
63
|
+
export { extractZipFiles };
|
|
64
|
+
export default {
|
|
81
65
|
extractZipFiles,
|
|
82
66
|
};
|
package/lib/bundle-validator.js
CHANGED
|
@@ -1,21 +1,17 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
const themeConfigValidationSchema = require('./schemas/themeConfig.json');
|
|
16
|
-
const themeValidationSchema = require('./schemas/themeSchema.json');
|
|
17
|
-
const ScssValidator = require('./ScssValidator');
|
|
18
|
-
const LangHelpersValidator = require('./lang/validator');
|
|
1
|
+
import 'colors';
|
|
2
|
+
import os from 'os';
|
|
3
|
+
import { isObject, isArray, difference, uniq, flatten, every } from 'lodash-es';
|
|
4
|
+
import async from 'async';
|
|
5
|
+
import fs from 'fs';
|
|
6
|
+
import sizeOf from 'image-size';
|
|
7
|
+
import path from 'path';
|
|
8
|
+
import Validator from 'ajv';
|
|
9
|
+
import yamlValidator from 'js-yaml';
|
|
10
|
+
import { recursiveReadDir } from './utils/fsUtils.js';
|
|
11
|
+
import { getFrontmatterContent, interpolateThemeSettings } from './utils/frontmatter.js';
|
|
12
|
+
import ValidatorSchemaTranslations from './validator/schema-translations.js';
|
|
13
|
+
import ScssValidator from './ScssValidator.js';
|
|
14
|
+
import LangHelpersValidator from './lang/validator.js';
|
|
19
15
|
|
|
20
16
|
const VALID_IMAGE_TYPES = ['.jpg', '.jpeg', '.png', '.gif'];
|
|
21
17
|
const WIDTH_COMPOSED = 600;
|
|
@@ -28,6 +24,15 @@ const MAX_SIZE_COMPOSED = 1024 * 1024 * 2; // 2MB
|
|
|
28
24
|
const MAX_SIZE_MOBILE = 1024 * 1024; // 1MB
|
|
29
25
|
const MAX_SIZE_DESKTOP = 1024 * 1024 * 5; // 5MB
|
|
30
26
|
|
|
27
|
+
const privateThemeConfigUrl = new URL('./schemas/privateThemeConfig.json', import.meta.url);
|
|
28
|
+
const privateThemeConfigValidationSchema = JSON.parse(fs.readFileSync(privateThemeConfigUrl));
|
|
29
|
+
|
|
30
|
+
const themeConfigValidationSchemaUrl = new URL('./schemas/themeConfig.json', import.meta.url);
|
|
31
|
+
const themeConfigValidationSchema = JSON.parse(fs.readFileSync(themeConfigValidationSchemaUrl));
|
|
32
|
+
|
|
33
|
+
const themeValidationSchemaUrl = new URL('./schemas/themeSchema.json', import.meta.url);
|
|
34
|
+
const themeValidationSchema = JSON.parse(fs.readFileSync(themeValidationSchemaUrl));
|
|
35
|
+
|
|
31
36
|
class BundleValidator {
|
|
32
37
|
/**
|
|
33
38
|
* Run some validations to ensure that the platform will accept the theme
|
|
@@ -44,7 +49,6 @@ class BundleValidator {
|
|
|
44
49
|
this.jsonSchemaValidatorOptions = { schemaId: 'auto', allErrors: true };
|
|
45
50
|
this.scssValidator = new ScssValidator(themePath, themeConfig);
|
|
46
51
|
this.langHelpersValidator = new LangHelpersValidator(themePath, themeConfig);
|
|
47
|
-
|
|
48
52
|
// Array of tasks used in async.series
|
|
49
53
|
this.validationTasks = [
|
|
50
54
|
this._validateThemeConfiguration.bind(this),
|
|
@@ -54,7 +58,6 @@ class BundleValidator {
|
|
|
54
58
|
this._validateCssFiles.bind(this),
|
|
55
59
|
this._validateLangFiles.bind(this),
|
|
56
60
|
];
|
|
57
|
-
|
|
58
61
|
if (!this.isPrivate) {
|
|
59
62
|
this.validationTasks.push(this._validateMetaImages.bind(this));
|
|
60
63
|
}
|
|
@@ -87,10 +90,8 @@ class BundleValidator {
|
|
|
87
90
|
new RegExp(`{+\\s*${element}\\s*}+`),
|
|
88
91
|
) !== -1,
|
|
89
92
|
);
|
|
90
|
-
|
|
91
93
|
validated.push(...matches);
|
|
92
94
|
}
|
|
93
|
-
|
|
94
95
|
cb(null, validated);
|
|
95
96
|
},
|
|
96
97
|
(err, validationRes) => {
|
|
@@ -98,18 +99,13 @@ class BundleValidator {
|
|
|
98
99
|
callback(err);
|
|
99
100
|
return;
|
|
100
101
|
}
|
|
101
|
-
|
|
102
|
-
const results = _.difference(
|
|
103
|
-
this.objectsToValidate,
|
|
104
|
-
_.uniq(_.flatten(validationRes)),
|
|
105
|
-
);
|
|
102
|
+
const results = difference(this.objectsToValidate, uniq(flatten(validationRes)));
|
|
106
103
|
if (results.length !== 0) {
|
|
107
104
|
callback(
|
|
108
105
|
new Error(`Missing required objects/properties: ${results.join('\n')}`),
|
|
109
106
|
);
|
|
110
107
|
return;
|
|
111
108
|
}
|
|
112
|
-
|
|
113
109
|
callback(null, !!results);
|
|
114
110
|
},
|
|
115
111
|
);
|
|
@@ -134,7 +130,6 @@ class BundleValidator {
|
|
|
134
130
|
const rawSchema = await this.themeConfig.getRawSchema();
|
|
135
131
|
this._validateJsonSchema('schema', themeValidationSchema, rawSchema);
|
|
136
132
|
}
|
|
137
|
-
|
|
138
133
|
return true;
|
|
139
134
|
}
|
|
140
135
|
|
|
@@ -151,17 +146,14 @@ class BundleValidator {
|
|
|
151
146
|
' file in your top level theme directory.';
|
|
152
147
|
throw new Error(errMsg);
|
|
153
148
|
}
|
|
154
|
-
|
|
155
149
|
if (!this.isPrivate && !this.themeConfig.schemaExists()) {
|
|
156
150
|
console.log("Warning: Your theme is missing a 'schema.json' file.".orange);
|
|
157
151
|
}
|
|
158
|
-
|
|
159
152
|
// Validate against the theme registry config schema
|
|
160
153
|
const validationSchema = this.isPrivate
|
|
161
154
|
? privateThemeConfigValidationSchema
|
|
162
155
|
: themeConfigValidationSchema;
|
|
163
156
|
const rawConfig = await this.themeConfig.getRawConfig();
|
|
164
|
-
|
|
165
157
|
return this._validateJsonSchema('config', validationSchema, rawConfig);
|
|
166
158
|
}
|
|
167
159
|
|
|
@@ -175,7 +167,6 @@ class BundleValidator {
|
|
|
175
167
|
_validateJsonSchema(type, schema, data) {
|
|
176
168
|
const validator = new Validator(this.jsonSchemaValidatorOptions);
|
|
177
169
|
validator.validate(schema, data);
|
|
178
|
-
|
|
179
170
|
if (validator.errors && validator.errors.length > 0) {
|
|
180
171
|
let errorMessage = `Your theme's ${type}.json has errors:`;
|
|
181
172
|
for (const error of validator.errors) {
|
|
@@ -183,7 +174,6 @@ class BundleValidator {
|
|
|
183
174
|
}
|
|
184
175
|
throw new Error(errorMessage.red);
|
|
185
176
|
}
|
|
186
|
-
|
|
187
177
|
return true;
|
|
188
178
|
}
|
|
189
179
|
|
|
@@ -195,11 +185,9 @@ class BundleValidator {
|
|
|
195
185
|
async _validateSchemaTranslations() {
|
|
196
186
|
const validatorSchemaTranslations = new ValidatorSchemaTranslations();
|
|
197
187
|
const validator = new Validator(this.jsonSchemaValidatorOptions);
|
|
198
|
-
|
|
199
188
|
if (this.themeConfig.schemaExists()) {
|
|
200
189
|
validatorSchemaTranslations.setSchema(await this.themeConfig.getRawSchema());
|
|
201
190
|
}
|
|
202
|
-
|
|
203
191
|
if (this.themeConfig.schemaTranslationsExists()) {
|
|
204
192
|
try {
|
|
205
193
|
const rawSchemaTranslations = await this.themeConfig.getRawSchemaTranslations();
|
|
@@ -210,38 +198,31 @@ class BundleValidator {
|
|
|
210
198
|
} else if (validatorSchemaTranslations.getSchemaKeys().length) {
|
|
211
199
|
throw new Error('Missed schemaTranslations.json file'.red);
|
|
212
200
|
}
|
|
213
|
-
|
|
214
201
|
const missedKeys = validatorSchemaTranslations.findMissedKeys();
|
|
215
202
|
const unusedKeys = validatorSchemaTranslations.findUnusedKeys();
|
|
216
203
|
validator.validate(
|
|
217
204
|
validatorSchemaTranslations.getValidationSchema(),
|
|
218
205
|
validatorSchemaTranslations.getTranslations(),
|
|
219
206
|
);
|
|
220
|
-
|
|
221
207
|
if (
|
|
222
208
|
(validator.errors && validator.errors.length) ||
|
|
223
209
|
missedKeys.length ||
|
|
224
210
|
unusedKeys.length
|
|
225
211
|
) {
|
|
226
212
|
let errorMessage = "Your theme's schemaTranslations.json has errors:";
|
|
227
|
-
|
|
228
213
|
missedKeys.forEach((key) => {
|
|
229
214
|
errorMessage += `\r\nmissing translation key "${key}"`;
|
|
230
215
|
});
|
|
231
|
-
|
|
232
216
|
unusedKeys.forEach((key) => {
|
|
233
217
|
errorMessage += `\r\nunused translation key "${key}"`;
|
|
234
218
|
});
|
|
235
|
-
|
|
236
219
|
if (validator.errors && validator.errors.length) {
|
|
237
220
|
validator.errors.forEach((error) => {
|
|
238
221
|
errorMessage += `\r\nschemaTranslations${error.message}`;
|
|
239
222
|
});
|
|
240
223
|
}
|
|
241
|
-
|
|
242
224
|
throw new Error(errorMessage.red);
|
|
243
225
|
}
|
|
244
|
-
|
|
245
226
|
return true;
|
|
246
227
|
}
|
|
247
228
|
|
|
@@ -254,7 +235,6 @@ class BundleValidator {
|
|
|
254
235
|
const { meta, variations } = await this.themeConfig.getConfig();
|
|
255
236
|
const composedImagePath = path.resolve(this.themePath, 'meta', meta.composed_image);
|
|
256
237
|
const imageTasks = [];
|
|
257
|
-
|
|
258
238
|
if (!this._isValidImageType(composedImagePath)) {
|
|
259
239
|
throw new Error(
|
|
260
240
|
'Invalid file type for "meta.composed_image".'.red +
|
|
@@ -269,16 +249,13 @@ class BundleValidator {
|
|
|
269
249
|
imageTasks.push((cb) =>
|
|
270
250
|
this._validateImage(composedImagePath, WIDTH_COMPOSED, HEIGHT_COMPOSED, cb),
|
|
271
251
|
);
|
|
272
|
-
|
|
273
252
|
for (const variation of variations) {
|
|
274
253
|
const id = variation.id.blue;
|
|
275
|
-
|
|
276
254
|
const desktopScreenshotPath = path.resolve(
|
|
277
255
|
this.themePath,
|
|
278
256
|
'meta',
|
|
279
257
|
variation.meta.desktop_screenshot,
|
|
280
258
|
);
|
|
281
|
-
|
|
282
259
|
if (!this._isValidImageType(desktopScreenshotPath)) {
|
|
283
260
|
throw new Error(
|
|
284
261
|
`Invalid file type for ${id} variation's "desktop_screenshot".`.red +
|
|
@@ -293,13 +270,11 @@ class BundleValidator {
|
|
|
293
270
|
imageTasks.push((cb) =>
|
|
294
271
|
this._validateImage(desktopScreenshotPath, WIDTH_DESKTOP, HEIGHT_DESKTOP, cb),
|
|
295
272
|
);
|
|
296
|
-
|
|
297
273
|
const mobileScreenshotPath = path.resolve(
|
|
298
274
|
this.themePath,
|
|
299
275
|
'meta',
|
|
300
276
|
variation.meta.mobile_screenshot,
|
|
301
277
|
);
|
|
302
|
-
|
|
303
278
|
if (!this._isValidImageType(mobileScreenshotPath)) {
|
|
304
279
|
throw new Error(
|
|
305
280
|
`Invalid file type for ${id} variation's "mobile_screenshot".`.red +
|
|
@@ -315,7 +290,6 @@ class BundleValidator {
|
|
|
315
290
|
this._validateImage(mobileScreenshotPath, WIDTH_MOBILE, HEIGHT_MOBILE, cb),
|
|
316
291
|
);
|
|
317
292
|
}
|
|
318
|
-
|
|
319
293
|
return async.parallel(imageTasks);
|
|
320
294
|
}
|
|
321
295
|
|
|
@@ -342,12 +316,10 @@ class BundleValidator {
|
|
|
342
316
|
cb(err);
|
|
343
317
|
return;
|
|
344
318
|
}
|
|
345
|
-
|
|
346
319
|
let failureMessage = '';
|
|
347
320
|
const imageHeight = dimensions.height;
|
|
348
321
|
const imageWidth = dimensions.width;
|
|
349
322
|
const { size } = fs.statSync(imagePath);
|
|
350
|
-
|
|
351
323
|
if (width === WIDTH_DESKTOP && height === HEIGHT_DESKTOP && size > MAX_SIZE_DESKTOP) {
|
|
352
324
|
failureMessage =
|
|
353
325
|
`Image of size ${size} bytes at path (${imagePath}) ` +
|
|
@@ -369,7 +341,6 @@ class BundleValidator {
|
|
|
369
341
|
`Image of size ${size} bytes at path (${imagePath}) ` +
|
|
370
342
|
`is greater than allowed size ${MAX_SIZE_MOBILE}\n`;
|
|
371
343
|
}
|
|
372
|
-
|
|
373
344
|
if (imageWidth !== width || imageHeight !== height) {
|
|
374
345
|
failureMessage +=
|
|
375
346
|
`Image at (${imagePath}) has incorrect dimensions (${imageWidth}x${imageHeight}) should be` +
|
|
@@ -377,7 +348,6 @@ class BundleValidator {
|
|
|
377
348
|
cb(new Error(failureMessage));
|
|
378
349
|
return;
|
|
379
350
|
}
|
|
380
|
-
|
|
381
351
|
cb(null, true);
|
|
382
352
|
});
|
|
383
353
|
}
|
|
@@ -387,13 +357,11 @@ class BundleValidator {
|
|
|
387
357
|
const filePaths = await recursiveReadDir(path.join(this.themePath, 'templates'), [
|
|
388
358
|
'!*.html',
|
|
389
359
|
]);
|
|
390
|
-
|
|
391
360
|
for await (const filePath of filePaths) {
|
|
392
361
|
const fileContent = await fs.promises.readFile(filePath, { encoding: 'utf-8' });
|
|
393
362
|
const frontmatter = getFrontmatterContent(fileContent);
|
|
394
363
|
if (frontmatter) {
|
|
395
364
|
const yaml = interpolateThemeSettings(frontmatter, config.settings);
|
|
396
|
-
|
|
397
365
|
try {
|
|
398
366
|
const result = yamlValidator.loadAll(yaml);
|
|
399
367
|
this.validateTrailingSymbols(result[0]);
|
|
@@ -404,7 +372,6 @@ class BundleValidator {
|
|
|
404
372
|
}
|
|
405
373
|
}
|
|
406
374
|
}
|
|
407
|
-
|
|
408
375
|
return true;
|
|
409
376
|
}
|
|
410
377
|
|
|
@@ -417,13 +384,12 @@ class BundleValidator {
|
|
|
417
384
|
}
|
|
418
385
|
|
|
419
386
|
validateTrailingSymbols(data) {
|
|
420
|
-
if (
|
|
421
|
-
return
|
|
387
|
+
if (isObject(data)) {
|
|
388
|
+
return every(data, (value) => this.validateTrailingSymbols(value));
|
|
422
389
|
}
|
|
423
|
-
if (
|
|
390
|
+
if (isArray(data)) {
|
|
424
391
|
return data.every((row) => this.validateTrailingSymbols(row));
|
|
425
392
|
}
|
|
426
|
-
|
|
427
393
|
return data ? this.hasFrontmatterValidValue(data) : true;
|
|
428
394
|
}
|
|
429
395
|
|
|
@@ -431,7 +397,6 @@ class BundleValidator {
|
|
|
431
397
|
if (this.hasUnallowedTrailingSymbol(value)) {
|
|
432
398
|
throw new Error(`Found unallowed trailing symbol in: "${value}"`);
|
|
433
399
|
}
|
|
434
|
-
|
|
435
400
|
return true;
|
|
436
401
|
}
|
|
437
402
|
|
|
@@ -445,5 +410,4 @@ class BundleValidator {
|
|
|
445
410
|
return symbols.includes(trailingSymbol);
|
|
446
411
|
}
|
|
447
412
|
}
|
|
448
|
-
|
|
449
|
-
module.exports = BundleValidator;
|
|
413
|
+
export default BundleValidator;
|
package/lib/cliCommon.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
import semver from 'semver';
|
|
2
|
+
import { PACKAGE_INFO } from '../constants.js';
|
|
3
|
+
import stencilCLISettings from './StencilCLISettings.js';
|
|
4
4
|
|
|
5
5
|
const messages = {
|
|
6
6
|
visitTroubleshootingPage:
|
|
@@ -8,7 +8,6 @@ const messages = {
|
|
|
8
8
|
submitGithubIssue:
|
|
9
9
|
'If this error persists, please visit https://github.com/bigcommerce/stencil-cli/issues and submit an issue.',
|
|
10
10
|
};
|
|
11
|
-
|
|
12
11
|
/**
|
|
13
12
|
* @param {Object} object
|
|
14
13
|
|
|
@@ -19,13 +18,13 @@ function printObject(object) {
|
|
|
19
18
|
console.log(`${property}: ${object[property]}`);
|
|
20
19
|
}
|
|
21
20
|
}
|
|
22
|
-
|
|
23
21
|
/**
|
|
24
22
|
* @param {Error} error
|
|
25
23
|
|
|
26
24
|
* @returns {void}
|
|
27
25
|
*/
|
|
28
26
|
function printNetworkError(config) {
|
|
27
|
+
console.log(config);
|
|
29
28
|
console.log(`URL: `.yellow + config.url);
|
|
30
29
|
console.log(`Method: `.yellow + config.method.toUpperCase());
|
|
31
30
|
if (config.data) {
|
|
@@ -33,7 +32,6 @@ function printNetworkError(config) {
|
|
|
33
32
|
printObject(config.data);
|
|
34
33
|
}
|
|
35
34
|
}
|
|
36
|
-
|
|
37
35
|
/**
|
|
38
36
|
* @param {Error} error
|
|
39
37
|
* @param {Array<{message: string}>} [error.messages]
|
|
@@ -41,7 +39,6 @@ function printNetworkError(config) {
|
|
|
41
39
|
*/
|
|
42
40
|
function printCliResultError(error) {
|
|
43
41
|
console.error(`\n\n${'not ok'.red} -- ${error || 'Unknown error'}\n`);
|
|
44
|
-
|
|
45
42
|
if (error && Array.isArray(error.messages)) {
|
|
46
43
|
for (const item of error.messages) {
|
|
47
44
|
if (item && item.message) {
|
|
@@ -49,41 +46,34 @@ function printCliResultError(error) {
|
|
|
49
46
|
}
|
|
50
47
|
}
|
|
51
48
|
}
|
|
52
|
-
|
|
53
49
|
if (error && (error.config || error.response)) {
|
|
54
50
|
// In case if request didn't receive any response, response object is not available
|
|
55
51
|
const networkReq = error.config || error.response;
|
|
56
52
|
printNetworkError(networkReq);
|
|
57
53
|
}
|
|
58
|
-
|
|
59
54
|
console.log(messages.visitTroubleshootingPage);
|
|
60
|
-
|
|
61
55
|
console.log(messages.submitGithubIssue);
|
|
62
56
|
}
|
|
63
|
-
|
|
64
57
|
/**
|
|
65
58
|
* @param {Error} error
|
|
66
59
|
* @returns {void}
|
|
67
60
|
*/
|
|
68
61
|
function printCliResultErrorAndExit(error) {
|
|
62
|
+
console.log(error);
|
|
69
63
|
printCliResultError(error);
|
|
70
64
|
// Exit with error code so automated systems recognize it as a failure
|
|
71
65
|
// eslint-disable-next-line no-process-exit
|
|
72
66
|
process.exit(1);
|
|
73
67
|
}
|
|
74
|
-
|
|
75
68
|
function checkNodeVersion() {
|
|
76
69
|
const satisfies = semver.satisfies(process.versions.node, PACKAGE_INFO.engines.node);
|
|
77
|
-
|
|
78
70
|
if (!satisfies) {
|
|
79
71
|
throw new Error(
|
|
80
72
|
`You are running an unsupported version of node. Please upgrade to ${PACKAGE_INFO.engines.node}`,
|
|
81
73
|
);
|
|
82
74
|
}
|
|
83
|
-
|
|
84
75
|
return satisfies;
|
|
85
76
|
}
|
|
86
|
-
|
|
87
77
|
function applyCommonOptions(program) {
|
|
88
78
|
program
|
|
89
79
|
.option('-h, --host [hostname]', 'specify the api host')
|
|
@@ -95,22 +85,22 @@ function applyCommonOptions(program) {
|
|
|
95
85
|
)
|
|
96
86
|
.parse(process.argv);
|
|
97
87
|
}
|
|
98
|
-
|
|
99
88
|
function setupCLI(options) {
|
|
100
89
|
stencilCLISettings.setVerbose(options.verbose);
|
|
101
90
|
}
|
|
102
|
-
|
|
103
91
|
function prepareCommand(program) {
|
|
104
92
|
applyCommonOptions(program);
|
|
105
93
|
checkNodeVersion();
|
|
106
|
-
|
|
107
94
|
const options = program.opts();
|
|
108
95
|
setupCLI(options);
|
|
109
|
-
|
|
110
96
|
return options;
|
|
111
97
|
}
|
|
112
|
-
|
|
113
|
-
|
|
98
|
+
export { messages };
|
|
99
|
+
export { printCliResultError };
|
|
100
|
+
export { printCliResultErrorAndExit };
|
|
101
|
+
export { checkNodeVersion };
|
|
102
|
+
export { prepareCommand };
|
|
103
|
+
export default {
|
|
114
104
|
messages,
|
|
115
105
|
printCliResultError,
|
|
116
106
|
printCliResultErrorAndExit,
|
package/lib/commander.js
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
|
|
1
|
+
import program from 'commander';
|
|
2
2
|
|
|
3
3
|
program
|
|
4
4
|
// Avoid option name clashes https://www.npmjs.com/package/commander#avoiding-option-name-clashes
|
|
5
5
|
.storeOptionsAsProperties(false)
|
|
6
6
|
.passCommandToAction(false);
|
|
7
|
-
|
|
8
|
-
module.exports = program;
|
|
7
|
+
export default program;
|