gscan 4.9.3 → 4.10.2

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
 
44
- return readTheme(themePath, options)
43
+ // Require readTheme late to avoid loading entire AST parser until used
44
+ const readTheme = require('./read-theme');
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,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
  }
@@ -116,15 +116,8 @@ function parseWithAST({theme, log, file, rules, callback}){
116
116
 
117
117
  const ruleImplementations = {
118
118
  'GS100-NO-UNUSED-CUSTOM-THEME-SETTING': {
119
- isEnabled: ({theme, result}) => {
120
- let [packageJSON] = _.filter(theme.files, {file: 'package.json'});
121
- if (packageJSON && packageJSON.content) {
122
- let packageJSONParsed = JSON.parse(packageJSON.content);
123
- if (packageJSONParsed.config && packageJSONParsed.config.custom) {
124
- result.customThemeSettingsConfig = packageJSONParsed.config.custom;
125
- }
126
- }
127
- return !!result.customThemeSettingsConfig;
119
+ isEnabled: ({theme}) => {
120
+ return !!theme.customSettings;
128
121
  },
129
122
  init: ({result}) => {
130
123
  result.customThemeSettings = new Set();
@@ -142,8 +135,8 @@ const ruleImplementations = {
142
135
  }});
143
136
  }
144
137
  },
145
- done: ({log, result}) => {
146
- const config = Object.keys(result.customThemeSettingsConfig);
138
+ done: ({log, theme, result}) => {
139
+ const config = Object.keys(theme.customSettings);
147
140
  const notUsedVariable = config.filter(x => !result.customThemeSettings.has(x));
148
141
 
149
142
  if (notUsedVariable.length > 0) {
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-theme.js CHANGED
@@ -75,11 +75,9 @@ const readThemeStructure = function readThemeFiles(themePath, subPath, arr) {
75
75
  /**
76
76
  *
77
77
  * @param {Theme} theme
78
- * @param {Object} options
79
- * @param {Object=} [options.labs] object containing boolean flags for enabled labs features
80
78
  * @returns {Promise<Theme>}
81
79
  */
82
- const readFiles = function readFiles(theme, options = {}) {
80
+ const readFiles = function readFiles(theme) {
83
81
  const themeFilesContent = _.filter(theme.files, function (themeFile) {
84
82
  if (themeFile && themeFile.ext) {
85
83
  return themeFile.ext.match(/\.hbs|\.css|\.js/ig) || themeFile.file.match(/package.json/i);
@@ -98,17 +96,19 @@ const readFiles = function readFiles(theme, options = {}) {
98
96
  return fs.readFile(path.join(theme.path, themeFile.file), 'utf8').then(function (content) {
99
97
  themeFile.content = content;
100
98
 
101
- if (options.labs && options.labs.customThemeSettings) {
102
- if (!theme.customSettings) {
103
- theme.customSettings = {};
104
- }
99
+ if (!theme.customSettings) {
100
+ theme.customSettings = {};
101
+ }
105
102
 
106
- const packageJsonMatch = themeFile.file === 'package.json';
107
- if (packageJsonMatch) {
103
+ const packageJsonMatch = themeFile.file === 'package.json';
104
+ if (packageJsonMatch) {
105
+ try {
108
106
  const packageJson = JSON.parse(themeFile.content);
109
107
  if (packageJson.config && packageJson.config.custom) {
110
108
  theme.customSettings = packageJson.config.custom;
111
109
  }
110
+ } catch (e) {
111
+ // Ignore error as they will be caught in 010-package-json.js
112
112
  }
113
113
  }
114
114
 
@@ -230,11 +230,9 @@ const extractTemplates = function extractTemplates(allFiles) {
230
230
  /**
231
231
  *
232
232
  * @param {string} themePath - path to the validated theme
233
- * @param {Object} options
234
- * @param {Object=} [options.labs] object containing boolean flags for enabled labs features
235
233
  * @returns {Promise<Theme>}
236
234
  */
237
- module.exports = function readTheme(themePath, options = {}) {
235
+ module.exports = function readTheme(themePath) {
238
236
  return readThemeStructure(themePath)
239
237
  .then(function (themeFiles) {
240
238
  var allTemplates = extractTemplates(themeFiles);
@@ -252,7 +250,7 @@ module.exports = function readTheme(themePath, options = {}) {
252
250
  pass: [],
253
251
  fail: {}
254
252
  }
255
- }, options);
253
+ });
256
254
  });
257
255
  };
258
256
 
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) {
@@ -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.3",
3
+ "version": "4.10.2",
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.0",
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.1",
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;