@platformos/platformos-check-common 0.0.6 → 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 (304) hide show
  1. package/CHANGELOG.md +16 -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/find-root.d.ts +7 -10
  84. package/dist/find-root.js +10 -17
  85. package/dist/find-root.js.map +1 -1
  86. package/dist/fixes/autofix.d.ts +4 -4
  87. package/dist/fixes/autofix.js +2 -2
  88. package/dist/fixes/autofix.js.map +1 -1
  89. package/dist/fixes/correctors/index.js +4 -0
  90. package/dist/fixes/correctors/index.js.map +1 -1
  91. package/dist/index.d.ts +4 -5
  92. package/dist/index.js +34 -17
  93. package/dist/index.js.map +1 -1
  94. package/dist/jsonc/parse.d.ts +1 -1
  95. package/dist/jsonc/parse.js +1 -1
  96. package/dist/liquid-doc/arguments.d.ts +7 -8
  97. package/dist/liquid-doc/arguments.js +20 -28
  98. package/dist/liquid-doc/arguments.js.map +1 -1
  99. package/dist/liquid-doc/liquidDoc.d.ts +1 -1
  100. package/dist/liquid-doc/liquidDoc.js.map +1 -1
  101. package/dist/liquid-doc/utils.d.ts +1 -1
  102. package/dist/liquid-doc/utils.js +4 -3
  103. package/dist/liquid-doc/utils.js.map +1 -1
  104. package/dist/path.d.ts +1 -0
  105. package/dist/path.js +5 -1
  106. package/dist/path.js.map +1 -1
  107. package/dist/test/MockApp.d.ts +16 -0
  108. package/dist/test/MockApp.js +16 -0
  109. package/dist/test/MockApp.js.map +1 -0
  110. package/dist/test/MockFileSystem.d.ts +3 -3
  111. package/dist/test/MockFileSystem.js +6 -6
  112. package/dist/test/MockFileSystem.js.map +1 -1
  113. package/dist/test/index.d.ts +1 -1
  114. package/dist/test/index.js +1 -1
  115. package/dist/test/index.js.map +1 -1
  116. package/dist/test/test-helper.d.ts +10 -9
  117. package/dist/test/test-helper.js +15 -106
  118. package/dist/test/test-helper.js.map +1 -1
  119. package/dist/to-source-code.d.ts +4 -3
  120. package/dist/to-source-code.js +20 -0
  121. package/dist/to-source-code.js.map +1 -1
  122. package/dist/tsconfig.tsbuildinfo +1 -1
  123. package/dist/types/platformos-liquid-docs.d.ts +128 -0
  124. package/dist/types/platformos-liquid-docs.js +3 -0
  125. package/dist/types/platformos-liquid-docs.js.map +1 -0
  126. package/dist/types/schemas/index.d.ts +0 -2
  127. package/dist/types/schemas/index.js.map +1 -1
  128. package/dist/types.d.ts +18 -67
  129. package/dist/types.js +3 -5
  130. package/dist/types.js.map +1 -1
  131. package/dist/utils/file-utils.js +1 -2
  132. package/dist/utils/file-utils.js.map +1 -1
  133. package/dist/utils/index.d.ts +0 -1
  134. package/dist/utils/index.js +0 -1
  135. package/dist/utils/index.js.map +1 -1
  136. package/dist/yaml/parse.d.ts +5 -0
  137. package/dist/yaml/parse.js +94 -0
  138. package/dist/yaml/parse.js.map +1 -0
  139. package/package.json +9 -9
  140. package/src/{AugmentedThemeDocset.spec.ts → AugmentedPlatformOSDocset.spec.ts} +47 -34
  141. package/src/AugmentedPlatformOSDocset.ts +89 -0
  142. package/src/JSONValidator.ts +1 -1
  143. package/src/checks/deprecated-filter/index.spec.ts +76 -248
  144. package/src/checks/deprecated-filter/index.ts +5 -53
  145. package/src/checks/deprecated-tag/index.spec.ts +85 -34
  146. package/src/checks/deprecated-tag/index.ts +27 -22
  147. package/src/checks/duplicate-function-arguments/index.ts +1 -1
  148. package/src/checks/duplicate-render-partial-arguments/index.ts +1 -1
  149. package/src/checks/graphql/index.ts +1 -1
  150. package/src/checks/img-width-and-height/index.ts +1 -1
  151. package/src/checks/index.ts +11 -80
  152. package/src/checks/invalid-hash-assign-target/index.spec.ts +14 -14
  153. package/src/checks/json-syntax-error/index.ts +1 -1
  154. package/src/checks/liquid-html-syntax-error/checks/InvalidBooleanExpression.spec.ts +0 -11
  155. package/src/checks/liquid-html-syntax-error/checks/InvalidLoopArguments.spec.ts +1 -2
  156. package/src/checks/liquid-html-syntax-error/index.spec.ts +1 -6
  157. package/src/checks/liquid-html-syntax-error/index.ts +2 -2
  158. package/src/checks/matching-translations/index.spec.ts +89 -346
  159. package/src/checks/matching-translations/index.ts +24 -35
  160. package/src/checks/metadata-params/index.ts +5 -7
  161. package/src/checks/missing-asset/index.ts +1 -1
  162. package/src/checks/{missing-template → missing-partial}/index.spec.ts +6 -6
  163. package/src/checks/{missing-template → missing-partial}/index.ts +6 -20
  164. package/src/checks/orphaned-partial/index.ts +3 -3
  165. package/src/checks/parser-blocking-script/index.spec.ts +0 -118
  166. package/src/checks/parser-blocking-script/index.ts +3 -33
  167. package/src/checks/parser-blocking-script/suggestions.ts +1 -28
  168. package/src/checks/translation-key-exists/index.ts +1 -1
  169. package/src/checks/unclosed-html-element/index.ts +5 -1
  170. package/src/checks/undefined-object/index.spec.ts +3 -109
  171. package/src/checks/undefined-object/index.ts +8 -33
  172. package/src/checks/unique-doc-param-names/index.ts +1 -1
  173. package/src/checks/unknown-filter/index.spec.ts +2 -2
  174. package/src/checks/unknown-filter/index.ts +3 -3
  175. package/src/checks/unknown-property/index.ts +1 -1
  176. package/src/checks/unrecognized-render-partial-arguments/index.spec.ts +5 -5
  177. package/src/checks/unrecognized-render-partial-arguments/index.ts +2 -5
  178. package/src/checks/unused-assign/index.spec.ts +0 -30
  179. package/src/checks/unused-assign/index.ts +1 -1
  180. package/src/checks/unused-doc-param/index.ts +1 -1
  181. package/src/checks/utils.ts +1 -1
  182. package/src/checks/valid-doc-param-types/index.ts +4 -4
  183. package/src/checks/valid-html-translation/index.spec.ts +42 -32
  184. package/src/checks/valid-html-translation/index.ts +7 -7
  185. package/src/checks/valid-json/index.ts +1 -1
  186. package/src/checks/valid-render-partial-argument-types/index.ts +2 -5
  187. package/src/checks/variable-name/index.ts +1 -1
  188. package/src/context-utils.spec.ts +49 -77
  189. package/src/context-utils.ts +39 -128
  190. package/src/disabled-checks/index.spec.ts +35 -0
  191. package/src/disabled-checks/index.ts +4 -2
  192. package/src/find-root.ts +12 -22
  193. package/src/fixes/autofix.spec.ts +2 -2
  194. package/src/fixes/autofix.ts +4 -4
  195. package/src/fixes/correctors/index.ts +4 -0
  196. package/src/ignore.spec.ts +0 -1
  197. package/src/index.ts +33 -21
  198. package/src/jsonc/parse.ts +1 -1
  199. package/src/liquid-doc/arguments.spec.ts +19 -45
  200. package/src/liquid-doc/arguments.ts +26 -39
  201. package/src/liquid-doc/liquidDoc.ts +1 -2
  202. package/src/liquid-doc/utils.ts +4 -3
  203. package/src/path.ts +1 -0
  204. package/src/test/{MockTheme.ts → MockApp.ts} +1 -1
  205. package/src/test/MockFileSystem.ts +9 -6
  206. package/src/test/index.ts +1 -1
  207. package/src/test/test-helper.ts +29 -127
  208. package/src/to-source-code.ts +20 -1
  209. package/src/types/{theme-liquid-docs.ts → platformos-liquid-docs.ts} +8 -13
  210. package/src/types/schemas/index.ts +0 -2
  211. package/src/types.ts +21 -92
  212. package/src/utils/file-utils.ts +0 -1
  213. package/src/utils/index.ts +0 -1
  214. package/src/yaml/parse.ts +111 -0
  215. package/src/AugmentedThemeDocset.ts +0 -137
  216. package/src/checks/app-block-missing-schema/index.spec.ts +0 -121
  217. package/src/checks/app-block-missing-schema/index.ts +0 -46
  218. package/src/checks/app-block-valid-tags/index.spec.ts +0 -96
  219. package/src/checks/app-block-valid-tags/index.ts +0 -54
  220. package/src/checks/asset-preload/index.spec.ts +0 -78
  221. package/src/checks/asset-preload/index.ts +0 -65
  222. package/src/checks/asset-size-app-block-css/index.spec.ts +0 -88
  223. package/src/checks/asset-size-app-block-css/index.ts +0 -78
  224. package/src/checks/asset-size-app-block-javascript/index.spec.ts +0 -66
  225. package/src/checks/asset-size-app-block-javascript/index.ts +0 -78
  226. package/src/checks/asset-size-css/index.spec.ts +0 -166
  227. package/src/checks/asset-size-css/index.ts +0 -160
  228. package/src/checks/asset-size-javascript/index.spec.ts +0 -184
  229. package/src/checks/asset-size-javascript/index.ts +0 -144
  230. package/src/checks/block-id-usage/index.spec.ts +0 -76
  231. package/src/checks/block-id-usage/index.ts +0 -72
  232. package/src/checks/cdn-preconnect/index.spec.ts +0 -40
  233. package/src/checks/cdn-preconnect/index.ts +0 -43
  234. package/src/checks/content-for-header-modification/index.spec.ts +0 -65
  235. package/src/checks/content-for-header-modification/index.ts +0 -72
  236. package/src/checks/deprecate-bgsizes/index.spec.ts +0 -41
  237. package/src/checks/deprecate-bgsizes/index.ts +0 -49
  238. package/src/checks/deprecate-lazysizes/index.spec.ts +0 -26
  239. package/src/checks/deprecate-lazysizes/index.ts +0 -58
  240. package/src/checks/deprecated-filter/fixes.ts +0 -264
  241. package/src/checks/deprecated-fonts-on-sections-and-blocks/deprecated-fonts-data.ts +0 -1343
  242. package/src/checks/deprecated-fonts-on-sections-and-blocks/index.spec.ts +0 -613
  243. package/src/checks/deprecated-fonts-on-sections-and-blocks/index.ts +0 -284
  244. package/src/checks/deprecated-fonts-on-settings-schema/index.spec.ts +0 -102
  245. package/src/checks/deprecated-fonts-on-settings-schema/index.ts +0 -66
  246. package/src/checks/duplicate-content-for-arguments/index.spec.ts +0 -98
  247. package/src/checks/duplicate-content-for-arguments/index.ts +0 -43
  248. package/src/checks/empty-block-content/index.spec.ts +0 -117
  249. package/src/checks/empty-block-content/index.ts +0 -60
  250. package/src/checks/hardcoded-routes/index.spec.ts +0 -58
  251. package/src/checks/hardcoded-routes/index.ts +0 -100
  252. package/src/checks/json-missing-block/index.spec.ts +0 -435
  253. package/src/checks/json-missing-block/index.ts +0 -56
  254. package/src/checks/json-missing-block/missing-block-utils.ts +0 -147
  255. package/src/checks/liquid-free-settings/index.spec.ts +0 -180
  256. package/src/checks/liquid-free-settings/index.ts +0 -79
  257. package/src/checks/missing-content-for-arguments/index.spec.ts +0 -144
  258. package/src/checks/missing-content-for-arguments/index.ts +0 -46
  259. package/src/checks/pagination-size/index.spec.ts +0 -158
  260. package/src/checks/pagination-size/index.ts +0 -104
  261. package/src/checks/remote-asset/index.spec.ts +0 -280
  262. package/src/checks/remote-asset/index.ts +0 -238
  263. package/src/checks/reserved-doc-param-names/index.spec.ts +0 -62
  264. package/src/checks/reserved-doc-param-names/index.ts +0 -57
  265. package/src/checks/schema-presets-block-order/index.spec.ts +0 -344
  266. package/src/checks/schema-presets-block-order/index.ts +0 -154
  267. package/src/checks/schema-presets-static-blocks/index.spec.ts +0 -145
  268. package/src/checks/schema-presets-static-blocks/index.ts +0 -126
  269. package/src/checks/static-stylesheet-and-javascript-tags/index.spec.ts +0 -257
  270. package/src/checks/static-stylesheet-and-javascript-tags/index.ts +0 -48
  271. package/src/checks/unique-settings-id/index.spec.ts +0 -24
  272. package/src/checks/unique-settings-id/index.ts +0 -84
  273. package/src/checks/unique-settings-id/test-data.ts +0 -1191
  274. package/src/checks/unique-static-block-id/index.spec.ts +0 -55
  275. package/src/checks/unique-static-block-id/index.ts +0 -60
  276. package/src/checks/unrecognized-content-for-arguments/index.spec.ts +0 -145
  277. package/src/checks/unrecognized-content-for-arguments/index.ts +0 -55
  278. package/src/checks/valid-block-target/index.spec.ts +0 -1396
  279. package/src/checks/valid-block-target/index.ts +0 -142
  280. package/src/checks/valid-content-for-argument-types/index.spec.ts +0 -382
  281. package/src/checks/valid-content-for-argument-types/index.ts +0 -42
  282. package/src/checks/valid-content-for-arguments/index.spec.ts +0 -107
  283. package/src/checks/valid-content-for-arguments/index.ts +0 -98
  284. package/src/checks/valid-local-blocks/index.spec.ts +0 -286
  285. package/src/checks/valid-local-blocks/index.ts +0 -100
  286. package/src/checks/valid-local-blocks/valid-block-utils.ts +0 -97
  287. package/src/checks/valid-schema/index.spec.ts +0 -174
  288. package/src/checks/valid-schema/index.ts +0 -41
  289. package/src/checks/valid-schema-name/index.spec.ts +0 -112
  290. package/src/checks/valid-schema-name/index.ts +0 -75
  291. package/src/checks/valid-settings-key/index.spec.ts +0 -321
  292. package/src/checks/valid-settings-key/index.ts +0 -144
  293. package/src/checks/valid-static-block-type/index.spec.ts +0 -38
  294. package/src/checks/valid-static-block-type/index.ts +0 -58
  295. package/src/checks/valid-visible-if/index.spec.ts +0 -619
  296. package/src/checks/valid-visible-if/index.ts +0 -184
  297. package/src/checks/valid-visible-if/visible-if-utils.ts +0 -158
  298. package/src/tags/content-for.ts +0 -25
  299. package/src/to-schema.ts +0 -231
  300. package/src/types/schemas/section.ts +0 -86
  301. package/src/types/schemas/theme-block.ts +0 -34
  302. package/src/types/theme-schemas.ts +0 -80
  303. package/src/utils/block.ts +0 -300
  304. package/src/utils/markup.ts +0 -10
