gscan 4.41.0 → 4.42.1
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 +22 -28
- package/lib/ast-linter/rules/mark-used-partials.js +2 -0
- package/lib/checker.js +27 -21
- package/lib/checks/005-template-compile.js +2 -2
- package/lib/checks/090-template-syntax.js +3 -3
- package/lib/checks/100-custom-template-settings-usage.js +3 -3
- package/lib/checks/110-page-builder-usage.js +3 -3
- package/lib/checks/120-no-unknown-globals.js +1 -2
- package/lib/read-theme.js +2 -0
- package/lib/specs/v4.js +1 -1
- package/package.json +14 -2
package/README.md
CHANGED
|
@@ -10,29 +10,26 @@ It is actively capable of dealing with the current and last major versions of Gh
|
|
|
10
10
|
|
|
11
11
|
GScan works on a system of rules. Each rule has a way to check whether it passes or fails and has help content which describes how to fix it. Each rule is also marked with an error level:
|
|
12
12
|
|
|
13
|
-
-
|
|
14
|
-
-
|
|
15
|
-
-
|
|
13
|
+
- Errors: issues that will cause your theme to not work properly. These must be fixed.
|
|
14
|
+
- Warnings: these are usually related to deprecated features. These should be fixed.
|
|
15
|
+
- Recommendations: these are advisories about best practice. Fixing these will improve your theme.
|
|
16
|
+
- Features: detected features which may impact on compatibility. Nothing to do!
|
|
16
17
|
|
|
17
18
|
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
19
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
Errors are only marked as **fatal errors** if they would cause errors, and therefore should block a boot or an upgrade.
|
|
20
|
+
## Usage
|
|
22
21
|
|
|
23
|
-
|
|
24
|
-
When developing new rules or testing gscan following tools are great to have in the toolbelt:
|
|
25
|
-
- [astexplorer](https://astexplorer.net) - absolutely awesome Handlebars AST fiddler, helpful when testing out new ideas and exploring what's possible through AST parser;
|
|
22
|
+
There are 4 ways to use gscan to validate your theme:
|
|
26
23
|
|
|
27
|
-
|
|
24
|
+
### 1. Inside Ghost
|
|
28
25
|
|
|
29
|
-
|
|
26
|
+
Gscan is pre-installed in Ghost. If there are theme errors, Ghost will show them on boot in the console logs and in the UI (Ghost Admin).
|
|
30
27
|
|
|
31
|
-
###
|
|
28
|
+
### 2. Web usage
|
|
32
29
|
|
|
33
30
|
Visit https://gscan.ghost.org and upload your zip to our online version of Gscan.
|
|
34
31
|
|
|
35
|
-
###
|
|
32
|
+
### 3. CLI usage
|
|
36
33
|
|
|
37
34
|
Install using yarn / npm:
|
|
38
35
|
|
|
@@ -48,21 +45,21 @@ To run a local zip file through the checks:
|
|
|
48
45
|
|
|
49
46
|
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
47
|
|
|
51
|
-
`--v1` or `-1`
|
|
52
|
-
`--v2` or `-2`
|
|
53
|
-
`--v3` or `-3`
|
|
48
|
+
`--v1` or `-1`
|
|
49
|
+
`--v2` or `-2`
|
|
50
|
+
`--v3` or `-3`
|
|
54
51
|
`--v4` or `-4` or `--canary`
|
|
55
|
-
`--v5` or `-5`
|
|
52
|
+
`--v5` or `-5`
|
|
56
53
|
|
|
57
54
|
Use the `--canary` parameter to check for the upcoming Ghost version.
|
|
58
55
|
|
|
59
56
|
Examples:
|
|
60
57
|
|
|
61
|
-
`gscan /path/to/theme.zip -z1` - scan a theme in a zip file for Ghost 1.0 compatibility
|
|
62
|
-
`gscan /path/to/theme/directory --v2` - can a theme in a directory for Ghost 2.0 compatibility
|
|
63
|
-
`gscan /path/to/theme/directory --canary` - scan a theme for the upcoming version of Ghost
|
|
58
|
+
`gscan /path/to/theme.zip -z1` - scan a theme in a zip file for Ghost 1.0 compatibility
|
|
59
|
+
`gscan /path/to/theme/directory --v2` - can a theme in a directory for Ghost 2.0 compatibility
|
|
60
|
+
`gscan /path/to/theme/directory --canary` - scan a theme for the upcoming version of Ghost
|
|
64
61
|
|
|
65
|
-
###
|
|
62
|
+
### 4. Lib usage
|
|
66
63
|
|
|
67
64
|
Install using yarn/npm and then:
|
|
68
65
|
|
|
@@ -98,14 +95,11 @@ gscan.checkZip({
|
|
|
98
95
|
|
|
99
96
|
- `yarn ship`
|
|
100
97
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
-
|
|
104
|
-
- Warnings: these are usually related to deprecated features. These should be fixed.
|
|
105
|
-
- Recommendations: these are advisories about best practice. Fixing these will improve your theme.
|
|
106
|
-
- Features: detected features which may impact on compatibility. Nothing to do :)
|
|
98
|
+
### Tools
|
|
99
|
+
When developing new rules or testing gscan following tools are great to have in the toolbelt:
|
|
100
|
+
- [astexplorer](https://astexplorer.net) - absolutely awesome Handlebars AST fiddler, helpful when testing out new ideas and exploring what's possible through AST parser;
|
|
107
101
|
|
|
108
|
-
##
|
|
102
|
+
## To Do
|
|
109
103
|
|
|
110
104
|
- Support for running the checks against a GitHub repository
|
|
111
105
|
- Many, many more checks
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
const Rule = require('./base');
|
|
2
2
|
const {getPartialName} = require('../helpers');
|
|
3
|
+
const {normalizePath} = require('../../utils');
|
|
3
4
|
|
|
4
5
|
module.exports = class MarkUsedPartials extends Rule {
|
|
5
6
|
_markUsedPartials(node) {
|
|
@@ -8,6 +9,7 @@ module.exports = class MarkUsedPartials extends Rule {
|
|
|
8
9
|
if (nodeName) {
|
|
9
10
|
this.scanner.context.partials.push({
|
|
10
11
|
node: nodeName,
|
|
12
|
+
normalizedName: normalizePath(nodeName),
|
|
11
13
|
type: node.type,
|
|
12
14
|
loc: node.loc,
|
|
13
15
|
parameters: node.params ? node.params.map(p => p.original) : null
|
package/lib/checker.js
CHANGED
|
@@ -22,7 +22,7 @@ const labsEnabledHelpers = {
|
|
|
22
22
|
* @param {Object=} [options.labs] object containing boolean flags for enabled labs features
|
|
23
23
|
* @returns {Promise<Object>}
|
|
24
24
|
*/
|
|
25
|
-
const check = function checkAll(themePath, options = {}) {
|
|
25
|
+
const check = async function checkAll(themePath, options = {}) {
|
|
26
26
|
// Require checks late to avoid loading all until used
|
|
27
27
|
const checks = requireDir('./checks');
|
|
28
28
|
const passedVersion = _.get(options, 'checkVersion', versions.default);
|
|
@@ -44,27 +44,33 @@ const check = function checkAll(themePath, options = {}) {
|
|
|
44
44
|
|
|
45
45
|
// Require readTheme late to avoid loading entire AST parser until used
|
|
46
46
|
const readTheme = require('./read-theme');
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
47
|
+
|
|
48
|
+
try {
|
|
49
|
+
const theme = await readTheme(themePath);
|
|
50
|
+
|
|
51
|
+
// set the major version to check
|
|
52
|
+
theme.checkedVersion = versions[version].major;
|
|
53
|
+
|
|
54
|
+
const timeBeforeChecks = Date.now();
|
|
55
|
+
|
|
56
|
+
for (const checkName in checks) {
|
|
57
|
+
const now = Date.now();
|
|
58
|
+
await checks[checkName](theme, options, themePath);
|
|
59
|
+
debug(checkName, 'took', Date.now() - now, 'ms');
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
debug('All checks took', Date.now() - timeBeforeChecks, 'ms');
|
|
63
|
+
|
|
64
|
+
return theme;
|
|
65
|
+
} catch (err) {
|
|
66
|
+
throw new errors.ValidationError({
|
|
67
|
+
message: 'Failed theme files check',
|
|
68
|
+
help: 'Your theme file structure is corrupted or contains errors',
|
|
69
|
+
errorDetails: err.message,
|
|
70
|
+
context: options.themeName,
|
|
71
|
+
err
|
|
67
72
|
});
|
|
73
|
+
}
|
|
68
74
|
};
|
|
69
75
|
|
|
70
76
|
const checkZip = async function checkZip(path, options) {
|
|
@@ -2,7 +2,6 @@ const _ = require('lodash');
|
|
|
2
2
|
const spec = require('../specs');
|
|
3
3
|
const versions = require('../utils').versions;
|
|
4
4
|
const ASTLinter = require('../ast-linter');
|
|
5
|
-
const {normalizePath} = require('../utils');
|
|
6
5
|
|
|
7
6
|
function processFileFunction(files, failures, theme, partialsFound) {
|
|
8
7
|
const processedFiles = [];
|
|
@@ -61,7 +60,8 @@ function processFileFunction(files, failures, theme, partialsFound) {
|
|
|
61
60
|
linter.partials.forEach((partial) => {
|
|
62
61
|
const partialName = partial.node;
|
|
63
62
|
partialsFound[partialName] = true;
|
|
64
|
-
|
|
63
|
+
|
|
64
|
+
const file = files.find(f => f.normalizedFile === `partials/${partial.normalizedName}.hbs`);
|
|
65
65
|
if (file) {
|
|
66
66
|
// Find all inline partial declaration that were within the partial usage block
|
|
67
67
|
const childrenInlinePartials = [...parentInlinePartials];
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
const _ = require('lodash');
|
|
2
2
|
const spec = require('../specs');
|
|
3
|
-
const {versions,
|
|
3
|
+
const {versions, getPackageJSON} = require('../utils');
|
|
4
4
|
const ASTLinter = require('../ast-linter');
|
|
5
5
|
|
|
6
6
|
function processFileFunction(files, failures, rules) {
|
|
@@ -30,8 +30,8 @@ function processFileFunction(files, failures, rules) {
|
|
|
30
30
|
});
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
-
linter.partials.forEach(({
|
|
34
|
-
const file = files.find(f =>
|
|
33
|
+
linter.partials.forEach(({normalizedName}) => {
|
|
34
|
+
const file = files.find(f => f.normalizedFile === `partials/${normalizedName}.hbs`);
|
|
35
35
|
if (file) {
|
|
36
36
|
processFile(linter, file);
|
|
37
37
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
const _ = require('lodash');
|
|
2
2
|
const spec = require('../specs');
|
|
3
|
-
const {versions
|
|
3
|
+
const {versions} = require('../utils');
|
|
4
4
|
const ASTLinter = require('../ast-linter');
|
|
5
5
|
|
|
6
6
|
function getRules(id, options) {
|
|
@@ -103,8 +103,8 @@ function parseWithAST({theme, log, file, rules, callback}){
|
|
|
103
103
|
callback(linter);
|
|
104
104
|
}
|
|
105
105
|
|
|
106
|
-
linter.partials.forEach(({
|
|
107
|
-
const partialFile = theme.files.find(f =>
|
|
106
|
+
linter.partials.forEach(({normalizedName}) => {
|
|
107
|
+
const partialFile = theme.files.find(f => f.normalizedFile === `partials/${normalizedName}.hbs`);
|
|
108
108
|
if (partialFile) {
|
|
109
109
|
processFile(partialFile);
|
|
110
110
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
const _ = require('lodash');
|
|
2
2
|
const spec = require('../specs');
|
|
3
|
-
const {versions
|
|
3
|
+
const {versions} = require('../utils');
|
|
4
4
|
const ASTLinter = require('../ast-linter');
|
|
5
5
|
|
|
6
6
|
function getRules(id, options) {
|
|
@@ -104,8 +104,8 @@ function parseWithAST({theme, log, file, rules, callback}) {
|
|
|
104
104
|
callback(linter);
|
|
105
105
|
}
|
|
106
106
|
|
|
107
|
-
linter.partials.forEach(({
|
|
108
|
-
const partialFile = theme.files.find(f =>
|
|
107
|
+
linter.partials.forEach(({normalizedName}) => {
|
|
108
|
+
const partialFile = theme.files.find(f => f.normalizedFile === `partials/${normalizedName}.hbs`);
|
|
109
109
|
if (partialFile) {
|
|
110
110
|
processFile(partialFile);
|
|
111
111
|
}
|
|
@@ -2,7 +2,6 @@ const _ = require('lodash');
|
|
|
2
2
|
const spec = require('../specs');
|
|
3
3
|
const versions = require('../utils').versions;
|
|
4
4
|
const ASTLinter = require('../ast-linter');
|
|
5
|
-
const {normalizePath} = require('../utils');
|
|
6
5
|
|
|
7
6
|
function processFileFunction(files, failures, theme, partialsFound) {
|
|
8
7
|
const processedFiles = [];
|
|
@@ -61,7 +60,7 @@ function processFileFunction(files, failures, theme, partialsFound) {
|
|
|
61
60
|
linter.partials.forEach((partial) => {
|
|
62
61
|
const partialName = partial.node;
|
|
63
62
|
partialsFound[partialName] = true;
|
|
64
|
-
const file = files.find(f =>
|
|
63
|
+
const file = files.find(f => f.normalizedFile === `partials/${partial.normalizedName}.hbs`);
|
|
65
64
|
if (file) {
|
|
66
65
|
// Find all inline partial declaration that were within the partial usage block
|
|
67
66
|
const childrenInlinePartials = [...parentInlinePartials];
|
package/lib/read-theme.js
CHANGED
|
@@ -3,6 +3,7 @@ const _ = require('lodash');
|
|
|
3
3
|
const os = require('os');
|
|
4
4
|
const path = require('path');
|
|
5
5
|
const ASTLinter = require('./ast-linter');
|
|
6
|
+
const {normalizePath} = require('./utils');
|
|
6
7
|
|
|
7
8
|
const ignore = [
|
|
8
9
|
'node_modules',
|
|
@@ -31,6 +32,7 @@ const readThemeStructure = function readThemeFiles(themePath, subPath, arr) {
|
|
|
31
32
|
var makeResult = function makeResult(result, subFilePath, ext, symlink) {
|
|
32
33
|
result.push({
|
|
33
34
|
file: subFilePath,
|
|
35
|
+
normalizedFile: normalizePath(subFilePath),
|
|
34
36
|
ext,
|
|
35
37
|
symlink
|
|
36
38
|
});
|
package/lib/specs/v4.js
CHANGED
|
@@ -54,7 +54,7 @@ let rules = {
|
|
|
54
54
|
'GS010-PJ-CUST-THEME-TOTAL-SETTINGS': {
|
|
55
55
|
level: 'error',
|
|
56
56
|
rule: '<code>package.json</code> property <code>"config.custom"</code> contains too many settings',
|
|
57
|
-
details: oneLineTrim`Remove key from <code>"config.custom"</code> in your <code>package.json</code> to have less than or exactly
|
|
57
|
+
details: oneLineTrim`Remove key from <code>"config.custom"</code> in your <code>package.json</code> to have less than or exactly 20 settings.<br>
|
|
58
58
|
Check the <a href="${docsBaseUrl}structure/#packagejson" target=_blank><code>package.json</code> documentation</a> for further information.`
|
|
59
59
|
},
|
|
60
60
|
'GS010-PJ-CUST-THEME-SETTINGS-CASE': {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gscan",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.42.1",
|
|
4
4
|
"description": "Scans Ghost themes looking for errors, deprecations, features and compatibility",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ghost",
|
|
@@ -13,6 +13,9 @@
|
|
|
13
13
|
"type": "git",
|
|
14
14
|
"url": "git@github.com:TryGhost/gscan.git"
|
|
15
15
|
},
|
|
16
|
+
"publishConfig": {
|
|
17
|
+
"registry": "https://registry.npmjs.org/"
|
|
18
|
+
},
|
|
16
19
|
"engines": {
|
|
17
20
|
"node": "^14.18.0 || ^16.13.0 || ^18.12.1"
|
|
18
21
|
},
|
|
@@ -80,5 +83,14 @@
|
|
|
80
83
|
"lib",
|
|
81
84
|
"bin",
|
|
82
85
|
"app"
|
|
83
|
-
]
|
|
86
|
+
],
|
|
87
|
+
"renovate": {
|
|
88
|
+
"extends": [
|
|
89
|
+
"@tryghost:quietJS",
|
|
90
|
+
"@tryghost:automergeDevDependencies"
|
|
91
|
+
],
|
|
92
|
+
"ignoreDeps": [
|
|
93
|
+
"validator"
|
|
94
|
+
]
|
|
95
|
+
}
|
|
84
96
|
}
|