gscan 6.1.0 → 6.2.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/lib/ast-linter/rules/base.js +1 -1
- package/lib/ast-linter/rules/lint-no-unknown-partials.js +2 -1
- package/lib/checks/005-template-compile.js +24 -21
- package/lib/checks/110-page-builder-usage.js +31 -5
- package/lib/specs/v1.js +8 -0
- package/lib/specs/v5.js +7 -9
- package/lib/specs/v6.js +1 -0
- package/lib/utils/check-utils.js +3 -3
- package/package.json +6 -6
|
@@ -9,7 +9,7 @@ module.exports = class BaseRule {
|
|
|
9
9
|
this.helpers = options.helpers;
|
|
10
10
|
this.inlinePartials = options.inlinePartials || [];
|
|
11
11
|
this.customThemeSettings = options.customThemeSettings;
|
|
12
|
-
//
|
|
12
|
+
// Keep in sync with `pageBuilderProperties` in lib/specs/v5.js.
|
|
13
13
|
this.knownPageBuilderProperties = options.knownPageBuilderProperties || ['show_title_and_feature_image'];
|
|
14
14
|
}
|
|
15
15
|
|
|
@@ -19,7 +19,8 @@ module.exports = class NoUnknownPartials extends Rule {
|
|
|
19
19
|
}
|
|
20
20
|
if (node.type === 'PartialStatement') {
|
|
21
21
|
return this.log({
|
|
22
|
-
|
|
22
|
+
code: 'GS005-NO-INLINE-DYNAMIC-PARTIAL',
|
|
23
|
+
message: `Inline dynamic partial on line ${logObject.line}: ${logObject.source}`,
|
|
23
24
|
...logObject
|
|
24
25
|
});
|
|
25
26
|
}
|
|
@@ -43,9 +43,11 @@ function processFileFunction(files, failures, theme, partialsFound) {
|
|
|
43
43
|
});
|
|
44
44
|
|
|
45
45
|
if (astResults.length) {
|
|
46
|
+
const first = astResults[0];
|
|
46
47
|
failures.push({
|
|
47
48
|
ref: themeFile.file,
|
|
48
|
-
message:
|
|
49
|
+
message: first.message,
|
|
50
|
+
...(first.code ? {code: first.code} : {})
|
|
49
51
|
});
|
|
50
52
|
}
|
|
51
53
|
|
|
@@ -86,6 +88,8 @@ function processFileFunction(files, failures, theme, partialsFound) {
|
|
|
86
88
|
};
|
|
87
89
|
}
|
|
88
90
|
|
|
91
|
+
const DEFAULT_CODE = 'GS005-TPL-ERR';
|
|
92
|
+
|
|
89
93
|
const checkTemplatesCompile = function checkTemplatesCompile(theme, options) {
|
|
90
94
|
const failures = [];
|
|
91
95
|
const checkVersion = _.get(options, 'checkVersion', versions.default);
|
|
@@ -96,35 +100,34 @@ const checkTemplatesCompile = function checkTemplatesCompile(theme, options) {
|
|
|
96
100
|
// Reset theme.helpers to make sure we only get helpers that are used
|
|
97
101
|
theme.helpers = {};
|
|
98
102
|
|
|
99
|
-
// CASE: 001-deprecations checks only needs `rules` that start with `GS001-DEPR-`
|
|
100
|
-
const ruleRegex = /GS005-.*/g;
|
|
101
|
-
|
|
102
103
|
const rulesToCheck = _.pickBy(ruleSet.rules, function (rule, ruleCode) {
|
|
103
|
-
|
|
104
|
-
return rule;
|
|
105
|
-
}
|
|
104
|
+
return /^GS005-/.test(ruleCode);
|
|
106
105
|
});
|
|
107
106
|
|
|
108
107
|
const processFile = processFileFunction(theme.files, failures, theme, partialsFound);
|
|
109
108
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
});
|
|
109
|
+
const linter = new ASTLinter({
|
|
110
|
+
partials: theme.partials,
|
|
111
|
+
helpers: ruleSet.knownHelpers
|
|
112
|
+
});
|
|
115
113
|
|
|
116
|
-
|
|
117
|
-
|
|
114
|
+
_.each(theme.files, function (themeFile) {
|
|
115
|
+
if (themeFile.normalizedFile.endsWith('.hbs') && !themeFile.normalizedFile.startsWith('partials/')) {
|
|
116
|
+
processFile(linter, themeFile);
|
|
117
|
+
}
|
|
118
|
+
});
|
|
118
119
|
|
|
119
|
-
|
|
120
|
-
processFile(linter, themeFile);
|
|
121
|
-
}
|
|
122
|
-
});
|
|
120
|
+
theme.partials = Object.keys(partialsFound);
|
|
123
121
|
|
|
124
|
-
|
|
122
|
+
// Route each failure to its target rule code (default for parse errors,
|
|
123
|
+
// missing partials, and missing helpers — they're all GS005-TPL-ERR).
|
|
124
|
+
// The lint rules attach a `code` field to flag the rule they belong to.
|
|
125
|
+
const failuresByCode = _.groupBy(failures, f => f.code || DEFAULT_CODE);
|
|
125
126
|
|
|
126
|
-
|
|
127
|
-
|
|
127
|
+
_.each(rulesToCheck, function (rule, ruleCode) {
|
|
128
|
+
const matchingFailures = (failuresByCode[ruleCode] || []).map(f => _.omit(f, 'code'));
|
|
129
|
+
if (matchingFailures.length > 0) {
|
|
130
|
+
theme.results.fail[ruleCode] = {failures: matchingFailures};
|
|
128
131
|
} else {
|
|
129
132
|
theme.results.pass.push(ruleCode);
|
|
130
133
|
}
|
|
@@ -1,6 +1,26 @@
|
|
|
1
1
|
const _ = require('lodash');
|
|
2
|
+
const spec = require('../specs');
|
|
3
|
+
const {versions} = require('../utils');
|
|
2
4
|
const {getRules, applyRule, parseWithAST} = require('../utils/check-utils');
|
|
3
5
|
|
|
6
|
+
function findPageTemplateRef(theme) {
|
|
7
|
+
if (!theme || !Array.isArray(theme.files)) {
|
|
8
|
+
return 'page.hbs';
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const pageHbs = theme.files.find(f => f.normalizedFile === 'page.hbs');
|
|
12
|
+
if (pageHbs) {
|
|
13
|
+
return pageHbs.normalizedFile;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const pageOverride = theme.files.find(f => /^page-[^/]+\.hbs$/.test(f.normalizedFile));
|
|
17
|
+
if (pageOverride) {
|
|
18
|
+
return pageOverride.normalizedFile;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
return 'page.hbs';
|
|
22
|
+
}
|
|
23
|
+
|
|
4
24
|
const ruleImplementations = {
|
|
5
25
|
'GS110-NO-MISSING-PAGE-BUILDER-USAGE': {
|
|
6
26
|
isEnabled: true,
|
|
@@ -20,15 +40,21 @@ const ruleImplementations = {
|
|
|
20
40
|
}});
|
|
21
41
|
}
|
|
22
42
|
},
|
|
23
|
-
done: ({log, result}) => {
|
|
24
|
-
|
|
25
|
-
const knownPageBuilderProperties = [
|
|
43
|
+
done: ({theme, log, result, options}) => {
|
|
44
|
+
const checkVersion = _.get(options, 'checkVersion', versions.default);
|
|
45
|
+
const knownPageBuilderProperties = spec.get([checkVersion]).pageBuilderProperties || [];
|
|
26
46
|
const notUsedProperties = knownPageBuilderProperties.filter(x => !result.pageBuilderProperties.has(x));
|
|
27
47
|
|
|
48
|
+
if (!notUsedProperties.length) {
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const ref = findPageTemplateRef(theme);
|
|
53
|
+
|
|
28
54
|
notUsedProperties.forEach((property) => {
|
|
29
55
|
log.failure({
|
|
30
|
-
ref
|
|
31
|
-
message:
|
|
56
|
+
ref,
|
|
57
|
+
message: `{{@page.${property}}} is not used`
|
|
32
58
|
});
|
|
33
59
|
});
|
|
34
60
|
}
|
package/lib/specs/v1.js
CHANGED
|
@@ -385,6 +385,14 @@ rules = {
|
|
|
385
385
|
details: oneLineTrim`Oops! You seemed to have used invalid Handlebars syntax. This mostly happens when you use a helper that is not supported.<br>
|
|
386
386
|
See the full list of available helpers <a href="${docsBaseUrl}helpers/" target=_blank>here</a>.`
|
|
387
387
|
},
|
|
388
|
+
'GS005-NO-INLINE-DYNAMIC-PARTIAL': {
|
|
389
|
+
level: 'error',
|
|
390
|
+
fatal: true,
|
|
391
|
+
rule: 'Use the block form for dynamic partials',
|
|
392
|
+
details: oneLineTrim`Inline dynamic partials (e.g. <code>{{> (dynamicPartial)}}</code>) throw a page error if the named partial doesn't exist. Use the block form, which falls back to the inner content when the partial is missing:<br>
|
|
393
|
+
<code>{{#> (dynamicPartial)}}fallback markup{{/undefined}}</code><br>
|
|
394
|
+
See the <a href="${docsBaseUrl}helpers/utility/partials/#dynamic-partials" target=_blank>partials documentation</a> for details.`
|
|
395
|
+
},
|
|
388
396
|
'GS010-PJ-REQ': {
|
|
389
397
|
level: 'error',
|
|
390
398
|
rule: '<code>package.json</code> file should be present',
|
package/lib/specs/v5.js
CHANGED
|
@@ -726,17 +726,14 @@ let rules = {
|
|
|
726
726
|
Check the <a href="${docsBaseUrl}custom-settings" target=_blank><code>config.custom</code> documentation</a> for further information.`
|
|
727
727
|
},
|
|
728
728
|
'GS110-NO-MISSING-PAGE-BUILDER-USAGE': {
|
|
729
|
-
level: '
|
|
730
|
-
rule: '
|
|
731
|
-
details: oneLineTrim
|
|
732
|
-
Find more information about the <code>{{@page}}</code> global <a href="${docsBaseUrl}helpers/page/" target=_blank>here</a>.`
|
|
729
|
+
level: 'warning',
|
|
730
|
+
rule: 'Support the <code>{{@page.show_title_and_feature_image}}</code> editor setting',
|
|
731
|
+
details: oneLineTrim`Pages have a toggle that lets editors hide a page's title and feature image on a per-page basis. Gate any relevant markup in page templates with <code>{{#if @page.show_title_and_feature_image}}</code> for the toggle to take effect.`
|
|
733
732
|
},
|
|
734
733
|
'GS110-NO-UNKNOWN-PAGE-BUILDER-USAGE': {
|
|
735
|
-
level: '
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
details: oneLineTrim`A page feature used via the <code>{{@page}}</code> global was detected but is not supported by this version of Ghost. Please upgrade to the latest version for full access.
|
|
739
|
-
You can find more information about the <code>{{@page}}</code> global <a href="${docsBaseUrl}helpers/page/" target=_blank>here</a>.`
|
|
734
|
+
level: 'warning',
|
|
735
|
+
rule: 'Remove or correct the unknown <code>{{@page}}</code> property',
|
|
736
|
+
details: oneLineTrim`Ghost currently supports only <code>{{@page.show_title_and_feature_image}}</code>. Remove the reference or correct the spelling — see the <a href="${docsBaseUrl}helpers/page/" target=_blank><code>{{@page}}</code> documentation</a> for details.`
|
|
740
737
|
},
|
|
741
738
|
'GS120-NO-UNKNOWN-GLOBALS': {
|
|
742
739
|
level: 'error',
|
|
@@ -776,6 +773,7 @@ module.exports = {
|
|
|
776
773
|
knownHelpers: knownHelpers,
|
|
777
774
|
templates: templates,
|
|
778
775
|
rules: rules,
|
|
776
|
+
pageBuilderProperties: ['show_title_and_feature_image'],
|
|
779
777
|
/**
|
|
780
778
|
* Copy of Ghost defaults for https://github.com/TryGhost/Ghost/blob/e25f1df0ae551c447da0d319bae06eadf9665444/core/frontend/services/theme-engine/config/defaults.json
|
|
781
779
|
*/
|
package/lib/specs/v6.js
CHANGED
package/lib/utils/check-utils.js
CHANGED
|
@@ -51,19 +51,19 @@ function applyRule(rule, theme) {
|
|
|
51
51
|
|
|
52
52
|
// Initialize the rule (optional)
|
|
53
53
|
if (typeof rule.init === 'function') {
|
|
54
|
-
rule.init({theme, log: getLogger({theme, rule}), result});
|
|
54
|
+
rule.init({theme, log: getLogger({theme, rule}), result, options: rule.options});
|
|
55
55
|
}
|
|
56
56
|
|
|
57
57
|
// Run the main function on each theme file (optional)
|
|
58
58
|
if (typeof rule.eachFile === 'function') {
|
|
59
59
|
_.each(theme.files, function (themeFile) {
|
|
60
|
-
rule.eachFile({file: themeFile, theme, log: getLogger({theme, rule}), result, partialVerificationCache});
|
|
60
|
+
rule.eachFile({file: themeFile, theme, log: getLogger({theme, rule}), result, partialVerificationCache, options: rule.options});
|
|
61
61
|
});
|
|
62
62
|
}
|
|
63
63
|
|
|
64
64
|
// Run the final function
|
|
65
65
|
if (typeof rule.done === 'function') {
|
|
66
|
-
rule.done({theme, log: getLogger({theme, rule}), result});
|
|
66
|
+
rule.done({theme, log: getLogger({theme, rule}), result, options: rule.options});
|
|
67
67
|
}
|
|
68
68
|
} catch (e) {
|
|
69
69
|
// Output something instead of failing silently (should never happen)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gscan",
|
|
3
|
-
"version": "6.
|
|
3
|
+
"version": "6.2.0",
|
|
4
4
|
"description": "Scans Ghost themes looking for errors, deprecations, features and compatibility",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ghost",
|
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
"gscan": "./bin/cli.js"
|
|
44
44
|
},
|
|
45
45
|
"dependencies": {
|
|
46
|
-
"@sentry/node": "10.
|
|
46
|
+
"@sentry/node": "10.53.1",
|
|
47
47
|
"@tryghost/config": "2.2.0",
|
|
48
48
|
"@tryghost/debug": "2.2.0",
|
|
49
49
|
"@tryghost/errors": "3.2.1",
|
|
@@ -66,12 +66,12 @@
|
|
|
66
66
|
"@eslint/compat": "2.1.0",
|
|
67
67
|
"@eslint/eslintrc": "3.3.5",
|
|
68
68
|
"@eslint/js": "10.0.1",
|
|
69
|
-
"@tryghost/pro-ship": "1.0.
|
|
70
|
-
"@vitest/coverage-v8": "4.1.
|
|
71
|
-
"eslint": "10.
|
|
69
|
+
"@tryghost/pro-ship": "1.0.10",
|
|
70
|
+
"@vitest/coverage-v8": "4.1.6",
|
|
71
|
+
"eslint": "10.4.0",
|
|
72
72
|
"eslint-plugin-ghost": "3.5.0",
|
|
73
73
|
"nodemon": "3.1.14",
|
|
74
|
-
"vitest": "4.1.
|
|
74
|
+
"vitest": "4.1.6"
|
|
75
75
|
},
|
|
76
76
|
"resolutions": {
|
|
77
77
|
"node-loggly-bulk": "4.0.2",
|