@@ -1,184 +0,0 @@
1
- import { type LiquidVariableLookup } from '@platformos/liquid-html-parser';
2
- import {
3
- Severity,
4
- SourceCodeType,
5
- type LiquidCheckDefinition,
6
- type JSONCheckDefinition,
7
- } from '../../types';
8
- import { getLocStart, nodeAtPath } from '../../json';
9
- import { getSchema, isBlockSchema, isSectionSchema } from '../../to-schema';
10
- import { reportWarning } from '../../utils';
11
- import {
12
- getGlobalSettings,
13
- getVariableLookupsInExpression,
14
- validateLookup,
15
- offsetAdjust,
16
- type Vars,
17
- } from './visible-if-utils';
18
-
19
- // Note that unlike most other files in the `checks` directory, this exports two
20
- // checks: one for Liquid files and one for 'config/settings_schema.json'. They
21
- // perform the same check using the same logic (modulo differences extracting
22
- // the schema and determining warning start and end indices).
23
-
24
- const meta = {
25
- code: 'ValidVisibleIf',
26
- name: 'Validate visible_if expressions',
27
- docs: {
28
- description:
29
- 'Ensures visible_if expressions are well-formed and only reference settings keys that are defined',
30
- recommended: true,
31
- url: 'https://shopify.dev/docs/storefronts/themes/tools/theme-check/checks/valid-visible-if',
32
- },
33
- severity: Severity.ERROR,
34
- schema: {},
35
- targets: [],
36
- };
37
-
38
- export const ValidVisibleIf: LiquidCheckDefinition = {
39
- meta: { ...meta, type: SourceCodeType.LiquidHtml },
40
-
41
- create(context) {
42
- return {
43
- async LiquidRawTag(node) {
44
- if (node.name !== 'schema' || node.body.kind !== 'json') return;
45
-
46
- const schema = await getSchema(context);
47
-
48
- const { validSchema, ast } = schema ?? {};
49
- if (
50
- !validSchema ||
51
- validSchema instanceof Error ||
52
- !validSchema.settings?.some((setting) => 'visible_if' in setting) ||
53
- !ast ||
54
- ast instanceof Error
55
- ) {
56
- return;
57
- }
58
-
59
- const offset = node.blockStartPosition.end;
60
- const settings = Object.fromEntries(
61
- (await getGlobalSettings(context)).map((s) => [s, true] as const),
62
- );
63
- const currentFileSettings = Object.fromEntries(
64
- validSchema.settings.map((setting) => [setting.id, true] as const),
65
- );
66
-
67
- const vars: Vars = { settings };
68
- if (isSectionSchema(schema)) {
69
- vars.section = { settings: currentFileSettings };
70
- } else if (isBlockSchema(schema)) {
71
- vars.block = { settings: currentFileSettings };
72
- }
73
-
74
- for (const [i, setting] of validSchema.settings.entries()) {
75
- if (!('visible_if' in setting) || typeof setting.visible_if !== 'string') continue;
76
-
77
- const visibleIfNode = nodeAtPath(ast, ['settings', i, 'visible_if'])!;
78
-
79
- const varLookupsOrWarning = await getVariableLookupsInExpression(setting.visible_if);
80
- if (varLookupsOrWarning === null) continue;
81
-
82
- if ('warning' in varLookupsOrWarning) {
83
- reportWarning(varLookupsOrWarning.warning, offset, visibleIfNode, context);
84
- continue;
85
- }
86
-
87
- const report = (message: string | null, lookup: LiquidVariableLookup) => {
88
- if (typeof message === 'string') {
89
- context.report({
90
- message,
91
- // the JSONNode start location returned by `getLocStart`
92
- // includes the opening quotation mark — whereas when we parse
93
- // the inner expression, 0 is the location _inside_ the quotes.
94
- // we add 1 to the offsets to compensate.
95
- startIndex:
96
- offset + getLocStart(visibleIfNode) + lookup.position.start + offsetAdjust + 1,
97
- endIndex:
98
- offset + getLocStart(visibleIfNode) + lookup.position.end + offsetAdjust + 1,
99
- });
100
- }
101
- };
102
-
103
- for (const lookup of varLookupsOrWarning) {
104
- if (lookup.name === 'section' && isBlockSchema(schema)) {
105
- //no-op, we don't know what section this block will be used in, so we can't validate that the setting exists
106
- } else if (lookup.name === 'section' && !isSectionSchema(schema)) {
107
- report(
108
- `Invalid visible_if: can't refer to "section" when not in a section or block file.`,
109
- lookup,
110
- );
111
- } else if (lookup.name === 'block' && !isBlockSchema(schema)) {
112
- report(
113
- `Invalid visible_if: can't refer to "block" when not in a block file.`,
114
- lookup,
115
- );
116
- } else {
117
- report(validateLookup(lookup, vars), lookup);
118
- }
119
- }
120
- }
121
- },
122
- };
123
- },
124
- };
125
-
126
- export const ValidVisibleIfSettingsSchema: JSONCheckDefinition = {
127
- meta: { ...meta, type: SourceCodeType.JSON },
128
-
129
- create(context) {
130
- const relativePath = context.toRelativePath(context.file.uri);
131
- if (relativePath !== 'config/settings_schema.json') return {};
132
-
133
- return {
134
- async Property(node) {
135
- if (node.key.value !== 'visible_if' || node.value.type !== 'Literal') return;
136
- const visibleIfExpression = node.value.value;
137
- if (typeof visibleIfExpression !== 'string') return;
138
- const offset = node.value.loc.start.offset;
139
-
140
- const varLookupsOrWarning = await getVariableLookupsInExpression(visibleIfExpression);
141
- if (varLookupsOrWarning === null) return;
142
-
143
- if ('warning' in varLookupsOrWarning) {
144
- context.report({
145
- message: varLookupsOrWarning.warning,
146
- startIndex: node.value.loc.start.offset,
147
- endIndex: node.value.loc.end.offset,
148
- });
149
- return;
150
- }
151
-
152
- const settings = Object.fromEntries(
153
- (await getGlobalSettings(context)).map((s) => [s, true] as const),
154
- );
155
-
156
- const vars: Vars = { settings };
157
-
158
- const report = (message: string | null, lookup: LiquidVariableLookup) => {
159
- if (typeof message === 'string') {
160
- context.report({
161
- message,
162
- startIndex: offset + lookup.position.start + offsetAdjust + 1,
163
- endIndex: offset + lookup.position.end + offsetAdjust + 1,
164
- });
165
- }
166
- };
167
-
168
- for (const lookup of varLookupsOrWarning) {
169
- // settings_schema.json can't reference `section` or `block`.
170
- if (lookup.name === 'section') {
171
- report(
172
- `Invalid visible_if: can't refer to "section" when not in a section file.`,
173
- lookup,
174
- );
175
- } else if (lookup.name === 'block') {
176
- report(`Invalid visible_if: can't refer to "block" when not in a block file.`, lookup);
177
- } else {
178
- report(validateLookup(lookup, vars), lookup);
179
- }
180
- }
181
- },
182
- };
183
- },
184
- };
@@ -1,158 +0,0 @@
1
- import {
2
- type LiquidVariableLookup,
3
- NodeTypes,
4
- toLiquidHtmlAST,
5
- } from '@platformos/liquid-html-parser';
6
- import { Context, SourceCodeType } from '../..';
7
- import { parseJSON } from '../../json';
8
- import { visit } from '../../visitor';
9
-
10
- export type Vars = { [key: string]: Vars | true };
11
-
12
- export const variableExpressionMatcher = /{{(.+?)}}/;
13
- export const adjustedPrefix = '{% if ';
14
- export const adjustedSuffix = ' %}{% endif %}';
15
- export const offsetAdjust = '{{'.length - adjustedPrefix.length;
16
-
17
- export async function getVariableLookupsInExpression(
18
- expression: string,
19
- ): Promise<LiquidVariableLookup[] | { warning: string } | null> {
20
- // As of February 2025, parsers other than LiquidJS don't yet support
21
- // expressions in {{ variable }} tags. So we have to do something a little
22
- // gnarly — before parsing it we extract the expression from within the tag
23
- // and plunk it into an `{% if <expression> %}{% endif %}` statement instead.
24
- // This requires us to adjust the reported character ranges and offer slightly
25
- // less useful messages on syntax errors, but otherwise should behave
26
- // similarly to a proper `{{ <expression> }}` syntax whenever it lands.
27
- const match = variableExpressionMatcher.exec(expression);
28
- if (match == null) {
29
- return {
30
- warning: `Invalid visible_if expression. It should take the form "{{ <expression> }}".`,
31
- };
32
- }
33
- const unwrappedExpression = match[1];
34
-
35
- const adjustedExpression = `${adjustedPrefix}${unwrappedExpression}${adjustedSuffix}`;
36
-
37
- try {
38
- const innerAst = toLiquidHtmlAST(adjustedExpression, {
39
- mode: 'strict',
40
- allowUnclosedDocumentNode: false,
41
- });
42
-
43
- if (innerAst.children.length !== 1) {
44
- throw new Error('Unexpected child count for DocumentNode');
45
- }
46
-
47
- const ifTag = innerAst.children[0];
48
-
49
- if (ifTag.type !== 'LiquidTag' || ifTag.name !== 'if') {
50
- throw new Error("Expected DocumentNode to contain 'if' tag");
51
- }
52
-
53
- const expressionNode = ifTag.markup;
54
- if (typeof expressionNode === 'string') {
55
- return {
56
- warning: `Invalid visible_if expression.`,
57
- };
58
- }
59
-
60
- if (
61
- expressionNode.type === NodeTypes.LiquidLiteral &&
62
- ['true', 'false'].includes(expressionNode.keyword)
63
- ) {
64
- // Those are OK
65
- return null;
66
- }
67
-
68
- const vars = await visit<SourceCodeType.LiquidHtml, LiquidVariableLookup>(ifTag, {
69
- VariableLookup: async (node) => node,
70
- });
71
-
72
- if (vars.length === 0) {
73
- return {
74
- warning: `visible_if expression contains no references to any settings. This is likely an error.`,
75
- };
76
- }
77
-
78
- return vars;
79
- } catch (error) {
80
- if (error instanceof SyntaxError) {
81
- // Because of our hackish approach, the underlying error is likely to
82
- // include an incorrect character range and/or mention {% if %} tags.
83
- // Squelch the details and just report it as a simple syntax error.
84
- return { warning: 'Syntax error: cannot parse visible_if expression.' };
85
- }
86
-
87
- return { warning: String(error) };
88
- }
89
- }
90
-
91
- export function validateLookup(lookup: LiquidVariableLookup, vars: Vars): string | null {
92
- const normalized = getNormalizedLookups(lookup);
93
-
94
- const poppedSegments = [];
95
- let scope = vars;
96
- while (normalized.length > 0) {
97
- const segment = normalized.shift()!;
98
- poppedSegments.push(segment);
99
-
100
- // "noUncheckedIndexedAccess" is false in our tsconfig.json
101
- const next = scope[segment] as true | Vars | undefined;
102
-
103
- if (!next) {
104
- return `Invalid variable: "${poppedSegments.join('.')}" was not found.`;
105
- }
106
-
107
- if (typeof next === 'boolean') {
108
- if (normalized.length > 0) {
109
- return `Invalid variable: "${poppedSegments.join(
110
- '.',
111
- )}" refers to a variable, but is being used here as a namespace.`;
112
- }
113
- return null;
114
- }
115
-
116
- scope = next;
117
- }
118
-
119
- // note this is the reverse of the above similar-looking case
120
- return `Invalid variable: "${poppedSegments.join(
121
- '.',
122
- )}" refers to a namespace, but is being used here as a variable.`;
123
- }
124
-
125
- function getNormalizedLookups(lookup: LiquidVariableLookup) {
126
- const nestedLookups = lookup.lookups.map((lookup) => {
127
- if (lookup.type !== NodeTypes.String) {
128
- throw new Error(`Expected lookups to be String nodes: ${JSON.stringify(lookup)}`);
129
- }
130
- return lookup.value;
131
- });
132
-
133
- return [lookup.name, ...nestedLookups];
134
- }
135
-
136
- export async function getGlobalSettings(context: Context<SourceCodeType>) {
137
- const globalSettings: string[] = [];
138
-
139
- try {
140
- const uri = context.toUri('config/settings_schema.json');
141
- const settingsFile = await context.fs.readFile(uri);
142
- const settings = parseJSON(settingsFile);
143
- if (Array.isArray(settings)) {
144
- for (const group of settings) {
145
- if ('settings' in group && Array.isArray(group.settings)) {
146
- globalSettings.push(
147
- ...group.settings.map((setting: any) => setting.id).filter((id: any) => id),
148
- );
149
- }
150
- }
151
- }
152
- } catch (e) {
153
- console.error('Error fetching global settings:', e);
154
- // ignore absent or malformed settings schema
155
- }
156
-
157
- return globalSettings;
158
- }
@@ -1,25 +0,0 @@
1
- export const RESERVED_CONTENT_FOR_ARGUMENTS = [
2
- 'attributes',
3
- 'block',
4
- 'blocks',
5
- 'class',
6
- 'context',
7
- 'inherit',
8
- 'resource',
9
- 'resources',
10
- 'schema',
11
- 'section',
12
- 'sections',
13
- 'settings',
14
- 'snippet',
15
- 'snippets',
16
- 'src',
17
- 'style',
18
- 'styles',
19
- 'template',
20
- 'templates',
21
- ];
22
-
23
- export const REQUIRED_CONTENT_FOR_ARGUMENTS = ['id', 'type'];
24
-
25
- export const CLOSEST_ARGUMENT = 'closest.';
package/src/to-schema.ts DELETED
@@ -1,231 +0,0 @@
1
- import {
2
- ContentForMarkup,
3
- LiquidRawTag,
4
- NamedTags,
5
- NodeTypes,
6
- } from '@platformos/liquid-html-parser';
7
- import { isPartial as isPartialPath } from '@platformos/platformos-common';
8
- import { parseJSON } from './json';
9
- import * as path from './path';
10
- import { toJSONAST } from './to-source-code';
11
- import {
12
- AppBlockSchema,
13
- ThemeBlockSchema,
14
- IsValidSchema,
15
- LiquidHtmlNode,
16
- SectionSchema,
17
- SourceCode,
18
- SourceCodeType,
19
- ThemeBlock,
20
- ThemeSchemaType,
21
- UriString,
22
- Context,
23
- Schema,
24
- StaticBlockDef,
25
- } from './types';
26
- import { visit } from './visitor';
27
-
28
- export async function toSchema(
29
- mode: 'app' | 'theme',
30
- uri: UriString,
31
- sourceCode: SourceCode,
32
- isValidSchema: IsValidSchema | undefined,
33
- isStrict: boolean = true,
34
- ): Promise<AppBlockSchema | SectionSchema | ThemeBlockSchema | undefined> {
35
- if (sourceCode.type !== SourceCodeType.LiquidHtml) return undefined;
36
- switch (true) {
37
- case mode === 'app' && isBlock(uri):
38
- return toAppBlockSchema(uri, sourceCode.ast, isStrict);
39
- case mode === 'theme' && isBlock(uri):
40
- return toBlockSchema(uri, sourceCode.ast, isValidSchema, isStrict);
41
- case mode === 'theme' && isSection(uri):
42
- return toSectionSchema(uri, sourceCode.ast, isValidSchema, isStrict);
43
- default:
44
- return undefined;
45
- }
46
- }
47
-
48
- export function isBlock(uri: UriString) {
49
- return path.dirname(uri).endsWith('blocks');
50
- }
51
-
52
- export function isSection(uri: UriString) {
53
- return path.dirname(uri).endsWith('sections');
54
- }
55
-
56
- export function isPartial(uri: UriString) {
57
- return isPartialPath(uri);
58
- }
59
-
60
- // Legacy alias for backwards compatibility
61
- export const isSnippet = isPartial;
62
-
63
- export function isBlockSchema(
64
- schema: AppBlockSchema | SectionSchema | ThemeBlockSchema | undefined,
65
- ): schema is ThemeBlockSchema {
66
- return schema?.type === ThemeSchemaType.Block;
67
- }
68
-
69
- export function isSectionSchema(
70
- schema: AppBlockSchema | SectionSchema | ThemeBlockSchema | undefined,
71
- ): schema is SectionSchema {
72
- return schema?.type === ThemeSchemaType.Section;
73
- }
74
-
75
- async function toValidSchema<T>(
76
- uri: string,
77
- schemaNode: LiquidRawTag | Error,
78
- parsed: any | Error,
79
- isValidSchema: IsValidSchema | undefined,
80
- ): Promise<T | Error> {
81
- if (!isValidSchema) return new Error('No JSON validator provided');
82
- if (schemaNode instanceof Error) return parsed;
83
- if (await isValidSchema(uri, schemaNode.body.value)) {
84
- return parsed as T;
85
- } else {
86
- return new Error('Invalid schema');
87
- }
88
- }
89
-
90
- export async function toBlockSchema(
91
- uri: UriString,
92
- liquidAst: LiquidHtmlNode | Error,
93
- isValidSchema: IsValidSchema | undefined,
94
- isStrict: boolean,
95
- ): Promise<ThemeBlockSchema> {
96
- const name = path.basename(uri, '.liquid');
97
- const schemaNode = await toSchemaNode(liquidAst);
98
- const staticBlockDefs = await toStaticBlockDefs(liquidAst);
99
- const parsed = toParsed(schemaNode, isStrict);
100
- const ast = toAst(schemaNode);
101
-
102
- return {
103
- type: ThemeSchemaType.Block,
104
- validSchema: await toValidSchema<ThemeBlock.Schema>(uri, schemaNode, parsed, isValidSchema),
105
- offset: schemaNode instanceof Error ? 0 : schemaNode.blockStartPosition.end,
106
- name,
107
- parsed,
108
- ast,
109
- value: schemaNode instanceof Error ? '' : schemaNode.body.value,
110
- staticBlockDefs,
111
- };
112
- }
113
-
114
- // Coincidentally very similar right now... but could be different in the future
115
- // given there might be a plan to support folders in the blocks folder.
116
- // e.g. if we start having a stricter "parsed" object / ways to get settings.
117
- export async function toSectionSchema(
118
- uri: UriString,
119
- liquidAst: LiquidHtmlNode | Error,
120
- isValidSchema: IsValidSchema | undefined,
121
- isStrict: boolean,
122
- ): Promise<SectionSchema> {
123
- const name = path.basename(uri, '.liquid');
124
- const schemaNode = await toSchemaNode(liquidAst);
125
- const staticBlockDefs = await toStaticBlockDefs(liquidAst);
126
- const parsed = toParsed(schemaNode, isStrict);
127
- const ast = toAst(schemaNode);
128
-
129
- return {
130
- type: ThemeSchemaType.Section,
131
- validSchema: await toValidSchema(uri, schemaNode, parsed, isValidSchema),
132
- offset: schemaNode instanceof Error ? 0 : schemaNode.blockStartPosition.end,
133
- name,
134
- parsed,
135
- ast,
136
- value: schemaNode instanceof Error ? '' : schemaNode.body.value,
137
- staticBlockDefs,
138
- };
139
- }
140
-
141
- // validSchema not implemented yet. You can still `visit` the ast.
142
- export async function toAppBlockSchema(
143
- uri: UriString,
144
- liquidAst: LiquidHtmlNode | Error,
145
- isStrict: boolean,
146
- ): Promise<AppBlockSchema> {
147
- const name = path.basename(uri, '.liquid');
148
- const schemaNode = await toSchemaNode(liquidAst);
149
- const parsed = toParsed(schemaNode, isStrict);
150
- const ast = toAst(schemaNode);
151
-
152
- return {
153
- type: ThemeSchemaType.AppBlock,
154
- offset: schemaNode instanceof Error ? 0 : schemaNode.blockStartPosition.end,
155
- name,
156
- parsed,
157
- ast,
158
- value: schemaNode instanceof Error ? '' : schemaNode.body.value,
159
- };
160
- }
161
-
162
- async function toSchemaNode(ast: LiquidHtmlNode | Error): Promise<Error | LiquidRawTag> {
163
- if (ast instanceof Error) return ast;
164
- return (
165
- (
166
- await visit<SourceCodeType.LiquidHtml, LiquidRawTag>(ast, {
167
- async LiquidRawTag(node) {
168
- if (node.name === 'schema') {
169
- return node;
170
- }
171
- },
172
- })
173
- )[0] ?? new Error('No schema tag found')
174
- );
175
- }
176
-
177
- async function toStaticBlockDefs(ast: LiquidHtmlNode | Error): Promise<StaticBlockDef[]> {
178
- if (ast instanceof Error) return [];
179
- return await visit<SourceCodeType.LiquidHtml, StaticBlockDef>(ast, {
180
- async LiquidTag(node) {
181
- if (node.name !== NamedTags.content_for) return;
182
- if (typeof node.markup === 'string') return;
183
- const contentForMarkup: ContentForMarkup = node.markup;
184
- if (contentForMarkup.contentForType.value !== 'block') return;
185
- const type = contentForMarkup.args.find((x) => x.name === 'type');
186
- const id = contentForMarkup.args.find((x) => x.name === 'id');
187
- if (!type || !id) return;
188
- if (type.value.type !== NodeTypes.String) return;
189
- if (id.value.type !== NodeTypes.String) return;
190
-
191
- return {
192
- type: type.value.value,
193
- id: id.value.value,
194
- };
195
- },
196
- });
197
- }
198
-
199
- export function getSchema(context: Context<SourceCodeType.LiquidHtml, Schema>) {
200
- const name = path.basename(context.file.uri, '.liquid');
201
- switch (true) {
202
- case isBlock(context.file.uri):
203
- return context.getBlockSchema?.(name);
204
- case isSection(context.file.uri):
205
- return context.getSectionSchema?.(name);
206
- default:
207
- return undefined;
208
- }
209
- }
210
-
211
- export async function getSchemaFromJSON(context: Context<SourceCodeType.JSON, Schema>) {
212
- const originalSource = context.file.source;
213
-
214
- const parsed = parseJSON(originalSource);
215
- const ast = toJSONAST(originalSource);
216
-
217
- return {
218
- parsed,
219
- ast,
220
- };
221
- }
222
-
223
- function toParsed(schemaNode: LiquidRawTag | Error, isStrict: boolean): any | Error {
224
- if (schemaNode instanceof Error) return schemaNode;
225
- return parseJSON(schemaNode.body.value, undefined, isStrict);
226
- }
227
-
228
- function toAst(schemaNode: LiquidRawTag | Error): SourceCode<SourceCodeType.JSON>['ast'] | Error {
229
- if (schemaNode instanceof Error) return schemaNode;
230
- return toJSONAST(schemaNode.body.value);
231
- }
@@ -1,86 +0,0 @@
1
- import { Setting } from './setting';
2
- import { Preset } from './preset';
3
-
4
- export declare namespace Section {
5
- /** {% schema %} */
6
- export interface Schema {
7
- name?: string;
8
- tag?: 'article' | 'aside' | 'div' | 'footer' | 'header' | 'section';
9
- class?: string;
10
- limit?: number;
11
- settings?: Setting.Any[];
12
- max_blocks?: number;
13
- blocks?: Block[];
14
- presets?: Preset.Preset[];
15
- default?: Default;
16
- locales?: Record<string, Record<string, string>>;
17
- enabled_on?: SectionToggle;
18
- disabled_on?: SectionToggle;
19
- }
20
-
21
- // Block definitions
22
- export type Block = ThemeBlock | AppBlock | SpecificThemeBlock | LocalBlock;
23
-
24
- export type ThemeBlock = {
25
- type: '@theme';
26
- };
27
-
28
- export type AppBlock = {
29
- type: '@app';
30
- };
31
-
32
- export type SpecificThemeBlock = {
33
- type: string;
34
- limit?: number;
35
- template?: string;
36
- };
37
-
38
- export type LocalBlock = {
39
- type: string;
40
- name: string;
41
- settings?: Setting.Any[];
42
- };
43
-
44
- export type DefaultBlock = {
45
- type: string;
46
- settings?: Record<string, string | number | boolean | string[]>;
47
- };
48
-
49
- // Default section configuration (kind of like presets)
50
- export interface Default {
51
- settings?: Record<string, string | number | boolean | string[]>;
52
- blocks?: Array<DefaultBlock>;
53
- }
54
-
55
- // Section toggle interface
56
- export interface SectionToggle {
57
- templates?: TemplateType[];
58
- groups?: string[];
59
- }
60
-
61
- // Template types for section toggle
62
- export type TemplateType =
63
- | '*'
64
- | '404'
65
- | 'article'
66
- | 'blog'
67
- | 'captcha'
68
- | 'cart'
69
- | 'collection'
70
- | 'customers/account'
71
- | 'customers/activate_account'
72
- | 'customers/addresses'
73
- | 'customers/login'
74
- | 'customers/order'
75
- | 'customers/register'
76
- | 'customers/reset_password'
77
- | 'gift_card'
78
- | 'index'
79
- | 'list-collections'
80
- | 'metaobject'
81
- | 'page'
82
- | 'password'
83
- | 'policy'
84
- | 'product'
85
- | 'search';
86
- }