gscan 4.9.4 → 4.11.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.
@@ -1,6 +1,6 @@
1
- const debug = require('ghost-ignition').debug('ghost-version');
1
+ const debug = require('@tryghost/debug')('ghost-version');
2
2
  const exec = require('child_process').exec;
3
- const config = require('ghost-ignition').config();
3
+ const config = require('@tryghost/config');
4
4
 
5
5
  let ttl;
6
6
  let ghostVersion;
package/app/index.js CHANGED
@@ -1,9 +1,10 @@
1
1
  const express = require('express');
2
- const debug = require('ghost-ignition').debug('app');
2
+ const debug = require('@tryghost/debug')('app');
3
3
  const hbs = require('express-hbs');
4
4
  const multer = require('multer');
5
- const server = require('ghost-ignition').server;
6
- const errors = require('ghost-ignition').errors;
5
+ const server = require('@tryghost/server');
6
+ const config = require('@tryghost/config');
7
+ const errors = require('@tryghost/ignition-errors');
7
8
  const gscan = require('../lib');
8
9
  const fs = require('fs-extra');
9
10
  const logRequest = require('./middlewares/log-request');
@@ -109,4 +110,4 @@ app.use(function (err, req, res, next) {
109
110
  res.render(template, {message: err.message, stack: err.stack, details: err.errorDetails, context: err.context});
110
111
  });
111
112
 
112
- server.start(app);
113
+ server.start(app, config.get('port'));
@@ -1,5 +1,5 @@
1
1
  var uuid = require('uuid'),
2
- logging = require('../logging');
2
+ logging = require('@tryghost/logging');
3
3
 
4
4
  /**
5
5
  * @TODO:
@@ -1,7 +1,7 @@
1
1
  // NOTE: this middleware was extracted from Ghost core validation for theme uploads
2
2
  // might be useful to unify this logic in the future if it's extracted to separate module
3
3
  const path = require('path');
4
- const errors = require('ghost-ignition').errors;
4
+ const errors = require('@tryghost/ignition-errors');
5
5
 
6
6
  const checkFileExists = function checkFileExists(fileData) {
7
7
  return !!(fileData.mimetype && fileData.path);
package/lib/checker.js CHANGED
@@ -1,12 +1,9 @@
1
1
  const Promise = require('bluebird');
2
2
  const _ = require('lodash');
3
3
  const requireDir = require('require-dir');
4
- const {errors} = require('ghost-ignition');
5
- const readTheme = require('./read-theme');
4
+ const errors = require('@tryghost/ignition-errors');
6
5
  const versions = require('./utils').versions;
7
6
 
8
- const checks = requireDir('./checks');
9
-
10
7
  // An object containing helpers as keys and their labs flag as values
11
8
  // E.g. match: 'matchHelper'
12
9
  const labsEnabledHelpers = {
@@ -24,7 +21,9 @@ const labsEnabledHelpers = {
24
21
  * @param {Object=} [options.labs] object containing boolean flags for enabled labs features
25
22
  * @returns {Promise<Object>}
26
23
  */
27
- const checker = function checkAll(themePath, options = {}) {
24
+ const check = function checkAll(themePath, options = {}) {
25
+ // Require checks late to avoid loading all until used
26
+ const checks = requireDir('./checks');
28
27
  const passedVersion = _.get(options, 'checkVersion', versions.default);
29
28
  let version = passedVersion;
30
29
 
@@ -41,13 +40,15 @@ const checker = function checkAll(themePath, options = {}) {
41
40
  }
42
41
  });
43
42
 
43
+ // Require readTheme late to avoid loading entire AST parser until used
44
+ const readTheme = require('./read-theme');
44
45
  return readTheme(themePath)
