gscan 4.20.2 → 4.21.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/LICENSE CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2013-2021 Ghost Foundation
1
+ Copyright (c) 2013-2022 Ghost Foundation
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person
4
4
  obtaining a copy of this software and associated documentation
package/README.md CHANGED
@@ -114,4 +114,4 @@ gscan.checkZip({
114
114
 
115
115
  # Copyright & License
116
116
 
117
- Copyright (c) 2013-2021 Ghost Foundation - Released under the [MIT license](LICENSE). Ghost and the Ghost Logo are trademarks of Ghost Foundation Ltd. Please see our [trademark policy](https://ghost.org/trademark/) for info on acceptable usage.
117
+ Copyright (c) 2013-2022 Ghost Foundation - Released under the [MIT license](LICENSE). Ghost and the Ghost Logo are trademarks of Ghost Foundation Ltd. Please see our [trademark policy](https://ghost.org/trademark/) for info on acceptable usage.
Binary file
Binary file
Binary file
Binary file
Binary file
@@ -0,0 +1,28 @@
1
+ <?xml version="1.0" standalone="no"?>
2
+ <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
3
+ "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
4
+ <svg version="1.0" xmlns="http://www.w3.org/2000/svg"
5
+ width="512.000000pt" height="512.000000pt" viewBox="0 0 512.000000 512.000000"
6
+ preserveAspectRatio="xMidYMid meet">
7
+ <metadata>
8
+ Created by potrace 1.14, written by Peter Selinger 2001-2017
9
+ </metadata>
10
+ <g transform="translate(0.000000,512.000000) scale(0.100000,-0.100000)"
11
+ fill="#000000" stroke="none">
12
+ <path d="M2175 5114 c-710 -35 -1152 -142 -1481 -359 -397 -263 -605 -746
13
+ -674 -1565 -6 -69 -13 -285 -17 -480 -15 -925 66 -1475 278 -1881 276 -527
14
+ 827 -767 1884 -820 251 -13 845 -7 1055 11 1085 90 1560 424 1770 1245 99 387
15
+ 138 839 127 1465 -11 582 -55 933 -158 1255 -179 562 -504 859 -1106 1011
16
+ -200 50 -401 82 -673 105 -123 10 -867 20 -1005 13z m530 -604 c371 -31 710
17
+ -154 1008 -367 97 -70 278 -241 359 -340 481 -589 575 -1404 241 -2078 -271
18
+ -547 -759 -921 -1372 -1052 -118 -26 -142 -27 -381 -27 -240 0 -263 1 -383 27
19
+ -548 117 -987 422 -1282 892 -292 465 -365 1067 -194 1603 134 417 407 775
20
+ 785 1027 255 171 594 288 906 314 149 12 172 12 313 1z"/>
21
+ <path d="M2425 4124 c-315 -48 -569 -134 -755 -254 -81 -53 -218 -185 -276
22
+ -266 -59 -82 -137 -248 -169 -359 -14 -49 -35 -146 -46 -215 -19 -116 -44
23
+ -243 -91 -450 -29 -127 -34 -314 -13 -441 46 -275 164 -501 369 -704 202 -200
24
+ 419 -317 699 -376 121 -26 374 -32 472 -11 297 63 813 327 1112 569 172 139
25
+ 289 280 367 444 60 124 86 227 93 371 16 317 -103 589 -448 1026 -68 86 -169
26
+ 202 -224 258 -159 160 -381 297 -599 369 -170 57 -305 68 -491 39z"/>
27
+ </g>
28
+ </svg>
@@ -13,7 +13,12 @@
13
13
 
14
14
  <link rel="stylesheet" href="/gscan.css?v=1">
15
15
 
16
- <link rel="icon" type="image/png" sizes="32x32" href="/favicon.png">
16
+ <link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png">
17
+ <link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png">
18
+ <link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png">
19
+ <link rel="mask-icon" href="/safari-pinned-tab.svg" color="#0d1013">
20
+ <meta name="msapplication-TileColor" content="#0d1013">
21
+ <meta name="theme-color" content="#ffffff">
17
22
 
18
23
  <meta name="HandheldFriendly" content="True">
19
24
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
@@ -6,17 +6,19 @@ class Linter {
6
6
  /**
7
7
  *
8
8
  * @param {Object} [options]
9
- * @param {string[]} options.partials - list of known theme partial names in ['mypartial', 'logo'] format
10
- * @param {string[]} options.helpers - list of registered theme helper names in ['is', 'has'] format
11
- * @param {Object} options.customThemeSettings - list of registered custom theme settings in {'main_accent':{}} format
9
+ * @param {string[]?} options.partials - list of known theme partial names in ['mypartial', 'logo'] format
10
+ * @param {string[]?} options.helpers - list of registered theme helper names in ['is', 'has'] format
11
+ * @param {string[]?} options.inlinePartials - list of known local partial names in ['mypartial', 'logo'] format
12
+ * @param {Object?} options.customThemeSettings - list of registered custom theme settings in {'main_accent':{}} format
12
13
  */
13
- constructor(options = {partials: [], helpers: [], customThemeSettings: {}}) {
14
+ constructor(options = {partials: [], helpers: [], inlinePartials: [], customThemeSettings: {}}) {
14
15
  this.options = options;
15
16
  // Ignore OS-specific path separator as Handlebars only uses forward-separators in its syntax
16
17
  this.options.partials = this.options.partials.map(partial => normalizePath(partial));
17
18
 
18
19
  this.partials = [];
19
20
  this.helpers = [];
21
+ this.inlinePartials = [];
20
22
  this.customThemeSettings = [];
21
23
  }
22
24
 
@@ -40,6 +42,7 @@ class Linter {
40
42
  source: config.source,
41
43
  partials: this.options.partials,
42
44
  helpers: this.options.helpers,
45
+ inlinePartials: this.options.inlinePartials,
43
46
  customThemeSettings: this.options.customThemeSettings
44
47
  });
45
48
 
@@ -53,6 +56,7 @@ class Linter {
53
56
  this.context = {
54
57
  partials: [],
55
58
  helpers: [],
59
+ inlinePartials: [],
56
60
  customThemeSettings: []
57
61
  };
58
62
  }
@@ -65,12 +69,12 @@ class Linter {
65
69
  'PartialStatement',
66
70
  'PartialBlockStatement',
67
71
  'PathExpression',
68
- 'SubExpression'
72
+ 'SubExpression',
73
+ 'DecoratorBlock'
69
74
  // the following types are not used in Ghost or we don't validate
70
75
  // 'ContentStatement',
71
76
  // 'CommentStatement,
72
77
  // 'Decorator',
73
- // 'DecoratorBlock'
74
78
  ];
75
79
 
76
80
  nodeTypes.forEach((nodeType) => {
@@ -153,6 +157,10 @@ class Linter {
153
157
  this.customThemeSettings = scanner.context.customThemeSettings;
154
158
  }
155
159
 
160
+ if (scanner.context.inlinePartials) {
161
+ this.inlinePartials = scanner.context.inlinePartials;
162
+ }
163
+
156
164
  return messages;
157
165
  }
158
166
  }
@@ -7,6 +7,7 @@ module.exports = class BaseRule {
7
7
  this.source = options.source;
8
8
  this.partials = options.partials;
9
9
  this.helpers = options.helpers;
10
+ this.inlinePartials = options.inlinePartials || [];
10
11
  this.customThemeSettings = options.customThemeSettings;
11
12
  }
12
13
 
@@ -27,7 +28,8 @@ module.exports = class BaseRule {
27
28
  'PartialStatement',
28
29
  'PartialBlockStatement',
29
30
  'PathExpression',
30
- 'SubExpression'
31
+ 'SubExpression',
32
+ 'DecoratorBlock'
31
33
  ];
32
34
 
33
35
  // this object keeps an array of functions for each node type, eventually
@@ -111,8 +113,44 @@ module.exports = class BaseRule {
111
113
  this._log(reportedResult);
112
114
  }
113
115
 
114
- isValidPartialReference(node) {
115
- return node.name.original === '@partial-block' || this.partials.includes(node.name.original);
116
+ isValidPartialReference(node, parents) {
117
+ return node.name.original === '@partial-block'
118
+ || this.partials.includes(node.name.original)
119
+ || this.isAccessibleInlinePartial(node, parents);
120
+ }
121
+
122
+ // Make sure one inline partial match the partial usage
123
+ isAccessibleInlinePartial(node, parents) {
124
+ const parentNodes = parents || [];
125
+ return this.inlinePartials.some((partial) => {
126
+ if (partial.node !== node.name.original) {
127
+ return false;
128
+ }
129
+
130
+ //filter candidates so that the inline partial is in the same scope as where it's used
131
+ for (let i = 0; i < partial.parents.length; i++) {
132
+ const declarationParent = partial.parents[i];
133
+ for (let j = 0; j < parentNodes.length; j++) {
134
+ const usageParent = parentNodes[j];
135
+
136
+ // If we found a common ancestor, we're good
137
+ // To compare two nodes, we check the type of the node and the location of the code
138
+ if (usageParent.type === declarationParent.type &&
139
+ usageParent.loc.source === declarationParent.loc.source &&
140
+ usageParent.loc.start.line === declarationParent.loc.start.line &&
141
+ usageParent.loc.start.column === declarationParent.loc.start.column &&
142
+ usageParent.loc.end.line === declarationParent.loc.end.line &&
143
+ usageParent.loc.end.column === declarationParent.loc.end.column) {
144
+ return true;
145
+ }
146
+
147
+ // If we found a block before finding a common ancestor, the usage can't access the declaration
148
+ if (['BlockStatement','PartialBlockStatement','DecoratorBlock'].includes(declarationParent.type)) {
149
+ return false;
150
+ }
151
+ }
152
+ }
153
+ });
116
154
  }
117
155
 
118
156
  isValidHelperReference(nodeName) {
@@ -2,14 +2,32 @@ const Rule = require('./base');
2
2
  const {getPartialName} = require('../helpers');
3
3
 
4
4
  module.exports = class NoUnknownPartials extends Rule {
5
- _checkForUnknownPartials(node) {
5
+ _checkForUnknownPartials(node, visitor) {
6
6
  if (node.name) {
7
- if (!this.isValidPartialReference(node)) {
8
- this.log({
9
- message: `The partial ${getPartialName(node)} could not be found`,
7
+ if (!this.isValidPartialReference(node, visitor.parents)) {
8
+ const logObject = {
10
9
  line: node.loc && node.loc.start.line,
11
10
  column: node.loc && node.loc.start.column,
12
11
  source: this.sourceForNode(node)
12
+ };
13
+
14
+ // Dynamic partials (https://handlebarsjs.com/guide/partials.html#dynamic-partials)
15
+ if (node.name.type === 'SubExpression') {
16
+ if (node.type === 'PartialBlockStatement') {
17
+ // A dynamic partial is valid when declared as a block
18
+ return;
19
+ }
20
+ if (node.type === 'PartialStatement') {
21
+ return this.log({
22
+ message: `Inlined dynamic partials like <code>{{> (dynamicPartial) }}</code> can result in page errors if the partial does not exist, use block dynamic partials instead.`,
23
+ ...logObject
24
+ });
25
+ }
26
+ }
27
+
28
+ this.log({
29
+ message: `The partial ${getPartialName(node)} could not be found`,
30
+ ...logObject
13
31
  });
14
32
  }
15
33
  }
@@ -0,0 +1,26 @@
1
+ const Rule = require('./base');
2
+ const get = require('lodash/get');
3
+
4
+ module.exports = class MarkDeclaredInlinePartials extends Rule {
5
+ _markDeclaredInlinePartials(node, visitor) {
6
+ if (get(node, 'path.original') === 'inline' && get(node, 'params.length') > 0) {
7
+ const nodeName = get(node, 'params[0].original');
8
+ this.scanner.context.inlinePartials.push({
9
+ node: nodeName,
10
+ parents: visitor.parents.map(p => ({
11
+ type: p.type,
12
+ loc: p.loc
13
+ })),
14
+ type: node.type,
15
+ loc: node.loc,
16
+ parameters: node.params ? node.params.map(p => p.original) : null
17
+ });
18
+ }
19
+ }
20
+
21
+ visitor() {
22
+ return {
23
+ DecoratorBlock: this._markDeclaredInlinePartials.bind(this)
24
+ };
25
+ }
26
+ };
@@ -14,6 +14,22 @@ function processFileFunction(files, failures, theme, partialsFound) {
14
14
 
15
15
  processedFiles.push(themeFile.file);
16
16
 
17
+ // Reset inline partial variables
18
+ linter.inlinePartials = [];
19
+ linter.options.inlinePartials = [];
20
+
21
+ linter.verify({
22
+ parsed: themeFile.parsed,
23
+ rules: [
24
+ require('../ast-linter/rules/mark-declared-inline-partials')
25
+ ],
26
+ source: themeFile.content,
27
+ moduleId: themeFile.file
28
+ });
29
+
30
+ // Store the inline partials for the actual partial linting
31
+ linter.options.inlinePartials = linter.inlinePartials;
32
+
17
33
  const astResults = linter.verify({
18
34
  parsed: themeFile.parsed,
19
35
  rules: [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gscan",
3
- "version": "4.20.2",
3
+ "version": "4.21.0",
4
4
  "description": "Scans Ghost themes looking for errors, deprecations, features and compatibility",
5
5
  "keywords": [
6
6
  "ghost",
@@ -41,12 +41,12 @@
41
41
  },
42
42
  "dependencies": {
43
43
  "@sentry/node": "6.16.1",
44
- "@tryghost/config": "0.2.1",
45
- "@tryghost/debug": "0.1.9",
44
+ "@tryghost/config": "0.2.2",
45
+ "@tryghost/debug": "0.1.10",
46
46
  "@tryghost/ignition-errors": "0.1.8",
47
- "@tryghost/logging": "2.0.0",
47
+ "@tryghost/logging": "2.0.1",
48
48
  "@tryghost/pretty-cli": "1.2.22",
49
- "@tryghost/server": "0.1.3",
49
+ "@tryghost/server": "0.1.4",
50
50
  "@tryghost/zip": "1.1.18",
51
51
  "bluebird": "3.7.2",
52
52
  "chalk": "4.1.2",
Binary file