gscan 4.29.2 → 4.31.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/app/tpl/index.hbs +2 -2
- package/lib/ast-linter/rules/index.js +5 -1
- package/lib/ast-linter/rules/internal/scope.js +14 -0
- package/lib/ast-linter/rules/lint-no-price-data-currency-context.js +26 -0
- package/lib/ast-linter/rules/lint-no-price-data-currency-global.js +26 -0
- package/lib/ast-linter/rules/lint-no-price-data-monthly-yearly.js +20 -0
- package/lib/ast-linter/rules/lint-no-tier-benefit-as-object.js +30 -0
- package/lib/ast-linter/rules/{lint-no-price-data-helper.js → lint-no-tier-price-as-object.js} +6 -3
- package/lib/checks/001-deprecations.js +2 -2
- package/lib/checks/002-comment-id.js +1 -1
- package/lib/checks/005-template-compile.js +1 -1
- package/lib/checks/010-package-json.js +9 -1
- package/lib/checks/080-helper-usage.js +1 -1
- package/lib/checks/090-template-syntax.js +1 -1
- package/lib/checks/100-custom-template-settings-usage.js +1 -1
- package/lib/format.js +1 -24
- package/lib/read-zip.js +1 -1
- package/lib/specs/canary.js +46 -8
- package/lib/utils/score-calculator.js +40 -0
- package/lib/utils/versions.json +1 -1
- package/package.json +8 -8
package/app/tpl/index.hbs
CHANGED
|
@@ -22,8 +22,8 @@
|
|
|
22
22
|
<span class="gh-input-icon select-arrow">{{> icon-arrow-up}}</span>
|
|
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
|
-
<option value="v5">{{ghostVersions.v5.major}}</option>
|
|
26
|
-
<option value="v4"
|
|
25
|
+
<option value="v5" selected>{{ghostVersions.v5.major}}</option>
|
|
26
|
+
<option value="v4">{{ghostVersions.v4.major}}</option>
|
|
27
27
|
<option value="v3">{{ghostVersions.v3.major}}</option>
|
|
28
28
|
<option value="v2">{{ghostVersions.v2.major}}</option>
|
|
29
29
|
<option value="v1">{{ghostVersions.v1.major}}</option>
|
|
@@ -7,7 +7,11 @@ module.exports = {
|
|
|
7
7
|
'GS090-NO-PRODUCT-DATA-HELPER': require('./lint-no-product-data-helper'),
|
|
8
8
|
'GS090-NO-PRODUCTS-DATA-HELPER': require('./lint-no-products-data-helper'),
|
|
9
9
|
'GS090-NO-MEMBER-PRODUCTS-DATA-HELPER': require('./lint-no-member-products-data-helper'),
|
|
10
|
-
'GS090-NO-PRICE-
|
|
10
|
+
'GS090-NO-TIER-PRICE-AS-OBJECT': require('./lint-no-tier-price-as-object'),
|
|
11
|
+
'GS090-NO-TIER-BENEFIT-AS-OBJECT': require('./lint-no-tier-benefit-as-object'),
|
|
12
|
+
'GS090-NO-PRICE-DATA-CURRENCY-GLOBAL': require('./lint-no-price-data-currency-global'),
|
|
13
|
+
'GS090-NO-PRICE-DATA-CURRENCY-CONTEXT': require('./lint-no-price-data-currency-context'),
|
|
14
|
+
'GS090-NO-PRICE-DATA-MONTHLY-YEARLY': require('./lint-no-price-data-monthly-yearly'),
|
|
11
15
|
'no-multi-param-conditionals': require('./lint-no-multi-param-conditionals'),
|
|
12
16
|
'no-nested-async-helpers': require('./lint-no-nested-async-helpers'),
|
|
13
17
|
'no-prev-next-post-outside-post-context': require('./lint-no-prev-next-post-outside-post-context'),
|
|
@@ -204,6 +204,20 @@ class Scope {
|
|
|
204
204
|
return found;
|
|
205
205
|
}
|
|
206
206
|
|
|
207
|
+
getParentContextNode(context) {
|
|
208
|
+
let matchedFrame = null;
|
|
209
|
+
|
|
210
|
+
if (this.frames && this.frames.length) {
|
|
211
|
+
matchedFrame = this.frames.find((frame) => {
|
|
212
|
+
if (frame.nodeName === context) {
|
|
213
|
+
return true;
|
|
214
|
+
}
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
return matchedFrame && matchedFrame.node;
|
|
219
|
+
}
|
|
220
|
+
|
|
207
221
|
isLocal(node) {
|
|
208
222
|
// @foo MustacheStatements are referencing globals rather than locals
|
|
209
223
|
if (node.type === 'MustacheStatement' && node.path.data) {
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
const Rule = require('./base');
|
|
2
|
+
const _ = require('lodash');
|
|
3
|
+
|
|
4
|
+
module.exports = class NoPriceDataCurrencyContext extends Rule {
|
|
5
|
+
_findPriceData(node) {
|
|
6
|
+
if (node.data && node.parts && node.parts.length === 2 && node.parts[0] === 'price' && node.parts[1] === 'currency') {
|
|
7
|
+
const foreachNode = this.scope.getParentContextNode('foreach');
|
|
8
|
+
const foreachObject = _.get(foreachNode, 'params[0].original');
|
|
9
|
+
|
|
10
|
+
if (['@member.subscriptions', 'tiers'].includes(foreachObject)) {
|
|
11
|
+
this.log(({
|
|
12
|
+
message: `{{@price.currency}} should be replaced with {{currency}}`,
|
|
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
|
+
|
|
21
|
+
visitor() {
|
|
22
|
+
return {
|
|
23
|
+
PathExpression: this._findPriceData.bind(this)
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
const Rule = require('./base');
|
|
2
|
+
const _ = require('lodash');
|
|
3
|
+
|
|
4
|
+
module.exports = class NoPriceDataCurrencyGlobal extends Rule {
|
|
5
|
+
_findPriceData(node) {
|
|
6
|
+
if (node.data && node.parts && node.parts.length === 2 && node.parts[0] === 'price' && node.parts[1] === 'currency') {
|
|
7
|
+
const foreachNode = this.scope.getParentContextNode('foreach');
|
|
8
|
+
const foreachObject = _.get(foreachNode, 'params[0].original');
|
|
9
|
+
|
|
10
|
+
if (!['@member.subscriptions', 'tiers'].includes(foreachObject)) {
|
|
11
|
+
this.log(({
|
|
12
|
+
message: `{{@price.currency}} should be replaced with {{currency}} and a 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
|
+
|
|
21
|
+
visitor() {
|
|
22
|
+
return {
|
|
23
|
+
PathExpression: this._findPriceData.bind(this)
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
const Rule = require('./base');
|
|
2
|
+
|
|
3
|
+
module.exports = class NoPriceDataMonthlyYearly extends Rule {
|
|
4
|
+
_findPriceData(node) {
|
|
5
|
+
if (node.data && node.parts && node.parts.length === 2 && node.parts[0] === 'price' && ['monthly', 'yearly'].includes(node.parts[1])) {
|
|
6
|
+
this.log(({
|
|
7
|
+
message: `{{@price.monthly}} and {{@price.yearly}} should be replaced with {{#get "tiers"}}`,
|
|
8
|
+
line: node.loc && node.loc.start.line,
|
|
9
|
+
column: node.loc && node.loc.start.column,
|
|
10
|
+
source: this.sourceForNode(node)
|
|
11
|
+
}));
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
visitor() {
|
|
16
|
+
return {
|
|
17
|
+
PathExpression: this._findPriceData.bind(this)
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
const Rule = require('./base');
|
|
2
|
+
const {getNodeName, logNode} = require('../helpers');
|
|
3
|
+
const _ = require('lodash');
|
|
4
|
+
|
|
5
|
+
module.exports = class NoTierBenefitAsObject extends Rule {
|
|
6
|
+
_checkForHelperName(node) {
|
|
7
|
+
const nodeName = getNodeName(node);
|
|
8
|
+
const isMatchingHelper = (nodeName.includes('name'));
|
|
9
|
+
const foreachNode = this.scope.getParentContextNode('foreach');
|
|
10
|
+
const getNode = this.scope.getParentContextNode('get');
|
|
11
|
+
|
|
12
|
+
const hasForeachBenefits = _.get(foreachNode, 'params[0].original') === 'benefits';
|
|
13
|
+
const hasGetTiers = _.get(getNode, 'params[0].original') === 'tiers';
|
|
14
|
+
|
|
15
|
+
if (hasForeachBenefits && hasGetTiers && isMatchingHelper) {
|
|
16
|
+
this.log({
|
|
17
|
+
message: `${logNode(node)} should not be used`,
|
|
18
|
+
line: node.loc && node.loc.start.line,
|
|
19
|
+
column: node.loc && node.loc.start.column,
|
|
20
|
+
source: this.sourceForNode(node)
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
visitor() {
|
|
26
|
+
return {
|
|
27
|
+
MustacheStatement: this._checkForHelperName.bind(this)
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
};
|
package/lib/ast-linter/rules/{lint-no-price-data-helper.js → lint-no-tier-price-as-object.js}
RENAMED
|
@@ -1,12 +1,15 @@
|
|
|
1
|
+
const _ = require('lodash');
|
|
1
2
|
const Rule = require('./base');
|
|
2
3
|
const {getNodeName, logNode} = require('../helpers');
|
|
3
4
|
|
|
4
|
-
module.exports = class
|
|
5
|
+
module.exports = class NoMonthlyYearlyPriceObjects extends Rule {
|
|
5
6
|
_checkForHelperName(node) {
|
|
6
7
|
const nodeName = getNodeName(node);
|
|
7
|
-
const isMatchingHelper = (nodeName.
|
|
8
|
+
const isMatchingHelper = (nodeName.includes('monthly_price.')) || (nodeName.includes('yearly_price.'));
|
|
9
|
+
const getNode = this.scope.getParentContextNode('get');
|
|
10
|
+
const hasGetTiers = _.get(getNode, 'params[0].original') === 'tiers';
|
|
8
11
|
|
|
9
|
-
if (isMatchingHelper) {
|
|
12
|
+
if (isMatchingHelper && hasGetTiers) {
|
|
10
13
|
this.log({
|
|
11
14
|
message: `${logNode(node)} should not be used`,
|
|
12
15
|
line: node.loc && node.loc.start.line,
|
|
@@ -17,9 +17,9 @@ const checkDeprecations = function checkDeprecations(theme, options) {
|
|
|
17
17
|
|
|
18
18
|
_.each(ruleSet, function (check, ruleCode) {
|
|
19
19
|
_.each(theme.files, function (themeFile) {
|
|
20
|
-
const template = themeFile.file.match(
|
|
20
|
+
const template = themeFile.file.match(/^.+\.hbs$/);
|
|
21
21
|
const skipTemplateCheck = check.notValidIn && check.notValidIn.match(template);
|
|
22
|
-
let css = themeFile.file.match(/\.css
|
|
22
|
+
let css = themeFile.file.match(/\.css$/);
|
|
23
23
|
let cssDeprecations;
|
|
24
24
|
|
|
25
25
|
if (template && !check.css && !skipTemplateCheck) {
|
|
@@ -16,7 +16,7 @@ const checkCommentID = function checkCommentID(theme, options) {
|
|
|
16
16
|
});
|
|
17
17
|
_.each(ruleSet, function (check, ruleCode) {
|
|
18
18
|
_.each(theme.files, function (themeFile) {
|
|
19
|
-
var template = themeFile.file.match(
|
|
19
|
+
var template = themeFile.file.match(/\.hbs$/);
|
|
20
20
|
|
|
21
21
|
if (template) {
|
|
22
22
|
if (themeFile.content.match(check.regex)) {
|
|
@@ -114,7 +114,7 @@ const checkTemplatesCompile = function checkTemplatesCompile(theme, options) {
|
|
|
114
114
|
});
|
|
115
115
|
|
|
116
116
|
_.each(theme.files, function (themeFile) {
|
|
117
|
-
let templateTest = themeFile.file.match(
|
|
117
|
+
let templateTest = themeFile.file.match(/(?<!partials\/.+?)\.hbs$/);
|
|
118
118
|
|
|
119
119
|
if (templateTest) {
|
|
120
120
|
processFile(linter, themeFile);
|
|
@@ -55,7 +55,10 @@ const v4PackageJSONValidationRules = _.extend({},
|
|
|
55
55
|
);
|
|
56
56
|
|
|
57
57
|
const canaryPackageJSONConditionalRules = {};
|
|
58
|
-
const canaryPackageJSONValidationRules = _.extend({}
|
|
58
|
+
const canaryPackageJSONValidationRules = _.extend({},
|
|
59
|
+
{isNotPresentCardAssets: 'GS010-PJ-GHOST-CARD-ASSETS-NOT-PRESENT'},
|
|
60
|
+
canaryPackageJSONConditionalRules
|
|
61
|
+
);
|
|
59
62
|
|
|
60
63
|
_private.validatePackageJSONFields = function validatePackageJSONFields(packageJSON, theme, packageJSONValidationRules) {
|
|
61
64
|
let failed = [];
|
|
@@ -138,6 +141,11 @@ _private.validatePackageJSONFields = function validatePackageJSONFields(packageJ
|
|
|
138
141
|
}
|
|
139
142
|
}
|
|
140
143
|
|
|
144
|
+
// @TODO: validate that the card_assets config is valid
|
|
145
|
+
if (!packageJSON.config || (_.isEmpty(packageJSON.config.card_assets) && !_.isBoolean(packageJSON.config.card_assets))) {
|
|
146
|
+
markFailed('isNotPresentCardAssets');
|
|
147
|
+
}
|
|
148
|
+
|
|
141
149
|
if (packageJSON.config && packageJSON.config.custom) {
|
|
142
150
|
const customSettingsKeys = Object.keys(packageJSON.config.custom);
|
|
143
151
|
|
|
@@ -17,7 +17,7 @@ module.exports = function checkUsage(theme, options) {
|
|
|
17
17
|
});
|
|
18
18
|
_.each(ruleSet, function (check, ruleCode) {
|
|
19
19
|
_.each(theme.files, function (themeFile) {
|
|
20
|
-
var template = themeFile.file.match(
|
|
20
|
+
var template = themeFile.file.match(/\.hbs$/);
|
|
21
21
|
const validApi = check.validInAPI ? check.validInAPI.includes(targetApiVersion) : true;
|
|
22
22
|
if (template) {
|
|
23
23
|
if (validApi && themeFile.content.match(check.regex)) {
|
|
@@ -78,7 +78,7 @@ const checkTemplatesCompile = function checkTemplatesCompile(theme, options) {
|
|
|
78
78
|
});
|
|
79
79
|
|
|
80
80
|
_.each(theme.files, function (themeFile) {
|
|
81
|
-
let templateTest = themeFile.file.match(
|
|
81
|
+
let templateTest = themeFile.file.match(/(?<!partials\/.+?)\.hbs$/);
|
|
82
82
|
|
|
83
83
|
if (templateTest) {
|
|
84
84
|
processFile(linter, themeFile);
|
|
@@ -123,7 +123,7 @@ const ruleImplementations = {
|
|
|
123
123
|
result.customThemeSettings = new Set();
|
|
124
124
|
},
|
|
125
125
|
eachFile: ({file, theme, log, result}) => {
|
|
126
|
-
let templateTest = file.file.match(
|
|
126
|
+
let templateTest = file.file.match(/(?<!partials\/.+?)\.hbs$/);
|
|
127
127
|
|
|
128
128
|
if (templateTest) {
|
|
129
129
|
parseWithAST({file, theme, rules: {
|
package/lib/format.js
CHANGED
|
@@ -1,30 +1,7 @@
|
|
|
1
1
|
const _ = require('lodash');
|
|
2
2
|
const spec = require('./specs');
|
|
3
|
-
const levelWeights = {
|
|
4
|
-
error: 10,
|
|
5
|
-
warning: 3,
|
|
6
|
-
recommendation: 1
|
|
7
|
-
};
|
|
8
3
|
const versions = require('./utils').versions;
|
|
9
|
-
|
|
10
|
-
const calcScore = function calcScore(results, stats) {
|
|
11
|
-
var maxScore, actualScore, balancedScore;
|
|
12
|
-
|
|
13
|
-
maxScore = _.reduce(levelWeights, function (max, weight, level) {
|
|
14
|
-
return max + (weight * stats[level]);
|
|
15
|
-
}, 0);
|
|
16
|
-
|
|
17
|
-
actualScore = _.reduce(levelWeights, function (max, weight, level) {
|
|
18
|
-
return max - (weight * _.size(results[level]));
|
|
19
|
-
}, maxScore);
|
|
20
|
-
|
|
21
|
-
balancedScore = _.ceil((100 / maxScore) * actualScore);
|
|
22
|
-
|
|
23
|
-
return {
|
|
24
|
-
value: balancedScore,
|
|
25
|
-
level: _.size(results.error) > 0 ? 'error' : balancedScore < 60 ? 'warning' : 'passing'
|
|
26
|
-
};
|
|
27
|
-
};
|
|
4
|
+
const calcScore = require('./utils/score-calculator');
|
|
28
5
|
|
|
29
6
|
const formatCLI = (theme) => {
|
|
30
7
|
_.each(theme.results, (results) => {
|
package/lib/read-zip.js
CHANGED
|
@@ -15,7 +15,7 @@ const resolveBaseDir = (zipPath) => {
|
|
|
15
15
|
|
|
16
16
|
if (!err && !_.isEmpty(matches)) {
|
|
17
17
|
debug('Found matches', matches);
|
|
18
|
-
matchedPath = matches[0].replace(/index
|
|
18
|
+
matchedPath = matches[0].replace(/index\.hbs$/, '');
|
|
19
19
|
zipPath = path.join(zipPath, matchedPath).replace(/\/$/, '');
|
|
20
20
|
}
|
|
21
21
|
|
package/lib/specs/canary.js
CHANGED
|
@@ -21,10 +21,16 @@ let rules = {
|
|
|
21
21
|
// New rules
|
|
22
22
|
'GS010-PJ-GHOST-API-PRESENT': {
|
|
23
23
|
level: 'warning',
|
|
24
|
-
rule: 'Remove <code>"engines.ghost-api"</code> from <code>package.json</code
|
|
24
|
+
rule: 'Remove <code>"engines.ghost-api"</code> from <code>package.json</code>',
|
|
25
25
|
details: oneLineTrim`The <code>"ghost-api"</code> version is no longer used and can be removed.<br>
|
|
26
26
|
Find more information about the <code>package.json</code> file <a href="${docsBaseUrl}structure/#packagejson" target=_blank>here</a>.`
|
|
27
27
|
},
|
|
28
|
+
'GS010-PJ-GHOST-CARD-ASSETS-NOT-PRESENT': {
|
|
29
|
+
level: 'warning',
|
|
30
|
+
rule: '<code>"card_assets"</code> will default to <code>true</code> in <code>package.json</code>',
|
|
31
|
+
details: oneLineTrim`The <code>"card_assets"</code> property is enabled by default if not explicitly set.<br>
|
|
32
|
+
Find more information about the <code>card_assets</code> property <a href="${docsBaseUrl}content/#editor-cards" target=_blank>here</a>.`
|
|
33
|
+
},
|
|
28
34
|
'GS090-NO-AUTHOR-HELPER-IN-POST-CONTEXT': {
|
|
29
35
|
level: 'error',
|
|
30
36
|
fatal: true,
|
|
@@ -69,14 +75,46 @@ let rules = {
|
|
|
69
75
|
Find more information about the <code>{{@member.subscriptions}}</code> property <a href="${docsBaseUrl}members/#member-subscriptions" target=_blank>here</a>.`,
|
|
70
76
|
helper: '{{@member.products}}'
|
|
71
77
|
},
|
|
72
|
-
'GS090-NO-PRICE-DATA-
|
|
78
|
+
'GS090-NO-PRICE-DATA-CURRENCY-GLOBAL': {
|
|
79
|
+
level: 'error',
|
|
80
|
+
fatal: true,
|
|
81
|
+
rule: 'Replace <code>{{@price.currency}}</code> with <code>{{#get "tiers"}}</code> and <code>{{currency}}</code> or <code>{{#foreach @member.subscriptions}}</code> and <code>{{plan.currency}}</code>',
|
|
82
|
+
details: oneLineTrim`There is no longer a global <code>@price</code> object. You need to use either <code>{{#get "tiers"}}</code> to fetch all tiers and use the <code>{{currency}}</code> property of a tier<br>
|
|
83
|
+
or use <code>{{#foreach @member.subscriptions}}</code> to fetch an individual member's subscriptions, and use the <code>{{plan.currency}}</code> property from the subscription.<br>
|
|
84
|
+
Find more information about the <code>{{price}}</code> helper <a href="${docsBaseUrl}helpers/price/" target=_blank>here</a>.`
|
|
85
|
+
},
|
|
86
|
+
'GS090-NO-PRICE-DATA-CURRENCY-CONTEXT': {
|
|
87
|
+
level: 'error',
|
|
88
|
+
fatal: true,
|
|
89
|
+
rule: 'Replace <code>{{@price.currency}}</code> with <code>{{currency}}</code> or <code>{{plan.currency}}</code>',
|
|
90
|
+
details: oneLineTrim`There is no longer a global <code>@price</code> object. Instead the <code>{{currency}}</code> property can be used inside <code>{{#get "tiers"}}</code><br>
|
|
91
|
+
or <code>{{plan.currency}}</code> can be used inside <code>{{#foreach @member.subscriptions}}</code><br>
|
|
92
|
+
Find more information about the <code>{{price}}</code> helper <a href="${docsBaseUrl}helpers/price/" target=_blank>here</a>.`
|
|
93
|
+
},
|
|
94
|
+
'GS090-NO-PRICE-DATA-MONTHLY-YEARLY': {
|
|
95
|
+
level: 'error',
|
|
96
|
+
fatal: true,
|
|
97
|
+
rule: 'Replace <code>{{@price.monthly}}</code> and <code>{{@price.yearly}}</code> with <code>{{#get "tiers"}}</code> and <code>{{monthly_price}}</code> or <code>{{yearly_price}}</code>',
|
|
98
|
+
details: oneLineTrim`There is no longer a global <code>@price</code> object. You need to use <code>{{#get "tiers"}}</code> to fetch all the tiers and get access to the <code>{{monthly_price}}</code> or <code>{{yearly_price}}</code> for each tier<br>
|
|
99
|
+
Find more information about the <code>{{price}}</code> helper <a href="${docsBaseUrl}helpers/price/" target=_blank>here</a>.`
|
|
100
|
+
},
|
|
101
|
+
'GS090-NO-TIER-PRICE-AS-OBJECT': {
|
|
102
|
+
level: 'error',
|
|
103
|
+
fatal: true,
|
|
104
|
+
rule: 'Remove usage of <code>{{monthly_price.*}}</code> and <code>{{yearly_price.*}}.</code>',
|
|
105
|
+
details: oneLineTrim`The usage of <code>{{monthly_price.*}}</code> and <code>{{yearly_price.*}} is no longer supported.</code><br>
|
|
106
|
+
${tierDesc}<br>
|
|
107
|
+
Find more information about the <code>{{#get "tiers"}}</code> <a href="${docsBaseUrl}helpers/tiers/" target=_blank>here</a>.`,
|
|
108
|
+
helper: '{{#get "tiers"}}'
|
|
109
|
+
},
|
|
110
|
+
'GS090-NO-TIER-BENEFIT-AS-OBJECT': {
|
|
73
111
|
level: 'error',
|
|
74
112
|
fatal: true,
|
|
75
|
-
rule: '
|
|
76
|
-
details: oneLineTrim`The <code>{{
|
|
113
|
+
rule: 'Remove usage of <code>{{name}}</code> for tier benefits.</code>',
|
|
114
|
+
details: oneLineTrim`The usage of <code>{{name}}</code> for tier benefits is no longer supported.</code><br>
|
|
77
115
|
${tierDesc}<br>
|
|
78
|
-
Find more information about the <code>{{
|
|
79
|
-
helper: '{{
|
|
116
|
+
Find more information about the <code>{{#get "tiers"}}</code> <a href="${docsBaseUrl}helpers/tiers/" target=_blank>here</a>.`,
|
|
117
|
+
helper: '{{#get "tiers"}}'
|
|
80
118
|
},
|
|
81
119
|
|
|
82
120
|
// Updated v1 & v2 rules
|
|
@@ -685,8 +723,8 @@ let rules = {
|
|
|
685
723
|
level: 'error',
|
|
686
724
|
fatal: true,
|
|
687
725
|
rule: 'Replace <code>{{[#].currency_symbol}}</code> with <code>{{price currency=currency}}</code>.',
|
|
688
|
-
details: oneLineTrim`The
|
|
689
|
-
Find more information about the updated <code>{{price}}</code> helper <a href="${docsBaseUrl}
|
|
726
|
+
details: oneLineTrim`The <code>currency_symbol</code> attribute is no longer supported in favour of passing the currency to updated <code>{{price}}</code> helper.<br>
|
|
727
|
+
Find more information about the updated <code>{{price}}</code> helper <a href="${docsBaseUrl}helpers/price" target=_blank>here</a>.`,
|
|
690
728
|
helper: '{{[#].currency_symbol}}',
|
|
691
729
|
regex: /currency_symbol/g
|
|
692
730
|
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
const _ = require('lodash');
|
|
2
|
+
|
|
3
|
+
const levelWeights = {
|
|
4
|
+
error: 10,
|
|
5
|
+
warning: 3,
|
|
6
|
+
recommendation: 1
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
*
|
|
11
|
+
* @param {Object} results
|
|
12
|
+
* @param {Object} results.error
|
|
13
|
+
* @param {Object} results.warning
|
|
14
|
+
* @param {Object} results.recommendation
|
|
15
|
+
* @param {Object} stats
|
|
16
|
+
* @param {Object} stats.error
|
|
17
|
+
* @param {Object} stats.warning
|
|
18
|
+
* @param {Object} stats.recommendation
|
|
19
|
+
* @returns {Object}
|
|
20
|
+
*/
|
|
21
|
+
const calcScore = function calcScore(results, stats) {
|
|
22
|
+
var maxScore, actualScore, balancedScore;
|
|
23
|
+
|
|
24
|
+
maxScore = _.reduce(levelWeights, function (max, weight, level) {
|
|
25
|
+
return max + (weight * stats[level]);
|
|
26
|
+
}, 0);
|
|
27
|
+
|
|
28
|
+
actualScore = _.reduce(levelWeights, function (max, weight, level) {
|
|
29
|
+
return max - (weight * _.size(results[level]));
|
|
30
|
+
}, maxScore);
|
|
31
|
+
|
|
32
|
+
balancedScore = _.floor((100 / maxScore) * actualScore);
|
|
33
|
+
|
|
34
|
+
return {
|
|
35
|
+
value: balancedScore,
|
|
36
|
+
level: _.size(results.error) > 0 ? 'error' : balancedScore < 60 ? 'warning' : 'passing'
|
|
37
|
+
};
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
module.exports = calcScore;
|
package/lib/utils/versions.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gscan",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.31.1",
|
|
4
4
|
"description": "Scans Ghost themes looking for errors, deprecations, features and compatibility",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ghost",
|
|
@@ -41,13 +41,13 @@
|
|
|
41
41
|
},
|
|
42
42
|
"dependencies": {
|
|
43
43
|
"@sentry/node": "6.19.7",
|
|
44
|
-
"@tryghost/config": "0.2.
|
|
45
|
-
"@tryghost/debug": "0.1.
|
|
46
|
-
"@tryghost/errors": "1.2.
|
|
47
|
-
"@tryghost/logging": "2.
|
|
48
|
-
"@tryghost/pretty-cli": "1.2.
|
|
49
|
-
"@tryghost/server": "0.1.
|
|
50
|
-
"@tryghost/zip": "1.1.
|
|
44
|
+
"@tryghost/config": "0.2.9",
|
|
45
|
+
"@tryghost/debug": "0.1.17",
|
|
46
|
+
"@tryghost/errors": "1.2.14",
|
|
47
|
+
"@tryghost/logging": "2.2.3",
|
|
48
|
+
"@tryghost/pretty-cli": "1.2.28",
|
|
49
|
+
"@tryghost/server": "0.1.21",
|
|
50
|
+
"@tryghost/zip": "1.1.26",
|
|
51
51
|
"bluebird": "3.7.2",
|
|
52
52
|
"chalk": "4.1.2",
|
|
53
53
|
"common-tags": "1.8.2",
|