45
46
  .then(function (theme) {
46
47
  // set the major version to check
47
48
  theme.checkedVersion = versions[version].major;
48
49
 
49
- return Promise.reduce(_.values(checks), function (themeToCheck, check) {
50
- return check(themeToCheck, options, themePath);
50
+ return Promise.reduce(_.values(checks), function (themeToCheck, checkFunction) {
51
+ return checkFunction(themeToCheck, options, themePath);
51
52
  }, theme);
52
53
  })
53
54
  .catch((error) => {
@@ -61,4 +62,50 @@ const checker = function checkAll(themePath, options = {}) {
61
62
  });
62
63
  };
63
64
 
64
- module.exports = checker;
65
+ const checkZip = async function checkZip(path, options) {
66
+ options = Object.assign({}, {
67
+ keepExtractedDir: false
68
+ }, options);
69
+
70
+ let zip;
71
+
72
+ if (_.isString(path)) {
73
+ zip = {
74
+ path,
75
+ name: path.match(/(.*\/)?(.*).zip$/)[2]
76
+ };
77
+ } else {
78
+ zip = _.clone(path);
79
+ }
80
+
81
+ try {
82
+ const readZip = require('./read-zip');
83
+ const {path: extractedZipPath} = await readZip(zip);
84
+ const theme = await check(extractedZipPath, Object.assign({themeName: zip.name}, options));
85
+
86
+ if (options.keepExtractedDir) {
87
+ return theme;
88
+ } else {
89
+ const fs = require('fs-extra');
90
+ await fs.remove(zip.origPath);
91
+ return theme;
92
+ }
93
+ } catch (error) {
94
+ if (!errors.utils.isIgnitionError(error)) {
95
+ throw new errors.ValidationError({
96
+ message: 'Failed to check zip file',
97
+ help: 'Your zip file might be corrupted, try unzipping and zipping again.',
98
+ errorDetails: error.message,
99
+ context: zip.name,
100
+ err: error
101
+ });
102
+ }
103
+
104
+ throw error;
105
+ }
106
+ };
107
+
108
+ module.exports = {
109
+ check,
110
+ checkZip
111
+ };
@@ -4,7 +4,6 @@ const isEmail = require('validator/lib/isEmail');
4
4
  const _private = {};
5
5
  const packageJSONFileName = 'package.json';
6
6
  const versions = require('../utils').versions;
7
- // const debug = require('ghost-ignition').debug('checks:package-json');
8
7
 
9
8
  const isSnakeCase = (str) => {
10
9
  return /^[a-z0-9]+[a-z0-9_]*$/.test(str);
@@ -1,6 +1,7 @@
1
1
  const _ = require('lodash');
2
2
  const spec = require('../specs');
3
3
  const versions = require('../utils').versions;
4
+ const {getPackageJSON} = require('../utils');
4
5
 
5
6
  const checkKoenigCssClasses = function checkKoenigCssClasses(theme, options) {
6
7
  const checkVersion = _.get(options, 'checkVersion', versions.default);
@@ -9,8 +10,13 @@ const checkKoenigCssClasses = function checkKoenigCssClasses(theme, options) {
9
10
  // CASE: 050-koenig-css-classes checks only needs `rules` that start with `GS050-`
10
11
  const ruleRegex = /GS050-.*/g;
11
12
 
13
+ // Following the introduction of card assets, we disable certain rules
14
+ // when it's enabled by the theme.
15
+ const packageJson = getPackageJSON(theme);
16
+ const cardAssetsEnabled = packageJson && packageJson.config && packageJson.config.card_assets === true;
17
+
12
18
  ruleSet = _.pickBy(ruleSet.rules, function (rule, ruleCode) {
13
- if (ruleCode.match(ruleRegex)) {
19
+ if (ruleCode.match(ruleRegex) && (!cardAssetsEnabled || !rule.cardAsset)) {
14
20
  return rule;
15
21
  }
16
22
  });
@@ -1,17 +1,12 @@
1
1
  const _ = require('lodash');
2
2
  const spec = require('../specs');
3
- const versions = require('../utils').versions;
4
- const packageJSONFileName = 'package.json';
3
+ const {versions, getPackageJSON} = require('../utils');
5
4
 
6
5
  module.exports = function checkUsage(theme, options) {
7
6
  const checkVersion = _.get(options, 'checkVersion', versions.default);
8
7
  let ruleSet = spec.get([checkVersion]);
9
- let [packageJSON] = _.filter(theme.files, {file: packageJSONFileName});
10
- let targetApiVersion = versions.default;
11
- if (packageJSON && packageJSON.content) {
12
- let packageJSONParsed = JSON.parse(packageJSON.content);
13
- targetApiVersion = (packageJSONParsed && packageJSONParsed.engines && packageJSONParsed.engines['ghost-api']) || versions.default;
14
- }
8
+ const packageJSON = getPackageJSON(theme);
9
+ let targetApiVersion = (packageJSON && packageJSON.engines && packageJSON.engines['ghost-api']) || versions.default;
15
10
 
16
11
  // CASE: 080-helper-usage checks only needs `rules` that start with `GS080-`
17
12
  const ruleRegex = /GS080-.*/g;
@@ -1,6 +1,6 @@
1
1
  const _ = require('lodash');
2
2
  const spec = require('../specs');
3
- const {versions, normalizePath} = require('../utils');
3
+ const {versions, normalizePath, getPackageJSON} = require('../utils');
4
4
  const ASTLinter = require('../ast-linter');
5
5
 
6
6
  function processFileFunction(files, failures, rules) {
@@ -40,13 +40,10 @@ function processFileFunction(files, failures, rules) {
40
40
  }
41
41
 
42
42
  function getCustomThemeSettings(theme) {
43
- let [packageJSON] = _.filter(theme.files, {file: 'package.json'});
43
+ const packageJSON = getPackageJSON(theme);
44
44
  let customThemeSettingsConfig;
45
- if (packageJSON && packageJSON.content) {
46
- let packageJSONParsed = JSON.parse(packageJSON.content);
47
- if (packageJSONParsed.config && packageJSONParsed.config.custom) {
48
- customThemeSettingsConfig = packageJSONParsed.config.custom;
49
- }
45
+ if (packageJSON && packageJSON.config && packageJSON.config.custom) {
46
+ customThemeSettingsConfig = packageJSON.config.custom;
50
47
  }
51
48
  return customThemeSettingsConfig;
52
49
  }
package/lib/index.js CHANGED
@@ -1,59 +1,5 @@
1
- const _ = require('lodash');
2
- const fs = require('fs-extra');
3
- const check = require('./checker');
1
+ const {check, checkZip} = require('./checker');
4
2
  const format = require('./format');
5
- const readZip = require('./read-zip');
6
- const {errors} = require('ghost-ignition');
7
-
8
- /**
9
- *
10
- * @param {string} path zip file path or a folder path containing a theme
11
- * @param {Object} options
12
- * @param {boolean} options.keepExtractedDir flag controling if the directory with extracted zip should stay after the check is complete
13
- * @param {string} options.checkVersion version to check the theme against
14
- * @param {Object=} [options.labs] object containing boolean flags for enabled labs features
15
- * @returns {Promise<any>}
16
- */
17
- const checkZip = async function checkZip(path, options) {
18
- options = Object.assign({}, {
19
- keepExtractedDir: false
20
- }, options);
21
-
22
- let zip;
23
-
24
- if (_.isString(path)) {
25
- zip = {
26
- path,
27
- name: path.match(/(.*\/)?(.*).zip$/)[2]
28
- };
29
- } else {
30
- zip = _.clone(path);
31
- }
32
-
33
- try {
34
- const {path: extractedZipPath} = await readZip(zip);
35
- const theme = await check(extractedZipPath, Object.assign({themeName: zip.name}, options));
36
-
37
- if (options.keepExtractedDir) {
38
- return theme;
39
- } else {
40
- await fs.remove(zip.origPath);
41
- return theme;
42
- }
43
- } catch (error) {
44
- if (!errors.utils.isIgnitionError(error)) {
45
- throw new errors.ValidationError({
46
- message: 'Failed to check zip file',
47
- help: 'Your zip file might be corrupted, try unzipping and zipping again.',
48
- errorDetails: error.message,
49
- context: zip.name,
50
- err: error
51
- });
52
- }
53
-
54
- throw error;
55
- }
56
- };
57
3
 
58
4
  module.exports = {
59
5
  check,
package/lib/read-zip.js CHANGED
@@ -1,10 +1,10 @@
1
- const debug = require('ghost-ignition').debug('zip');
1
+ const debug = require('@tryghost/debug')('zip');
2
2
  const path = require('path');
3
3
  const Promise = require('bluebird');
4
4
  const os = require('os');
5
5
  const glob = require('glob');
6
6
  const {extract} = require('@tryghost/zip');
7
- const {errors} = require('ghost-ignition');
7
+ const errors = require('@tryghost/ignition-errors');
8
8
  const uuid = require('uuid');
9
9
  const _ = require('lodash');
10
10
 
@@ -1,4 +1,4 @@
1
- const debug = require('ghost-ignition').debug('ghost-spec');
1
+ const debug = require('@tryghost/debug')('ghost-spec');
2
2
 
3
3
  module.exports = {
4
4
  get: function get(key) {
package/lib/specs/v2.js CHANGED
@@ -451,7 +451,8 @@ let rules = {
451
451
  Find out more about required theme changes for the Koenig editor <a href="${docsBaseUrl}editor/#image-size-implementations" target=_blank>here</a>.`,
452
452
  regex: /\.kg-width-wide/g,
453
453
  className: '.kg-width-wide',
454
- css: true
454
+ css: true,
455
+ cardAsset: true
455
456
  },
456
457
  'GS050-CSS-KGWF': {
457
458
  level: 'error',
@@ -460,7 +461,8 @@ let rules = {
460
461
  Find out more about required theme changes for the Koenig editor <a href="${docsBaseUrl}editor/#image-size-implementations" target=_blank>here</a>.`,
461
462
  regex: /\.kg-width-full/g,
462
463
  className: '.kg-width-full',
463
- css: true
464
+ css: true,
465
+ cardAsset: true
464
466
  },
465
467
  'GS050-CSS-KGGC': {
466
468
  level: 'error',
@@ -469,7 +471,8 @@ let rules = {
469
471
  Find out more about required theme changes for the Koenig editor <a href="${docsBaseUrl}editor/#gallery-card" target=_blank>here</a>.`,
470
472
  regex: /\.kg-gallery-container/g,
471
473
  className: '.kg-gallery-container',
472
- css: true
474
+ css: true,
475
+ cardAsset: true
473
476
  },
474
477
  'GS050-CSS-KGGR': {
475
478
  level: 'error',
@@ -478,7 +481,8 @@ let rules = {
478
481
  Find out more about required theme changes for the Koenig editor <a href="${docsBaseUrl}editor/#gallery-card" target=_blank>here</a>.`,
479
482
  regex: /\.kg-gallery-row/g,
480
483
  className: '.kg-gallery-row',
481
- css: true
484
+ css: true,
485
+ cardAsset: true
482
486
  },
483
487
  'GS050-CSS-KGGI': {
484
488
  level: 'error',
@@ -487,7 +491,8 @@ let rules = {
487
491
  Find out more about required theme changes for the Koenig editor <a href="${docsBaseUrl}editor/#gallery-card" target=_blank>here</a>.`,
488
492
  regex: /\.kg-gallery-image/g,
489
493
  className: '.kg-gallery-image',
490
- css: true
494
+ css: true,
495
+ cardAsset: true
491
496
  },
492
497
  'GS050-CSS-KGBM': {
493
498
  level: 'error',
@@ -496,7 +501,8 @@ let rules = {
496
501
  Find out more about required theme changes for the Koenig editor <a href="${docsBaseUrl}editor/#bookmark-card" target=_blank>here</a>.`,
497
502
  regex: /\.kg-bookmark-card/g,
498
503
  className: '.kg-bookmark-card',
499
- css: true
504
+ css: true,
505
+ cardAsset: true
500
506
  },
501
507
  'GS050-CSS-KGBMCO': {
502
508
  level: 'error',
@@ -505,7 +511,8 @@ let rules = {
505
511
  Find out more about required theme changes for the Koenig editor <a href="${docsBaseUrl}editor/#bookmark-card" target=_blank>here</a>.`,
506
512
  regex: /\.kg-bookmark-container/g,
507
513
  className: '.kg-bookmark-container',
508
- css: true
514
+ css: true,
515
+ cardAsset: true
509
516
  },
510
517
  'GS050-CSS-KGBMCON': {
511
518
  level: 'error',
@@ -514,7 +521,8 @@ let rules = {
514
521
  Find out more about required theme changes for the Koenig editor <a href="${docsBaseUrl}editor/#bookmark-card" target=_blank>here</a>.`,
515
522
  regex: /\.kg-bookmark-content/g,
516
523
  className: '.kg-bookmark-content',
517
- css: true
524
+ css: true,
525
+ cardAsset: true
518
526
  },
519
527
  'GS050-CSS-KGBMTI': {
520
528
  level: 'error',
@@ -523,7 +531,8 @@ let rules = {
523
531
  Find out more about required theme changes for the Koenig editor <a href="${docsBaseUrl}editor/#bookmark-card" target=_blank>here</a>.`,
524
532
  regex: /\.kg-bookmark-title/g,
525
533
  className: '.kg-bookmark-title',
526
- css: true
534
+ css: true,
535
+ cardAsset: true
527
536
  },
528
537
  'GS050-CSS-KGBMDE': {
529
538
  level: 'error',
@@ -532,7 +541,8 @@ let rules = {
532
541
  Find out more about required theme changes for the Koenig editor <a href="${docsBaseUrl}editor/#bookmark-card" target=_blank>here</a>.`,
533
542
  regex: /\.kg-bookmark-description/g,
534
543
  className: '.kg-bookmark-description',
535
- css: true
544
+ css: true,
545
+ cardAsset: true
536
546
  },
537
547
  'GS050-CSS-KGBMME': {
538
548
  level: 'error',
@@ -541,7 +551,8 @@ let rules = {
541
551
  Find out more about required theme changes for the Koenig editor <a href="${docsBaseUrl}editor/#bookmark-card" target=_blank>here</a>.`,
542
552
  regex: /\.kg-bookmark-metadata/g,
543
553
  className: '.kg-bookmark-metadata',
544
- css: true
554
+ css: true,
555
+ cardAsset: true
545
556
  },
546
557
  'GS050-CSS-KGBMIC': {
547
558
  level: 'error',
@@ -550,7 +561,8 @@ let rules = {
550
561
  Find out more about required theme changes for the Koenig editor <a href="${docsBaseUrl}editor/#bookmark-card" target=_blank>here</a>.`,
551
562
  regex: /\.kg-bookmark-icon/g,
552
563
  className: '.kg-bookmark-icon',
553
- css: true
564
+ css: true,
565
+ cardAsset: true
554
566
  },
555
567
  'GS050-CSS-KGBMAU': {
556
568
  level: 'error',
@@ -559,7 +571,8 @@ let rules = {
559
571
  Find out more about required theme changes for the Koenig editor <a href="${docsBaseUrl}editor/#bookmark-card" target=_blank>here</a>.`,
560
572
  regex: /\.kg-bookmark-author/g,
561
573
  className: '.kg-bookmark-author',
562
- css: true
574
+ css: true,
575
+ cardAsset: true
563
576
  },
564
577
  'GS050-CSS-KGBMPU': {
565
578
  level: 'error',
@@ -568,7 +581,8 @@ let rules = {
568
581
  Find out more about required theme changes for the Koenig editor <a href="${docsBaseUrl}editor/#bookmark-card" target=_blank>here</a>.`,
569
582
  regex: /\.kg-bookmark-publisher/g,
570
583
  className: '.kg-bookmark-publisher',
571
- css: true
584
+ css: true,
585
+ cardAsset: true
572
586
  },
573
587
  'GS050-CSS-KGBMTH': {
574
588
  level: 'error',
@@ -577,7 +591,8 @@ let rules = {
577
591
  Find out more about required theme changes for the Koenig editor <a href="${docsBaseUrl}editor/#bookmark-card" target=_blank>here</a>.`,
578
592
  regex: /\.kg-bookmark-thumbnail/g,
579
593
  className: '.kg-bookmark-thumbnail',
580
- css: true
594
+ css: true,
595
+ cardAsset: true
581
596
  },
582
597
  // Updated v1 rules
583
598
  'GS001-DEPR-AC': {
@@ -16,5 +16,6 @@ module.exports = {
16
16
  // `docs` is used to generate the URLs that link to documentation and needs to be updated whenever
17
17
  // we release a new version on ghost.org/docs/api/
18
18
  versions: require('./versions.json'),
19
- normalizePath
19
+ normalizePath,
20
+ getPackageJSON: require('./package-json')
20
21
  };
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Extracts the package.json JSON content. Note that this function never throws,
3
+ * even when there is a JSON parsing error.
4
+ * @param {Object} theme The theme to extract package.json from.
5
+ * @returns {Object} The content of the package.json file, or `null` if
6
+ * something happened (no file, JSON parsing error...).
7
+ */
8
+ function getJSON(theme) {
9
+ let packageJSON = theme.files.find(item => item.file === 'package.json');
10
+ if (packageJSON && packageJSON.content) {
11
+ try {
12
+ return JSON.parse(packageJSON.content);
13
+ } catch (e) {
14
+ // Do nothing here
15
+ }
16
+ }
17
+ return null;
18
+ }
19
+
20
+ module.exports = getJSON;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gscan",
3
- "version": "4.9.4",
3
+ "version": "4.11.0",
4
4
  "description": "Scans Ghost themes looking for errors, deprecations, features and compatibility",
5
5
  "keywords": [
6
6
  "ghost",
@@ -14,7 +14,7 @@
14
14
  "url": "git@github.com:TryGhost/gscan.git"
15
15
  },
16
16
  "engines": {
17
- "node": "^12.10.0 || ^14.13.0"
17
+ "node": "^12.22.1 || ^14.17.0 || ^16.13.0"
18
18
  },
19
19
  "bugs": {
20
20
  "url": "https://github.com/TryGhost/gscan/issues"
@@ -40,16 +40,20 @@
40
40
  "gscan": "./bin/cli.js"
41
41
  },
42
42
  "dependencies": {
43
- "@sentry/node": "6.13.3",
43
+ "@sentry/node": "6.15.0",
44
+ "@tryghost/config": "0.2.0",
45
+ "@tryghost/debug": "0.1.9",
46
+ "@tryghost/ignition-errors": "0.1.8",
47
+ "@tryghost/logging": "1.0.1",
44
48
  "@tryghost/pretty-cli": "1.2.22",
49
+ "@tryghost/server": "0.1.0",
45
50
  "@tryghost/zip": "1.1.18",
46
51
  "bluebird": "3.7.2",
47
52
  "chalk": "4.1.2",
48
- "common-tags": "1.8.0",
53
+ "common-tags": "1.8.2",
49
54
  "express": "4.17.1",
50
55
  "express-hbs": "2.4.0",
51
56
  "fs-extra": "9.1.0",
52
- "ghost-ignition": "4.6.3",
53
57
  "glob": "7.2.0",
54
58
  "lodash": "4.17.21",
55
59
  "multer": "1.4.3",
@@ -60,14 +64,14 @@
60
64
  "validator": "13.0.0"
61
65
  },
62
66
  "devDependencies": {
63
- "eslint": "7.25.0",
67
+ "eslint": "8.1.0",
64
68
  "eslint-plugin-ghost": "2.1.0",
65
69
  "istanbul": "0.4.5",
66
70
  "mocha": "9.0.2",
67
71
  "nodemon": "2.0.7",
68
72
  "rewire": "5.0.0",
69
73
  "should": "13.2.3",
70
- "sinon": "11.0.0"
74
+ "sinon": "12.0.0"
71
75
  },
72
76
  "files": [
73
77
  "lib",
package/app/logging.js DELETED
@@ -1,12 +0,0 @@
1
- var config = require('ghost-ignition').config();
2
-
3
- // see defaults in GhostLogger
4
- var logging = require('ghost-ignition').logging({
5
- path: config.get('logging:path'),
6
- domain: config.get('logging:domain'),
7
- mode: config.get('logging:mode'),
8
- level: config.get('logging:level'),
9
- transports: config.get('logging:transports')
10
- });
11
-
12
- module.exports = logging;