gscan 4.23.0 → 4.25.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,7 +16,7 @@ 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
21
  Errors are only marked as **fatal errors** if they would cause errors, and therefore should block a boot or an upgrade.
22
22
 
@@ -51,8 +51,8 @@ By default, GScan scans themes for the latest Ghost version compatibility. You c
51
51
  `--v1` or `-1`
52
52
  `--v2` or `-2`
53
53
  `--v3` or `-3`
54
- `--v4` or `-4`
55
- `--v5` or `-5` or `--canary`
54
+ `--v4` or `-4` or `--canary`
55
+ `--v5` or `-5`
56
56
 
57
57
  Use the `--canary` parameter to check for the upcoming Ghost version.
58
58
 
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="canary" selected>{{ghostVersions.canary.major}}</option>
26
- <option value="v4">{{ghostVersions.v4.major}}</option>
25
+ <option value="v5">{{ghostVersions.v5.major}}</option>
26
+ <option value="v4" selected>{{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>
@@ -3,6 +3,9 @@ module.exports = {
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
5
  'GS090-NO-AUTHOR-HELPER-IN-POST-CONTEXT': require('./lint-no-author-helper-in-post-page-context'),
6
+ 'GS090-NO-PRODUCTS-HELPER': require('./lint-no-products-helper'),
7
+ 'GS090-NO-PRODUCT-DATA-HELPER': require('./lint-no-product-data-helper'),
8
+ 'GS090-NO-PRODUCTS-DATA-HELPER': require('./lint-no-products-data-helper'),
6
9
  'no-multi-param-conditionals': require('./lint-no-multi-param-conditionals'),
7
10
  'no-nested-async-helpers': require('./lint-no-nested-async-helpers'),
8
11
  'no-prev-next-post-outside-post-context': require('./lint-no-prev-next-post-outside-post-context'),
@@ -0,0 +1,24 @@
1
+ const Rule = require('./base');
2
+ const {getNodeName, logNode} = require('../helpers');
3
+
4
+ module.exports = class NoProductsHelper extends Rule {
5
+ _checkForHelerInPostContext(node) {
6
+ const nodeName = getNodeName(node);
7
+ const isProductsHelper = (nodeName === '@product');
8
+
9
+ if (isProductsHelper) {
10
+ this.log({
11
+ message: `${logNode(node)} should not be used`,
12
+ line: node.loc && node.loc.start.line,
13
+ column: node.loc && node.loc.start.column,
14
+ source: this.sourceForNode(node)
15
+ });
16
+ }
17
+ }
18
+
19
+ visitor() {
20
+ return {
21
+ MustacheStatement: this._checkForHelerInPostContext.bind(this)
22
+ };
23
+ }
24
+ };
@@ -0,0 +1,24 @@
1
+ const Rule = require('./base');
2
+ const {getNodeName, logNode} = require('../helpers');
3
+
4
+ module.exports = class NoProductsHelper extends Rule {
5
+ _checkForHelerInPostContext(node) {
6
+ const nodeName = getNodeName(node);
7
+ const isProductsHelper = (nodeName === '@products');
8
+
9
+ if (isProductsHelper) {
10
+ this.log({
11
+ message: `${logNode(node)} should not be used`,
12
+ line: node.loc && node.loc.start.line,
13
+ column: node.loc && node.loc.start.column,
14
+ source: this.sourceForNode(node)
15
+ });
16
+ }
17
+ }
18
+
19
+ visitor() {
20
+ return {
21
+ MustacheStatement: this._checkForHelerInPostContext.bind(this)
22
+ };
23
+ }
24
+ };
@@ -0,0 +1,24 @@
1
+ const Rule = require('./base');
2
+ const {getNodeName, logNode} = require('../helpers');
3
+
4
+ module.exports = class NoProductsHelper extends Rule {
5
+ _checkForHelerInPostContext(node) {
6
+ const nodeName = getNodeName(node);
7
+ const isProductsHelper = (nodeName === 'products');
8
+
9
+ if (isProductsHelper) {
10
+ this.log({
11
+ message: `${logNode(node)} should not be used`,
12
+ line: node.loc && node.loc.start.line,
13
+ column: node.loc && node.loc.start.column,
14
+ source: this.sourceForNode(node)
15
+ });
16
+ }
17
+ }
18
+
19
+ visitor() {
20
+ return {
21
+ MustacheStatement: this._checkForHelerInPostContext.bind(this)
22
+ };
23
+ }
24
+ };
package/lib/checker.js CHANGED
@@ -27,8 +27,9 @@ 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 === 'v5') {
31
- version = 'canary';
30
+ if (passedVersion === 'canary') {
31
+ version = 'v4';
32
+ options.checkVersion = 'v4';
32
33
  }
33
34
 
34
35
  _.each(labsEnabledHelpers, (flag, helper) => {
@@ -3,7 +3,7 @@ const oneLineTrim = require('common-tags/lib/oneLineTrim');
3
3
  const previousSpec = require('./v4');
4
4
  const ghostVersions = require('../utils').versions;
5
5
  const docsBaseUrl = `https://ghost.org/docs/api/handlebars-themes/`;
6
- const prevDocsBaseUrl = `https://themes.ghost.org/v${ghostVersions.canary.docs}/docs/`;
6
+ const prevDocsBaseUrl = `https://themes.ghost.org/v${ghostVersions.v5.docs}/docs/`;
7
7
  const prevDocsBaseUrlRegEx = new RegExp(prevDocsBaseUrl, 'g');
8
8
 
9
9
  const previousKnownHelpers = previousSpec.knownHelpers;
@@ -22,6 +22,44 @@ let rules = {
22
22
  The <code>ghost-api</code> is not supported starting Ghost v5 and should not be used.
23
23
  Check the <a href="${docsBaseUrl}packagejson/" target=_blank><code>package.json</code> documentation</a> for further information.`
24
24
  },
25
+ 'GS090-NO-AUTHOR-HELPER-IN-POST-CONTEXT': {
26
+ level: 'error',
27
+ fatal: true,
28
+ rule: 'The <code>{{author}}</code> helper should be replaces with <code>{{authors}}</code>',
29
+ details: oneLineTrim`The <code>{{author}}</code> helper has been deprecated since Ghost 1.22.0 in favor of <code>{{<authors>}}</code><br>
30
+ The <code>{{author}}</code> helper was removed in Ghost v5 and should not be used.
31
+ Find more information about the <code>{{authors}}</code> property <a href="${docsBaseUrl}helpers/authors/" target=_blank>here</a>.`,
32
+ helper: '{{author}}'
33
+ },
34
+ 'GS090-NO-PRODUCTS-HELPER': {
35
+ level: 'error',
36
+ fatal: true,
37
+ rule: 'The <code>{{products}}</code> helper should be replaces with <code>{{tiers}}</code>',
38
+ details: oneLineTrim`The <code>{{products}}</code> helper has been deprecated in favor of <code>{{tiers}}</code><br>
39
+ The <code>{{products}}</code> helper was removed in Ghost v5 and should not be used.
40
+ Find more information about the <code>{{tiers}}</code> property <a href="${docsBaseUrl}helpers/tiers/" target=_blank>here</a>.`,
41
+ helper: '{{products}}'
42
+ },
43
+ 'GS090-NO-PRODUCT-DATA-HELPER': {
44
+ level: 'error',
45
+ fatal: true,
46
+ rule: 'The <code>{{@product}}</code> data helper should be replaces with <code>{{#get "tiers"}}</code>',
47
+ details: oneLineTrim`The <code>{{@product}}</code> data helper has been deprecated in favor of <code>{{#get "tiers"}}</code><br>
48
+ The <code>{{@product}}</code> data helper was removed in Ghost v5 and should not be used.
49
+ Find more information about the <code>{{#get "tiers"}}</code> property <a href="${docsBaseUrl}helpers/tiers/" target=_blank>here</a>.`,
50
+ helper: '{{@product}}'
51
+ },
52
+ 'GS090-NO-PRODUCTS-DATA-HELPER': {
53
+ level: 'error',
54
+ fatal: true,
55
+ rule: 'The <code>{{@products}}</code> data helper should be replaces with <code>{{#get "tiers"}}</code>',
56
+ details: oneLineTrim`The <code>{{@products}}</code> data helper has been deprecated in favor of <code>{{#get "tiers"}}</code><br>
57
+ The <code>{{@products}}</code> data helper was removed in Ghost v5 and should not be used.
58
+ Find more information about the <code>{{#get "tiers"}}</code> property <a href="${docsBaseUrl}helpers/tiers/" target=_blank>here</a>.`,
59
+ helper: '{{@products}}'
60
+ },
61
+
62
+ // Updated v1 & v2 rules
25
63
  'GS001-DEPR-BLOG': {
26
64
  level: 'error',
27
65
  fatal: true,
@@ -32,14 +70,413 @@ let rules = {
32
70
  regex: /{{\s*?@blog\.[a-zA-Z0-9_]+\s*?}}/g,
33
71
  helper: '{{@blog}}'
34
72
  },
35
- 'GS090-NO-AUTHOR-HELPER-IN-POST-CONTEXT': {
73
+ 'GS001-DEPR-AUTH-ID': {
36
74
  level: 'error',
37
75
  fatal: true,
38
- rule: 'The <code>{{author}}</code> helper should be replaces with <code>{{authors}}</code>',
39
- details: oneLineTrim`The <code>{{author}}</code> helper has been deprecated since Ghost 1.22.0 in favor of <code>{{<authors>}}</code><br>
40
- The <code>{{author}}</code> helper was removed in Ghost v5 and should not be used.
41
- Find more information about the <code>@site</code> property <a href="${docsBaseUrl}helpers/site/" target=_blank>here</a>.`,
42
- helper: '{{author}}'
76
+ rule: 'Replace the <code>{{author.id}}</code> helper with <code>{{primary_author.id}}</code> or <code>{{authors.[#].id}}</code>',
77
+ details: oneLineTrim`The usage of <code>{{author.id}}</code> is deprecated and should be replaced with either <code>{{primary_author.id}}</code>
78
+ or <code>{{authors.[#].id}}</code>.<br>
79
+ Find more information about the <code>{{authors}}</code> helper <a href="${docsBaseUrl}helpers/authors/" target=_blank>here</a>.`,
80
+ regex: /{{\s*?author\.id\s*?}}/g,
81
+ helper: '{{author.id}}'
82
+ },
83
+ 'GS001-DEPR-AUTH-SLUG': {
84
+ level: 'error',
85
+ fatal: true,
86
+ rule: 'Replace the <code>{{author.slug}}</code> helper with <code>{{primary_author.slug}}</code> or <code>{{authors.[#].slug}}</code>',
87
+ details: oneLineTrim`The usage of <code>{{author.slug}}</code> is deprecated and should be replaced with either <code>{{primary_author.slug}}</code>
88
+ or <code>{{authors.[#].slug}}</code>.<br>
89
+ Find more information about the <code>{{authors}}</code> helper <a href="${docsBaseUrl}helpers/authors/" target=_blank>here</a>.`,
90
+ regex: /{{\s*?author\.slug\s*?}}/g,
91
+ helper: '{{author.slug}}'
92
+ },
93
+ 'GS001-DEPR-AUTH-MAIL': {
94
+ level: 'error',
95
+ fatal: true,
96
+ rule: 'Replace the <code>{{author.email}}</code> helper with <code>{{primary_author.email}}</code> or <code>{{authors.[#].email}}</code>',
97
+ details: oneLineTrim`The usage of <code>{{author.email}}</code> is deprecated and should be replaced with either <code>{{primary_author.email}}</code>
98
+ or <code>{{authors.[#].email}}</code>.<br>
99
+ Find more information about the <code>{{authors}}</code> helper <a href="${docsBaseUrl}helpers/authors/" target=_blank>here</a>.`,
100
+ regex: /{{\s*?author\.email\s*?}}/g,
101
+ helper: '{{author.email}}'
102
+ },
103
+ 'GS001-DEPR-AUTH-MT': {
104
+ level: 'error',
105
+ fatal: true,
106
+ rule: 'Replace the <code>{{author.meta_title}}</code> helper with <code>{{primary_author.meta_title}}</code> or <code>{{authors.[#].meta_title}}</code>',
107
+ details: oneLineTrim`The usage of <code>{{author.meta_title}}</code> is deprecated and should be replaced with either <code>{{primary_author.meta_title}}</code>
108
+ or <code>{{authors.[#].meta_title}}</code>.<br>
109
+ Find more information about the <code>{{authors}}</code> helper <a href="${docsBaseUrl}helpers/authors/" target=_blank>here</a>.`,
110
+ regex: /{{\s*?author\.meta_title\s*?}}/g,
111
+ helper: '{{author.meta_title}}'
112
+ },
113
+ 'GS001-DEPR-AUTH-MD': {
114
+ level: 'error',
115
+ fatal: true,
116
+ rule: 'Replace the <code>{{author.meta_description}}</code> helper with <code>{{primary_author.meta_description}}</code> or <code>{{authors.[#].meta_description}}</code>',
117
+ details: oneLineTrim`The usage of <code>{{author.meta_description}}</code> is deprecated and should be replaced with either <code>{{primary_author.meta_description}}</code>
118
+ or <code>{{authors.[#].meta_description}}</code>.<br>
119
+ Find more information about the <code>{{authors}}</code> helper <a href="${docsBaseUrl}helpers/authors/" target=_blank>here</a>.`,
120
+ regex: /{{\s*?author\.meta_description\s*?}}/g,
121
+ helper: '{{author.meta_description}}'
122
+ },
123
+ 'GS001-DEPR-AUTH-NAME': {
124
+ level: 'error',
125
+ fatal: true,
126
+ rule: 'Replace the <code>{{author.name}}</code> helper with <code>{{primary_author.name}}</code> or <code>{{authors.[#].name}}</code>',
127
+ details: oneLineTrim`The usage of <code>{{author.name}}</code> is deprecated and should be replaced with either <code>{{primary_author.name}}</code>
128
+ or <code>{{authors.[#].name}}</code>.<br>
129
+ Find more information about the <code>{{authors}}</code> helper <a href="${docsBaseUrl}helpers/authors/" target=_blank>here</a>.`,
130
+ regex: /{{\s*?author\.name\s*?}}/g,
131
+ helper: '{{author.name}}'
132
+ },
133
+ 'GS001-DEPR-AUTH-BIO': {
134
+ level: 'error',
135
+ fatal: true,
136
+ rule: 'Replace the <code>{{author.bio}}</code> helper with <code>{{primary_author.bio}}</code> or <code>{{authors.[#].bio}}</code>',
137
+ details: oneLineTrim`The usage of <code>{{author.bio}}</code> is deprecated and should be replaced with either <code>{{primary_author.bio}}</code>
138
+ or <code>{{authors.[#].bio}}</code>.<br>
139
+ Find more information about the <code>{{authors}}</code> helper <a href="${docsBaseUrl}helpers/authors/" target=_blank>here</a>.`,
140
+ regex: /{{\s*?author\.bio\s*?}}/g,
141
+ helper: '{{author.bio}}'
142
+ },
143
+ 'GS001-DEPR-AUTH-LOC': {
144
+ level: 'error',
145
+ fatal: true,
146
+ rule: 'Replace the <code>{{author.location}}</code> helper with <code>{{primary_author.location}}</code> or <code>{{authors.[#].location}}</code>',
147
+ details: oneLineTrim`The usage of <code>{{author.location}}</code> is deprecated and should be replaced with either <code>{{primary_author.location}}</code>
148
+ or <code>{{authors.[#].location}}</code>.<br>
149
+ Find more information about the <code>{{authors}}</code> helper <a href="${docsBaseUrl}helpers/authors/" target=_blank>here</a>.`,
150
+ regex: /{{\s*?author\.location\s*?}}/g,
151
+ helper: '{{author.location}}'
152
+ },
153
+ 'GS001-DEPR-AUTH-WEB': {
154
+ level: 'error',
155
+ fatal: true,
156
+ rule: 'Replace the <code>{{author.website}}</code> helper with <code>{{primary_author.website}}</code> or <code>{{authors.[#].website}}</code>',
157
+ details: oneLineTrim`The usage of <code>{{author.website}}</code> is deprecated and should be replaced with either <code>{{primary_author.website}}</code>
158
+ or <code>{{authors.[#].website}}</code>.<br>
159
+ Find more information about the <code>{{authors}}</code> helper <a href="${docsBaseUrl}helpers/authors/" target=_blank>here</a>.`,
160
+ regex: /{{\s*?author\.website\s*?}}/g,
161
+ helper: '{{author.website}}'
162
+ },
163
+ 'GS001-DEPR-AUTH-TW': {
164
+ level: 'error',
165
+ fatal: true,
166
+ rule: 'Replace the <code>{{author.twitter}}</code> helper with <code>{{primary_author.twitter}}</code> or <code>{{authors.[#].twitter}}</code>',
167
+ details: oneLineTrim`The usage of <code>{{author.twitter}}</code> is deprecated and should be replaced with either <code>{{primary_author.twitter}}</code>
168
+ or <code>{{authors.[#].twitter}}</code>.<br>
169
+ Find more information about the <code>{{authors}}</code> helper <a href="${docsBaseUrl}helpers/authors/" target=_blank>here</a>.`,
170
+ regex: /{{\s*?author\.twitter\s*?}}/g,
171
+ helper: '{{author.twitter}}'
172
+ },
173
+ 'GS001-DEPR-AUTH-FB': {
174
+ level: 'error',
175
+ fatal: true,
176
+ rule: 'Replace the <code>{{author.facebook}}</code> helper with <code>{{primary_author.facebook}}</code> or <code>{{authors.[#].facebook}}</code>',
177
+ details: oneLineTrim`The usage of <code>{{author.facebook}}</code> is deprecated and should be replaced with either <code>{{primary_author.facebook}}</code>
178
+ or <code>{{authors.[#].facebook}}</code>.<br>
179
+ Find more information about the <code>{{authors}}</code> helper <a href="${docsBaseUrl}helpers/authors/" target=_blank>here</a>.`,
180
+ regex: /{{\s*?author\.facebook\s*?}}/g,
181
+ helper: '{{author.facebook}}'
182
+ },
183
+ 'GS001-DEPR-AUTH-PIMG': {
184
+ level: 'error',
185
+ fatal: true,
186
+ rule: 'Replace the <code>{{author.profile_image}}</code> helper with <code>{{primary_author.profile_image}}</code> or <code>{{authors.[#].profile_image}}</code>',
187
+ details: oneLineTrim`The usage of <code>{{author.profile_image}}</code> is deprecated and should be replaced with either <code>{{primary_author.profile_image}}</code>
188
+ or <code>{{authors.[#].profile_image}}</code>.<br>
189
+ Find more information about the <code>{{authors}}</code> helper <a href="${docsBaseUrl}helpers/authors/" target=_blank>here</a>.`,
190
+ regex: /{{\s*?author\.profile_image\s*?}}/g,
191
+ helper: '{{author.profile_image}}'
192
+ },
193
+ 'GS001-DEPR-AUTH-CIMG': {
194
+ level: 'error',
195
+ fatal: true,
196
+ rule: 'Replace the <code>{{author.cover_image}}</code> helper with <code>{{primary_author.cover_image}}</code> or <code>{{authors.[#].cover_image}}</code>',
197
+ details: oneLineTrim`The usage of <code>{{author.cover_image}}</code> is deprecated and should be replaced with either <code>{{primary_author.cover_image}}</code>
198
+ or <code>{{authors.[#].cover_image}}</code>.<br>
199
+ Find more information about the <code>{{authors}}</code> helper <a href="${docsBaseUrl}helpers/authors/" target=_blank>here</a>.`,
200
+ regex: /{{\s*?author\.cover_image\s*?}}/g,
201
+ helper: '{{author.cover_image}}'
202
+ },
203
+ 'GS001-DEPR-AUTH-URL': {
204
+ level: 'error',
205
+ fatal: true,
206
+ rule: 'Replace the <code>{{author.url}}</code> helper with <code>{{primary_author.url}}</code> or <code>{{authors.[#].url}}</code>',
207
+ details: oneLineTrim`The usage of <code>{{author.url}}</code> is deprecated and should be replaced with either <code>{{primary_author.url}}</code>
208
+ or <code>{{authors.[#].url}}</code>.<br>
209
+ Find more information about the <code>{{authors}}</code> helper <a href="${docsBaseUrl}helpers/authors/" target=_blank>here</a>.`,
210
+ regex: /{{\s*?author\.url\s*?}}/g,
211
+ helper: '{{author.url}}'
212
+ },
213
+ 'GS001-DEPR-PAUTH': {
214
+ level: 'error',
215
+ fatal: true,
216
+ rule: 'Replace the <code>{{post.author}}</code> helper with <code>{{post.primary_author}}</code> or <code>{{authors.[#]}}</code>',
217
+ details: oneLineTrim`The usage of <code>{{post.author}}</code> is deprecated and should be replaced with either <code>{{post.primary_author}}</code>
218
+ or <code>{{post.authors.[#]}}</code>.<br>
219
+ Find more information about the <code>{{authors}}</code> helper <a href="${docsBaseUrl}helpers/authors/" target=_blank>here</a>.`,
220
+ regex: /{{\s*?post\.author\s*?}}/g,
221
+ helper: '{{post.author}}'
222
+ },
223
+ 'GS001-DEPR-PAUTH-ID': {
224
+ level: 'error',
225
+ fatal: true,
226
+ rule: 'Replace the <code>{{post.author.id}}</code> helper with <code>{{post.primary_author.id}}</code> or <code>{{authors.[#].id}}</code>',
227
+ details: oneLineTrim`The usage of <code>{{post.author.id}}</code> is deprecated and should be replaced with either <code>{{post.primary_author.id}}</code>
228
+ or <code>{{post.authors.[#].id}}</code>.<br>
229
+ Find more information about the <code>{{authors}}</code> helper <a href="${docsBaseUrl}helpers/authors/" target=_blank>here</a>.`,
230
+ regex: /{{\s*?post\.author\.id\s*?}}/g,
231
+ helper: '{{post.author.id}}'
232
+ },
233
+ 'GS001-DEPR-PAUTH-SLUG': {
234
+ level: 'error',
235
+ fatal: true,
236
+ rule: 'Replace the <code>{{post.author.slug}}</code> helper with <code>{{post.primary_author.slug}}</code> or <code>{{post.authors.[#].slug}}</code>',
237
+ details: oneLineTrim`The usage of <code>{{post.author.slug}}</code> is deprecated and should be replaced with either <code>{{post.primary_author.slug}}</code>
238
+ or <code>{{post.authors.[#].slug}}</code>.<br>
239
+ Find more information about the <code>{{authors}}</code> helper <a href="${docsBaseUrl}helpers/authors/" target=_blank>here</a>.`,
240
+ regex: /{{\s*?post\.author\.slug\s*?}}/g,
241
+ helper: '{{post.author.slug}}'
242
+ },
243
+ 'GS001-DEPR-PAUTH-MAIL': {
244
+ level: 'error',
245
+ fatal: true,
246
+ rule: 'Replace the <code>{{post.author.email}}</code> helper with <code>{{post.primary_author.email}}</code> or <code>{{post.authors.[#].email}}</code>',
247
+ details: oneLineTrim`The usage of <code>{{post.author.email}}</code> is deprecated and should be replaced with either <code>{{post.primary_author.email}}</code>
248
+ or <code>{{post.authors.[#].email}}</code>.<br>
249
+ Find more information about the <code>{{authors}}</code> helper <a href="${docsBaseUrl}helpers/authors/" target=_blank>here</a>.`,
250
+ regex: /{{\s*?post\.author\.email\s*?}}/g,
251
+ helper: '{{post.author.email}}'
252
+ },
253
+ 'GS001-DEPR-PAUTH-MT': {
254
+ level: 'error',
255
+ fatal: true,
256
+ rule: 'Replace the <code>{{post.author.meta_title}}</code> helper with <code>{{post.primary_author.meta_title}}</code> or <code>{{post.authors.[#].meta_title}}</code>',
257
+ details: oneLineTrim`The usage of <code>{{post.author.meta_title}}</code> is deprecated and should be replaced with either <code>{{post.primary_author.meta_title}}</code>
258
+ or <code>{{post.authors.[#].meta_title}}</code>.<br>
259
+ Find more information about the <code>{{authors}}</code> helper <a href="${docsBaseUrl}helpers/authors/" target=_blank>here</a>.`,
260
+ regex: /{{\s*?post\.author\.meta_title\s*?}}/g,
261
+ helper: '{{post.author.meta_title}}'
262
+ },
263
+ 'GS001-DEPR-PAUTH-MD': {
264
+ level: 'error',
265
+ fatal: true,
266
+ rule: 'Replace the <code>{{post.author.meta_description}}</code> helper with <code>{{post.primary_author.meta_description}}</code> or <code>{{post.authors.[#].meta_description}}</code>',
267
+ details: oneLineTrim`The usage of <code>{{post.author.meta_description}}</code> is deprecated and should be replaced with either <code>{{post.primary_author.meta_description}}</code>
268
+ or <code>{{post.authors.[#].meta_description}}</code>.<br>
269
+ Find more information about the <code>{{authors}}</code> helper <a href="${docsBaseUrl}helpers/authors/" target=_blank>here</a>.`,
270
+ regex: /{{\s*?post\.author\.meta_description\s*?}}/g,
271
+ helper: '{{post.author.meta_description}}'
272
+ },
273
+ 'GS001-DEPR-PAUTH-NAME': {
274
+ level: 'error',
275
+ fatal: true,
276
+ rule: 'Replace the <code>{{post.author.name}}</code> helper with <code>{{post.primary_author.name}}</code> or <code>{{post.authors.[#].name}}</code>',
277
+ details: oneLineTrim`The usage of <code>{{post.author.name}}</code> is deprecated and should be replaced with either <code>{{post.primary_author.name}}</code>
278
+ or <code>{{post.authors.[#].name}}</code>.<br>
279
+ Find more information about the <code>{{authors}}</code> helper <a href="${docsBaseUrl}helpers/authors/" target=_blank>here</a>.`,
280
+ regex: /{{\s*?post\.author\.name\s*?}}/g,
281
+ helper: '{{post.author.name}}'
282
+ },
283
+ 'GS001-DEPR-PAUTH-BIO': {
284
+ level: 'error',
285
+ fatal: true,
286
+ rule: 'Replace the <code>{{post.author.bio}}</code> helper with <code>{{post.primary_author.bio}}</code> or <code>{{post.authors.[#].bio}}</code>',
287
+ details: oneLineTrim`The usage of <code>{{post.author.bio}}</code> is deprecated and should be replaced with either <code>{{post.primary_author.bio}}</code>
288
+ or <code>{{post.authors.[#].bio}}</code>.<br>
289
+ Find more information about the <code>{{authors}}</code> helper <a href="${docsBaseUrl}helpers/authors/" target=_blank>here</a>.`,
290
+ regex: /{{\s*?post\.author\.bio\s*?}}/g,
291
+ helper: '{{post.author.bio}}'
292
+ },
293
+ 'GS001-DEPR-PAUTH-LOC': {
294
+ level: 'error',
295
+ fatal: true,
296
+ rule: 'Replace the <code>{{post.author.location}}</code> helper with <code>{{post.primary_author.location}}</code> or <code>{{post.authors.[#].location}}</code>',
297
+ details: oneLineTrim`The usage of <code>{{post.author.location}}</code> is deprecated and should be replaced with either <code>{{post.primary_author.location}}</code>
298
+ or <code>{{post.authors.[#].location}}</code>.<br>
299
+ Find more information about the <code>{{authors}}</code> helper <a href="${docsBaseUrl}helpers/authors/" target=_blank>here</a>.`,
300
+ regex: /{{\s*?post\.author\.location\s*?}}/g,
301
+ helper: '{{post.author.location}}'
302
+ },
303
+ 'GS001-DEPR-PAUTH-WEB': {
304
+ level: 'error',
305
+ fatal: true,
306
+ rule: 'Replace the <code>{{post.author.website}}</code> helper with <code>{{post.primary_author.website}}</code> or <code>{{post.authors.[#].website}}</code>',
307
+ details: oneLineTrim`The usage of <code>{{post.author.website}}</code> is deprecated and should be replaced with either <code>{{post.primary_author.website}}</code>
308
+ or <code>{{post.authors.[#].website}}</code>.<br>
309
+ Find more information about the <code>{{authors}}</code> helper <a href="${docsBaseUrl}helpers/authors/" target=_blank>here</a>.`,
310
+ regex: /{{\s*?post\.author\.website\s*?}}/g,
311
+ helper: '{{post.author.website}}'
312
+ },
313
+ 'GS001-DEPR-PAUTH-TW': {
314
+ level: 'error',
315
+ fatal: true,
316
+ rule: 'Replace the <code>{{post.author.twitter}}</code> helper with <code>{{post.primary_author.twitter}}</code> or <code>{{post.authors.[#].twitter}}</code>',
317
+ details: oneLineTrim`The usage of <code>{{post.author.twitter}}</code> is deprecated and should be replaced with either <code>{{post.primary_author.twitter}}</code>
318
+ or <code>{{post.authors.[#].twitter}}</code>.<br>
319
+ Find more information about the <code>{{authors}}</code> helper <a href="${docsBaseUrl}helpers/authors/" target=_blank>here</a>.`,
320
+ regex: /{{\s*?post\.author\.twitter\s*?}}/g,
321
+ helper: '{{post.author.twitter}}'
322
+ },
323
+ 'GS001-DEPR-PAUTH-FB': {
324
+ level: 'error',
325
+ fatal: true,
326
+ rule: 'Replace the <code>{{post.author.facebook}}</code> helper with <code>{{post.primary_author.facebook}}</code> or <code>{{post.authors.[#].facebook}}</code>',
327
+ details: oneLineTrim`The usage of <code>{{post.author.facebook}}</code> is deprecated and should be replaced with either <code>{{post.primary_author.facebook}}</code>
328
+ or <code>{{post.authors.[#].facebook}}</code>.<br>
329
+ Find more information about the <code>{{authors}}</code> helper <a href="${docsBaseUrl}helpers/authors/" target=_blank>here</a>.`,
330
+ regex: /{{\s*?post\.author\.facebook\s*?}}/g,
331
+ helper: '{{post.author.facebook}}'
332
+ },
333
+ 'GS001-DEPR-PAUTH-PIMG': {
334
+ level: 'error',
335
+ fatal: true,
336
+ rule: 'Replace the <code>{{post.author.profile_image}}</code> helper with <code>{{post.primary_author.profile_image}}</code> or <code>{{post.authors.[#].profile_image}}</code>',
337
+ details: oneLineTrim`The usage of <code>{{post.author.profile_image}}</code> is deprecated and should be replaced with either <code>{{post.primary_author.profile_image}}</code>
338
+ or <code>{{post.authors.[#].profile_image}}</code>.<br>
339
+ Find more information about the <code>{{authors}}</code> helper <a href="${docsBaseUrl}helpers/authors/" target=_blank>here</a>.`,
340
+ regex: /{{\s*?post\.author\.profile_image\s*?}}/g,
341
+ helper: '{{post.author.profile_image}}'
342
+ },
343
+ 'GS001-DEPR-PAUTH-CIMG': {
344
+ level: 'error',
345
+ fatal: true,
346
+ rule: 'Replace the <code>{{post.author.cover_image}}</code> helper with <code>{{post.primary_author.cover_image}}</code> or <code>{{post.authors.[#].cover_image}}</code>',
347
+ details: oneLineTrim`The usage of <code>{{post.author.cover_image}}</code> is deprecated and should be replaced with either <code>{{post.primary_author.cover_image}}</code>
348
+ or <code>{{post.authors.[#].cover_image}}</code>.<br>
349
+ Find more information about the <code>{{authors}}</code> helper <a href="${docsBaseUrl}helpers/authors/" target=_blank>here</a>.`,
350
+ regex: /{{\s*?post\.author\.cover_image\s*?}}/g,
351
+ helper: '{{post.author.cover_image}}'
352
+ },
353
+ 'GS001-DEPR-PAUTH-URL': {
354
+ level: 'error',
355
+ fatal: true,
356
+ rule: 'Replace the <code>{{post.author.url}}</code> helper with <code>{{post.primary_author.url}}</code> or <code>{{post.authors.[#].url}}</code>',
357
+ details: oneLineTrim`The usage of <code>{{post.author.url}}</code> is deprecated and should be replaced with either <code>{{post.primary_author.url}}</code>
358
+ or <code>{{post.authors.[#].url}}</code>.<br>
359
+ Find more information about the <code>{{authors}}</code> helper <a href="${docsBaseUrl}helpers/authors/" target=_blank>here</a>.`,
360
+ regex: /{{\s*?post\.author\.url\s*?}}/g,
361
+ helper: '{{post.author.url}}'
362
+ },
363
+ 'GS001-DEPR-PAID': {
364
+ level: 'error',
365
+ fatal: true,
366
+ rule: 'Replace <code>{{post.author_id}}</code> code with <code>{{post.primary_author.id}}</code>',
367
+ details: oneLineTrim`The <code>{{post.author_id}}</code> attribute in post context was removed<br>
368
+ Instead of <code>{{post.author_id}}</code> you need to use <code>{{post.primary_author.id}}</code>.<br>
369
+ See the object attributes of <code>post</code> <a href="${docsBaseUrl}context/post/#post-object-attributes" target=_blank>here</a>.`,
370
+ regex: /{{\s*?post\.author_id\s*?}}/g,
371
+ helper: '{{post.author_id}}'
372
+ },
373
+ 'GS001-DEPR-NAUTH': {
374
+ level: 'error',
375
+ fatal: true,
376
+ rule: 'Replace <code>../author</code> with <code>../primary_author</code> or <code>../authors.[#]</code>',
377
+ details: oneLineTrim`The usage of <code>../author</code> is deprecated and should be replaced with either <code>../primary_author</code>
378
+ or <code>../authors.[#]</code>.<br>
379
+ Find more information about the <code>{{authors}}</code> helper <a href="${docsBaseUrl}helpers/authors/" target=_blank>here</a>.`,
380
+ regex: /{{\s*?(?:#|#if)?\s*?\.\.\/author(?:\.\S*?)?\s*?}}/g,
381
+ helper: '{{../author}}'
382
+ },
383
+ 'GS001-DEPR-IUA': {
384
+ level: 'error',
385
+ fatal: true,
386
+ rule: 'Replace <code>{{img_url author.*}}</code> with <code>{{img_url primary_author.*}}</code> or <code>.{img_url author.[#].*}}</code>',
387
+ details: oneLineTrim`The usage of <code>{{img_url author.*}}</code> is deprecated and should be replaced with either <code>{{img_url primary_author.*}}</code>
388
+ or <code>{{img_url author.[#].*}}</code>.<br>
389
+ Find more information about the <code>{{authors}}</code> helper <a href="${docsBaseUrl}helpers/authors/" target=_blank>here</a>.`,
390
+ regex: /{{\s*?img_url\s*?(author.).*}}/g,
391
+ helper: '{{img_url author.*}}'
392
+ },
393
+ 'GS001-DEPR-AC': {
394
+ level: 'error',
395
+ fatal: true,
396
+ rule: 'Replace the <code>{{author.cover}}</code> helper with <code>{{primary_author.cover_image}}</code>',
397
+ details: oneLineTrim`The <code>cover</code> attribute was replaced with <code>cover_image</code>.<br>
398
+ Instead of <code>{{author.cover}}</code> you need to use
399
+ <code>{{primary_author.cover_image}}</code> or <code>{{authors.[#].cover_image}}</code>.<br>
400
+ See the object attributes of <code>author</code> <a href="${docsBaseUrl}context/author/#author-object-attributes" target=_blank>here</a>.`,
401
+ regex: /{{\s*?author\.cover\s*?}}/g,
402
+ helper: '{{author.cover}}'
403
+ },
404
+ 'GS001-DEPR-AIMG': {
405
+ level: 'error',
406
+ fatal: true,
407
+ rule: 'Replace the <code>{{author.image}}</code> helper with <code>{{primary_author.profile_image}}</code> or <code>{{authors.[#].profile_image}}</code>',
408
+ details: oneLineTrim`The <code>image</code> attribute was replaced with <code>profile_image</code>.<br>
409
+ Instead of <code>{{author.image}}</code>, you need to use
410
+ <code>{{primary_author.profile_image}}</code> or <code>{{authors.[#].profile_image}}</code>.<br>
411
+ See the object attributes of <code>author</code> <a href="${docsBaseUrl}context/author/#author-object-attributes" target=_blank>here</a>.`,
412
+ regex: /{{\s*?author\.image\s*?}}/g,
413
+ helper: '{{author.image}}'
414
+ },
415
+ 'GS001-DEPR-PAC': {
416
+ level: 'error',
417
+ fatal: true,
418
+ rule: 'Replace the <code>{{post.author.cover}}</code> helper with <code>{{post.primary_author.cover_image}}</code> or <code>{{post.authors.[#].cover_image}}</code>',
419
+ details: oneLineTrim`The <code>cover</code> attribute was replaced with <code>cover_image</code>.<br>
420
+ Instead of <code>{{post.author.cover}}</code>, you need to use
421
+ <code>{{post.primary_author.cover_image}}</code> or <code>{{post.authors.[#].cover_image}}</code>.<br>
422
+ See the object attributes of <code>author</code> <a href="${docsBaseUrl}context/author/#author-object-attributes" target=_blank>here</a>.`,
423
+ regex: /{{\s*?post\.author\.cover\s*?}}/g,
424
+ helper: '{{post.author.cover}}'
425
+ },
426
+ 'GS001-DEPR-PAIMG': {
427
+ level: 'error',
428
+ fatal: true,
429
+ rule: 'Replace the <code>{{post.author.image}}</code> helper with <code>{{post.primary_author.profile_image}}</code> or <code>{{post.authors.[#].profile_image}}</code>',
430
+ details: oneLineTrim`The <code>image</code> attribute was replaced with <code>profile_image</code>.<br>
431
+ Instead of <code>{{post.author.image}}</code>, you need to use
432
+ <code>{{post.primary_author.profile_image}}</code> or <code>{{post.authors.[#].profile_image}}</code>.<br>
433
+ See the object attributes of <code>author</code> <a href="${docsBaseUrl}context/author/#author-object-attributes" target=_blank>here</a>.`,
434
+ regex: /{{\s*?post\.author\.image\s*?}}/g,
435
+ helper: '{{post.author.image}}'
436
+ },
437
+ 'GS001-DEPR-CON-AC': {
438
+ level: 'error',
439
+ fatal: true,
440
+ rule: 'Replace the <code>{{#if author.cover}}</code> helper with <code>{{#if primary_author.cover_image}}</code> or <code>{{#if authors.[#].cover_image}}</code>',
441
+ details: oneLineTrim`The <code>cover</code> attribute was replaced with <code>cover_image</code>.<br>
442
+ Instead of <code>{{#if author.cover}}</code>, you need to use
443
+ <code>{{#if primary_author.cover_image}}</code> or <code>{{#if authors.[#].cover_image}}</code>.<br>
444
+ See the object attributes of <code>author</code> <a href="${docsBaseUrl}context/author/#author-object-attributes" target=_blank>here</a>.`,
445
+ regex: /{{\s*?#if\s*?author\.cover\s*?}}/g,
446
+ helper: '{{#if author.cover}}'
447
+ },
448
+ 'GS001-DEPR-CON-AIMG': {
449
+ level: 'error',
450
+ fatal: true,
451
+ rule: 'Replace the <code>{{#if author.image}}</code> helper with <code>{{#if primary_author.profile_image}}</code> or <code>{{#if authors.[#].profile_image}}</code>',
452
+ details: oneLineTrim`The <code>image</code> attribute was replaced with <code>profile_image</code>.<br>
453
+ Instead of <code>{{#if author.image}}</code>, you need to use
454
+ <code>{{#if primary_author.profile_image}}</code> or <code>{{#if authors.[#].profile_image}}</code>.<br>
455
+ See the object attributes of <code>author</code> <a href="${docsBaseUrl}context/author/#author-object-attributes" target=_blank>here</a>.`,
456
+ regex: /{{\s*?#if\s*?author\.image\s*?}}/g,
457
+ helper: '{{#if author.image}}'
458
+ },
459
+ 'GS001-DEPR-CON-PAC': {
460
+ level: 'error',
461
+ fatal: true,
462
+ rule: 'Replace the <code>{{#if post.author.cover}}</code> helper with <code>{{#if post.primary_author.cover_image}}</code> or <code>{{#if post.authors.[#].cover_image}}</code>',
463
+ details: oneLineTrim`The <code>cover</code> attribute was replaced with <code>cover_image</code>.<br>
464
+ Instead of <code>{{#if post.author.cover}}</code>, you need to use
465
+ <code>{{#if post.primary_author.cover_image}}</code> or <code>{{#if post.authors.[#].cover_image}}</code>.<br>
466
+ See the object attributes of <code>author</code> <a href="${docsBaseUrl}context/author/#author-object-attributes" target=_blank>here</a>.`,
467
+ regex: /{{\s*?#if\s*?post\.author\.cover\s*?}}/g,
468
+ helper: '{{#if post.author.cover}}'
469
+ },
470
+ 'GS001-DEPR-CON-PAIMG': {
471
+ level: 'error',
472
+ fatal: true,
473
+ rule: 'Replace the <code>{{#if post.author.image}}</code> helper with <code>{{#if post.primary_author.profile_image}}</code> or <code>{{#if post.authors.[#].profile_image}}</code>',
474
+ details: oneLineTrim`The <code>image</code> attribute was replaced with <code>profile_image</code>.<br>
475
+ Instead of <code>{{#if post.author.image}}</code>, you need to use
476
+ <code>{{#if post.primary_author.profile_image}}</code> or <code>{{#if post.authors.[#].profile_image}}</code>.<br>
477
+ See the object attributes of <code>author</code> <a href="${docsBaseUrl}context/author/#author-object-attributes" target=_blank>here</a>.`,
478
+ regex: /{{\s*?#if\s*?post\.author\.image\s*?}}/g,
479
+ helper: '{{#if post.author.image}}'
43
480
  }
44
481
  };
45
482
 
package/lib/specs/v4.js CHANGED
@@ -180,6 +180,31 @@ let rules = {
180
180
  fatal: false,
181
181
  details: oneLineTrim`Custom theme settings of type <code>select</code> can only be compared to their defined <code>options</code> when used in a <code>match</code> block.`
182
182
  },
183
+ 'GS090-NO-PRODUCTS-HELPER': {
184
+ level: 'warning',
185
+ rule: 'The <code>{{products}}</code> helper should be replaces with <code>{{tiers}}</code>',
186
+ details: oneLineTrim`The <code>{{products}}</code> helper has been deprecated in favor of <code>{{tiers}}</code><br>
187
+ The <code>{{products}}</code> helper will be removed in Ghost v5 and should not be used.
188
+ Find more information about the <code>{{tiers}}</code> property <a href="${docsBaseUrl}helpers/tiers/" target=_blank>here</a>.`,
189
+ helper: '{{products}}'
190
+ },
191
+ 'GS090-NO-PRODUCT-DATA-HELPER': {
192
+ level: 'warning',
193
+ rule: 'The <code>{{@product}}</code> data helper should be replaces with <code>{{#get "tiers"}}</code>',
194
+ details: oneLineTrim`The <code>{{@product}}</code> data helper has been deprecated in favor of <code>{{#get "tiers"}}</code><br>
195
+ The <code>{{@product}}</code> data helper was removed in Ghost v5 and should not be used.
196
+ Find more information about the <code>{{#get "tiers"}}</code> property <a href="${docsBaseUrl}helpers/tiers/" target=_blank>here</a>.`,
197
+ helper: '{{@product}}'
198
+ },
199
+ 'GS090-NO-PRODUCTS-DATA-HELPER': {
200
+ level: 'warning',
201
+ rule: 'The <code>{{@products}}</code> data helper should be replaces with <code>{{#get "tiers"}}</code>',
202
+ details: oneLineTrim`The <code>{{@products}}</code> data helper has been deprecated in favor of <code>{{#get "tiers"}}</code><br>
203
+ The <code>{{@products}}</code> data helper was removed in Ghost v5 and should not be used.
204
+ Find more information about the <code>{{#get "tiers"}}</code> property <a href="${docsBaseUrl}helpers/tiers/" target=_blank>here</a>.`,
205
+ helper: '{{@products}}'
206
+ },
207
+
183
208
  'GS100-NO-UNUSED-CUSTOM-THEME-SETTING': {
184
209
  level: 'error',
185
210
  rule: 'A custom theme setting defined in <code>package.json</code> hasn\'t been used in any theme file.',
@@ -15,7 +15,7 @@
15
15
  "major": "4.x",
16
16
  "docs": "4.0.0"
17
17
  },
18
- "canary": {
18
+ "v5": {
19
19
  "major": "5.x",
20
20
  "docs": "5.0.0"
21
21
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gscan",
3
- "version": "4.23.0",
3
+ "version": "4.25.0",
4
4
  "description": "Scans Ghost themes looking for errors, deprecations, features and compatibility",
5
5
  "keywords": [
6
6
  "ghost",
@@ -29,22 +29,22 @@
29
29
  "scripts": {
30
30
  "start": "node app/index.js",
31
31
  "dev": "NODE_ENV=development DEBUG=gscan:* nodemon",
32
- "lint": "eslint ./app --ext .js --cache",
32
+ "lint": "eslint . --ext .js --cache",
33
33
  "test": "NODE_ENV=testing mocha -- $(find test -name '*.test.js')",
34
34
  "posttest": "yarn lint",
35
35
  "coverage": "NODE_ENV=testing istanbul cover --dir test/coverage _mocha -- $(find test -name '*.test.js')",
36
36
  "preship": "yarn test",
37
- "ship": "yarn publish && git push ${GHOST_UPSTREAM:-upstream} main --follow-tags"
37
+ "ship": "STATUS=$(git status --porcelain); echo $STATUS; if [ -z \"$STATUS\" ]; then yarn publish && git push ${GHOST_UPSTREAM:-upstream} main --follow-tags; fi"
38
38
  },
39
39
  "bin": {
40
40
  "gscan": "./bin/cli.js"
41
41
  },
42
42
  "dependencies": {
43
- "@sentry/node": "6.18.2",
43
+ "@sentry/node": "6.19.2",
44
44
  "@tryghost/config": "0.2.2",
45
45
  "@tryghost/debug": "0.1.11",
46
46
  "@tryghost/ignition-errors": "0.1.8",
47
- "@tryghost/logging": "2.0.4",
47
+ "@tryghost/logging": "2.1.0",
48
48
  "@tryghost/pretty-cli": "1.2.24",
49
49
  "@tryghost/server": "0.1.4",
50
50
  "@tryghost/zip": "1.1.20",