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 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, the blog will not boot. 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.
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 be marked as **fatal errors** if they would cause errors, and therefore should block a boot or an upgrade.
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 4.0):
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` or `--canary`
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 3.0:
77
- // checkVersion: 'v3',
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 === 'v4') {
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
- {isPresentEngineGhostAPI: 'GS010-PJ-GHOST-API'},
36
+ {isNotPresentEngineGhostAPI: 'GS010-PJ-GHOST-API'},
37
37
  {isv01EngineGhostAPI: 'GS010-PJ-GHOST-API-V01'},
38
38
  v3PackageJSONConditionalRules
39
39
  );
40
40
 
41
- const canaryPackageJSONConditionalRules = {};
42
- const canaryPackageJSONValidationRules = _.extend({},
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
- canaryPackageJSONConditionalRules
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({}, v1PackageJSONValidationRules, v2PackageJSONValidationRules);
225
- packageJSONConditionalRules = _.merge({}, v1PackageJSONConditionalRules, v2PackageJSONConditionalRules);
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({}, v1PackageJSONValidationRules, v2PackageJSONValidationRules, v3PackageJSONValidationRules);
228
- packageJSONConditionalRules = _.merge({}, v1PackageJSONConditionalRules, v2PackageJSONConditionalRules, v3PackageJSONConditionalRules);
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'/'canary' rules
231
- packageJSONValidationRules = _.merge({}, v1PackageJSONValidationRules, v2PackageJSONValidationRules, v3PackageJSONValidationRules, canaryPackageJSONValidationRules);
232
- packageJSONConditionalRules = _.merge({}, v1PackageJSONConditionalRules, v2PackageJSONConditionalRules, v3PackageJSONConditionalRules, canaryPackageJSONConditionalRules);
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,