@platformos/platformos-check-common 0.0.7 → 0.0.8

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.
Files changed (309) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/dist/AugmentedPlatformOSDocset.d.ts +11 -0
  3. package/dist/AugmentedPlatformOSDocset.js +81 -0
  4. package/dist/AugmentedPlatformOSDocset.js.map +1 -0
  5. package/dist/JSONValidator.js +1 -1
  6. package/dist/JSONValidator.js.map +1 -1
  7. package/dist/checks/deprecated-filter/index.js +4 -41
  8. package/dist/checks/deprecated-filter/index.js.map +1 -1
  9. package/dist/checks/deprecated-tag/index.js +21 -22
  10. package/dist/checks/deprecated-tag/index.js.map +1 -1
  11. package/dist/checks/duplicate-function-arguments/index.js +1 -1
  12. package/dist/checks/duplicate-function-arguments/index.js.map +1 -1
  13. package/dist/checks/duplicate-render-partial-arguments/index.js +1 -1
  14. package/dist/checks/duplicate-render-partial-arguments/index.js.map +1 -1
  15. package/dist/checks/graphql/index.js +1 -1
  16. package/dist/checks/graphql/index.js.map +1 -1
  17. package/dist/checks/img-width-and-height/index.js +1 -1
  18. package/dist/checks/img-width-and-height/index.js.map +1 -1
  19. package/dist/checks/index.d.ts +3 -3
  20. package/dist/checks/index.js +4 -79
  21. package/dist/checks/index.js.map +1 -1
  22. package/dist/checks/json-syntax-error/index.js +1 -1
  23. package/dist/checks/json-syntax-error/index.js.map +1 -1
  24. package/dist/checks/liquid-html-syntax-error/index.js +2 -2
  25. package/dist/checks/liquid-html-syntax-error/index.js.map +1 -1
  26. package/dist/checks/matching-translations/index.d.ts +2 -2
  27. package/dist/checks/matching-translations/index.js +20 -31
  28. package/dist/checks/matching-translations/index.js.map +1 -1
  29. package/dist/checks/metadata-params/index.js +6 -3
  30. package/dist/checks/metadata-params/index.js.map +1 -1
  31. package/dist/checks/missing-asset/index.js +1 -1
  32. package/dist/checks/missing-asset/index.js.map +1 -1
  33. package/dist/checks/missing-partial/index.d.ts +6 -0
  34. package/dist/checks/missing-partial/index.js +70 -0
  35. package/dist/checks/missing-partial/index.js.map +1 -0
  36. package/dist/checks/orphaned-partial/index.js +4 -4
  37. package/dist/checks/orphaned-partial/index.js.map +1 -1
  38. package/dist/checks/parser-blocking-script/index.js +10 -36
  39. package/dist/checks/parser-blocking-script/index.js.map +1 -1
  40. package/dist/checks/parser-blocking-script/suggestions.d.ts +1 -2
  41. package/dist/checks/parser-blocking-script/suggestions.js +1 -11
  42. package/dist/checks/parser-blocking-script/suggestions.js.map +1 -1
  43. package/dist/checks/reserved-doc-param-names/index.js +6 -5
  44. package/dist/checks/reserved-doc-param-names/index.js.map +1 -1
  45. package/dist/checks/translation-key-exists/index.js +1 -1
  46. package/dist/checks/translation-key-exists/index.js.map +1 -1
  47. package/dist/checks/unclosed-html-element/index.js +5 -1
  48. package/dist/checks/unclosed-html-element/index.js.map +1 -1
  49. package/dist/checks/undefined-object/index.js +7 -30
  50. package/dist/checks/undefined-object/index.js.map +1 -1
  51. package/dist/checks/unique-doc-param-names/index.js +1 -1
  52. package/dist/checks/unique-doc-param-names/index.js.map +1 -1
  53. package/dist/checks/unknown-filter/index.js +3 -3
  54. package/dist/checks/unknown-filter/index.js.map +1 -1
  55. package/dist/checks/unknown-property/index.js +1 -1
  56. package/dist/checks/unknown-property/index.js.map +1 -1
  57. package/dist/checks/unrecognized-render-partial-arguments/index.js +2 -2
  58. package/dist/checks/unrecognized-render-partial-arguments/index.js.map +1 -1
  59. package/dist/checks/unused-assign/index.js +1 -1
  60. package/dist/checks/unused-assign/index.js.map +1 -1
  61. package/dist/checks/unused-doc-param/index.js +1 -1
  62. package/dist/checks/unused-doc-param/index.js.map +1 -1
  63. package/dist/checks/utils.js +1 -1
  64. package/dist/checks/utils.js.map +1 -1
  65. package/dist/checks/valid-content-for-arguments/index.js +1 -1
  66. package/dist/checks/valid-content-for-arguments/index.js.map +1 -1
  67. package/dist/checks/valid-doc-param-types/index.js +4 -4
  68. package/dist/checks/valid-doc-param-types/index.js.map +1 -1
  69. package/dist/checks/valid-html-translation/index.d.ts +2 -2
  70. package/dist/checks/valid-html-translation/index.js +4 -4
  71. package/dist/checks/valid-html-translation/index.js.map +1 -1
  72. package/dist/checks/valid-json/index.js +1 -1
  73. package/dist/checks/valid-json/index.js.map +1 -1
  74. package/dist/checks/valid-render-partial-argument-types/index.js +2 -2
  75. package/dist/checks/valid-render-partial-argument-types/index.js.map +1 -1
  76. package/dist/checks/variable-name/index.js +1 -1
  77. package/dist/checks/variable-name/index.js.map +1 -1
  78. package/dist/context-utils.d.ts +2 -7
  79. package/dist/context-utils.js +39 -109
  80. package/dist/context-utils.js.map +1 -1
  81. package/dist/disabled-checks/index.js +4 -2
  82. package/dist/disabled-checks/index.js.map +1 -1
  83. package/dist/doc-generator/DocBlockGenerator.d.ts +16 -0
  84. package/dist/doc-generator/DocBlockGenerator.js +464 -0
  85. package/dist/doc-generator/DocBlockGenerator.js.map +1 -0
  86. package/dist/doc-generator/index.d.ts +1 -0
  87. package/dist/doc-generator/index.js +6 -0
  88. package/dist/doc-generator/index.js.map +1 -0
  89. package/dist/find-root.d.ts +7 -10
  90. package/dist/find-root.js +10 -17
  91. package/dist/find-root.js.map +1 -1
  92. package/dist/fixes/autofix.d.ts +4 -4
  93. package/dist/fixes/autofix.js +2 -2
  94. package/dist/fixes/autofix.js.map +1 -1
  95. package/dist/fixes/correctors/index.js +4 -0
  96. package/dist/fixes/correctors/index.js.map +1 -1
  97. package/dist/index.d.ts +4 -5
  98. package/dist/index.js +34 -17
  99. package/dist/index.js.map +1 -1
  100. package/dist/jsonc/parse.d.ts +1 -1
  101. package/dist/jsonc/parse.js +1 -1
  102. package/dist/liquid-doc/arguments.d.ts +7 -8
  103. package/dist/liquid-doc/arguments.js +20 -28
  104. package/dist/liquid-doc/arguments.js.map +1 -1
  105. package/dist/liquid-doc/liquidDoc.d.ts +1 -1
  106. package/dist/liquid-doc/liquidDoc.js.map +1 -1
  107. package/dist/liquid-doc/utils.d.ts +1 -1
  108. package/dist/liquid-doc/utils.js +4 -3
  109. package/dist/liquid-doc/utils.js.map +1 -1
  110. package/dist/path.d.ts +1 -0
  111. package/dist/path.js +5 -1
  112. package/dist/path.js.map +1 -1
  113. package/dist/test/MockApp.d.ts +16 -0
  114. package/dist/test/MockApp.js +16 -0
  115. package/dist/test/MockApp.js.map +1 -0
  116. package/dist/test/MockFileSystem.d.ts +3 -3
  117. package/dist/test/MockFileSystem.js +6 -6
  118. package/dist/test/MockFileSystem.js.map +1 -1
  119. package/dist/test/index.d.ts +1 -1
  120. package/dist/test/index.js +1 -1
  121. package/dist/test/index.js.map +1 -1
  122. package/dist/test/test-helper.d.ts +10 -9
  123. package/dist/test/test-helper.js +15 -106
  124. package/dist/test/test-helper.js.map +1 -1
  125. package/dist/to-schema.d.ts +1 -1
  126. package/dist/to-source-code.d.ts +3 -2
  127. package/dist/to-source-code.js +20 -0
  128. package/dist/to-source-code.js.map +1 -1
  129. package/dist/tsconfig.tsbuildinfo +1 -1
  130. package/dist/types/platformos-liquid-docs.d.ts +128 -0
  131. package/dist/types/platformos-liquid-docs.js +3 -0
  132. package/dist/types/platformos-liquid-docs.js.map +1 -0
  133. package/dist/types/schemas/index.d.ts +0 -2
  134. package/dist/types/schemas/index.js.map +1 -1
  135. package/dist/types.d.ts +18 -67
  136. package/dist/types.js +3 -5
  137. package/dist/types.js.map +1 -1
  138. package/dist/utils/block.js.map +1 -1
  139. package/dist/utils/index.d.ts +0 -1
  140. package/dist/utils/index.js +0 -1
  141. package/dist/utils/index.js.map +1 -1
  142. package/dist/yaml/parse.d.ts +5 -0
  143. package/dist/yaml/parse.js +94 -0
  144. package/dist/yaml/parse.js.map +1 -0
  145. package/package.json +4 -3
  146. package/src/{AugmentedThemeDocset.spec.ts → AugmentedPlatformOSDocset.spec.ts} +47 -34
  147. package/src/AugmentedPlatformOSDocset.ts +89 -0
  148. package/src/JSONValidator.ts +1 -1
  149. package/src/checks/deprecated-filter/index.spec.ts +76 -248
  150. package/src/checks/deprecated-filter/index.ts +5 -53
  151. package/src/checks/deprecated-tag/index.spec.ts +85 -34
  152. package/src/checks/deprecated-tag/index.ts +27 -22
  153. package/src/checks/duplicate-function-arguments/index.ts +1 -1
  154. package/src/checks/duplicate-render-partial-arguments/index.ts +1 -1
  155. package/src/checks/graphql/index.ts +1 -1
  156. package/src/checks/img-width-and-height/index.ts +1 -1
  157. package/src/checks/index.ts +11 -80
  158. package/src/checks/invalid-hash-assign-target/index.spec.ts +14 -14
  159. package/src/checks/json-syntax-error/index.ts +1 -1
  160. package/src/checks/liquid-html-syntax-error/checks/InvalidBooleanExpression.spec.ts +0 -11
  161. package/src/checks/liquid-html-syntax-error/checks/InvalidLoopArguments.spec.ts +1 -2
  162. package/src/checks/liquid-html-syntax-error/index.spec.ts +1 -6
  163. package/src/checks/liquid-html-syntax-error/index.ts +2 -2
  164. package/src/checks/matching-translations/index.spec.ts +89 -346
  165. package/src/checks/matching-translations/index.ts +24 -35
  166. package/src/checks/metadata-params/index.ts +5 -7
  167. package/src/checks/missing-asset/index.ts +1 -1
  168. package/src/checks/{missing-template → missing-partial}/index.spec.ts +6 -6
  169. package/src/checks/{missing-template → missing-partial}/index.ts +6 -20
  170. package/src/checks/orphaned-partial/index.ts +3 -3
  171. package/src/checks/parser-blocking-script/index.spec.ts +0 -118
  172. package/src/checks/parser-blocking-script/index.ts +3 -33
  173. package/src/checks/parser-blocking-script/suggestions.ts +1 -28
  174. package/src/checks/translation-key-exists/index.ts +1 -1
  175. package/src/checks/unclosed-html-element/index.ts +5 -1
  176. package/src/checks/undefined-object/index.spec.ts +3 -109
  177. package/src/checks/undefined-object/index.ts +8 -33
  178. package/src/checks/unique-doc-param-names/index.ts +1 -1
  179. package/src/checks/unknown-filter/index.spec.ts +2 -2
  180. package/src/checks/unknown-filter/index.ts +3 -3
  181. package/src/checks/unknown-property/index.ts +1 -1
  182. package/src/checks/unrecognized-render-partial-arguments/index.spec.ts +5 -5
  183. package/src/checks/unrecognized-render-partial-arguments/index.ts +2 -5
  184. package/src/checks/unused-assign/index.spec.ts +0 -30
  185. package/src/checks/unused-assign/index.ts +1 -1
  186. package/src/checks/unused-doc-param/index.ts +1 -1
  187. package/src/checks/utils.ts +1 -1
  188. package/src/checks/valid-doc-param-types/index.ts +4 -4
  189. package/src/checks/valid-html-translation/index.spec.ts +42 -32
  190. package/src/checks/valid-html-translation/index.ts +7 -7
  191. package/src/checks/valid-json/index.ts +1 -1
  192. package/src/checks/valid-render-partial-argument-types/index.ts +2 -5
  193. package/src/checks/variable-name/index.ts +1 -1
  194. package/src/context-utils.spec.ts +49 -77
  195. package/src/context-utils.ts +39 -128
  196. package/src/disabled-checks/index.spec.ts +35 -0
  197. package/src/disabled-checks/index.ts +4 -2
  198. package/src/find-root.ts +12 -22
  199. package/src/fixes/autofix.spec.ts +2 -2
  200. package/src/fixes/autofix.ts +4 -4
  201. package/src/fixes/correctors/index.ts +4 -0
  202. package/src/ignore.spec.ts +0 -1
  203. package/src/index.ts +33 -21
  204. package/src/jsonc/parse.ts +1 -1
  205. package/src/liquid-doc/arguments.spec.ts +19 -45
  206. package/src/liquid-doc/arguments.ts +26 -39
  207. package/src/liquid-doc/liquidDoc.ts +1 -2
  208. package/src/liquid-doc/utils.ts +4 -3
  209. package/src/path.ts +1 -0
  210. package/src/test/{MockTheme.ts → MockApp.ts} +1 -1
  211. package/src/test/MockFileSystem.ts +6 -6
  212. package/src/test/index.ts +1 -1
  213. package/src/test/test-helper.ts +29 -127
  214. package/src/to-source-code.ts +20 -1
  215. package/src/types/{theme-liquid-docs.ts → platformos-liquid-docs.ts} +8 -13
  216. package/src/types/schemas/index.ts +0 -2
  217. package/src/types.ts +21 -92
  218. package/src/utils/index.ts +0 -1
  219. package/src/yaml/parse.ts +111 -0
  220. package/src/AugmentedThemeDocset.ts +0 -137
  221. package/src/checks/app-block-missing-schema/index.spec.ts +0 -121
  222. package/src/checks/app-block-missing-schema/index.ts +0 -46
  223. package/src/checks/app-block-valid-tags/index.spec.ts +0 -96
  224. package/src/checks/app-block-valid-tags/index.ts +0 -54
  225. package/src/checks/asset-preload/index.spec.ts +0 -78
  226. package/src/checks/asset-preload/index.ts +0 -65
  227. package/src/checks/asset-size-app-block-css/index.spec.ts +0 -88
  228. package/src/checks/asset-size-app-block-css/index.ts +0 -78
  229. package/src/checks/asset-size-app-block-javascript/index.spec.ts +0 -66
  230. package/src/checks/asset-size-app-block-javascript/index.ts +0 -78
  231. package/src/checks/asset-size-css/index.spec.ts +0 -166
  232. package/src/checks/asset-size-css/index.ts +0 -160
  233. package/src/checks/asset-size-javascript/index.spec.ts +0 -184
  234. package/src/checks/asset-size-javascript/index.ts +0 -144
  235. package/src/checks/block-id-usage/index.spec.ts +0 -76
  236. package/src/checks/block-id-usage/index.ts +0 -72
  237. package/src/checks/cdn-preconnect/index.spec.ts +0 -40
  238. package/src/checks/cdn-preconnect/index.ts +0 -43
  239. package/src/checks/content-for-header-modification/index.spec.ts +0 -65
  240. package/src/checks/content-for-header-modification/index.ts +0 -72
  241. package/src/checks/deprecate-bgsizes/index.spec.ts +0 -41
  242. package/src/checks/deprecate-bgsizes/index.ts +0 -49
  243. package/src/checks/deprecate-lazysizes/index.spec.ts +0 -26
  244. package/src/checks/deprecate-lazysizes/index.ts +0 -58
  245. package/src/checks/deprecated-filter/fixes.ts +0 -264
  246. package/src/checks/deprecated-fonts-on-sections-and-blocks/deprecated-fonts-data.ts +0 -1343
  247. package/src/checks/deprecated-fonts-on-sections-and-blocks/index.spec.ts +0 -613
  248. package/src/checks/deprecated-fonts-on-sections-and-blocks/index.ts +0 -284
  249. package/src/checks/deprecated-fonts-on-settings-schema/index.spec.ts +0 -102
  250. package/src/checks/deprecated-fonts-on-settings-schema/index.ts +0 -66
  251. package/src/checks/duplicate-content-for-arguments/index.spec.ts +0 -98
  252. package/src/checks/duplicate-content-for-arguments/index.ts +0 -43
  253. package/src/checks/empty-block-content/index.spec.ts +0 -117
  254. package/src/checks/empty-block-content/index.ts +0 -60
  255. package/src/checks/hardcoded-routes/index.spec.ts +0 -58
  256. package/src/checks/hardcoded-routes/index.ts +0 -100
  257. package/src/checks/json-missing-block/index.spec.ts +0 -435
  258. package/src/checks/json-missing-block/index.ts +0 -56
  259. package/src/checks/json-missing-block/missing-block-utils.ts +0 -147
  260. package/src/checks/liquid-free-settings/index.spec.ts +0 -180
  261. package/src/checks/liquid-free-settings/index.ts +0 -79
  262. package/src/checks/missing-content-for-arguments/index.spec.ts +0 -144
  263. package/src/checks/missing-content-for-arguments/index.ts +0 -46
  264. package/src/checks/pagination-size/index.spec.ts +0 -158
  265. package/src/checks/pagination-size/index.ts +0 -104
  266. package/src/checks/remote-asset/index.spec.ts +0 -280
  267. package/src/checks/remote-asset/index.ts +0 -238
  268. package/src/checks/reserved-doc-param-names/index.spec.ts +0 -62
  269. package/src/checks/reserved-doc-param-names/index.ts +0 -57
  270. package/src/checks/schema-presets-block-order/index.spec.ts +0 -344
  271. package/src/checks/schema-presets-block-order/index.ts +0 -154
  272. package/src/checks/schema-presets-static-blocks/index.spec.ts +0 -145
  273. package/src/checks/schema-presets-static-blocks/index.ts +0 -126
  274. package/src/checks/static-stylesheet-and-javascript-tags/index.spec.ts +0 -257
  275. package/src/checks/static-stylesheet-and-javascript-tags/index.ts +0 -48
  276. package/src/checks/unique-settings-id/index.spec.ts +0 -24
  277. package/src/checks/unique-settings-id/index.ts +0 -84
  278. package/src/checks/unique-settings-id/test-data.ts +0 -1191
  279. package/src/checks/unique-static-block-id/index.spec.ts +0 -55
  280. package/src/checks/unique-static-block-id/index.ts +0 -60
  281. package/src/checks/unrecognized-content-for-arguments/index.spec.ts +0 -145
  282. package/src/checks/unrecognized-content-for-arguments/index.ts +0 -55
  283. package/src/checks/valid-block-target/index.spec.ts +0 -1396
  284. package/src/checks/valid-block-target/index.ts +0 -142
  285. package/src/checks/valid-content-for-argument-types/index.spec.ts +0 -382
  286. package/src/checks/valid-content-for-argument-types/index.ts +0 -42
  287. package/src/checks/valid-content-for-arguments/index.spec.ts +0 -107
  288. package/src/checks/valid-content-for-arguments/index.ts +0 -98
  289. package/src/checks/valid-local-blocks/index.spec.ts +0 -286
  290. package/src/checks/valid-local-blocks/index.ts +0 -100
  291. package/src/checks/valid-local-blocks/valid-block-utils.ts +0 -97
  292. package/src/checks/valid-schema/index.spec.ts +0 -174
  293. package/src/checks/valid-schema/index.ts +0 -41
  294. package/src/checks/valid-schema-name/index.spec.ts +0 -112
  295. package/src/checks/valid-schema-name/index.ts +0 -75
  296. package/src/checks/valid-settings-key/index.spec.ts +0 -321
  297. package/src/checks/valid-settings-key/index.ts +0 -144
  298. package/src/checks/valid-static-block-type/index.spec.ts +0 -38
  299. package/src/checks/valid-static-block-type/index.ts +0 -58
  300. package/src/checks/valid-visible-if/index.spec.ts +0 -619
  301. package/src/checks/valid-visible-if/index.ts +0 -184
  302. package/src/checks/valid-visible-if/visible-if-utils.ts +0 -158
  303. package/src/tags/content-for.ts +0 -25
  304. package/src/to-schema.ts +0 -231
  305. package/src/types/schemas/section.ts +0 -86
  306. package/src/types/schemas/theme-block.ts +0 -34
  307. package/src/types/theme-schemas.ts +0 -80
  308. package/src/utils/block.ts +0 -300
  309. package/src/utils/markup.ts +0 -10
