gscan 4.22.0 → 4.24.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/README.md +8 -7
- package/app/tpl/index.hbs +1 -0
- package/bin/cli.js +5 -0
- package/lib/ast-linter/rules/index.js +1 -0
- package/lib/ast-linter/rules/internal/scope.js +14 -0
- package/lib/ast-linter/rules/lint-no-author-helper-in-post-page-context.js +25 -0
- package/lib/checker.js +1 -1
- package/lib/checks/010-package-json.js +70 -11
- package/lib/checks/090-template-syntax.js +1 -1
- package/lib/specs/canary.js +407 -242
- package/lib/specs/index.js +1 -1
- package/lib/specs/v4.js +306 -0
- package/lib/utils/index.js +1 -1
- package/lib/utils/versions.json +6 -2
- package/package.json +9 -9
package/README.md
CHANGED
|
@@ -16,9 +16,9 @@ GScan works on a system of rules. Each rule has a way to check whether it passes
|
|
|
16
16
|
|
|
17
17
|
In addition, an **error** can be marked as **fatal**. A **fatal error** means, left unchecked a Ghost publication would throw 500 errors on certain pages because of the detected out-of-date or erroneous code.
|
|
18
18
|
|
|
19
|
-
In Ghost, we call GScan on boot. If any fatal errors are detected
|
|
19
|
+
In Ghost, we call GScan on boot. If any fatal errors are detected in Ghost(Pro) and in Ghost-CLI we call GScan as part of major upgrades. The upgrade will not be allowed to continue if any fatal errors are detected.
|
|
20
20
|
|
|
21
|
-
Errors are only
|
|
21
|
+
Errors are only marked as **fatal errors** if they would cause errors, and therefore should block a boot or an upgrade.
|
|
22
22
|
|
|
23
23
|
### Tooling
|
|
24
24
|
When developing new rules or testing gscan following tools are great to have in the toolbelt:
|
|
@@ -46,12 +46,13 @@ To run a local zip file through the checks:
|
|
|
46
46
|
|
|
47
47
|
`gscan /path/to/theme.zip -z`
|
|
48
48
|
|
|
49
|
-
By default, GScan scans themes for the latest Ghost version compatibility. You can also specify a Ghost version by using the following parameters (for Ghost 1.0, 2.0, 3.0 and
|
|
49
|
+
By default, GScan scans themes for the latest Ghost version compatibility. You can also specify a Ghost version by using the following parameters (for Ghost 1.0, 2.0, 3.0, 4.0 and 5.0):
|
|
50
50
|
|
|
51
51
|
`--v1` or `-1`
|
|
52
52
|
`--v2` or `-2`
|
|
53
53
|
`--v3` or `-3`
|
|
54
|
-
`--v4` or `-4`
|
|
54
|
+
`--v4` or `-4`
|
|
55
|
+
`--v5` or `-5` or `--canary`
|
|
55
56
|
|
|
56
57
|
Use the `--canary` parameter to check for the upcoming Ghost version.
|
|
57
58
|
|
|
@@ -72,9 +73,9 @@ gscan.checkZip({
|
|
|
72
73
|
path: 'path-to-zip',
|
|
73
74
|
// if you need to check the theme for a different
|
|
74
75
|
// major Ghost version, you can pass it. Currently
|
|
75
|
-
// v1, v2, v3 and canary are supported. Default is
|
|
76
|
-
// the latest Ghost version
|
|
77
|
-
// checkVersion: '
|
|
76
|
+
// v1, v2, v3, v4 and canary (v5) are supported. Default is
|
|
77
|
+
// the latest Ghost version 4.0:
|
|
78
|
+
// checkVersion: 'v4',
|
|
78
79
|
name: 'my-theme'
|
|
79
80
|
}).then(function (result) {
|
|
80
81
|
console.log(result);
|
package/app/tpl/index.hbs
CHANGED
|
@@ -23,6 +23,7 @@
|
|
|
23
23
|
<span class="gh-input-icon select-arrow active">{{> icon-arrow-down}}</span>
|
|
24
24
|
<select class="gh-input gh-select" name="version" id="version">
|
|
25
25
|
<option value="canary" selected>{{ghostVersions.canary.major}}</option>
|
|
26
|
+
<option value="v4">{{ghostVersions.v4.major}}</option>
|
|
26
27
|
<option value="v3">{{ghostVersions.v3.major}}</option>
|
|
27
28
|
<option value="v2">{{ghostVersions.v2.major}}</option>
|
|
28
29
|
<option value="v1">{{ghostVersions.v1.major}}</option>
|
package/bin/cli.js
CHANGED
|
@@ -53,6 +53,9 @@ prettyCLI
|
|
|
53
53
|
.boolean('-4, --v4', {
|
|
54
54
|
desc: 'Check theme for Ghost 4.0 compatibility'
|
|
55
55
|
})
|
|
56
|
+
.boolean('-5, --v5', {
|
|
57
|
+
desc: 'Check theme for Ghost 4.0 compatibility'
|
|
58
|
+
})
|
|
56
59
|
.boolean('-c, --canary', {
|
|
57
60
|
desc: 'Check theme for upcoming Ghost version compatibility'
|
|
58
61
|
})
|
|
@@ -75,6 +78,8 @@ prettyCLI
|
|
|
75
78
|
cliOptions.checkVersion = 'v3';
|
|
76
79
|
} else if (argv.v4) {
|
|
77
80
|
cliOptions.checkVersion = 'v4';
|
|
81
|
+
} else if (argv.v5) {
|
|
82
|
+
cliOptions.checkVersion = 'v5';
|
|
78
83
|
} else if (argv.canary) {
|
|
79
84
|
cliOptions.checkVersion = 'canary';
|
|
80
85
|
} else {
|
|
@@ -2,6 +2,7 @@ module.exports = {
|
|
|
2
2
|
'GS090-NO-IMG-URL-IN-CONDITIONALS': require('./lint-no-img-url-in-conditionals'),
|
|
3
3
|
'GS090-NO-UNKNOWN-CUSTOM-THEME-SETTINGS': require('./lint-no-unknown-custom-theme-settings'),
|
|
4
4
|
'GS090-NO-UNKNOWN-CUSTOM-THEME-SELECT-VALUE-IN-MATCH': require('./lint-no-unknown-custom-theme-select-value-in-match'),
|
|
5
|
+
'GS090-NO-AUTHOR-HELPER-IN-POST-CONTEXT': require('./lint-no-author-helper-in-post-page-context'),
|
|
5
6
|
'no-multi-param-conditionals': require('./lint-no-multi-param-conditionals'),
|
|
6
7
|
'no-nested-async-helpers': require('./lint-no-nested-async-helpers'),
|
|
7
8
|
'no-prev-next-post-outside-post-context': require('./lint-no-prev-next-post-outside-post-context'),
|
|
@@ -190,6 +190,20 @@ class Scope {
|
|
|
190
190
|
return this.currentFrame && this.currentFrame.context === context || false;
|
|
191
191
|
}
|
|
192
192
|
|
|
193
|
+
hasParentContext(context) {
|
|
194
|
+
let found = false;
|
|
195
|
+
|
|
196
|
+
if (this.frames && this.frames.length) {
|
|
197
|
+
this.frames.forEach((frame) => {
|
|
198
|
+
if (frame.nodeName === context) {
|
|
199
|
+
found = true;
|
|
200
|
+
}
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
return found;
|
|
205
|
+
}
|
|
206
|
+
|
|
193
207
|
isLocal(node) {
|
|
194
208
|
// @foo MustacheStatements are referencing globals rather than locals
|
|
195
209
|
if (node.type === 'MustacheStatement' && node.path.data) {
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
const Rule = require('./base');
|
|
2
|
+
const {getNodeName, logNode} = require('../helpers');
|
|
3
|
+
|
|
4
|
+
module.exports = class NoAuthorHelperInPostContext extends Rule {
|
|
5
|
+
_checkForHelerInPostContext(node) {
|
|
6
|
+
const nodeName = getNodeName(node);
|
|
7
|
+
const isAuthorHelper = (nodeName === 'author');
|
|
8
|
+
const isPostContext = this.scope.hasParentContext('post');
|
|
9
|
+
|
|
10
|
+
if (isAuthorHelper && isPostContext) {
|
|
11
|
+
this.log({
|
|
12
|
+
message: `${logNode(node)} should not be used in ${this.scope.currentFrame.context} context`,
|
|
13
|
+
line: node.loc && node.loc.start.line,
|
|
14
|
+
column: node.loc && node.loc.start.column,
|
|
15
|
+
source: this.sourceForNode(node)
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
visitor() {
|
|
21
|
+
return {
|
|
22
|
+
MustacheStatement: this._checkForHelerInPostContext.bind(this)
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
};
|
package/lib/checker.js
CHANGED
|
@@ -27,7 +27,7 @@ const check = function checkAll(themePath, options = {}) {
|
|
|
27
27
|
const passedVersion = _.get(options, 'checkVersion', versions.default);
|
|
28
28
|
let version = passedVersion;
|
|
29
29
|
|
|
30
|
-
if (passedVersion === '
|
|
30
|
+
if (passedVersion === 'v5') {
|
|
31
31
|
version = 'canary';
|
|
32
32
|
}
|
|
33
33
|
|
|
@@ -33,14 +33,15 @@ const v2PackageJSONValidationRules = _.extend({},
|
|
|
33
33
|
|
|
34
34
|
const v3PackageJSONConditionalRules = {};
|
|
35
35
|
const v3PackageJSONValidationRules = _.extend({},
|
|
36
|
-
{
|
|
36
|
+
{isNotPresentEngineGhostAPI: 'GS010-PJ-GHOST-API'},
|
|
37
37
|
{isv01EngineGhostAPI: 'GS010-PJ-GHOST-API-V01'},
|
|
38
38
|
v3PackageJSONConditionalRules
|
|
39
39
|
);
|
|
40
40
|
|
|
41
|
-
const
|
|
42
|
-
const
|
|
41
|
+
const v4PackageJSONConditionalRules = {};
|
|
42
|
+
const v4PackageJSONValidationRules = _.extend({},
|
|
43
43
|
{isv2EngineGhostAPI: 'GS010-PJ-GHOST-API-V2'},
|
|
44
|
+
{isPresentEngineGhostAPI: 'GS010-PJ-GHOST-API-PRESENT'},
|
|
44
45
|
{hasTooManyCustomThemeSettings: 'GS010-PJ-CUST-THEME-TOTAL-SETTINGS'},
|
|
45
46
|
{customThemeSettingsMustBeSnakecased: 'GS010-PJ-CUST-THEME-SETTINGS-CASE'},
|
|
46
47
|
{unkownCustomThemeSettingsType: 'GS010-PJ-CUST-THEME-SETTINGS-TYPE'},
|
|
@@ -50,9 +51,12 @@ const canaryPackageJSONValidationRules = _.extend({},
|
|
|
50
51
|
{invalidCustomThemeSetingBooleanDefault: 'GS010-PJ-CUST-THEME-SETTINGS-BOOLEAN-DEFAULT'},
|
|
51
52
|
{invalidCustomThemeSetingColorDefault: 'GS010-PJ-CUST-THEME-SETTINGS-COLOR-DEFAULT'},
|
|
52
53
|
{invalidCustomThemeSetingImageDefault: 'GS010-PJ-CUST-THEME-SETTINGS-IMAGE-DEFAULT'},
|
|
53
|
-
|
|
54
|
+
v4PackageJSONConditionalRules
|
|
54
55
|
);
|
|
55
56
|
|
|
57
|
+
const canaryPackageJSONConditionalRules = {};
|
|
58
|
+
const canaryPackageJSONValidationRules = _.extend({});
|
|
59
|
+
|
|
56
60
|
_private.validatePackageJSONFields = function validatePackageJSONFields(packageJSON, theme, packageJSONValidationRules) {
|
|
57
61
|
let failed = [];
|
|
58
62
|
const passedRulesToOmit = [];
|
|
@@ -109,6 +113,10 @@ _private.validatePackageJSONFields = function validatePackageJSONFields(packageJ
|
|
|
109
113
|
}
|
|
110
114
|
|
|
111
115
|
if (!packageJSON.engines || !packageJSON.engines['ghost-api']) {
|
|
116
|
+
markFailed('isNotPresentEngineGhostAPI');
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
if (packageJSON.engines && packageJSON.engines['ghost-api']) {
|
|
112
120
|
markFailed('isPresentEngineGhostAPI');
|
|
113
121
|
}
|
|
114
122
|
|
|
@@ -221,15 +229,66 @@ module.exports = function checkPackageJSON(theme, options) {
|
|
|
221
229
|
packageJSONValidationRules = v1PackageJSONValidationRules;
|
|
222
230
|
packageJSONConditionalRules = v1PackageJSONConditionalRules;
|
|
223
231
|
} else if (checkVersion === 'v2') {
|
|
224
|
-
packageJSONValidationRules = _.merge(
|
|
225
|
-
|
|
232
|
+
packageJSONValidationRules = _.merge(
|
|
233
|
+
{},
|
|
234
|
+
v1PackageJSONValidationRules,
|
|
235
|
+
v2PackageJSONValidationRules
|
|
236
|
+
);
|
|
237
|
+
packageJSONConditionalRules = _.merge(
|
|
238
|
+
{},
|
|
239
|
+
v1PackageJSONConditionalRules,
|
|
240
|
+
v2PackageJSONConditionalRules
|
|
241
|
+
);
|
|
226
242
|
} else if (checkVersion === 'v3') {
|
|
227
|
-
packageJSONValidationRules = _.merge(
|
|
228
|
-
|
|
243
|
+
packageJSONValidationRules = _.merge(
|
|
244
|
+
{},
|
|
245
|
+
v1PackageJSONValidationRules,
|
|
246
|
+
v2PackageJSONValidationRules,
|
|
247
|
+
v3PackageJSONValidationRules
|
|
248
|
+
);
|
|
249
|
+
packageJSONConditionalRules = _.merge(
|
|
250
|
+
{},
|
|
251
|
+
v1PackageJSONConditionalRules,
|
|
252
|
+
v2PackageJSONConditionalRules,
|
|
253
|
+
v3PackageJSONConditionalRules
|
|
254
|
+
);
|
|
255
|
+
} else if (checkVersion === 'v5') {
|
|
256
|
+
packageJSONValidationRules = _.merge(
|
|
257
|
+
{},
|
|
258
|
+
v1PackageJSONValidationRules,
|
|
259
|
+
v2PackageJSONValidationRules,
|
|
260
|
+
v3PackageJSONValidationRules,
|
|
261
|
+
v4PackageJSONValidationRules,
|
|
262
|
+
canaryPackageJSONValidationRules
|
|
263
|
+
);
|
|
264
|
+
delete packageJSONValidationRules.isNotPresentEngineGhostAPI;
|
|
265
|
+
|
|
266
|
+
packageJSONConditionalRules = _.merge(
|
|
267
|
+
{},
|
|
268
|
+
v1PackageJSONConditionalRules,
|
|
269
|
+
v2PackageJSONConditionalRules,
|
|
270
|
+
v3PackageJSONConditionalRules,
|
|
271
|
+
v4PackageJSONConditionalRules,
|
|
272
|
+
canaryPackageJSONConditionalRules
|
|
273
|
+
);
|
|
229
274
|
} else {
|
|
230
|
-
// default check for current version 'v4'
|
|
231
|
-
packageJSONValidationRules = _.merge(
|
|
232
|
-
|
|
275
|
+
// default check for current version 'v4' rules
|
|
276
|
+
packageJSONValidationRules = _.merge(
|
|
277
|
+
{},
|
|
278
|
+
v1PackageJSONValidationRules,
|
|
279
|
+
v2PackageJSONValidationRules,
|
|
280
|
+
v3PackageJSONValidationRules,
|
|
281
|
+
v4PackageJSONValidationRules
|
|
282
|
+
);
|
|
283
|
+
delete packageJSONValidationRules.isNotPresentEngineGhostAPI;
|
|
284
|
+
|
|
285
|
+
packageJSONConditionalRules = _.merge(
|
|
286
|
+
{},
|
|
287
|
+
v1PackageJSONConditionalRules,
|
|
288
|
+
v2PackageJSONConditionalRules,
|
|
289
|
+
v3PackageJSONConditionalRules,
|
|
290
|
+
v4PackageJSONConditionalRules
|
|
291
|
+
);
|
|
233
292
|
}
|
|
234
293
|
|
|
235
294
|
let [packageJSON] = _.filter(theme.files, {file: packageJSONFileName});
|
|
@@ -49,7 +49,6 @@ function getCustomThemeSettings(theme) {
|
|
|
49
49
|
}
|
|
50
50
|
|
|
51
51
|
const checkTemplatesCompile = function checkTemplatesCompile(theme, options) {
|
|
52
|
-
const failures = [];
|
|
53
52
|
const checkVersion = _.get(options, 'checkVersion', versions.default);
|
|
54
53
|
const ruleSet = spec.get([checkVersion]);
|
|
55
54
|
const customThemeSettings = getCustomThemeSettings(theme);
|
|
@@ -63,6 +62,7 @@ const checkTemplatesCompile = function checkTemplatesCompile(theme, options) {
|
|
|
63
62
|
});
|
|
64
63
|
|
|
65
64
|
_.each(rulesToCheck, function (check, ruleCode) {
|
|
65
|
+
const failures = [];
|
|
66
66
|
const processFile = processFileFunction(
|
|
67
67
|
theme.files,
|
|
68
68
|
failures,
|