@platformos/platformos-language-server-common 0.0.7 → 0.0.9

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 (249) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/dist/PropertyShapeInference.d.ts +18 -0
  3. package/dist/PropertyShapeInference.js +62 -0
  4. package/dist/PropertyShapeInference.js.map +1 -1
  5. package/dist/TypeSystem.d.ts +4 -9
  6. package/dist/TypeSystem.js +246 -390
  7. package/dist/TypeSystem.js.map +1 -1
  8. package/dist/codeActions/providers/GenerateDocBlockProvider.d.ts +10 -0
  9. package/dist/codeActions/providers/GenerateDocBlockProvider.js +39 -0
  10. package/dist/codeActions/providers/GenerateDocBlockProvider.js.map +1 -0
  11. package/dist/commands/providers/GenerateDocBlockCommandProvider.d.ts +19 -0
  12. package/dist/commands/providers/GenerateDocBlockCommandProvider.js +64 -0
  13. package/dist/commands/providers/GenerateDocBlockCommandProvider.js.map +1 -0
  14. package/dist/completions/CompletionsProvider.d.ts +6 -10
  15. package/dist/completions/CompletionsProvider.js +6 -9
  16. package/dist/completions/CompletionsProvider.js.map +1 -1
  17. package/dist/completions/params/LiquidCompletionParams.js +6 -17
  18. package/dist/completions/params/LiquidCompletionParams.js.map +1 -1
  19. package/dist/completions/providers/ContentForCompletionProvider.js +2 -4
  20. package/dist/completions/providers/ContentForCompletionProvider.js.map +1 -1
  21. package/dist/completions/providers/FilterNamedParameterCompletionProvider.d.ts +3 -3
  22. package/dist/completions/providers/FilterNamedParameterCompletionProvider.js +6 -6
  23. package/dist/completions/providers/FilterNamedParameterCompletionProvider.js.map +1 -1
  24. package/dist/completions/providers/LiquidDocParamTypeCompletionProvider.d.ts +3 -3
  25. package/dist/completions/providers/LiquidDocParamTypeCompletionProvider.js +3 -3
  26. package/dist/completions/providers/LiquidDocParamTypeCompletionProvider.js.map +1 -1
  27. package/dist/completions/providers/LiquidTagsCompletionProvider.d.ts +3 -3
  28. package/dist/completions/providers/LiquidTagsCompletionProvider.js +3 -3
  29. package/dist/completions/providers/LiquidTagsCompletionProvider.js.map +1 -1
  30. package/dist/completions/providers/RenderPartialParameterCompletionProvider.js +1 -1
  31. package/dist/completions/providers/RenderPartialParameterCompletionProvider.js.map +1 -1
  32. package/dist/completions/providers/index.d.ts +0 -3
  33. package/dist/completions/providers/index.js +1 -7
  34. package/dist/completions/providers/index.js.map +1 -1
  35. package/dist/css/CSSLanguageService.d.ts +19 -5
  36. package/dist/css/CSSLanguageService.js +77 -63
  37. package/dist/css/CSSLanguageService.js.map +1 -1
  38. package/dist/definitions/DefinitionProvider.d.ts +1 -1
  39. package/dist/definitions/DefinitionProvider.js +1 -3
  40. package/dist/definitions/DefinitionProvider.js.map +1 -1
  41. package/dist/diagnostics/offenseToDiagnostic.js +1 -1
  42. package/dist/diagnostics/offenseToDiagnostic.js.map +1 -1
  43. package/dist/diagnostics/runChecks.d.ts +3 -5
  44. package/dist/diagnostics/runChecks.js +12 -64
  45. package/dist/diagnostics/runChecks.js.map +1 -1
  46. package/dist/docset/MarkdownRenderer.js +1 -1
  47. package/dist/docset/MarkdownRenderer.js.map +1 -1
  48. package/dist/documentLinks/DocumentLinksProvider.d.ts +3 -3
  49. package/dist/documentLinks/DocumentLinksProvider.js +3 -3
  50. package/dist/documentLinks/DocumentLinksProvider.js.map +1 -1
  51. package/dist/documents/DocumentManager.d.ts +4 -5
  52. package/dist/documents/DocumentManager.js +17 -12
  53. package/dist/documents/DocumentManager.js.map +1 -1
  54. package/dist/documents/types.d.ts +16 -3
  55. package/dist/documents/types.js +3 -3
  56. package/dist/documents/types.js.map +1 -1
  57. package/dist/hover/HoverProvider.d.ts +5 -8
  58. package/dist/hover/HoverProvider.js +8 -14
  59. package/dist/hover/HoverProvider.js.map +1 -1
  60. package/dist/hover/providers/LiquidFilterArgumentHoverProvider.d.ts +3 -3
  61. package/dist/hover/providers/LiquidFilterArgumentHoverProvider.js +3 -3
  62. package/dist/hover/providers/LiquidFilterArgumentHoverProvider.js.map +1 -1
  63. package/dist/hover/providers/LiquidFilterHoverProvider.d.ts +3 -3
  64. package/dist/hover/providers/LiquidFilterHoverProvider.js +3 -3
  65. package/dist/hover/providers/LiquidFilterHoverProvider.js.map +1 -1
  66. package/dist/hover/providers/LiquidTagHoverProvider.d.ts +3 -3
  67. package/dist/hover/providers/LiquidTagHoverProvider.js +3 -3
  68. package/dist/hover/providers/LiquidTagHoverProvider.js.map +1 -1
  69. package/dist/hover/providers/RenderPartialHoverProvider.js +1 -1
  70. package/dist/hover/providers/RenderPartialHoverProvider.js.map +1 -1
  71. package/dist/hover/providers/RenderPartialParameterHoverProvider.js +1 -1
  72. package/dist/hover/providers/RenderPartialParameterHoverProvider.js.map +1 -1
  73. package/dist/hover/providers/TranslationHoverProvider.d.ts +3 -3
  74. package/dist/hover/providers/TranslationHoverProvider.js +3 -3
  75. package/dist/hover/providers/TranslationHoverProvider.js.map +1 -1
  76. package/dist/index.d.ts +1 -1
  77. package/dist/index.js.map +1 -1
  78. package/dist/internal-types.d.ts +1 -1
  79. package/dist/json/JSONContributions.d.ts +1 -5
  80. package/dist/json/JSONContributions.js +4 -36
  81. package/dist/json/JSONContributions.js.map +1 -1
  82. package/dist/json/JSONLanguageService.d.ts +4 -12
  83. package/dist/json/JSONLanguageService.js +17 -80
  84. package/dist/json/JSONLanguageService.js.map +1 -1
  85. package/dist/json/test/test-helpers.d.ts +1 -3
  86. package/dist/json/test/test-helpers.js +3 -19
  87. package/dist/json/test/test-helpers.js.map +1 -1
  88. package/dist/json/utils.d.ts +0 -5
  89. package/dist/json/utils.js +0 -24
  90. package/dist/json/utils.js.map +1 -1
  91. package/dist/rename/RenameProvider.d.ts +2 -2
  92. package/dist/rename/RenameProvider.js +2 -2
  93. package/dist/rename/RenameProvider.js.map +1 -1
  94. package/dist/rename/providers/LiquidVariableRenameProvider.d.ts +3 -3
  95. package/dist/rename/providers/LiquidVariableRenameProvider.js +4 -4
  96. package/dist/rename/providers/LiquidVariableRenameProvider.js.map +1 -1
  97. package/dist/renamed/RenameHandler.d.ts +2 -3
  98. package/dist/renamed/RenameHandler.js +3 -8
  99. package/dist/renamed/RenameHandler.js.map +1 -1
  100. package/dist/renamed/handlers/AssetRenameHandler.d.ts +3 -3
  101. package/dist/renamed/handlers/AssetRenameHandler.js +5 -5
  102. package/dist/renamed/handlers/AssetRenameHandler.js.map +1 -1
  103. package/dist/renamed/handlers/PartialRenameHandler.d.ts +3 -3
  104. package/dist/renamed/handlers/PartialRenameHandler.js +5 -5
  105. package/dist/renamed/handlers/PartialRenameHandler.js.map +1 -1
  106. package/dist/renamed/handlers/utils.d.ts +1 -1
  107. package/dist/server/AppGraphManager.d.ts +32 -0
  108. package/dist/server/AppGraphManager.js +155 -0
  109. package/dist/server/AppGraphManager.js.map +1 -0
  110. package/dist/server/startServer.d.ts +2 -2
  111. package/dist/server/startServer.js +62 -164
  112. package/dist/server/startServer.js.map +1 -1
  113. package/dist/tsconfig.tsbuildinfo +1 -1
  114. package/dist/types.d.ts +33 -54
  115. package/dist/types.js +21 -26
  116. package/dist/types.js.map +1 -1
  117. package/dist/utils/liquidDoc.js +1 -1
  118. package/dist/utils/liquidDoc.js.map +1 -1
  119. package/dist/utils/uri.d.ts +2 -9
  120. package/dist/utils/uri.js +5 -21
  121. package/dist/utils/uri.js.map +1 -1
  122. package/package.json +4 -4
  123. package/src/ClientCapabilities.ts +1 -1
  124. package/src/PropertyShapeInference.ts +81 -1
  125. package/src/TypeSystem.spec.ts +206 -449
  126. package/src/TypeSystem.ts +267 -460
  127. package/src/codeActions/providers/FixAllProvider.spec.ts +1 -1
  128. package/src/codeActions/providers/FixProvider.spec.ts +11 -11
  129. package/src/codeActions/providers/SuggestionProvider.spec.ts +2 -2
  130. package/src/commands/providers/ApplyFixesProvider.ts +2 -2
  131. package/src/commands/providers/ApplySuggestionProvider.ts +2 -2
  132. package/src/commands/providers/RunChecksProvider.ts +2 -2
  133. package/src/completions/CompletionsProvider.ts +14 -35
  134. package/src/completions/params/LiquidCompletionParams.spec.ts +0 -9
  135. package/src/completions/params/LiquidCompletionParams.ts +8 -17
  136. package/src/completions/params/fix.spec.ts +0 -3
  137. package/src/completions/providers/FilterCompletionProvider.spec.ts +2 -8
  138. package/src/completions/providers/FilterNamedParameterCompletionProvider.spec.ts +1 -5
  139. package/src/completions/providers/FilterNamedParameterCompletionProvider.ts +6 -6
  140. package/src/completions/providers/HtmlAttributeCompletionProvider.spec.ts +1 -4
  141. package/src/completions/providers/HtmlAttributeValueCompletionProvider.spec.ts +1 -4
  142. package/src/completions/providers/HtmlTagCompletionProvider.spec.ts +1 -4
  143. package/src/completions/providers/LiquidDocParamTypeCompletionProvider.spec.ts +1 -28
  144. package/src/completions/providers/LiquidDocParamTypeCompletionProvider.ts +3 -3
  145. package/src/completions/providers/LiquidDocTagCompletionProvider.spec.ts +1 -26
  146. package/src/completions/providers/LiquidTagsCompletionProvider.spec.ts +4 -18
  147. package/src/completions/providers/LiquidTagsCompletionProvider.ts +3 -3
  148. package/src/completions/providers/ObjectAttributeCompletionProvider.spec.ts +2 -31
  149. package/src/completions/providers/ObjectCompletionProvider.spec.ts +34 -123
  150. package/src/completions/providers/PartialCompletionProvider.spec.ts +6 -15
  151. package/src/completions/providers/RenderPartialParameterCompletionProvider.spec.ts +13 -15
  152. package/src/completions/providers/RenderPartialParameterCompletionProvider.ts +2 -3
  153. package/src/completions/providers/TranslationCompletionProvider.spec.ts +1 -4
  154. package/src/completions/providers/TranslationCompletionProvider.ts +3 -3
  155. package/src/completions/providers/index.ts +0 -3
  156. package/src/css/CSSLanguageService.ts +97 -81
  157. package/src/definitions/DefinitionProvider.ts +0 -6
  158. package/src/definitions/providers/TranslationStringDefinitionProvider.spec.ts +1 -5
  159. package/src/diagnostics/offenseToDiagnostic.ts +1 -1
  160. package/src/diagnostics/runChecks.spec.ts +22 -30
  161. package/src/diagnostics/runChecks.ts +17 -91
  162. package/src/docset/MarkdownRenderer.ts +1 -1
  163. package/src/documentLinks/DocumentLinksProvider.ts +10 -10
  164. package/src/documents/DocumentManager.spec.ts +46 -119
  165. package/src/documents/DocumentManager.ts +19 -15
  166. package/src/documents/types.ts +18 -9
  167. package/src/hover/HoverProvider.ts +9 -21
  168. package/src/hover/providers/HtmlAttributeHoverProvider.spec.ts +1 -3
  169. package/src/hover/providers/HtmlAttributeValueHoverProvider.spec.ts +1 -3
  170. package/src/hover/providers/HtmlTagHoverProvider.spec.ts +1 -3
  171. package/src/hover/providers/LiquidDocTagHoverProvider.spec.ts +1 -4
  172. package/src/hover/providers/LiquidFilterArgumentHoverProvider.spec.ts +0 -3
  173. package/src/hover/providers/LiquidFilterArgumentHoverProvider.ts +3 -3
  174. package/src/hover/providers/LiquidFilterHoverProvider.spec.ts +0 -3
  175. package/src/hover/providers/LiquidFilterHoverProvider.ts +3 -3
  176. package/src/hover/providers/LiquidObjectAttributeHoverProvider.spec.ts +1 -3
  177. package/src/hover/providers/LiquidObjectHoverProvider.spec.ts +46 -129
  178. package/src/hover/providers/LiquidTagHoverProvider.spec.ts +0 -3
  179. package/src/hover/providers/LiquidTagHoverProvider.ts +3 -3
  180. package/src/hover/providers/RenderPartialHoverProvider.spec.ts +11 -14
  181. package/src/hover/providers/RenderPartialHoverProvider.ts +1 -5
  182. package/src/hover/providers/RenderPartialParameterHoverProvider.spec.ts +10 -13
  183. package/src/hover/providers/RenderPartialParameterHoverProvider.ts +2 -3
  184. package/src/hover/providers/TranslationHoverProvider.spec.ts +1 -4
  185. package/src/hover/providers/TranslationHoverProvider.ts +3 -3
  186. package/src/index.ts +4 -7
  187. package/src/internal-types.ts +1 -1
  188. package/src/json/JSONContributions.ts +4 -61
  189. package/src/json/JSONLanguageService.ts +19 -117
  190. package/src/json/test/test-helpers.ts +4 -35
  191. package/src/json/utils.ts +0 -27
  192. package/src/rename/RenameProvider.ts +4 -4
  193. package/src/rename/providers/LiquidVariableRenameProvider.spec.ts +5 -5
  194. package/src/rename/providers/LiquidVariableRenameProvider.ts +6 -6
  195. package/src/renamed/RenameHandler.ts +4 -9
  196. package/src/renamed/handlers/AssetRenameHandler.spec.ts +28 -28
  197. package/src/renamed/handlers/AssetRenameHandler.ts +6 -6
  198. package/src/renamed/handlers/PartialRenameHandler.spec.ts +9 -9
  199. package/src/renamed/handlers/PartialRenameHandler.ts +10 -10
  200. package/src/server/{ThemeGraphManager.ts → AppGraphManager.ts} +21 -90
  201. package/src/server/safe.ts +1 -1
  202. package/src/server/startServer.spec.ts +10 -12
  203. package/src/server/startServer.ts +69 -222
  204. package/src/test/CompletionItemsAssertion.spec.ts +1 -4
  205. package/src/types.ts +33 -63
  206. package/src/utils/debounce.ts +1 -1
  207. package/src/utils/liquidDoc.ts +6 -6
  208. package/src/utils/uri.ts +5 -17
  209. package/src/completions/providers/ContentForBlockTypeCompletionProvider.spec.ts +0 -41
  210. package/src/completions/providers/ContentForBlockTypeCompletionProvider.ts +0 -42
  211. package/src/completions/providers/ContentForCompletionProvider.spec.ts +0 -96
  212. package/src/completions/providers/ContentForCompletionProvider.ts +0 -113
  213. package/src/completions/providers/ContentForParameterCompletionProvider.spec.ts +0 -362
  214. package/src/completions/providers/ContentForParameterCompletionProvider.ts +0 -187
  215. package/src/completions/providers/data/contentForParameterCompletionOptions.ts +0 -9
  216. package/src/css/CSSLanguageService.spec.ts +0 -159
  217. package/src/css/test/test-helpers.ts +0 -30
  218. package/src/definitions/providers/SchemaTranslationStringDefinitionProvider.spec.ts +0 -96
  219. package/src/definitions/providers/SchemaTranslationStringDefinitionProvider.ts +0 -82
  220. package/src/hover/providers/ContentForArgumentHoverProvider.spec.ts +0 -76
  221. package/src/hover/providers/ContentForArgumentHoverProvider.ts +0 -56
  222. package/src/hover/providers/ContentForTypeHoverProvider.spec.ts +0 -91
  223. package/src/hover/providers/ContentForTypeHoverProvider.ts +0 -42
  224. package/src/json/JSONLanguageService.spec.ts +0 -424
  225. package/src/json/completions/providers/BlockSettingsPropertyCompletionProvider.spec.ts +0 -163
  226. package/src/json/completions/providers/BlockSettingsPropertyCompletionProvider.ts +0 -121
  227. package/src/json/completions/providers/BlockTypeCompletionProvider.spec.ts +0 -137
  228. package/src/json/completions/providers/BlockTypeCompletionProvider.ts +0 -108
  229. package/src/json/completions/providers/ReferencedBlockTypeCompletionProvider.spec.ts +0 -132
  230. package/src/json/completions/providers/ReferencedBlockTypeCompletionProvider.ts +0 -120
  231. package/src/json/completions/providers/SchemaTranslationCompletionProvider.ts +0 -51
  232. package/src/json/completions/providers/SettingsPropertyCompletionProvider.spec.ts +0 -211
  233. package/src/json/completions/providers/SettingsPropertyCompletionProvider.ts +0 -75
  234. package/src/json/documentLinks/DocumentLinksProvider.spec.ts +0 -223
  235. package/src/json/documentLinks/DocumentLinksProvider.ts +0 -61
  236. package/src/json/hover/providers/BlockSettingsHoverProvider.spec.ts +0 -292
  237. package/src/json/hover/providers/BlockSettingsHoverProvider.ts +0 -99
  238. package/src/json/hover/providers/SchemaTranslationHoverProvider.ts +0 -35
  239. package/src/json/hover/providers/SettingsHoverProvider.spec.ts +0 -139
  240. package/src/json/hover/providers/SettingsHoverProvider.ts +0 -79
  241. package/src/json/schemaSettings.spec.ts +0 -103
  242. package/src/json/schemaSettings.ts +0 -53
  243. package/src/plugins/css.ts +0 -12
  244. package/src/renamed/handlers/BlockRenameHandler.spec.ts +0 -399
  245. package/src/renamed/handlers/BlockRenameHandler.ts +0 -402
  246. package/src/renamed/handlers/SectionRenameHandler.spec.ts +0 -321
  247. package/src/renamed/handlers/SectionRenameHandler.ts +0 -224
  248. package/src/renamed/handlers/utils.ts +0 -22
  249. package/src/settings/index.ts +0 -50