@@ -1,104 +0,0 @@
1
- import {
2
- LiquidRawTag,
3
- LiquidTag,
4
- LiquidVariableLookup,
5
- NodeTypes,
6
- } from '@platformos/liquid-html-parser';
7
- import { LiquidCheckDefinition, SchemaProp, Severity, SourceCodeType } from '../../types';
8
- import { isError, last } from '../../utils';
9
- import { isNodeOfType } from '../utils';
10
- import { parseJSON } from '../../json';
11
-
12
- const schema = {
13
- minSize: SchemaProp.number(1),
14
- maxSize: SchemaProp.number(250),
15
- };
16
-
17
- export const PaginationSize: LiquidCheckDefinition<typeof schema> = {
18
- meta: {
19
- code: 'PaginationSize',
20
- name: 'Ensure paginate tags are used with performant sizes',
21
- docs: {
22
- description: 'This check is aimed at keeping response times low.',
23
- url: 'https://shopify.dev/docs/storefronts/themes/tools/theme-check/checks/pagination-size',
24
- recommended: true,
25
- },
26
- type: SourceCodeType.LiquidHtml,
27
- severity: Severity.WARNING,
28
- schema,
29
- targets: [],
30
- },
31
-
32
- create(context) {
33
- const minSize = context.settings.minSize;
34
- const maxSize = context.settings.maxSize;
35
- let schemaSettings: any[] = [];
36
- const pageSizeLookups: LiquidVariableLookup[] = [];
37
-
38
- function checkPageSize(
39
- pageSizeNode: any,
40
- value: number,
41
- message: string = `Pagination size must be a positive integer between ${minSize} and ${maxSize}.`,
42
- ) {
43
- if (minSize <= value && value <= maxSize) return;
44
- context.report({
45
- message,
46
- startIndex: pageSizeNode.position.start,
47
- endIndex: pageSizeNode.position.end,
48
- });
49
- }
50
-
51
- return {
52
- async LiquidTag(node: LiquidTag) {
53
- if (typeof node.markup === 'string' || node.name !== 'paginate') return;
54
-
55
- const pageSizeNode = node.markup.pageSize;
56
-
57
- if (isNodeOfType(NodeTypes.VariableLookup, pageSizeNode)) {
58
- pageSizeLookups.push(pageSizeNode);
59
- } else if (isNodeOfType(NodeTypes.Number, pageSizeNode)) {
60
- checkPageSize(pageSizeNode, Number(pageSizeNode.value));
61
- }
62
- },
63
-
64
- async LiquidRawTag(node: LiquidRawTag) {
65
- if (node.name === 'schema') {
66
- const schema = parseJSON(node.body.value);
67
- if (isError(schema)) return;
68
- if (schema.settings && Array.isArray(schema.settings)) {
69
- schemaSettings = schema.settings;
70
- }
71
- }
72
- },
73
-
74
- async onCodePathEnd() {
75
- pageSizeLookups.forEach((pageSizeVariableLookup) => {
76
- // Kind of assumes that you're using settings of some sort.
77
- const lastLookup = last(pageSizeVariableLookup.lookups);
78
- if (lastLookup === undefined) return;
79
- if (lastLookup.type !== NodeTypes.String) return;
80
-
81
- const settingId = lastLookup.value;
82
- const setting = schemaSettings.find((setting) => setting.id === settingId);
83
-
84
- if (setting === undefined) return;
85
-
86
- if (setting.default === undefined) {
87
- context.report({
88
- message: `Default pagination size should be defined in the section settings.`,
89
- startIndex: pageSizeVariableLookup.position.start,
90
- endIndex: pageSizeVariableLookup.position.end,
91
- });
92
- return;
93
- }
94
-
95
- checkPageSize(
96
- pageSizeVariableLookup,
97
- setting.default,
98
- `This setting's default value should be between ${minSize} and ${maxSize} but is currently ${setting.default}.`,
99
- );
100
- });
101
- },
102
- };
103
- },
104
- };
@@ -1,280 +0,0 @@
1
- import { describe, it, expect } from 'vitest';
2
- import { runLiquidCheck, highlightedOffenses, check, MockTheme } from '../../test';
3
- import { RemoteAsset } from './index';
4
-
5
- describe('Module: RemoteAsset', () => {
6
- it('should report an offense when asset_url or img_url filters are not used', async () => {
7
- const sourceCode = `
8
- <img src="{{ 'image.png' }}" />
9
- <link href="{{ 'style.css' }}" />
10
- <script src="{{ 'script.js' }}" defer="defer"></script>
11
- {{ url | img_tag }}
12
- `;
13
-
14
- const offenses = await runLiquidCheck(RemoteAsset, sourceCode);
15
- expect(offenses).to.have.length(4);
16
-
17
- const highlights = highlightedOffenses({ 'file.liquid': sourceCode }, offenses);
18
- expect(highlights).to.eql([
19
- 'src="{{ \'image.png\' }}"',
20
- 'href="{{ \'style.css\' }}"',
21
- 'src="{{ \'script.js\' }}"',
22
- 'url | img_tag',
23
- ]);
24
- });
25
-
26
- it('should not report an offense when asset_url or img_url filters are used', async () => {
27
- const sourceCode = `
28
- <img src="{{ 'image.png' | asset_url }}" />
29
- <link href="{{ 'style.css' | img_url }}" />
30
- <script src="{{ 'script.js' | img_url }}" defer="defer"></script>
31
- {{ url | img_tag | asset_url }},
32
- {{ 'bootstrap.min.css' | asset_url | stylesheet_tag }}
33
- {{ product | image_url: width: 450 | img_tag }}
34
- `;
35
-
36
- const offenses = await runLiquidCheck(RemoteAsset, sourceCode);
37
- expect(offenses).to.be.empty;
38
- const highlights = highlightedOffenses({ 'file.liquid': sourceCode }, offenses);
39
- expect(highlights).to.be.empty;
40
- });
41
-
42
- it('should not report an offense for links, videos, iframes', async () => {
43
- const sourceCode = `
44
- <iframe
45
- id="inlineFrameExample"
46
- title="Inline Frame Example"
47
- width="300"
48
- height="200"
49
- src="https://www.openstreetmap.org/export/embed.html?bbox=-0.004017949104309083%2C51.47612752641776%2C0.00030577182769775396%2C51.478569861898606&layer=mapnik"
50
- >
51
- </iframe>
52
- <a href="https://google.com"></a>
53
- <embed type="video/webm" src="https://google.com/..." width="250" height="200">
54
- `;
55
-
56
- const offenses = await runLiquidCheck(RemoteAsset, sourceCode);
57
- expect(offenses).to.be.empty;
58
- });
59
-
60
- it('should not report an offense when asset_url or img_url filters are used with other filters', async () => {
61
- const sourceCode = `
62
- <img src="{{ 'image.png' | asset_url | img_url }}" />
63
- <link href="{{ 'style.css' | img_url | asset_url }}" />
64
- <script src="{{ 'script.js' | img_url | asset_url}}" defer="defer"></script>
65
- `;
66
-
67
- const offenses = await runLiquidCheck(RemoteAsset, sourceCode);
68
- expect(offenses).to.be.empty;
69
- const highlights = highlightedOffenses({ 'file.liquid': sourceCode }, offenses);
70
- expect(highlights).to.be.empty;
71
- });
72
-
73
- it('should not report an offense for non-asset elements', async () => {
74
- const sourceCode = `
75
- <div class="{{ 'my-class' }}"></div>
76
- <a href="{{ 'page.html' }}">Link</a>
77
- `;
78
-
79
- const offenses = await runLiquidCheck(RemoteAsset, sourceCode);
80
- expect(offenses).to.be.empty;
81
- const highlights = highlightedOffenses({ 'file.liquid': sourceCode }, offenses);
82
- expect(highlights).to.be.empty;
83
- });
84
-
85
- it('should not report an offense when asset_url filters are used with deprecated assets', async () => {
86
- const sourceCode = `
87
- {{ product.featured_image | product_img_url }}
88
- {{ article.image | article_img_url }}
89
- {{ collection.image | collection_img_url }}
90
- {{ 'image.png' | img_url }}
91
- `;
92
-
93
- const offenses = await runLiquidCheck(RemoteAsset, sourceCode);
94
- expect(offenses).to.be.empty;
95
- const highlights = highlightedOffenses({ 'file.liquid': sourceCode }, offenses);
96
- expect(highlights).to.be.empty;
97
- });
98
-
99
- it('should not report an offense when asset_url filters are used with non deprecated assets', async () => {
100
- const sourceCode = `
101
- {{ 'option_selection.js' | shopify_asset_url }}
102
- {{ 'lightbox.js' | global_asset_url | script_tag }}
103
- {{ 'disclaimer.pdf' | file_url }}
104
- {{ 'potions-header.png' | file_img_url: 'large' }}
105
- {{ 'cart.js' | asset_url }}
106
- {{ 'red-and-black-bramble-berries.jpg' | asset_img_url }}
107
- `;
108
-
109
- const offenses = await runLiquidCheck(RemoteAsset, sourceCode);
110
- expect(offenses).to.be.empty;
111
- const highlights = highlightedOffenses({ 'file.liquid': sourceCode }, offenses);
112
- expect(highlights).to.be.empty;
113
- });
114
-
115
- it('should report the correct message and index for a single offense', async () => {
116
- const sourceCode = `<img src="{{ 'image.png' }}" />`;
117
-
118
- const offenses = await runLiquidCheck(RemoteAsset, sourceCode);
119
- expect(offenses).to.have.length(1);
120
-
121
- const offense = offenses[0];
122
- expect(offense.message).to.equal(
123
- 'Use one of the asset_url filters to serve assets for better performance.',
124
- );
125
- expect(offense.start.index).to.equal(5);
126
- expect(offense.end.index).to.equal(28);
127
-
128
- const highlights = highlightedOffenses({ 'file.liquid': sourceCode }, offenses);
129
- expect(highlights).to.eql(['src="{{ \'image.png\' }}"']);
130
- });
131
-
132
- it('should not report an offense when asset_url filters are used with known assets', async () => {
133
- const sourceCode = `<link rel="canonical" href="{{ canonical_url }}">
134
- <link href={{ canonical_url }}
135
- {{ 'example.js' | canonical_url }}`;
136
-
137
- const offenses = await runLiquidCheck(RemoteAsset, sourceCode);
138
- expect(offenses).to.be.empty;
139
- const highlights = highlightedOffenses({ 'file.liquid': sourceCode }, offenses);
140
- expect(highlights).to.be.empty;
141
- });
142
-
143
- it('should report an offense for scripts from remote domains', async () => {
144
- const sourceCode = `<script src="https://example.com/jquery.js" defer></script>`;
145
-
146
- const offenses = await runLiquidCheck(RemoteAsset, sourceCode);
147
- expect(offenses).to.have.length(1);
148
- expect(offenses[0].message).to.equal(
149
- 'Asset should be served by the Shopify CDN for better performance.',
150
- );
151
- });
152
-
153
- it('should report an offense for remote stylesheets', async () => {
154
- const sourceCode = `
155
- <link href="https://example.com/bootstrap.css" rel="stylesheet">
156
- <link href="{{ "https://example.com/bootstrap.css" | replace: 'bootstrap', 'tailwind' }}" rel="stylesheet">
157
- <link href=“https://thisisbad.com/styles.css“>
158
- `;
159
-
160
- const offenses = await runLiquidCheck(RemoteAsset, sourceCode);
161
-
162
- expect(offenses).to.have.length(3);
163
- offenses.forEach((offense) => {
164
- expect(offense.message).to.equal(
165
- 'Asset should be served by the Shopify CDN for better performance.',
166
- );
167
- });
168
- });
169
-
170
- it('should report an offense when a non shopify cdn is used as a liquid filter and when a asset_url filter is not used.', async () => {
171
- const sourceCode = `
172
- {{ "https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" | stylesheet_tag }}
173
- `;
174
- const offenses = await runLiquidCheck(RemoteAsset, sourceCode);
175
- expect(offenses).to.have.length(2);
176
-
177
- expect(offenses[0].message).to.equal(
178
- 'Asset should be served by the Shopify CDN for better performance.',
179
- );
180
-
181
- expect(offenses[1].message).to.equal(
182
- 'Use one of the asset_url filters to serve assets for better performance.',
183
- );
184
- });
185
-
186
- it('should report an offense for image drops without img_url filter', async () => {
187
- const sourceCode = `
188
- <img src="{{ image }}">
189
- <img src="{{ image.src }}">
190
- `;
191
-
192
- const offenses = await runLiquidCheck(RemoteAsset, sourceCode);
193
- expect(offenses).to.have.length(2);
194
- offenses.forEach((offense) => {
195
- expect(offense.message).to.equal(
196
- 'Use one of the asset_url filters to serve assets for better performance.',
197
- );
198
- });
199
- });
200
-
201
- it('should not report an offence if url is a shopify CDN', async () => {
202
- const sourceCode = `
203
- <link rel="preconnect" href="https://fonts.shopifycdn.com" crossorigin>
204
- <link id="ModelViewerStyle" rel="stylesheet" href="https://cdn.shopify.com/shopifycloud/model-viewer-ui/assets/v1.0/model-viewer-ui.css" media="print" onload="this.media='all'">
205
- <script id="hot-reload-client" src="/cdn/shopifycloud/theme-hot-reload/theme-hot-reload.js" defer></script>
206
- `;
207
-
208
- const offenses = await runLiquidCheck(RemoteAsset, sourceCode);
209
- expect(offenses).to.be.empty;
210
- const highlights = highlightedOffenses({ 'file.liquid': sourceCode }, offenses);
211
- expect(highlights).to.be.empty;
212
- });
213
-
214
- it('should report an offense if url is not listed in allowedDomains', async () => {
215
- const themeFiles: MockTheme = {
216
- 'layout/theme.liquid': `
217
- <script src="https://domain.com" defer></script>
218
- `,
219
- };
220
-
221
- const offenses = await check(
222
- themeFiles,
223
- [RemoteAsset],
224
- {},
225
- {
226
- RemoteAsset: {
227
- enabled: true,
228
- allowedDomains: ['someotherdomain.com'],
229
- },
230
- },
231
- );
232
-
233
- expect(offenses).to.have.length(1);
234
- });
235
-
236
- it('should report an offense if the url in the config is malformed/missing protocol', async () => {
237
- const themeFiles: MockTheme = {
238
- 'layout/theme.liquid': `
239
- <script src="https://domain.com" defer></script>
240
- <script src="https://www.domain.com" defer></script>
241
- `,
242
- };
243
-
244
- const offenses = await check(
245
- themeFiles,
246
- [RemoteAsset],
247
- {},
248
- {
249
- RemoteAsset: {
250
- enabled: true,
251
- allowedDomains: ['www.domain.com', 'domain.com'],
252
- },
253
- },
254
- );
255
-
256
- expect(offenses).to.have.length(2);
257
- });
258
-
259
- it('should not report an offense if url is listed in allowedDomains', async () => {
260
- const themeFiles: MockTheme = {
261
- 'layout/theme.liquid': `
262
- <script src="https://domain.com" defer></script>
263
- `,
264
- };
265
-
266
- const offenses = await check(
267
- themeFiles,
268
- [RemoteAsset],
269
- {},
270
- {
271
- RemoteAsset: {
272
- enabled: true,
273
- allowedDomains: ['https://domain.com', 'http://domain.com', 'https://www.domain.com'],
274
- },
275
- },
276
- );
277
-
278
- expect(offenses).to.be.empty;
279
- });
280
- });
@@ -1,238 +0,0 @@
1
- import {
2
- HtmlRawNode,
3
- HtmlVoidElement,
4
- TextNode,
5
- LiquidVariable,
6
- LiquidVariableOutput,
7
- } from '@platformos/liquid-html-parser';
8
- import {
9
- LiquidHtmlNodeTypes as NodeTypes,
10
- LiquidHtmlNodeOfType as NodeOfType,
11
- Severity,
12
- SourceCodeType,
13
- LiquidCheckDefinition,
14
- LiquidHtmlNode,
15
- SchemaProp,
16
- } from '../../types';
17
- import { isAttr, isValuedHtmlAttribute, isNodeOfType, ValuedHtmlAttribute } from '../utils';
18
- import { last } from '../../utils';
19
-
20
- const RESOURCE_TAGS = ['img', 'link', 'source', 'script'];
21
- const SHOPIFY_CDN_DOMAINS = ['fonts.shopifycdn.com', 'cdn.shopify.com'];
22
- const TAGNAMES = ['stylesheet_tag', 'script_tag', 'image_tag', 'img_tag'];
23
- const DEPRECATED = ['product_img_url', 'article_img_url', 'collection_img_url', 'img_url'];
24
-
25
- const NON_DEPRECATED = [
26
- 'asset_url',
27
- 'image_url',
28
- 'asset_img_url',
29
- 'file_img_url',
30
- 'file_url',
31
- 'global_asset_url',
32
- 'shopify_asset_url',
33
- 'external_video_url',
34
- 'font_url',
35
- ];
36
- const LIQUID_OBJECT = 'canonical_url';
37
-
38
- function isLiquidVariableOutput(node: LiquidHtmlNode): node is LiquidVariableOutput {
39
- return node.type === NodeTypes.LiquidVariableOutput;
40
- }
41
-
42
- function isLiquidVariable(node: LiquidHtmlNode | string): node is LiquidVariable {
43
- return typeof node !== 'string' && node.type === NodeTypes.LiquidVariable;
44
- }
45
-
46
- function isUrlHostedbyShopify(url: string, allowedDomains: string[] = []): boolean {
47
- if (/^\/cdn\//.test(url)) {
48
- return true;
49
- }
50
- try {
51
- const urlObj = new URL(url);
52
- return [...SHOPIFY_CDN_DOMAINS, ...allowedDomains].includes(urlObj.hostname);
53
- } catch (_error) {
54
- // Return false for any invalid URLs (missing protocol, malformed URLs, invalid characters etc.)
55
- // Since we're validating if URLs are Shopify-hosted, any invalid URL should return false
56
- return false;
57
- }
58
- }
59
-
60
- function valueIsDefinitelyNotShopifyHosted(
61
- attr: ValuedHtmlAttribute,
62
- allowedDomains: string[] = [],
63
- ): boolean {
64
- return attr.value.some((node) => {
65
- if (node.type === NodeTypes.TextNode && /^(https?:)?\/\//.test(node.value)) {
66
- if (!isUrlHostedbyShopify(node.value, allowedDomains)) {
67
- return true;
68
- }
69
- }
70
-
71
- if (isLiquidVariableOutput(node)) {
72
- const variable = node.markup;
73
- if (isLiquidVariable(variable)) {
74
- const expression = variable.expression;
75
- if (expression.type === NodeTypes.String && /^https?:\/\//.test(expression.value)) {
76
- if (!isUrlHostedbyShopify(expression.value, allowedDomains)) {
77
- return true;
78
- }
79
- }
80
- }
81
- }
82
- return false;
83
- });
84
- }
85
-
86
- function valueIsShopifyHosted(attr: ValuedHtmlAttribute): boolean {
87
- const ASSET_URL_FILTER_NAMES = [...DEPRECATED, ...NON_DEPRECATED];
88
- const ASSET_URL_OBJECT_NAMES = [LIQUID_OBJECT];
89
-
90
- return attr.value.some((node) => {
91
- if (!isLiquidVariableOutput(node)) return false;
92
- if (!isLiquidVariable(node.markup)) return false;
93
-
94
- const includesFilter = node.markup.filters.some((filter) =>
95
- ASSET_URL_FILTER_NAMES.includes(filter.name),
96
- );
97
- if (includesFilter) return true;
98
-
99
- if (isNodeOfType(NodeTypes.VariableLookup, node.markup.expression)) {
100
- if (
101
- node.markup.expression.name
102
- ? ASSET_URL_OBJECT_NAMES.includes(node.markup.expression.name)
103
- : false
104
- )
105
- return true;
106
- }
107
-
108
- return false;
109
- });
110
- }
111
-
112
- // Takes a list of allowed domains, and normalises them into an expected domain: www.domain.com -> domain.com for equality checks.
113
- function normaliseAllowedDomains(allowedDomains: string[]): string[] {
114
- return allowedDomains
115
- .map((domain) => {
116
- try {
117
- const url = new URL(domain);
118
- // Hostname can still return www. from https://www.domain.com we want it to be https://www.domain.com -> domain.com
119
- return url.hostname.replace(/^www\./, '');
120
- } catch (_error) {
121
- // we shouldn't return the malformed domain - should be strict and stick to web standards (new URL validation).
122
- return undefined;
123
- }
124
- })
125
- .filter((domain): domain is string => domain !== undefined);
126
- }
127
-
128
- const schema = {
129
- allowedDomains: SchemaProp.array(SchemaProp.string()).optional(),
130
- };
131
-
132
- export const RemoteAsset: LiquidCheckDefinition<typeof schema> = {
133
- meta: {
134
- code: 'RemoteAsset',
135
- aliases: ['AssetUrlFilters'],
136
- name: 'Remote Asset',
137
- docs: {
138
- description: 'This check is aimed at eliminating unnecessary HTTP connections.',
139
- url: 'https://shopify.dev/docs/storefronts/themes/tools/theme-check/checks/remote-asset',
140
- recommended: true,
141
- },
142
- type: SourceCodeType.LiquidHtml,
143
- severity: Severity.WARNING,
144
- schema,
145
- targets: [],
146
- },
147
-
148
- create(context) {
149
- const allowedDomains = normaliseAllowedDomains(context.settings.allowedDomains || []);
150
-
151
- function checkHtmlNode(node: HtmlVoidElement | HtmlRawNode) {
152
- if (!RESOURCE_TAGS.includes(node.name)) return;
153
-
154
- const urlAttribute: ValuedHtmlAttribute | undefined = node.attributes
155
- .filter(isValuedHtmlAttribute)
156
- .find((attr: ValuedHtmlAttribute) => isAttr(attr, 'src') || isAttr(attr, 'href'));
157
-
158
- if (!urlAttribute) return;
159
-
160
- const isShopifyUrl = urlAttribute.value
161
- .filter((node): node is TextNode => node.type === NodeTypes.TextNode)
162
- .some((textNode) => isUrlHostedbyShopify(textNode.value, allowedDomains));
163
-
164
- if (isShopifyUrl) return;
165
-
166
- const hasDefinitelyARemoteAssetUrl = valueIsDefinitelyNotShopifyHosted(
167
- urlAttribute,
168
- allowedDomains,
169
- );
170
- if (hasDefinitelyARemoteAssetUrl) {
171
- context.report({
172
- message: 'Asset should be served by the Shopify CDN for better performance.',
173
- startIndex: urlAttribute.position.start,
174
- endIndex: urlAttribute.position.end,
175
- });
176
- return;
177
- }
178
-
179
- const hasShopifyHostedValue = valueIsShopifyHosted(urlAttribute);
180
- if (hasShopifyHostedValue) return;
181
-
182
- context.report({
183
- message: 'Use one of the asset_url filters to serve assets for better performance.',
184
- startIndex: urlAttribute.position.start,
185
- endIndex: urlAttribute.position.end,
186
- });
187
- }
188
-
189
- function checkLiquidFilter(
190
- node: NodeOfType<NodeTypes.LiquidFilter>,
191
- ancestors: LiquidHtmlNode[],
192
- ) {
193
- const tagName = node.name;
194
-
195
- if (!TAGNAMES.includes(tagName)) return;
196
-
197
- const parentNode = last(ancestors);
198
- if (!parentNode || !isNodeOfType(NodeTypes.LiquidVariable, parentNode)) return;
199
-
200
- const hasAsset = parentNode.filters.some(
201
- (filter: { name: string }) =>
202
- DEPRECATED.includes(filter.name) || NON_DEPRECATED.includes(filter.name),
203
- );
204
-
205
- if (hasAsset) return;
206
-
207
- const urlNode = parentNode.expression;
208
- if (
209
- urlNode.type === NodeTypes.String &&
210
- !isUrlHostedbyShopify(urlNode.value, allowedDomains)
211
- ) {
212
- context.report({
213
- message: 'Asset should be served by the Shopify CDN for better performance.',
214
- startIndex: urlNode.position.start,
215
- endIndex: urlNode.position.end,
216
- });
217
- }
218
-
219
- context.report({
220
- message: `Use one of the asset_url filters to serve assets for better performance.`,
221
- startIndex: parentNode.expression.position.start,
222
- endIndex: node.position.end,
223
- });
224
- }
225
-
226
- return {
227
- async HtmlVoidElement(node) {
228
- checkHtmlNode(node);
229
- },
230
- async HtmlRawNode(node) {
231
- checkHtmlNode(node);
232
- },
233
- async LiquidFilter(node, ancestors) {
234
- checkLiquidFilter(node, ancestors);
235
- },
236
- };
237
- },
238
- };
@@ -1,62 +0,0 @@
1
- import { expect, describe, it } from 'vitest';
2
- import { ReservedDocParamNames } from './index';
3
- import { runLiquidCheck } from '../../test';
4
-
5
- describe('Module: ReservedDocParamNames', () => {
6
- describe('block file', () => {
7
- it(`should not report an error when no doc params share names with reserved content_for tag params`, async () => {
8
- const sourceCode = `
9
- {% doc %}
10
- @param param1 - Example param
11
- {% enddoc %}
12
- `;
13
-
14
- const offenses = await runLiquidCheck(
15
- ReservedDocParamNames,
16
- sourceCode,
17
- 'blocks/file.liquid',
18
- );
19
-
20
- expect(offenses).to.be.empty;
21
- });
22
-
23
- it('should report an error when a doc param shares names with reserved content_for tag params', async () => {
24
- const sourceCode = `
25
- {% doc %}
26
- @param param1 - Example param
27
- @param id - Example param
28
- {% enddoc %}
29
- `;
30
-
31
- const offenses = await runLiquidCheck(
32
- ReservedDocParamNames,
33
- sourceCode,
34
- 'blocks/file.liquid',
35
- );
36
-
37
- expect(offenses).to.have.length(1);
38
- expect(offenses[0].message).to.contain(
39
- `The parameter name is not supported because it's a reserved argument for 'content_for' tags.`,
40
- );
41
- });
42
- });
43
-
44
- describe('partial file', () => {
45
- it('should not report an error when a doc param shares names with reserved content_for tag params', async () => {
46
- const sourceCode = `
47
- {% doc %}
48
- @param param1 - Example param
49
- @param id - Example param
50
- {% enddoc %}
51
- `;
52
-
53
- const offenses = await runLiquidCheck(
54
- ReservedDocParamNames,
55
- sourceCode,
56
- 'app/views/partials/file.liquid',
57
- );
58
-
59
- expect(offenses).to.have.length(0);
60
- });
61
- });
62
- });