@@ -7,20 +7,17 @@ import {
7
7
  toLiquidHtmlAST,
8
8
  } from '@platformos/liquid-html-parser';
9
9
  import {
10
- MetafieldDefinitionMap,
11
10
  path as pathUtils,
12
11
  BasicParamTypes,
13
12
  ObjectEntry,
14
13
  } from '@platformos/platformos-check-common';
15
14
  import { assert, beforeEach, describe, expect, it, vi } from 'vitest';
16
15
  import { URI } from 'vscode-uri';
17
- import { SettingsSchemaJSONFile } from './settings';
18
16
  import { ArrayType, ShapeType, TypeSystem, UnionType } from './TypeSystem';
19
17
  import { isLiquidVariableOutput, isNamedLiquidTag } from './utils';
20
18
 
21
19
  describe('Module: TypeSystem', () => {
22
20
  let typeSystem: TypeSystem;
23
- let settingsProvider: any;
24
21
  const literalContexts = [
25
22
  { value: `10`, type: 'number' },
26
23
  { value: `'string'`, type: 'string' },
@@ -28,53 +25,62 @@ describe('Module: TypeSystem', () => {
28
25
  // { value: `null`, type: 'untyped' },
29
26
  ];
30
27
 
31
- beforeEach(() => {
28
+ beforeEach(async () => {
32
29
  const _objects: ObjectEntry[] = [
33
30
  {
34
- name: 'all_products',
35
- return_type: [{ type: 'array', array_value: 'product' }],
31
+ name: 'context',
32
+ access: { global: true, parents: [], template: [] },
33
+ return_type: [],
34
+ properties: [
35
+ {
36
+ name: 'models',
37
+ description: 'a list of user-defined data models (e.g. from GraphQL)',
38
+ return_type: [{ type: 'array', array_value: 'model' }],
39
+ },
40
+ {
41
+ name: 'current_user',
42
+ description: 'the current user',
43
+ return_type: [{ type: 'current_user', name: '' }],
44
+ },
45
+ ],
36
46
  },
37
47
  {
38
- name: 'product',
39
- access: {
40
- global: true,
41
- parents: [],
42
- template: [],
43
- },
44
- return_type: [],
48
+ // 'model' represents a generic user-defined data model in platformOS
49
+ // (e.g. a record returned from a GraphQL query)
50
+ name: 'model',
45
51
  properties: [
46
52
  {
47
- name: 'featured_image',
48
- description: 'ze best image for ze product',
53
+ name: 'thumbnail',
54
+ description: 'a thumbnail image',
49
55
  return_type: [{ type: 'image', name: '' }],
50
56
  },
51
57
  {
52
58
  name: 'images',
53
- description: 'all images for ze product',
59
+ description: 'all images for the model',
54
60
  return_type: [{ type: 'array', array_value: 'image' }],
55
61
  },
56
62
  {
57
63
  name: 'title',
58
- description: 'the title of the product',
64
+ description: 'the title of the model',
59
65
  return_type: [{ type: 'string', name: '' }],
60
66
  },
61
67
  {
62
- name: 'metafields',
68
+ name: 'metadata',
63
69
  return_type: [{ type: 'untyped', name: '' }],
64
70
  },
65
71
  ],
66
72
  },
67
73
  {
68
- name: 'metafield',
74
+ name: 'current_user',
69
75
  properties: [
70
76
  {
71
- name: 'type',
72
- description: 'the type of the metafield',
77
+ name: 'name',
78
+ description: 'the name of the user',
73
79
  return_type: [{ type: 'string', name: '' }],
74
80
  },
75
81
  {
76
- name: 'value',
77
- description: 'the value of the metafield',
82
+ name: 'info',
83
+ description: 'additional info',
78
84
  return_type: [{ type: 'untyped', name: '' }],
79
85
  },
80
86
  ],
@@ -85,17 +91,7 @@ describe('Module: TypeSystem', () => {
85
91
  properties: [], // these should be populated dynamically
86
92
  },
87
93
  {
88
- name: 'predictive_search',
89
- access: { global: false, parents: [], template: [] },
90
- return_type: [],
91
- },
92
- {
93
- name: 'comment',
94
- access: { global: false, parents: [], template: [] },
95
- return_type: [],
96
- },
97
- {
98
- name: 'recommendations',
94
+ name: 'locale',
99
95
  access: { global: false, parents: [], template: [] },
100
96
  return_type: [],
101
97
  },
@@ -104,109 +100,19 @@ describe('Module: TypeSystem', () => {
104
100
  access: { global: false, parents: [], template: [] },
105
101
  return_type: [],
106
102
  },
107
- {
108
- name: 'section',
109
- access: { global: false, parents: [], template: [] },
110
- return_type: [],
111
- properties: [
112
- {
113
- name: 'settings',
114
- return_type: [{ type: 'untyped', name: '' }],
115
- },
116
- ],
117
- },
118
- {
119
- name: 'block',
120
- access: { global: false, parents: [], template: [] },
121
- return_type: [],
122
- properties: [
123
- {
124
- name: 'settings',
125
- return_type: [{ type: 'untyped', name: '' }],
126
- },
127
- ],
128
- },
129
- {
130
- name: 'locale',
131
- access: { global: false, parents: [], template: [] },
132
- return_type: [],
133
- },
134
103
  ];
135
- settingsProvider = vi.fn().mockResolvedValue([]);
136
- typeSystem = new TypeSystem(
137
- {
138
- graphQL: async () => null,
139
- tags: async () => [],
140
- objects: async () => _objects,
141
- liquidDrops: async () => _objects,
142
- filters: async () => [
143
- {
144
- name: 'size',
145
- return_type: [{ type: 'number', name: '' }],
146
- },
147
- ],
148
- systemTranslations: async () => ({}),
149
- },
150
- settingsProvider,
151
- async (_uri: string) => {
152
- return {
153
- article: [],
154
- blog: [],
155
- collection: [],
156
- company: [],
157
- company_location: [],
158
- location: [],
159
- market: [],
160
- order: [
161
- {
162
- key: 'prods',
163
- name: 'products',
164
- namespace: 'related',
165
- description: 'related products',
166
- type: {
167
- category: 'REFERENCE',
168
- name: 'list.product_reference',
169
- },
170
- },
171
- ],
172
- page: [],
173
- product: [
174
- {
175
- key: 'code',
176
- name: 'code',
177
- namespace: 'manufacturer',
178
- description: 'the code provided by the manufacturer',
179
- type: {
180
- category: 'TEXT',
181
- name: 'single_line_text_field',
182
- },
183
- },
184
- {
185
- key: 'id',
186
- name: 'id',
187
- namespace: 'manufacturer',
188
- description: 'the id provided by the manufacturer',
189
- type: {
190
- category: 'INTEGER',
191
- name: 'number_integer',
192
- },
193
- },
194
- {
195
- key: 'is_rare',
196
- name: 'is_rare',
197
- namespace: 'custom',
198
- description: 'is this product rare?',
199
- type: {
200
- category: 'BOOLEAN',
201
- name: 'boolean',
202
- },
203
- },
204
- ],
205
- variant: [],
206
- shop: [],
207
- } as MetafieldDefinitionMap;
208
- },
209
- );
104
+ typeSystem = new TypeSystem({
105
+ graphQL: async () => null,
106
+ tags: async () => [],
107
+ objects: async () => _objects,
108
+ liquidDrops: async () => _objects,
109
+ filters: async () => [
110
+ {
111
+ name: 'size',
112
+ return_type: [{ type: 'number', name: '' }],
113
+ },
114
+ ],
115
+ });
210
116
  });
211
117
 
212
118
  it('should return the type of assign markup nodes (basic test)', async () => {
@@ -240,21 +146,21 @@ describe('Module: TypeSystem', () => {
240
146
  });
241
147
 
242
148
  it('should return the type of array variables', async () => {
243
- const ast = toLiquidHtmlAST(`{% assign x = all_products %}`);
149
+ const ast = toLiquidHtmlAST(`{% assign x = context.models %}`);
244
150
  const xVariable = (ast as any).children[0].markup as AssignMarkup;
245
151
  const inferredType = await typeSystem.inferType(xVariable, ast, 'file:///file.liquid');
246
- expect(inferredType).to.eql({ kind: 'array', valueType: 'product' });
152
+ expect(inferredType).to.eql({ kind: 'array', valueType: 'model' });
247
153
  });
248
154
 
249
155
  it('should return the type of object properties', async () => {
250
- const ast = toLiquidHtmlAST(`{% assign x = all_products[0].featured_image %}`);
156
+ const ast = toLiquidHtmlAST(`{% assign x = context.models[0].thumbnail %}`);
251
157
  const xVariable = (ast as any).children[0].markup as AssignMarkup;
252
158
  const inferredType = await typeSystem.inferType(xVariable, ast, 'file:///file.liquid');
253
159
  expect(inferredType).to.equal('image');
254
160
  });
255
161
 
256
162
  it('should return the type of filtered variables', async () => {
257
- const ast = toLiquidHtmlAST(`{% assign x = product | size %}`);
163
+ const ast = toLiquidHtmlAST(`{% assign x = context | size %}`);
258
164
  const xVariable = (ast as any).children[0].markup as AssignMarkup;
259
165
  const inferredType = await typeSystem.inferType(xVariable, ast, 'file:///file.liquid');
260
166
  expect(inferredType).to.equal('number');
@@ -262,7 +168,7 @@ describe('Module: TypeSystem', () => {
262
168
 
263
169
  describe('when using string builtin methods', () => {
264
170
  it('should return number for size', async () => {
265
- const ast = toLiquidHtmlAST(`{{ product.title.size }}`);
171
+ const ast = toLiquidHtmlAST(`{{ context.current_user.name.size }}`);
266
172
  const xVariable = (ast as any).children[0].markup as LiquidVariable;
267
173
  const inferredType = await typeSystem.inferType(xVariable, ast, 'file:///file.liquid');
268
174
  expect(inferredType).to.equal('number');
@@ -270,7 +176,7 @@ describe('Module: TypeSystem', () => {
270
176
 
271
177
  ['first', 'last'].forEach((method) => {
272
178
  it(`should return string for ${method}`, async () => {
273
- const ast = toLiquidHtmlAST(`{{ product.title.${method} }}`);
179
+ const ast = toLiquidHtmlAST(`{{ context.current_user.name.${method} }}`);
274
180
  const xVariable = (ast as any).children[0].markup as LiquidVariable;
275
181
  const inferredType = await typeSystem.inferType(xVariable, ast, 'file:///file.liquid');
276
182
  expect(inferredType).to.equal('string');
@@ -280,7 +186,7 @@ describe('Module: TypeSystem', () => {
280
186
 
281
187
  describe('when using array builtin methods', () => {
282
188
  it('should return number for size', async () => {
283
- const ast = toLiquidHtmlAST(`{{ product.images.size }}`);
189
+ const ast = toLiquidHtmlAST(`{{ context.models[0].images.size }}`);
284
190
  const xVariable = (ast as any).children[0].markup as LiquidVariable;
285
191
  const inferredType = await typeSystem.inferType(xVariable, ast, 'file:///file.liquid');
286
192
  expect(inferredType).to.equal('number');
@@ -288,7 +194,7 @@ describe('Module: TypeSystem', () => {
288
194
 
289
195
  ['first', 'last'].forEach((method) => {
290
196
  it(`should return the value type of the array for ${method}`, async () => {
291
- const ast = toLiquidHtmlAST(`{{ product.images.${method} }}`);
197
+ const ast = toLiquidHtmlAST(`{{ context.models[0].images.${method} }}`);
292
198
  const xVariable = (ast as any).children[0].markup as LiquidVariable;
293
199
  const inferredType = await typeSystem.inferType(xVariable, ast, 'file:///file.liquid');
294
200
  expect(inferredType).to.equal('image');
@@ -308,7 +214,7 @@ describe('Module: TypeSystem', () => {
308
214
 
309
215
  it('should return the type of the default value lookup', async () => {
310
216
  const ast = toLiquidHtmlAST(`
311
- {% assign d = product.featured_image %}
217
+ {% assign d = context.models[0].thumbnail %}
312
218
  {% assign x = unknown | default: d %}
313
219
  `);
314
220
  const xVariable = (ast as any).children[1].markup as AssignMarkup;
@@ -318,7 +224,7 @@ describe('Module: TypeSystem', () => {
318
224
  });
319
225
 
320
226
  it('should return the type of variables in for loop', async () => {
321
- const ast = toLiquidHtmlAST(`{% for item in all_products %}{{ item }}{% endfor %}`);
227
+ const ast = toLiquidHtmlAST(`{% for item in context.models %}{{ item }}{% endfor %}`);
322
228
  const forLoop = ast.children[0];
323
229
  assert(isNamedLiquidTag(forLoop, NamedTags.for) && forLoop.children?.length === 1);
324
230
  const branch = forLoop.children[0];
@@ -328,150 +234,14 @@ describe('Module: TypeSystem', () => {
328
234
  const variable = variableOutput.markup;
329
235
 
330
236
  const inferredType = await typeSystem.inferType(variable, ast, 'file:///file.liquid');
331
- expect(inferredType).to.equal('product');
332
- });
333
-
334
- it('should patch the properties of settings when a schema is available', async () => {
335
- settingsProvider.mockResolvedValue([
336
- {
337
- name: 'category',
338
- settings: [
339
- {
340
- id: 'slide',
341
- label: 'Slide label',
342
- type: 'checkbox',
343
- },
344
- {
345
- id: 'my_font',
346
- label: 'my font',
347
- type: 'font_picker',
348
- },
349
- ],
350
- },
351
- ] as SettingsSchemaJSONFile);
352
-
353
- const contexts = [
354
- { id: 'slide', expectedType: 'boolean' },
355
- { id: 'my_font', expectedType: 'font' },
356
- ];
357
- for (const { id, expectedType } of contexts) {
358
- const ast = toLiquidHtmlAST(`{{ settings.${id} }}`);
359
- const variableOutput = ast.children[0];
360
- assert(isLiquidVariableOutput(variableOutput));
361
- const inferredType = await typeSystem.inferType(
362
- variableOutput.markup,
363
- ast,
364
- 'file:///file.liquid',
365
- );
366
- expect(inferredType).to.eql(expectedType);
367
- }
368
- });
369
-
370
- it('should support section settings in section files', async () => {
371
- const sourceCode = `
372
- {{ section.settings.my_list }}
373
- {% schema %}
374
- {
375
- "name": "section-settings-example",
376
- "tag": "section",
377
- "settings": [
378
- {
379
- "id": "my_list",
380
- "label": "t:my-setting.label",
381
- "type": "product_list"
382
- }
383
- ]
384
- }
385
- {% endschema %}
386
- `;
387
- const ast = toLiquidHtmlAST(sourceCode);
388
- const variableOutput = ast.children[0];
389
- assert(isLiquidVariableOutput(variableOutput));
390
- const inferredType = await typeSystem.inferType(
391
- variableOutput.markup,
392
- ast,
393
- 'file:///sections/my-section.liquid',
394
- );
395
- expect(inferredType).to.eql({ kind: 'array', valueType: 'product' } as ArrayType);
396
- });
397
-
398
- it('should support block settings in blocks files', async () => {
399
- const sourceCode = `
400
- {{ block.settings.my_list }}
401
- {% schema %}
402
- {
403
- "name": "section-settings-example",
404
- "tag": "section",
405
- "settings": [
406
- {
407
- "id": "my_list",
408
- "label": "t:my-setting.label",
409
- "type": "product_list"
410
- }
411
- ]
412
- }
413
- {% endschema %}
414
- `;
415
- const ast = toLiquidHtmlAST(sourceCode);
416
- const variableOutput = ast.children[0];
417
- assert(isLiquidVariableOutput(variableOutput));
418
- const inferredType = await typeSystem.inferType(
419
- variableOutput.markup,
420
- ast,
421
- 'file:///blocks/my-section.liquid',
422
- );
423
- expect(inferredType).to.eql({ kind: 'array', valueType: 'product' } as ArrayType);
424
- });
425
-
426
- // TODO
427
- it.skip('should support narrowing the type of blocks', async () => {
428
- const sourceCode = `
429
- {% for block in section.blocks %}
430
- {% case block.type %}
431
- {% when 'slide' %}
432
- {{ block.settings.image }}
433
- {% else %}
434
- {% endcase }
435
- {% if block.type == 'slide' %}
436
- {{ block.settings.image }}
437
- {% endif %}
438
- {% endfor %}
439
- {% schema %}
440
- {
441
- "name": "Slideshow",
442
- "tag": "section",
443
- "class": "slideshow",
444
- "settings": [],
445
- "blocks": [
446
- {
447
- "name": "Slide",
448
- "type": "slide",
449
- "settings": [
450
- {
451
- "type": "image_picker",
452
- "id": "image",
453
- "label": "Image"
454
- }
455
- ]
456
- }
457
- ]
458
- }
459
- {% endschema %}
460
- `;
461
- const ast = toLiquidHtmlAST(sourceCode);
237
+ expect(inferredType).to.equal('model');
462
238
  });
463
239
 
464
- it('should support path-contextual variable types', async () => {
240
+ it('should support path-contextual variable types for partials', async () => {
465
241
  let inferredType: string | ArrayType | ShapeType | UnionType;
466
242
  const contexts: [string, string][] = [
467
- ['section', 'sections/my-section.liquid'],
468
- ['comment', 'sections/main-article.liquid'],
469
- ['block', 'blocks/my-block.liquid'],
470
- ['predictive_search', 'sections/predictive-search.liquid'],
471
- ['recommendations', 'sections/recommendations.liquid'],
472
- ['app', 'blocks/recommendations.liquid'],
473
243
  ['app', 'app/views/partials/recommendations.liquid'],
474
- ['locale', 'layout/checkout.liquid'],
244
+ ['app', 'app/lib/helpers/my-helper.liquid'],
475
245
  ];
476
246
  for (const [object, path] of contexts) {
477
247
  const sourceCode = `{{ ${object} }}`;
@@ -518,16 +288,16 @@ describe('Module: TypeSystem', () => {
518
288
  const inferredType = await typeSystem.inferType(
519
289
  variableOutput.markup,
520
290
  ast,
521
- 'file:///snippets/example.liquid',
291
+ 'file:///app/views/partials/example.liquid',
522
292
  );
523
293
  expect(inferredType).to.eql(expectedType);
524
294
  });
525
295
  });
526
296
 
527
- it(`should support complex liquid doc params type: product`, async () => {
297
+ it(`should support complex liquid doc params type: current_user`, async () => {
528
298
  const sourceCode = `
529
299
  {% doc %}
530
- @param {product} data - some data
300
+ @param {current_user} data - the current user object
531
301
  {% enddoc %}
532
302
  {{ data }}
533
303
  `;
@@ -537,15 +307,15 @@ describe('Module: TypeSystem', () => {
537
307
  const inferredType = await typeSystem.inferType(
538
308
  variableOutput.markup,
539
309
  ast,
540
- 'file:///snippets/example.liquid',
310
+ 'file:///app/views/partials/example.liquid',
541
311
  );
542
- expect(inferredType).to.eql('product');
312
+ expect(inferredType).to.eql('current_user');
543
313
  });
544
314
 
545
- it(`should support array liquid doc params type: product[]`, async () => {
315
+ it(`should support array liquid doc params type: current_user[]`, async () => {
546
316
  const sourceCode = `
547
317
  {% doc %}
548
- @param {product[]} data - some data
318
+ @param {current_user[]} data - a list of users
549
319
  {% enddoc %}
550
320
  {{ data }}
551
321
  `;
@@ -555,11 +325,11 @@ describe('Module: TypeSystem', () => {
555
325
  const inferredType = await typeSystem.inferType(
556
326
  variableOutput.markup,
557
327
  ast,
558
- 'file:///snippets/example.liquid',
328
+ 'file:///app/views/partials/example.liquid',
559
329
  );
560
330
  expect(inferredType).to.eql({
561
331
  kind: 'array',
562
- valueType: 'product',
332
+ valueType: 'current_user',
563
333
  });
564
334
  });
565
335
  });
@@ -614,23 +384,7 @@ query {
614
384
  objects: async () => [],
615
385
  liquidDrops: async () => [],
616
386
  filters: async () => [],
617
- systemTranslations: async () => ({}),
618
387
  },
619
- async () => [],
620
- async () => ({
621
- article: [],
622
- blog: [],
623
- collection: [],
624
- company: [],
625
- company_location: [],
626
- location: [],
627
- market: [],
628
- order: [],
629
- page: [],
630
- product: [],
631
- variant: [],
632
- shop: [],
633
- }),
634
388
  fs,
635
389
  documentsLocator,
636
390
  async () => rootUri,
@@ -717,23 +471,7 @@ query {
717
471
  objects: async () => [],
718
472
  liquidDrops: async () => [],
719
473
  filters: async () => [],
720
- systemTranslations: async () => ({}),
721
474
  },
722
- async () => [],
723
- async () => ({
724
- article: [],
725
- blog: [],
726
- collection: [],
727
- company: [],
728
- company_location: [],
729
- location: [],
730
- market: [],
731
- order: [],
732
- page: [],
733
- product: [],
734
- variant: [],
735
- shop: [],
736
- }),
737
475
  fs,
738
476
  documentsLocator,
739
477
  async () => rootUri,
@@ -791,23 +529,7 @@ query {
791
529
  objects: async () => [],
792
530
  liquidDrops: async () => [],
793
531
  filters: async () => [],
794
- systemTranslations: async () => ({}),
795
532
  },
796
- async () => [],
797
- async () => ({
798
- article: [],
799
- blog: [],
800
- collection: [],
801
- company: [],
802
- company_location: [],
803
- location: [],
804
- market: [],
805
- order: [],
806
- page: [],
807
- product: [],
808
- variant: [],
809
- shop: [],
810
- }),
811
533
  fs,
812
534
  documentsLocator,
813
535
  async () => rootUri,
@@ -871,23 +593,7 @@ query {
871
593
  objects: async () => [],
872
594
  liquidDrops: async () => [],
873
595
  filters: async () => [],
874
- systemTranslations: async () => ({}),
875
596
  },
876
- async () => [],
877
- async () => ({
878
- article: [],
879
- blog: [],
880
- collection: [],
881
- company: [],
882
- company_location: [],
883
- location: [],
884
- market: [],
885
- order: [],
886
- page: [],
887
- product: [],
888
- variant: [],
889
- shop: [],
890
- }),
891
597
  fs,
892
598
  documentsLocator,
893
599
  async () => rootUri,
@@ -957,23 +663,7 @@ query {
957
663
  objects: async () => [],
958
664
  liquidDrops: async () => [],
959
665
  filters: async () => [],
960
- systemTranslations: async () => ({}),
961
666
  },
962
- async () => [],
963
- async () => ({
964
- article: [],
965
- blog: [],
966
- collection: [],
967
- company: [],
968
- company_location: [],
969
- location: [],
970
- market: [],
971
- order: [],
972
- page: [],
973
- product: [],
974
- variant: [],
975
- shop: [],
976
- }),
977
667
  fs,
978
668
  documentsLocator,
979
669
  async () => rootUri,
@@ -1018,23 +708,7 @@ query {
1018
708
  objects: async () => [],
1019
709
  liquidDrops: async () => [],
1020
710
  filters: async () => [],
1021
- systemTranslations: async () => ({}),
1022
711
  },
1023
- async () => [],
1024
- async () => ({
1025
- article: [],
1026
- blog: [],
1027
- collection: [],
1028
- company: [],
1029
- company_location: [],
1030
- location: [],
1031
- market: [],
1032
- order: [],
1033
- page: [],
1034
- product: [],
1035
- variant: [],
1036
- shop: [],
1037
- }),
1038
712
  fs,
1039
713
  documentsLocator,
1040
714
  async () => rootUri,
@@ -1066,82 +740,165 @@ query {
1066
740
  });
1067
741
  });
1068
742
 
1069
- describe('metafieldDefinitionsObjectMap', async () => {
1070
- it('should convert metafield definitions into types', async () => {
1071
- const metafieldObjectMap =
1072
- await typeSystem.metafieldDefinitionsObjectMap('file:///any/file.liquid');
743
+ describe('JSON literal type inference', () => {
744
+ it('should infer shape from a JSON hash literal', async () => {
745
+ const ast = toLiquidHtmlAST(`{% assign a = {x: 1, y: "hello"} %}{{ a }}`);
746
+ const variableOutput = ast.children[1];
747
+ assert(isLiquidVariableOutput(variableOutput));
748
+ const inferredType = await typeSystem.inferType(
749
+ variableOutput.markup,
750
+ ast,
751
+ 'file:///file.liquid',
752
+ );
753
+ expect(inferredType).to.have.property('kind', 'shape');
754
+ if (typeof inferredType !== 'string' && inferredType.kind === 'shape') {
755
+ expect(inferredType.shape.kind).to.equal('object');
756
+ expect(inferredType.shape.properties?.get('x')).to.deep.equal({
757
+ kind: 'primitive',
758
+ primitiveType: 'number',
759
+ });
760
+ expect(inferredType.shape.properties?.get('y')).to.deep.equal({
761
+ kind: 'primitive',
762
+ primitiveType: 'string',
763
+ });
764
+ }
765
+ });
1073
766
 
1074
- assert(metafieldObjectMap['product_metafields']);
1075
- assert(metafieldObjectMap['product_metafield_custom']);
1076
- assert(metafieldObjectMap['product_metafield_manufacturer']);
767
+ it('should infer an empty object shape from {}', async () => {
768
+ const ast = toLiquidHtmlAST(`{% assign a = {} %}{{ a }}`);
769
+ const variableOutput = ast.children[1];
770
+ assert(isLiquidVariableOutput(variableOutput));
771
+ const inferredType = await typeSystem.inferType(
772
+ variableOutput.markup,
773
+ ast,
774
+ 'file:///file.liquid',
775
+ );
776
+ expect(inferredType).to.have.property('kind', 'shape');
777
+ if (typeof inferredType !== 'string' && inferredType.kind === 'shape') {
778
+ expect(inferredType.shape.kind).to.equal('object');
779
+ expect(inferredType.shape.properties?.size).to.equal(0);
780
+ }
1077
781
  });
1078
782
 
1079
- it('should group metafield definitions by namespace', async () => {
1080
- const metafieldObjectMap =
1081
- await typeSystem.metafieldDefinitionsObjectMap('file:///any/file.liquid');
1082
- const properties = metafieldObjectMap['product_metafields'].properties;
1083
-
1084
- assert(properties);
1085
- expect(properties).toHaveLength(2);
1086
- expect(properties).toContainEqual(
1087
- expect.objectContaining({
1088
- name: 'custom',
1089
- return_type: [{ type: 'product_metafield_custom', name: '' }],
1090
- }),
783
+ it('should infer array shape from a JSON array literal', async () => {
784
+ const ast = toLiquidHtmlAST(`{% assign a = [1, 2, 3] %}{{ a }}`);
785
+ const variableOutput = ast.children[1];
786
+ assert(isLiquidVariableOutput(variableOutput));
787
+ const inferredType = await typeSystem.inferType(
788
+ variableOutput.markup,
789
+ ast,
790
+ 'file:///file.liquid',
1091
791
  );
1092
- expect(metafieldObjectMap['product_metafields'].properties).toContainEqual(
1093
- expect.objectContaining({
1094
- name: 'manufacturer',
1095
- return_type: [{ type: 'product_metafield_manufacturer', name: '' }],
1096
- }),
792
+ expect(inferredType).to.have.property('kind', 'shape');
793
+ if (typeof inferredType !== 'string' && inferredType.kind === 'shape') {
794
+ expect(inferredType.shape.kind).to.equal('array');
795
+ expect(inferredType.shape.itemShape).to.deep.equal({
796
+ kind: 'primitive',
797
+ primitiveType: 'number',
798
+ });
799
+ }
800
+ });
801
+
802
+ it('should infer nested object shapes', async () => {
803
+ const ast = toLiquidHtmlAST(`{% assign a = {"nested": {"deep": 42}} %}{{ a.nested.deep }}`);
804
+ const variableOutput = ast.children[1];
805
+ assert(isLiquidVariableOutput(variableOutput));
806
+ const inferredType = await typeSystem.inferType(
807
+ variableOutput.markup,
808
+ ast,
809
+ 'file:///file.liquid',
1097
810
  );
811
+ expect(inferredType).to.equal('number');
812
+ });
1098
813
 
1099
- const manufacturerProperties =
1100
- metafieldObjectMap['product_metafield_manufacturer'].properties;
814
+ it('should produce the same shape as parse_json for equivalent JSON', async () => {
815
+ const astLiteral = toLiquidHtmlAST(`{% assign a = {a: 2} %}{{ a }}`);
816
+ const astParseJson = toLiquidHtmlAST(`{% assign b = '{"a": 2}' | parse_json %}{{ b }}`);
1101
817
 
1102
- assert(manufacturerProperties);
1103
- expect(manufacturerProperties).toHaveLength(2);
818
+ const outputLiteral = astLiteral.children[1];
819
+ const outputParseJson = astParseJson.children[1];
820
+ assert(isLiquidVariableOutput(outputLiteral));
821
+ assert(isLiquidVariableOutput(outputParseJson));
1104
822
 
1105
- expect(manufacturerProperties).toContainEqual(
1106
- expect.objectContaining({
1107
- name: 'code',
1108
- return_type: [{ type: 'metafield_string', name: '' }],
1109
- }),
823
+ const typeLiteral = await typeSystem.inferType(
824
+ outputLiteral.markup,
825
+ astLiteral,
826
+ 'file:///file.liquid',
1110
827
  );
1111
- expect(manufacturerProperties).toContainEqual(
1112
- expect.objectContaining({
1113
- name: 'id',
1114
- return_type: [{ type: 'metafield_number', name: '' }],
1115
- }),
828
+ const typeParseJson = await typeSystem.inferType(
829
+ outputParseJson.markup,
830
+ astParseJson,
831
+ 'file:///file.liquid',
1116
832
  );
1117
833
 
1118
- const customProperties = metafieldObjectMap['product_metafield_custom'].properties;
1119
-
1120
- assert(customProperties);
1121
- expect(customProperties).toHaveLength(1);
834
+ expect(typeLiteral).to.have.property('kind', 'shape');
835
+ expect(typeParseJson).to.have.property('kind', 'shape');
836
+ if (
837
+ typeof typeLiteral !== 'string' &&
838
+ typeLiteral.kind === 'shape' &&
839
+ typeof typeParseJson !== 'string' &&
840
+ typeParseJson.kind === 'shape'
841
+ ) {
842
+ // Both should have an 'a' property with number type
843
+ expect(typeLiteral.shape.properties?.get('a')).to.deep.equal(
844
+ typeParseJson.shape.properties?.get('a'),
845
+ );
846
+ }
847
+ });
1122
848
 
1123
- expect(customProperties).toContainEqual(
1124
- expect.objectContaining({
1125
- name: 'is_rare',
1126
- return_type: [{ type: 'metafield_boolean', name: '' }],
1127
- }),
849
+ it('should support LHS lookups with assign (assign x["key"] = value)', async () => {
850
+ const ast = toLiquidHtmlAST(
851
+ `{% assign config = {} %}{% assign config["key"] = "value" %}{{ config }}`,
1128
852
  );
853
+ const variableOutput = ast.children[2];
854
+ assert(isLiquidVariableOutput(variableOutput));
855
+ const inferredType = await typeSystem.inferType(
856
+ variableOutput.markup,
857
+ ast,
858
+ 'file:///file.liquid',
859
+ );
860
+ expect(inferredType).to.have.property('kind', 'shape');
861
+ if (typeof inferredType !== 'string' && inferredType.kind === 'shape') {
862
+ expect(inferredType.shape.properties?.get('key')).to.exist;
863
+ }
1129
864
  });
1130
865
 
1131
- it('should have `metafield_x_array` return_type for array of references', async () => {
1132
- const metafieldObjectMap =
1133
- await typeSystem.metafieldDefinitionsObjectMap('file:///any/file.liquid');
1134
- const relatedProperties = metafieldObjectMap['order_metafield_related'].properties;
1135
-
1136
- assert(relatedProperties);
1137
- expect(relatedProperties).toHaveLength(1);
866
+ it('should support << operator (array append)', async () => {
867
+ const ast = toLiquidHtmlAST(`{% assign arr = [] %}{% assign arr << "item" %}{{ arr }}`);
868
+ const variableOutput = ast.children[2];
869
+ assert(isLiquidVariableOutput(variableOutput));
870
+ const inferredType = await typeSystem.inferType(
871
+ variableOutput.markup,
872
+ ast,
873
+ 'file:///file.liquid',
874
+ );
875
+ expect(inferredType).to.have.property('kind', 'shape');
876
+ if (typeof inferredType !== 'string' && inferredType.kind === 'shape') {
877
+ expect(inferredType.shape.kind).to.equal('array');
878
+ expect(inferredType.shape.itemShape).to.deep.equal({
879
+ kind: 'primitive',
880
+ primitiveType: 'string',
881
+ });
882
+ }
883
+ });
1138
884
 
1139
- expect(relatedProperties).toContainEqual(
1140
- expect.objectContaining({
1141
- name: 'prods',
1142
- return_type: [{ type: 'metafield_product_array', name: '' }],
1143
- }),
885
+ it('should support explicit push form (assign a = source << value)', async () => {
886
+ const ast = toLiquidHtmlAST(`{% assign arr = [] %}{% assign arr = arr << "item" %}{{ arr }}`);
887
+ const variableOutput = ast.children[2];
888
+ assert(isLiquidVariableOutput(variableOutput));
889
+ const inferredType = await typeSystem.inferType(
890
+ variableOutput.markup,
891
+ ast,
892
+ 'file:///file.liquid',
1144
893
  );
894
+ expect(inferredType).to.have.property('kind', 'shape');
895
+ if (typeof inferredType !== 'string' && inferredType.kind === 'shape') {
896
+ expect(inferredType.shape.kind).to.equal('array');
897
+ expect(inferredType.shape.itemShape).to.deep.equal({
898
+ kind: 'primitive',
899
+ primitiveType: 'string',
900
+ });
901
+ }
1145
902
  });
1146
903
  });
1147
904
  });