@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.
- package/CHANGELOG.md +20 -0
- package/dist/PropertyShapeInference.d.ts +18 -0
- package/dist/PropertyShapeInference.js +62 -0
- package/dist/PropertyShapeInference.js.map +1 -1
- package/dist/TypeSystem.d.ts +4 -9
- package/dist/TypeSystem.js +246 -390
- package/dist/TypeSystem.js.map +1 -1
- package/dist/codeActions/providers/GenerateDocBlockProvider.d.ts +10 -0
- package/dist/codeActions/providers/GenerateDocBlockProvider.js +39 -0
- package/dist/codeActions/providers/GenerateDocBlockProvider.js.map +1 -0
- package/dist/commands/providers/GenerateDocBlockCommandProvider.d.ts +19 -0
- package/dist/commands/providers/GenerateDocBlockCommandProvider.js +64 -0
- package/dist/commands/providers/GenerateDocBlockCommandProvider.js.map +1 -0
- package/dist/completions/CompletionsProvider.d.ts +6 -10
- package/dist/completions/CompletionsProvider.js +6 -9
- package/dist/completions/CompletionsProvider.js.map +1 -1
- package/dist/completions/params/LiquidCompletionParams.js +6 -17
- package/dist/completions/params/LiquidCompletionParams.js.map +1 -1
- package/dist/completions/providers/ContentForCompletionProvider.js +2 -4
- package/dist/completions/providers/ContentForCompletionProvider.js.map +1 -1
- package/dist/completions/providers/FilterNamedParameterCompletionProvider.d.ts +3 -3
- package/dist/completions/providers/FilterNamedParameterCompletionProvider.js +6 -6
- package/dist/completions/providers/FilterNamedParameterCompletionProvider.js.map +1 -1
- package/dist/completions/providers/LiquidDocParamTypeCompletionProvider.d.ts +3 -3
- package/dist/completions/providers/LiquidDocParamTypeCompletionProvider.js +3 -3
- package/dist/completions/providers/LiquidDocParamTypeCompletionProvider.js.map +1 -1
- package/dist/completions/providers/LiquidTagsCompletionProvider.d.ts +3 -3
- package/dist/completions/providers/LiquidTagsCompletionProvider.js +3 -3
- package/dist/completions/providers/LiquidTagsCompletionProvider.js.map +1 -1
- package/dist/completions/providers/RenderPartialParameterCompletionProvider.js +1 -1
- package/dist/completions/providers/RenderPartialParameterCompletionProvider.js.map +1 -1
- package/dist/completions/providers/index.d.ts +0 -3
- package/dist/completions/providers/index.js +1 -7
- package/dist/completions/providers/index.js.map +1 -1
- package/dist/css/CSSLanguageService.d.ts +19 -5
- package/dist/css/CSSLanguageService.js +77 -63
- package/dist/css/CSSLanguageService.js.map +1 -1
- package/dist/definitions/DefinitionProvider.d.ts +1 -1
- package/dist/definitions/DefinitionProvider.js +1 -3
- package/dist/definitions/DefinitionProvider.js.map +1 -1
- package/dist/diagnostics/offenseToDiagnostic.js +1 -1
- package/dist/diagnostics/offenseToDiagnostic.js.map +1 -1
- package/dist/diagnostics/runChecks.d.ts +3 -5
- package/dist/diagnostics/runChecks.js +12 -64
- package/dist/diagnostics/runChecks.js.map +1 -1
- package/dist/docset/MarkdownRenderer.js +1 -1
- package/dist/docset/MarkdownRenderer.js.map +1 -1
- package/dist/documentLinks/DocumentLinksProvider.d.ts +3 -3
- package/dist/documentLinks/DocumentLinksProvider.js +3 -3
- package/dist/documentLinks/DocumentLinksProvider.js.map +1 -1
- package/dist/documents/DocumentManager.d.ts +4 -5
- package/dist/documents/DocumentManager.js +17 -12
- package/dist/documents/DocumentManager.js.map +1 -1
- package/dist/documents/types.d.ts +16 -3
- package/dist/documents/types.js +3 -3
- package/dist/documents/types.js.map +1 -1
- package/dist/hover/HoverProvider.d.ts +5 -8
- package/dist/hover/HoverProvider.js +8 -14
- package/dist/hover/HoverProvider.js.map +1 -1
- package/dist/hover/providers/LiquidFilterArgumentHoverProvider.d.ts +3 -3
- package/dist/hover/providers/LiquidFilterArgumentHoverProvider.js +3 -3
- package/dist/hover/providers/LiquidFilterArgumentHoverProvider.js.map +1 -1
- package/dist/hover/providers/LiquidFilterHoverProvider.d.ts +3 -3
- package/dist/hover/providers/LiquidFilterHoverProvider.js +3 -3
- package/dist/hover/providers/LiquidFilterHoverProvider.js.map +1 -1
- package/dist/hover/providers/LiquidTagHoverProvider.d.ts +3 -3
- package/dist/hover/providers/LiquidTagHoverProvider.js +3 -3
- package/dist/hover/providers/LiquidTagHoverProvider.js.map +1 -1
- package/dist/hover/providers/RenderPartialHoverProvider.js +1 -1
- package/dist/hover/providers/RenderPartialHoverProvider.js.map +1 -1
- package/dist/hover/providers/RenderPartialParameterHoverProvider.js +1 -1
- package/dist/hover/providers/RenderPartialParameterHoverProvider.js.map +1 -1
- package/dist/hover/providers/TranslationHoverProvider.d.ts +3 -3
- package/dist/hover/providers/TranslationHoverProvider.js +3 -3
- package/dist/hover/providers/TranslationHoverProvider.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js.map +1 -1
- package/dist/internal-types.d.ts +1 -1
- package/dist/json/JSONContributions.d.ts +1 -5
- package/dist/json/JSONContributions.js +4 -36
- package/dist/json/JSONContributions.js.map +1 -1
- package/dist/json/JSONLanguageService.d.ts +4 -12
- package/dist/json/JSONLanguageService.js +17 -80
- package/dist/json/JSONLanguageService.js.map +1 -1
- package/dist/json/test/test-helpers.d.ts +1 -3
- package/dist/json/test/test-helpers.js +3 -19
- package/dist/json/test/test-helpers.js.map +1 -1
- package/dist/json/utils.d.ts +0 -5
- package/dist/json/utils.js +0 -24
- package/dist/json/utils.js.map +1 -1
- package/dist/rename/RenameProvider.d.ts +2 -2
- package/dist/rename/RenameProvider.js +2 -2
- package/dist/rename/RenameProvider.js.map +1 -1
- package/dist/rename/providers/LiquidVariableRenameProvider.d.ts +3 -3
- package/dist/rename/providers/LiquidVariableRenameProvider.js +4 -4
- package/dist/rename/providers/LiquidVariableRenameProvider.js.map +1 -1
- package/dist/renamed/RenameHandler.d.ts +2 -3
- package/dist/renamed/RenameHandler.js +3 -8
- package/dist/renamed/RenameHandler.js.map +1 -1
- package/dist/renamed/handlers/AssetRenameHandler.d.ts +3 -3
- package/dist/renamed/handlers/AssetRenameHandler.js +5 -5
- package/dist/renamed/handlers/AssetRenameHandler.js.map +1 -1
- package/dist/renamed/handlers/PartialRenameHandler.d.ts +3 -3
- package/dist/renamed/handlers/PartialRenameHandler.js +5 -5
- package/dist/renamed/handlers/PartialRenameHandler.js.map +1 -1
- package/dist/renamed/handlers/utils.d.ts +1 -1
- package/dist/server/AppGraphManager.d.ts +32 -0
- package/dist/server/AppGraphManager.js +155 -0
- package/dist/server/AppGraphManager.js.map +1 -0
- package/dist/server/startServer.d.ts +2 -2
- package/dist/server/startServer.js +62 -164
- package/dist/server/startServer.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/types.d.ts +33 -54
- package/dist/types.js +21 -26
- package/dist/types.js.map +1 -1
- package/dist/utils/liquidDoc.js +1 -1
- package/dist/utils/liquidDoc.js.map +1 -1
- package/dist/utils/uri.d.ts +2 -9
- package/dist/utils/uri.js +5 -21
- package/dist/utils/uri.js.map +1 -1
- package/package.json +4 -4
- package/src/ClientCapabilities.ts +1 -1
- package/src/PropertyShapeInference.ts +81 -1
- package/src/TypeSystem.spec.ts +206 -449
- package/src/TypeSystem.ts +267 -460
- package/src/codeActions/providers/FixAllProvider.spec.ts +1 -1
- package/src/codeActions/providers/FixProvider.spec.ts +11 -11
- package/src/codeActions/providers/SuggestionProvider.spec.ts +2 -2
- package/src/commands/providers/ApplyFixesProvider.ts +2 -2
- package/src/commands/providers/ApplySuggestionProvider.ts +2 -2
- package/src/commands/providers/RunChecksProvider.ts +2 -2
- package/src/completions/CompletionsProvider.ts +14 -35
- package/src/completions/params/LiquidCompletionParams.spec.ts +0 -9
- package/src/completions/params/LiquidCompletionParams.ts +8 -17
- package/src/completions/params/fix.spec.ts +0 -3
- package/src/completions/providers/FilterCompletionProvider.spec.ts +2 -8
- package/src/completions/providers/FilterNamedParameterCompletionProvider.spec.ts +1 -5
- package/src/completions/providers/FilterNamedParameterCompletionProvider.ts +6 -6
- package/src/completions/providers/HtmlAttributeCompletionProvider.spec.ts +1 -4
- package/src/completions/providers/HtmlAttributeValueCompletionProvider.spec.ts +1 -4
- package/src/completions/providers/HtmlTagCompletionProvider.spec.ts +1 -4
- package/src/completions/providers/LiquidDocParamTypeCompletionProvider.spec.ts +1 -28
- package/src/completions/providers/LiquidDocParamTypeCompletionProvider.ts +3 -3
- package/src/completions/providers/LiquidDocTagCompletionProvider.spec.ts +1 -26
- package/src/completions/providers/LiquidTagsCompletionProvider.spec.ts +4 -18
- package/src/completions/providers/LiquidTagsCompletionProvider.ts +3 -3
- package/src/completions/providers/ObjectAttributeCompletionProvider.spec.ts +2 -31
- package/src/completions/providers/ObjectCompletionProvider.spec.ts +34 -123
- package/src/completions/providers/PartialCompletionProvider.spec.ts +6 -15
- package/src/completions/providers/RenderPartialParameterCompletionProvider.spec.ts +13 -15
- package/src/completions/providers/RenderPartialParameterCompletionProvider.ts +2 -3
- package/src/completions/providers/TranslationCompletionProvider.spec.ts +1 -4
- package/src/completions/providers/TranslationCompletionProvider.ts +3 -3
- package/src/completions/providers/index.ts +0 -3
- package/src/css/CSSLanguageService.ts +97 -81
- package/src/definitions/DefinitionProvider.ts +0 -6
- package/src/definitions/providers/TranslationStringDefinitionProvider.spec.ts +1 -5
- package/src/diagnostics/offenseToDiagnostic.ts +1 -1
- package/src/diagnostics/runChecks.spec.ts +22 -30
- package/src/diagnostics/runChecks.ts +17 -91
- package/src/docset/MarkdownRenderer.ts +1 -1
- package/src/documentLinks/DocumentLinksProvider.ts +10 -10
- package/src/documents/DocumentManager.spec.ts +46 -119
- package/src/documents/DocumentManager.ts +19 -15
- package/src/documents/types.ts +18 -9
- package/src/hover/HoverProvider.ts +9 -21
- package/src/hover/providers/HtmlAttributeHoverProvider.spec.ts +1 -3
- package/src/hover/providers/HtmlAttributeValueHoverProvider.spec.ts +1 -3
- package/src/hover/providers/HtmlTagHoverProvider.spec.ts +1 -3
- package/src/hover/providers/LiquidDocTagHoverProvider.spec.ts +1 -4
- package/src/hover/providers/LiquidFilterArgumentHoverProvider.spec.ts +0 -3
- package/src/hover/providers/LiquidFilterArgumentHoverProvider.ts +3 -3
- package/src/hover/providers/LiquidFilterHoverProvider.spec.ts +0 -3
- package/src/hover/providers/LiquidFilterHoverProvider.ts +3 -3
- package/src/hover/providers/LiquidObjectAttributeHoverProvider.spec.ts +1 -3
- package/src/hover/providers/LiquidObjectHoverProvider.spec.ts +46 -129
- package/src/hover/providers/LiquidTagHoverProvider.spec.ts +0 -3
- package/src/hover/providers/LiquidTagHoverProvider.ts +3 -3
- package/src/hover/providers/RenderPartialHoverProvider.spec.ts +11 -14
- package/src/hover/providers/RenderPartialHoverProvider.ts +1 -5
- package/src/hover/providers/RenderPartialParameterHoverProvider.spec.ts +10 -13
- package/src/hover/providers/RenderPartialParameterHoverProvider.ts +2 -3
- package/src/hover/providers/TranslationHoverProvider.spec.ts +1 -4
- package/src/hover/providers/TranslationHoverProvider.ts +3 -3
- package/src/index.ts +4 -7
- package/src/internal-types.ts +1 -1
- package/src/json/JSONContributions.ts +4 -61
- package/src/json/JSONLanguageService.ts +19 -117
- package/src/json/test/test-helpers.ts +4 -35
- package/src/json/utils.ts +0 -27
- package/src/rename/RenameProvider.ts +4 -4
- package/src/rename/providers/LiquidVariableRenameProvider.spec.ts +5 -5
- package/src/rename/providers/LiquidVariableRenameProvider.ts +6 -6
- package/src/renamed/RenameHandler.ts +4 -9
- package/src/renamed/handlers/AssetRenameHandler.spec.ts +28 -28
- package/src/renamed/handlers/AssetRenameHandler.ts +6 -6
- package/src/renamed/handlers/PartialRenameHandler.spec.ts +9 -9
- package/src/renamed/handlers/PartialRenameHandler.ts +10 -10
- package/src/server/{ThemeGraphManager.ts → AppGraphManager.ts} +21 -90
- package/src/server/safe.ts +1 -1
- package/src/server/startServer.spec.ts +10 -12
- package/src/server/startServer.ts +69 -222
- package/src/test/CompletionItemsAssertion.spec.ts +1 -4
- package/src/types.ts +33 -63
- package/src/utils/debounce.ts +1 -1
- package/src/utils/liquidDoc.ts +6 -6
- package/src/utils/uri.ts +5 -17
- package/src/completions/providers/ContentForBlockTypeCompletionProvider.spec.ts +0 -41
- package/src/completions/providers/ContentForBlockTypeCompletionProvider.ts +0 -42
- package/src/completions/providers/ContentForCompletionProvider.spec.ts +0 -96
- package/src/completions/providers/ContentForCompletionProvider.ts +0 -113
- package/src/completions/providers/ContentForParameterCompletionProvider.spec.ts +0 -362
- package/src/completions/providers/ContentForParameterCompletionProvider.ts +0 -187
- package/src/completions/providers/data/contentForParameterCompletionOptions.ts +0 -9
- package/src/css/CSSLanguageService.spec.ts +0 -159
- package/src/css/test/test-helpers.ts +0 -30
- package/src/definitions/providers/SchemaTranslationStringDefinitionProvider.spec.ts +0 -96
- package/src/definitions/providers/SchemaTranslationStringDefinitionProvider.ts +0 -82
- package/src/hover/providers/ContentForArgumentHoverProvider.spec.ts +0 -76
- package/src/hover/providers/ContentForArgumentHoverProvider.ts +0 -56
- package/src/hover/providers/ContentForTypeHoverProvider.spec.ts +0 -91
- package/src/hover/providers/ContentForTypeHoverProvider.ts +0 -42
- package/src/json/JSONLanguageService.spec.ts +0 -424
- package/src/json/completions/providers/BlockSettingsPropertyCompletionProvider.spec.ts +0 -163
- package/src/json/completions/providers/BlockSettingsPropertyCompletionProvider.ts +0 -121
- package/src/json/completions/providers/BlockTypeCompletionProvider.spec.ts +0 -137
- package/src/json/completions/providers/BlockTypeCompletionProvider.ts +0 -108
- package/src/json/completions/providers/ReferencedBlockTypeCompletionProvider.spec.ts +0 -132
- package/src/json/completions/providers/ReferencedBlockTypeCompletionProvider.ts +0 -120
- package/src/json/completions/providers/SchemaTranslationCompletionProvider.ts +0 -51
- package/src/json/completions/providers/SettingsPropertyCompletionProvider.spec.ts +0 -211
- package/src/json/completions/providers/SettingsPropertyCompletionProvider.ts +0 -75
- package/src/json/documentLinks/DocumentLinksProvider.spec.ts +0 -223
- package/src/json/documentLinks/DocumentLinksProvider.ts +0 -61
- package/src/json/hover/providers/BlockSettingsHoverProvider.spec.ts +0 -292
- package/src/json/hover/providers/BlockSettingsHoverProvider.ts +0 -99
- package/src/json/hover/providers/SchemaTranslationHoverProvider.ts +0 -35
- package/src/json/hover/providers/SettingsHoverProvider.spec.ts +0 -139
- package/src/json/hover/providers/SettingsHoverProvider.ts +0 -79
- package/src/json/schemaSettings.spec.ts +0 -103
- package/src/json/schemaSettings.ts +0 -53
- package/src/plugins/css.ts +0 -12
- package/src/renamed/handlers/BlockRenameHandler.spec.ts +0 -399
- package/src/renamed/handlers/BlockRenameHandler.ts +0 -402
- package/src/renamed/handlers/SectionRenameHandler.spec.ts +0 -321
- package/src/renamed/handlers/SectionRenameHandler.ts +0 -224
- package/src/renamed/handlers/utils.ts +0 -22
- package/src/settings/index.ts +0 -50
package/src/TypeSystem.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
AssignMarkup,
|
|
3
|
+
AssignPushRhs,
|
|
3
4
|
ComplexLiquidExpression,
|
|
4
5
|
FunctionMarkup,
|
|
5
6
|
LiquidDocParamNode,
|
|
@@ -23,33 +24,26 @@ import {
|
|
|
23
24
|
ArrayReturnType,
|
|
24
25
|
DocsetEntry,
|
|
25
26
|
FilterEntry,
|
|
26
|
-
MetafieldDefinitionMap,
|
|
27
|
-
MetafieldDefinition,
|
|
28
27
|
ObjectEntry,
|
|
29
28
|
ReturnType,
|
|
30
29
|
SourceCodeType,
|
|
31
|
-
|
|
30
|
+
PlatformOSDocset,
|
|
32
31
|
isError,
|
|
33
32
|
parseJSON,
|
|
34
33
|
path,
|
|
35
|
-
FETCHED_METAFIELD_CATEGORIES,
|
|
36
34
|
BasicParamTypes,
|
|
37
35
|
getValidParamTypes,
|
|
38
36
|
parseParamType,
|
|
39
37
|
} from '@platformos/platformos-check-common';
|
|
40
|
-
import {
|
|
41
|
-
GetThemeSettingsSchemaForURI,
|
|
42
|
-
InputSetting,
|
|
43
|
-
isInputSetting,
|
|
44
|
-
isSettingsCategory,
|
|
45
|
-
} from './settings';
|
|
46
38
|
import { findLast, memo } from './utils';
|
|
47
39
|
import { visit } from '@platformos/platformos-check-common';
|
|
48
40
|
import {
|
|
49
41
|
PropertyShape,
|
|
50
42
|
inferShapeFromJSONString,
|
|
43
|
+
inferShapeFromJsonLiteral,
|
|
51
44
|
inferShapeFromGraphQL,
|
|
52
45
|
lookupPropertyPath,
|
|
46
|
+
mergeShapes,
|
|
53
47
|
shapeToTypeString,
|
|
54
48
|
shapeToDetailString,
|
|
55
49
|
} from './PropertyShapeInference';
|
|
@@ -61,17 +55,15 @@ export class TypeSystem {
|
|
|
61
55
|
private graphqlSchemaLoaded = false;
|
|
62
56
|
|
|
63
57
|
constructor(
|
|
64
|
-
private readonly
|
|
65
|
-
private readonly getThemeSettingsSchemaForURI: GetThemeSettingsSchemaForURI,
|
|
66
|
-
private readonly getMetafieldDefinitions: (rootUri: string) => Promise<MetafieldDefinitionMap>,
|
|
58
|
+
private readonly platformosDocset: PlatformOSDocset,
|
|
67
59
|
private readonly fs?: AbstractFileSystem,
|
|
68
60
|
private readonly documentsLocator?: DocumentsLocator,
|
|
69
|
-
private readonly
|
|
61
|
+
private readonly findAppRootURI?: (uri: string) => Promise<string | null>,
|
|
70
62
|
) {}
|
|
71
63
|
|
|
72
64
|
private async getGraphQLSchema(): Promise<string | undefined> {
|
|
73
65
|
if (!this.graphqlSchemaLoaded) {
|
|
74
|
-
this.graphqlSchemaCache = (await this.
|
|
66
|
+
this.graphqlSchemaCache = (await this.platformosDocset.graphQL()) ?? undefined;
|
|
75
67
|
this.graphqlSchemaLoaded = true;
|
|
76
68
|
}
|
|
77
69
|
return this.graphqlSchemaCache;
|
|
@@ -125,174 +117,15 @@ export class TypeSystem {
|
|
|
125
117
|
});
|
|
126
118
|
}
|
|
127
119
|
|
|
128
|
-
public async themeSettingProperties(uri: string): Promise<ObjectEntry[]> {
|
|
129
|
-
const themeSettingsSchema = await this.getThemeSettingsSchemaForURI(uri);
|
|
130
|
-
const categories = themeSettingsSchema.filter(isSettingsCategory);
|
|
131
|
-
const result: ObjectEntry[] = [];
|
|
132
|
-
for (const category of categories) {
|
|
133
|
-
const inputSettings = category.settings.filter(isInputSetting);
|
|
134
|
-
for (const setting of inputSettings) {
|
|
135
|
-
result.push({
|
|
136
|
-
name: setting.id,
|
|
137
|
-
summary: '', // TODO, this should lookup the locale file for settings... setting.label
|
|
138
|
-
description: '', // TODO , this should lookup the locale file as well... setting.info,
|
|
139
|
-
return_type: settingReturnType(setting),
|
|
140
|
-
access: {
|
|
141
|
-
global: false,
|
|
142
|
-
parents: [],
|
|
143
|
-
template: [],
|
|
144
|
-
},
|
|
145
|
-
});
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
return result;
|
|
149
|
-
}
|
|
150
|
-
|
|
151
120
|
/**
|
|
152
121
|
* An indexed representation of objects.json by name
|
|
153
122
|
*
|
|
154
123
|
* e.g. objectMap['product'] returns the product ObjectEntry.
|
|
155
124
|
*/
|
|
156
125
|
public objectMap = async (uri: string, ast: LiquidHtmlNode): Promise<ObjectMap> => {
|
|
157
|
-
|
|
158
|
-
this._objectMap(),
|
|
159
|
-
this.themeSettingProperties(uri),
|
|
160
|
-
this.metafieldDefinitionsObjectMap(uri),
|
|
161
|
-
]);
|
|
162
|
-
|
|
163
|
-
// Here we shallow mutate `settings.properties` to have the properties made
|
|
164
|
-
// available by settings_schema.json
|
|
165
|
-
const result: ObjectMap = {
|
|
166
|
-
...objectMap,
|
|
167
|
-
settings: {
|
|
168
|
-
...(objectMap.settings ?? {}),
|
|
169
|
-
properties: themeSettingProperties,
|
|
170
|
-
},
|
|
171
|
-
...customMetafieldTypeEntries(objectMap['metafield']),
|
|
172
|
-
...metafieldDefinitionsObjectMap,
|
|
173
|
-
};
|
|
174
|
-
|
|
175
|
-
// For each metafield definition fetched, we need to override existing types with `metafields` property
|
|
176
|
-
// to `${category}_metafield`.
|
|
177
|
-
//
|
|
178
|
-
// WARNING: Since we aren't cloning the object, we are mutating the original type for all themes in
|
|
179
|
-
// the workspace. However, this is fine since these changes are not unique to a theme.
|
|
180
|
-
for (let category of FETCHED_METAFIELD_CATEGORIES) {
|
|
181
|
-
if (!result[category]) continue;
|
|
182
|
-
|
|
183
|
-
let metafieldsProperty = result[category].properties?.find(
|
|
184
|
-
(prop) => prop.name === 'metafields',
|
|
185
|
-
);
|
|
186
|
-
|
|
187
|
-
if (!metafieldsProperty) continue;
|
|
188
|
-
|
|
189
|
-
metafieldsProperty.return_type = [{ type: `${category}_metafields`, name: '' }];
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
// Deal with sections/file.liquid section.settings by infering the type from the {% schema %}
|
|
193
|
-
if (/[\/\\]sections[\/\\]/.test(uri) && result.section) {
|
|
194
|
-
result.section = JSON.parse(JSON.stringify(result.section)); // easy deep clone
|
|
195
|
-
const settings = result.section.properties?.find((x) => x.name === 'settings');
|
|
196
|
-
if (!settings || !settings.return_type) return result;
|
|
197
|
-
settings.return_type = [{ type: 'section_settings', name: '' }];
|
|
198
|
-
result.section_settings = {
|
|
199
|
-
name: 'section_settings',
|
|
200
|
-
access: {
|
|
201
|
-
global: false,
|
|
202
|
-
parents: [],
|
|
203
|
-
template: [],
|
|
204
|
-
},
|
|
205
|
-
properties: schemaSettingsAsProperties(ast),
|
|
206
|
-
return_type: [],
|
|
207
|
-
};
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
// Deal with blocks/files.liquid block.settings in a similar fashion
|
|
211
|
-
if (/[\/\\]blocks[\/\\]/.test(uri) && result.block) {
|
|
212
|
-
result.block = JSON.parse(JSON.stringify(result.block)); // easy deep clone
|
|
213
|
-
const settings = result.block.properties?.find((x) => x.name === 'settings');
|
|
214
|
-
if (!settings || !settings.return_type) return result;
|
|
215
|
-
settings.return_type = [{ type: 'block_settings', name: '' }];
|
|
216
|
-
result.block_settings = {
|
|
217
|
-
name: 'block_settings',
|
|
218
|
-
access: {
|
|
219
|
-
global: false,
|
|
220
|
-
parents: [],
|
|
221
|
-
template: [],
|
|
222
|
-
},
|
|
223
|
-
properties: schemaSettingsAsProperties(ast),
|
|
224
|
-
return_type: [],
|
|
225
|
-
};
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
return result;
|
|
126
|
+
return this._objectMap();
|
|
229
127
|
};
|
|
230
128
|
|
|
231
|
-
public async metafieldDefinitionsObjectMap(uri: string): Promise<ObjectMap> {
|
|
232
|
-
let result: ObjectMap = {};
|
|
233
|
-
|
|
234
|
-
const metafieldDefinitionMap = await this.getMetafieldDefinitions(uri);
|
|
235
|
-
|
|
236
|
-
for (let [category, definitions] of Object.entries(metafieldDefinitionMap)) {
|
|
237
|
-
// Metafield definitions need to be grouped by their namespace
|
|
238
|
-
let metafieldNamespaces = new Map<string, ObjectEntry[]>();
|
|
239
|
-
|
|
240
|
-
for (let definition of definitions as MetafieldDefinition[]) {
|
|
241
|
-
if (!metafieldNamespaces.has(definition.namespace)) {
|
|
242
|
-
metafieldNamespaces.set(definition.namespace, []);
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
metafieldNamespaces.get(definition.namespace)!.push({
|
|
246
|
-
name: definition.key,
|
|
247
|
-
description: definition.description,
|
|
248
|
-
return_type: metafieldReturnType(definition.type.name),
|
|
249
|
-
});
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
let metafieldGroupProperties: ObjectEntry[] = [];
|
|
253
|
-
|
|
254
|
-
for (let [namespace, namespaceProperties] of metafieldNamespaces) {
|
|
255
|
-
const metafieldCategoryNamespaceHandle = `${category}_metafield_${namespace}`;
|
|
256
|
-
|
|
257
|
-
// Since the namespace can be shared by multiple categories, we need to make sure the return_type
|
|
258
|
-
// handle is unique across all categories
|
|
259
|
-
metafieldGroupProperties.push({
|
|
260
|
-
name: namespace,
|
|
261
|
-
return_type: [{ type: metafieldCategoryNamespaceHandle, name: '' }],
|
|
262
|
-
access: {
|
|
263
|
-
global: false,
|
|
264
|
-
parents: [],
|
|
265
|
-
template: [],
|
|
266
|
-
},
|
|
267
|
-
});
|
|
268
|
-
|
|
269
|
-
result[metafieldCategoryNamespaceHandle] = {
|
|
270
|
-
name: metafieldCategoryNamespaceHandle,
|
|
271
|
-
properties: namespaceProperties,
|
|
272
|
-
access: {
|
|
273
|
-
global: false,
|
|
274
|
-
parents: [],
|
|
275
|
-
template: [],
|
|
276
|
-
},
|
|
277
|
-
};
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
const metafieldCategoryHandle = `${category}_metafields`;
|
|
281
|
-
|
|
282
|
-
result[metafieldCategoryHandle] = {
|
|
283
|
-
name: metafieldCategoryHandle,
|
|
284
|
-
properties: metafieldGroupProperties,
|
|
285
|
-
access: {
|
|
286
|
-
global: false,
|
|
287
|
-
parents: [],
|
|
288
|
-
template: [],
|
|
289
|
-
},
|
|
290
|
-
};
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
return result;
|
|
294
|
-
}
|
|
295
|
-
|
|
296
129
|
// This is the big one we reuse (memoized)
|
|
297
130
|
private _objectMap = memo(async (): Promise<ObjectMap> => {
|
|
298
131
|
const entries = await this.objectEntries();
|
|
@@ -312,20 +145,20 @@ export class TypeSystem {
|
|
|
312
145
|
});
|
|
313
146
|
|
|
314
147
|
public filterEntries = memo(async () => {
|
|
315
|
-
return this.
|
|
148
|
+
return this.platformosDocset.filters();
|
|
316
149
|
});
|
|
317
150
|
|
|
318
151
|
public objectEntries = memo(async () => {
|
|
319
|
-
return this.
|
|
152
|
+
return this.platformosDocset.objects();
|
|
320
153
|
});
|
|
321
154
|
|
|
322
155
|
private async symbolsTable(partialAst: LiquidHtmlNode, uri: string): Promise<SymbolsTable> {
|
|
323
156
|
const [seedSymbolsTable, liquidDrops, graphqlSchema, rootUri, objectMap, filtersMap] =
|
|
324
157
|
await Promise.all([
|
|
325
158
|
this.seedSymbolsTable(uri),
|
|
326
|
-
this.
|
|
159
|
+
this.platformosDocset.liquidDrops(),
|
|
327
160
|
this.getGraphQLSchema(),
|
|
328
|
-
this.
|
|
161
|
+
this.findAppRootURI?.(uri) ?? null,
|
|
329
162
|
this.objectMap(uri, partialAst),
|
|
330
163
|
this.filtersMap(),
|
|
331
164
|
]);
|
|
@@ -382,36 +215,10 @@ export class TypeSystem {
|
|
|
382
215
|
};
|
|
383
216
|
}
|
|
384
217
|
|
|
385
|
-
const SECTION_FILE_REGEX = /sections[\/\\][^.\\\/]*\.liquid$/;
|
|
386
|
-
const BLOCK_FILE_REGEX = /blocks[\/\\][^.\\\/]*\.liquid$/;
|
|
387
218
|
const PARTIAL_FILE_REGEX = /(views[\/\\]partials[\/\\]|[\/\\]lib[\/\\])[^.]*\.liquid$/;
|
|
388
|
-
const LAYOUT_FILE_REGEX = /layout[\/\\]checkout\.liquid$/;
|
|
389
219
|
|
|
390
220
|
function getContextualEntries(uri: string): string[] {
|
|
391
221
|
const normalizedUri = path.normalize(uri);
|
|
392
|
-
if (LAYOUT_FILE_REGEX.test(normalizedUri)) {
|
|
393
|
-
return [
|
|
394
|
-
'locale',
|
|
395
|
-
'direction',
|
|
396
|
-
'skip_to_content_link',
|
|
397
|
-
'checkout_html_classes',
|
|
398
|
-
'checkout_stylesheets',
|
|
399
|
-
'checkout_scripts',
|
|
400
|
-
'content_for_logo',
|
|
401
|
-
'breadcrumb',
|
|
402
|
-
'order_summary_toggle',
|
|
403
|
-
'content_for_order_summary',
|
|
404
|
-
'alternative_payment_methods',
|
|
405
|
-
'content_for_footer',
|
|
406
|
-
'tracking_code',
|
|
407
|
-
];
|
|
408
|
-
}
|
|
409
|
-
if (SECTION_FILE_REGEX.test(normalizedUri)) {
|
|
410
|
-
return ['section', 'predictive_search', 'recommendations', 'comment'];
|
|
411
|
-
}
|
|
412
|
-
if (BLOCK_FILE_REGEX.test(normalizedUri)) {
|
|
413
|
-
return ['app', 'section', 'recommendations', 'block'];
|
|
414
|
-
}
|
|
415
222
|
if (PARTIAL_FILE_REGEX.test(normalizedUri)) {
|
|
416
223
|
return ['app'];
|
|
417
224
|
}
|
|
@@ -434,7 +241,7 @@ type FilterEntryName = FilterEntry['name'];
|
|
|
434
241
|
export const Untyped = 'untyped' as const;
|
|
435
242
|
export type Untyped = typeof Untyped;
|
|
436
243
|
|
|
437
|
-
/** Unknown is for variables that don't exist, type would come from context (e.g.
|
|
244
|
+
/** Unknown is for variables that don't exist, type would come from context (e.g. partial var without LiquidDoc) */
|
|
438
245
|
export const Unknown = 'unknown' as const;
|
|
439
246
|
export type Unknown = typeof Untyped;
|
|
440
247
|
|
|
@@ -574,54 +381,215 @@ async function buildSymbolsTable(
|
|
|
574
381
|
const typeRanges = await visit<SourceCodeType.LiquidHtml, TypeRange | TypeRange[]>(partialAst, {
|
|
575
382
|
// {% assign x = foo.x | filter %}
|
|
576
383
|
// {% assign x = '{"a": 1}' | parse_json %}
|
|
384
|
+
// {% assign x = {a: 1, b: "hello"} %}
|
|
385
|
+
// {% assign x["key"] = value %}
|
|
386
|
+
// {% assign arr << item %}
|
|
387
|
+
// {% assign arr = source << item %}
|
|
577
388
|
async AssignMarkup(node) {
|
|
578
|
-
//
|
|
579
|
-
const
|
|
580
|
-
|
|
581
|
-
|
|
389
|
+
// For explicit push form (assign a = source << value), treat as array append
|
|
390
|
+
const isPushRhs = node.value.type === NodeTypes.AssignPushRhs;
|
|
391
|
+
const effectiveValue = isPushRhs
|
|
392
|
+
? (node.value as AssignPushRhs).pushValue
|
|
393
|
+
: (node.value as LiquidVariable);
|
|
394
|
+
const effectiveOperator = isPushRhs ? '<<' : node.operator;
|
|
395
|
+
const expression = effectiveValue.expression;
|
|
396
|
+
let valueShape: PropertyShape | undefined;
|
|
397
|
+
|
|
398
|
+
// Resolver for variable references inside JSON literals
|
|
399
|
+
const resolveExpr = (expr: LiquidExpression): PropertyShape | undefined => {
|
|
400
|
+
if (expr.type !== NodeTypes.VariableLookup || !expr.name) return undefined;
|
|
401
|
+
|
|
402
|
+
// Look up the variable's shape from already-processed assignments
|
|
403
|
+
const shapes = variableShapes.get(expr.name);
|
|
404
|
+
const shapeEntry = shapes
|
|
405
|
+
? findLastApplicableShape(shapes, node.position.start)
|
|
406
|
+
: undefined;
|
|
407
|
+
let shape = shapeEntry?.shape;
|
|
408
|
+
|
|
409
|
+
// Fall back to seedSymbolsTable
|
|
410
|
+
if (!shape && seedSymbolsTable[expr.name]) {
|
|
411
|
+
for (const tr of seedSymbolsTable[expr.name]) {
|
|
412
|
+
if (tr.range[0] < node.position.start) {
|
|
413
|
+
if (typeof tr.type !== 'string' && tr.type.kind === 'shape') {
|
|
414
|
+
shape = tr.type.shape;
|
|
415
|
+
} else if (typeof tr.type === 'string' || tr.type.kind === 'array') {
|
|
416
|
+
shape = resolvedTypeToShape(tr.type);
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
}
|
|
582
421
|
|
|
583
|
-
|
|
584
|
-
let jsonString: string | undefined;
|
|
422
|
+
if (!shape) return undefined;
|
|
585
423
|
|
|
586
|
-
//
|
|
587
|
-
|
|
588
|
-
|
|
424
|
+
// Resolve lookups (e.g. a.x or a["key"])
|
|
425
|
+
for (const lookup of expr.lookups) {
|
|
426
|
+
if (shape.kind === 'object' && lookup.type === NodeTypes.String && shape.properties) {
|
|
427
|
+
const prop = shape.properties.get(lookup.value);
|
|
428
|
+
if (prop) {
|
|
429
|
+
shape = prop;
|
|
430
|
+
} else {
|
|
431
|
+
return undefined;
|
|
432
|
+
}
|
|
433
|
+
} else if (shape.kind === 'array') {
|
|
434
|
+
if (
|
|
435
|
+
lookup.type === NodeTypes.Number ||
|
|
436
|
+
(lookup.type === NodeTypes.String &&
|
|
437
|
+
(lookup.value === 'first' || lookup.value === 'last'))
|
|
438
|
+
) {
|
|
439
|
+
shape = shape.itemShape;
|
|
440
|
+
if (!shape) return undefined;
|
|
441
|
+
} else {
|
|
442
|
+
return undefined;
|
|
443
|
+
}
|
|
444
|
+
} else {
|
|
445
|
+
return undefined;
|
|
446
|
+
}
|
|
589
447
|
}
|
|
590
448
|
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
449
|
+
return shape;
|
|
450
|
+
};
|
|
451
|
+
|
|
452
|
+
// Case 1: JSON literal expression
|
|
453
|
+
if (
|
|
454
|
+
expression.type === NodeTypes.JsonHashLiteral ||
|
|
455
|
+
expression.type === NodeTypes.JsonArrayLiteral
|
|
456
|
+
) {
|
|
457
|
+
valueShape = inferShapeFromJsonLiteral(expression, resolveExpr);
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
// Case 2: parse_json / to_hash filter on a string
|
|
461
|
+
if (!valueShape) {
|
|
462
|
+
const hasParseJsonFilter = effectiveValue.filters?.some(
|
|
463
|
+
(f: { name: string }) => f.name === 'parse_json' || f.name === 'to_hash',
|
|
464
|
+
);
|
|
465
|
+
if (hasParseJsonFilter) {
|
|
466
|
+
let jsonString: string | undefined;
|
|
467
|
+
if (expression.type === NodeTypes.String) {
|
|
468
|
+
jsonString = expression.value;
|
|
469
|
+
}
|
|
470
|
+
if (!jsonString && effectiveValue.filters) {
|
|
471
|
+
const defaultFilter = effectiveValue.filters.find(
|
|
472
|
+
(f: { name: string }) => f.name === 'default',
|
|
473
|
+
);
|
|
474
|
+
if (
|
|
475
|
+
defaultFilter &&
|
|
476
|
+
defaultFilter.args &&
|
|
477
|
+
defaultFilter.args.length > 0 &&
|
|
478
|
+
defaultFilter.args[0].type === NodeTypes.String
|
|
479
|
+
) {
|
|
480
|
+
jsonString = defaultFilter.args[0].value;
|
|
600
481
|
}
|
|
601
482
|
}
|
|
483
|
+
if (jsonString) {
|
|
484
|
+
valueShape = inferShapeFromJSONString(jsonString) ?? undefined;
|
|
485
|
+
}
|
|
602
486
|
}
|
|
487
|
+
}
|
|
603
488
|
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
489
|
+
// Case 3: Infer primitive shape for << and LHS lookup scenarios
|
|
490
|
+
if (
|
|
491
|
+
!valueShape &&
|
|
492
|
+
((node.lookups && node.lookups.length > 0) || effectiveOperator === '<<')
|
|
493
|
+
) {
|
|
494
|
+
if (expression.type === NodeTypes.String) {
|
|
495
|
+
valueShape = { kind: 'primitive', primitiveType: 'string' };
|
|
496
|
+
} else if (expression.type === NodeTypes.Number) {
|
|
497
|
+
valueShape = { kind: 'primitive', primitiveType: 'number' };
|
|
498
|
+
} else if (expression.type === NodeTypes.LiquidLiteral) {
|
|
499
|
+
if (expression.value === null) {
|
|
500
|
+
valueShape = { kind: 'primitive', primitiveType: 'null' };
|
|
501
|
+
} else if (typeof expression.value === 'boolean') {
|
|
502
|
+
valueShape = { kind: 'primitive', primitiveType: 'boolean' };
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
}
|
|
612
506
|
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
507
|
+
// Handle lookups on the LHS (assign x["key"] = value)
|
|
508
|
+
if (node.lookups && node.lookups.length > 0) {
|
|
509
|
+
const lookupPath = getAssignLookupPath(node.lookups);
|
|
510
|
+
if (lookupPath && lookupPath.length > 0) {
|
|
511
|
+
const existingShapes = variableShapes.get(node.name) || [];
|
|
512
|
+
const existingShapeEntry = findLastApplicableShape(existingShapes, node.position.start);
|
|
513
|
+
|
|
514
|
+
let baseShape: PropertyShape;
|
|
515
|
+
if (existingShapeEntry) {
|
|
516
|
+
baseShape = existingShapeEntry.shape;
|
|
517
|
+
} else {
|
|
518
|
+
let existingType: PseudoType | ArrayType | ShapeType | UnionType | undefined;
|
|
519
|
+
if (seedSymbolsTable[node.name]) {
|
|
520
|
+
for (const tr of seedSymbolsTable[node.name]) {
|
|
521
|
+
if (tr.range[0] < node.position.start) {
|
|
522
|
+
if (typeof tr.type !== 'string' && tr.type.kind === 'shape') {
|
|
523
|
+
existingType = tr.type;
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
baseShape =
|
|
529
|
+
existingType && isShapeType(existingType)
|
|
530
|
+
? existingType.shape
|
|
531
|
+
: { kind: 'object', properties: new Map() };
|
|
618
532
|
}
|
|
533
|
+
|
|
534
|
+
const newShape = mergeNestedPropertyIntoShape(
|
|
535
|
+
baseShape,
|
|
536
|
+
lookupPath,
|
|
537
|
+
valueShape ?? { kind: 'primitive' },
|
|
538
|
+
);
|
|
539
|
+
|
|
540
|
+
if (!variableShapes.has(node.name)) variableShapes.set(node.name, []);
|
|
541
|
+
variableShapes.get(node.name)!.push({ shape: newShape, rangeEnd: node.position.end });
|
|
542
|
+
|
|
543
|
+
return {
|
|
544
|
+
identifier: node.name,
|
|
545
|
+
type: shapeType(newShape),
|
|
546
|
+
range: [node.position.end],
|
|
547
|
+
};
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
// Handle << operator (array append), including explicit form (assign a = source << value)
|
|
552
|
+
if (effectiveOperator === '<<') {
|
|
553
|
+
const itemShape: PropertyShape = valueShape ?? { kind: 'primitive' };
|
|
554
|
+
const existingShapes = variableShapes.get(node.name) || [];
|
|
555
|
+
const existingShapeEntry = findLastApplicableShape(existingShapes, node.position.start);
|
|
556
|
+
|
|
557
|
+
let arrayShape: PropertyShape;
|
|
558
|
+
if (existingShapeEntry?.shape.kind === 'array') {
|
|
559
|
+
const existing = existingShapeEntry.shape.itemShape;
|
|
560
|
+
arrayShape = {
|
|
561
|
+
kind: 'array',
|
|
562
|
+
itemShape: existing ? mergeShapes(existing, itemShape) : itemShape,
|
|
563
|
+
};
|
|
564
|
+
} else {
|
|
565
|
+
arrayShape = { kind: 'array', itemShape };
|
|
619
566
|
}
|
|
567
|
+
|
|
568
|
+
if (!variableShapes.has(node.name)) variableShapes.set(node.name, []);
|
|
569
|
+
variableShapes.get(node.name)!.push({ shape: arrayShape, rangeEnd: node.position.end });
|
|
570
|
+
|
|
571
|
+
return {
|
|
572
|
+
identifier: node.name,
|
|
573
|
+
type: shapeType(arrayShape),
|
|
574
|
+
range: [node.position.end],
|
|
575
|
+
};
|
|
620
576
|
}
|
|
621
577
|
|
|
578
|
+
// Simple assignment with shape (JSON literal or parse_json)
|
|
579
|
+
if (valueShape) {
|
|
580
|
+
if (!variableShapes.has(node.name)) variableShapes.set(node.name, []);
|
|
581
|
+
variableShapes.get(node.name)!.push({ shape: valueShape, rangeEnd: node.position.end });
|
|
582
|
+
return {
|
|
583
|
+
identifier: node.name,
|
|
584
|
+
type: shapeType(valueShape),
|
|
585
|
+
range: [node.position.end],
|
|
586
|
+
};
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
// Default: lazy variable type
|
|
622
590
|
return {
|
|
623
591
|
identifier: node.name,
|
|
624
|
-
type: lazyVariable(
|
|
592
|
+
type: lazyVariable(effectiveValue, node.position.start),
|
|
625
593
|
range: [node.position.end],
|
|
626
594
|
};
|
|
627
595
|
},
|
|
@@ -657,12 +625,6 @@ async function buildSymbolsTable(
|
|
|
657
625
|
type: String,
|
|
658
626
|
range: [node.position.end],
|
|
659
627
|
};
|
|
660
|
-
} else if (['form', 'paginate'].includes(node.name)) {
|
|
661
|
-
return {
|
|
662
|
-
identifier: node.name,
|
|
663
|
-
type: node.name,
|
|
664
|
-
range: [node.blockStartPosition.end, end(node.blockEndPosition?.end)],
|
|
665
|
-
};
|
|
666
628
|
} else if (['for', 'tablerow'].includes(node.name)) {
|
|
667
629
|
return {
|
|
668
630
|
identifier: node.name + 'loop',
|
|
@@ -1022,10 +984,22 @@ function inferType(
|
|
|
1022
984
|
return arrayType('number');
|
|
1023
985
|
}
|
|
1024
986
|
|
|
987
|
+
// JSON literal expressions: {key: val} or [1, 2, 3]
|
|
988
|
+
case NodeTypes.JsonHashLiteral:
|
|
989
|
+
case NodeTypes.JsonArrayLiteral: {
|
|
990
|
+
const resolver = (expr: LiquidExpression): PropertyShape | undefined => {
|
|
991
|
+
const type = inferType(expr, symbolsTable, objectMap, filtersMap);
|
|
992
|
+
return resolvedTypeToShape(type);
|
|
993
|
+
};
|
|
994
|
+
return shapeType(inferShapeFromJsonLiteral(thing, resolver));
|
|
995
|
+
}
|
|
996
|
+
|
|
1025
997
|
// The type of the assign markup is the type of the right hand side.
|
|
1026
998
|
// {% assign x = y.property | filter1 | filter2 %}
|
|
1027
999
|
case NodeTypes.AssignMarkup: {
|
|
1028
|
-
|
|
1000
|
+
const assignValue =
|
|
1001
|
+
thing.value.type === NodeTypes.AssignPushRhs ? thing.value.pushValue : thing.value;
|
|
1002
|
+
return inferType(assignValue, symbolsTable, objectMap, filtersMap);
|
|
1029
1003
|
}
|
|
1030
1004
|
|
|
1031
1005
|
// A variable lookup is expression[.lookup]*
|
|
@@ -1346,6 +1320,36 @@ function shapeToType(shape: PropertyShape): PseudoType | ArrayType | ShapeType {
|
|
|
1346
1320
|
return Untyped;
|
|
1347
1321
|
}
|
|
1348
1322
|
|
|
1323
|
+
/**
|
|
1324
|
+
* Convert a resolved type back to a PropertyShape (inverse of shapeToType).
|
|
1325
|
+
* Used when embedding resolved variable types into JSON literal shapes.
|
|
1326
|
+
*/
|
|
1327
|
+
function resolvedTypeToShape(
|
|
1328
|
+
type: PseudoType | ArrayType | ShapeType | UnionType,
|
|
1329
|
+
): PropertyShape | undefined {
|
|
1330
|
+
if (typeof type === 'string') {
|
|
1331
|
+
switch (type) {
|
|
1332
|
+
case 'string':
|
|
1333
|
+
return { kind: 'primitive', primitiveType: 'string' };
|
|
1334
|
+
case 'number':
|
|
1335
|
+
return { kind: 'primitive', primitiveType: 'number' };
|
|
1336
|
+
case 'boolean':
|
|
1337
|
+
return { kind: 'primitive', primitiveType: 'boolean' };
|
|
1338
|
+
case Untyped:
|
|
1339
|
+
case Unknown:
|
|
1340
|
+
return undefined;
|
|
1341
|
+
default:
|
|
1342
|
+
return { kind: 'primitive' };
|
|
1343
|
+
}
|
|
1344
|
+
}
|
|
1345
|
+
if (isShapeType(type)) return type.shape;
|
|
1346
|
+
if (isArrayType(type)) {
|
|
1347
|
+
const itemShape = resolvedTypeToShape(type.valueType);
|
|
1348
|
+
return { kind: 'array', itemShape };
|
|
1349
|
+
}
|
|
1350
|
+
return undefined;
|
|
1351
|
+
}
|
|
1352
|
+
|
|
1349
1353
|
function inferPseudoTypePropertyType(
|
|
1350
1354
|
curr: PseudoType, // settings
|
|
1351
1355
|
lookup: LiquidExpression,
|
|
@@ -1656,6 +1660,24 @@ export function typeToDisplayString(type: PseudoType | ArrayType | ShapeType | U
|
|
|
1656
1660
|
return 'unknown';
|
|
1657
1661
|
}
|
|
1658
1662
|
|
|
1663
|
+
/**
|
|
1664
|
+
* Extract the lookup path from assign lookups.
|
|
1665
|
+
* For {% assign a["key1"]["key2"] = value %}, returns ['key1', 'key2']
|
|
1666
|
+
*/
|
|
1667
|
+
function getAssignLookupPath(lookups: LiquidExpression[]): string[] | undefined {
|
|
1668
|
+
const path: string[] = [];
|
|
1669
|
+
for (const lookup of lookups) {
|
|
1670
|
+
if (lookup.type === NodeTypes.String) {
|
|
1671
|
+
path.push(lookup.value);
|
|
1672
|
+
} else if (lookup.type === NodeTypes.Number) {
|
|
1673
|
+
path.push(`${lookup.value}`);
|
|
1674
|
+
} else {
|
|
1675
|
+
return undefined;
|
|
1676
|
+
}
|
|
1677
|
+
}
|
|
1678
|
+
return path.length > 0 ? path : undefined;
|
|
1679
|
+
}
|
|
1680
|
+
|
|
1659
1681
|
/**
|
|
1660
1682
|
* Extract the lookup path from a hash_assign target.
|
|
1661
1683
|
* For {% hash_assign a['key1']['key2'] = value %}, returns ['key1', 'key2']
|
|
@@ -1731,218 +1753,3 @@ function findLastApplicableShape(
|
|
|
1731
1753
|
}
|
|
1732
1754
|
return result;
|
|
1733
1755
|
}
|
|
1734
|
-
|
|
1735
|
-
function settingReturnType(setting: InputSetting): ObjectEntry['return_type'] {
|
|
1736
|
-
switch (setting.type) {
|
|
1737
|
-
// basic settings
|
|
1738
|
-
case 'checkbox':
|
|
1739
|
-
return [{ type: 'boolean', name: '' }];
|
|
1740
|
-
|
|
1741
|
-
case 'range':
|
|
1742
|
-
case 'number':
|
|
1743
|
-
return [{ type: 'number', name: '' }];
|
|
1744
|
-
|
|
1745
|
-
case 'radio':
|
|
1746
|
-
case 'select':
|
|
1747
|
-
case 'text':
|
|
1748
|
-
case 'textarea':
|
|
1749
|
-
return [{ type: 'string', name: '' }];
|
|
1750
|
-
|
|
1751
|
-
// specialized settings
|
|
1752
|
-
case 'article':
|
|
1753
|
-
return [{ type: 'article', name: '' }];
|
|
1754
|
-
|
|
1755
|
-
case 'blog':
|
|
1756
|
-
return [{ type: 'blog', name: '' }];
|
|
1757
|
-
|
|
1758
|
-
case 'collection':
|
|
1759
|
-
return [{ type: 'collection', name: '' }];
|
|
1760
|
-
|
|
1761
|
-
case 'collection_list':
|
|
1762
|
-
return [{ type: 'array', array_value: 'collection' }];
|
|
1763
|
-
|
|
1764
|
-
case 'color':
|
|
1765
|
-
return [{ type: 'color', name: '' }];
|
|
1766
|
-
|
|
1767
|
-
case 'color_background':
|
|
1768
|
-
return [{ type: 'string', name: '' }];
|
|
1769
|
-
|
|
1770
|
-
case 'color_scheme':
|
|
1771
|
-
return [{ type: 'color_scheme', name: '' }];
|
|
1772
|
-
|
|
1773
|
-
// TODO ??
|
|
1774
|
-
case 'color_scheme_group':
|
|
1775
|
-
return [];
|
|
1776
|
-
|
|
1777
|
-
case 'font_picker':
|
|
1778
|
-
return [{ type: 'font', name: '' }];
|
|
1779
|
-
|
|
1780
|
-
case 'html':
|
|
1781
|
-
return [{ type: 'string', name: '' }];
|
|
1782
|
-
|
|
1783
|
-
case 'image_picker':
|
|
1784
|
-
return [{ type: 'image', name: '' }];
|
|
1785
|
-
|
|
1786
|
-
case 'inline_richtext':
|
|
1787
|
-
return [{ type: 'string', name: '' }];
|
|
1788
|
-
|
|
1789
|
-
case 'link_list':
|
|
1790
|
-
return [{ type: 'linklist', name: '' }];
|
|
1791
|
-
|
|
1792
|
-
case 'liquid':
|
|
1793
|
-
return [{ type: 'string', name: '' }];
|
|
1794
|
-
|
|
1795
|
-
case 'page':
|
|
1796
|
-
return [{ type: 'page', name: '' }];
|
|
1797
|
-
|
|
1798
|
-
case 'product':
|
|
1799
|
-
return [{ type: 'product', name: '' }];
|
|
1800
|
-
|
|
1801
|
-
case 'product_list':
|
|
1802
|
-
return [{ type: 'array', array_value: 'product' }];
|
|
1803
|
-
|
|
1804
|
-
case 'richtext':
|
|
1805
|
-
return [{ type: 'string', name: '' }];
|
|
1806
|
-
|
|
1807
|
-
case 'text_alignment':
|
|
1808
|
-
return [{ type: 'string', name: '' }];
|
|
1809
|
-
|
|
1810
|
-
case 'url':
|
|
1811
|
-
return [{ type: 'string', name: '' }];
|
|
1812
|
-
|
|
1813
|
-
case 'video':
|
|
1814
|
-
return [{ type: 'video', name: '' }];
|
|
1815
|
-
|
|
1816
|
-
case 'video_url':
|
|
1817
|
-
return [{ type: 'string', name: '' }];
|
|
1818
|
-
|
|
1819
|
-
default:
|
|
1820
|
-
return [];
|
|
1821
|
-
}
|
|
1822
|
-
}
|
|
1823
|
-
|
|
1824
|
-
const METAFIELD_TYPE_TO_TYPE = Object.freeze({
|
|
1825
|
-
single_line_text_field: String,
|
|
1826
|
-
multi_line_text_field: String,
|
|
1827
|
-
url_reference: String,
|
|
1828
|
-
date: String,
|
|
1829
|
-
date_time: String,
|
|
1830
|
-
number_integer: 'number',
|
|
1831
|
-
number_decimal: 'number',
|
|
1832
|
-
product_reference: 'product',
|
|
1833
|
-
collection_reference: 'collection',
|
|
1834
|
-
variant_reference: 'variant',
|
|
1835
|
-
page_reference: 'page',
|
|
1836
|
-
boolean: 'boolean',
|
|
1837
|
-
color: 'color',
|
|
1838
|
-
weight: 'measurement',
|
|
1839
|
-
volume: 'measurement',
|
|
1840
|
-
dimension: 'measurement',
|
|
1841
|
-
rating: 'rating',
|
|
1842
|
-
money: 'money',
|
|
1843
|
-
json: Untyped,
|
|
1844
|
-
metaobject_reference: 'metaobject',
|
|
1845
|
-
mixed_reference: Untyped,
|
|
1846
|
-
rich_text_field: Untyped,
|
|
1847
|
-
file_reference: Untyped,
|
|
1848
|
-
});
|
|
1849
|
-
|
|
1850
|
-
const REFERENCE_TYPE_METAFIELDS = Object.entries(METAFIELD_TYPE_TO_TYPE)
|
|
1851
|
-
.filter(([metafieldType, _type]) => metafieldType.endsWith('_reference'))
|
|
1852
|
-
.map(([_metafieldType, type]) => type);
|
|
1853
|
-
|
|
1854
|
-
function metafieldReturnType(metafieldType: string): ObjectEntry['return_type'] {
|
|
1855
|
-
let isArray = metafieldType.startsWith('list.');
|
|
1856
|
-
|
|
1857
|
-
if (isArray) {
|
|
1858
|
-
metafieldType = metafieldType.split('.')[1];
|
|
1859
|
-
}
|
|
1860
|
-
|
|
1861
|
-
let type = 'metafield_' + ((METAFIELD_TYPE_TO_TYPE as any)[metafieldType] ?? Untyped);
|
|
1862
|
-
|
|
1863
|
-
if (isArray) {
|
|
1864
|
-
return [{ type: `${type}_array`, name: '' }];
|
|
1865
|
-
}
|
|
1866
|
-
|
|
1867
|
-
return [{ type: type, name: '' }];
|
|
1868
|
-
}
|
|
1869
|
-
|
|
1870
|
-
// The default `metafield` type has an untyped `value` property.
|
|
1871
|
-
// We need to create new metafield types with the labels `metafield_x` and `metafield_x_array`
|
|
1872
|
-
// where x is the type of metafield inside the `value` property. The metafields ending with `x_array`
|
|
1873
|
-
// is where the value is an array of type x.
|
|
1874
|
-
const customMetafieldTypeEntries = memo((baseMetafieldEntry: ObjectEntry) => {
|
|
1875
|
-
if (!baseMetafieldEntry) return {} as ObjectMap;
|
|
1876
|
-
|
|
1877
|
-
return [
|
|
1878
|
-
...new Set([...Object.values(METAFIELD_TYPE_TO_TYPE), ...FETCHED_METAFIELD_CATEGORIES]),
|
|
1879
|
-
].reduce((map, type) => {
|
|
1880
|
-
{
|
|
1881
|
-
const metafieldEntry = JSON.parse(JSON.stringify(baseMetafieldEntry)); // easy deep clone
|
|
1882
|
-
const metafieldValueProp = metafieldEntry.properties?.find(
|
|
1883
|
-
(prop: any) => prop.name === 'value',
|
|
1884
|
-
);
|
|
1885
|
-
|
|
1886
|
-
if (metafieldValueProp) {
|
|
1887
|
-
metafieldValueProp.return_type = [{ type: type, name: '' }];
|
|
1888
|
-
metafieldValueProp.description = '';
|
|
1889
|
-
metafieldEntry.name = `metafield_${type}`;
|
|
1890
|
-
map[metafieldEntry.name] = metafieldEntry;
|
|
1891
|
-
}
|
|
1892
|
-
}
|
|
1893
|
-
|
|
1894
|
-
{
|
|
1895
|
-
const metafieldArrayEntry = JSON.parse(JSON.stringify(baseMetafieldEntry)); // easy deep clone
|
|
1896
|
-
const metafieldArrayValueProp = metafieldArrayEntry.properties?.find(
|
|
1897
|
-
(prop: any) => prop.name === 'value',
|
|
1898
|
-
);
|
|
1899
|
-
|
|
1900
|
-
if (metafieldArrayValueProp) {
|
|
1901
|
-
// A metafield definition using a list of references does not use an array, but a separate type of collection.
|
|
1902
|
-
// For auto-completion purposes, we can't use the array type
|
|
1903
|
-
// https://shopify.dev/docs/api/liquid/objects/metafield#metafield-determining-the-length-of-a-list-metafield
|
|
1904
|
-
if (REFERENCE_TYPE_METAFIELDS.includes(type as any)) {
|
|
1905
|
-
metafieldArrayValueProp.return_type = [{ type: 'untyped', name: '' }];
|
|
1906
|
-
} else {
|
|
1907
|
-
metafieldArrayValueProp.return_type = [{ type: 'array', name: '', array_value: type }];
|
|
1908
|
-
}
|
|
1909
|
-
metafieldArrayValueProp.description = '';
|
|
1910
|
-
metafieldArrayEntry.name = `metafield_${type}_array`;
|
|
1911
|
-
map[metafieldArrayEntry.name] = metafieldArrayEntry;
|
|
1912
|
-
}
|
|
1913
|
-
}
|
|
1914
|
-
|
|
1915
|
-
return map;
|
|
1916
|
-
}, {} as ObjectMap);
|
|
1917
|
-
});
|
|
1918
|
-
|
|
1919
|
-
function schemaSettingsAsProperties(ast: LiquidHtmlNode): ObjectEntry[] {
|
|
1920
|
-
if (ast.type !== NodeTypes.Document) return [];
|
|
1921
|
-
try {
|
|
1922
|
-
const source = ast._source; // (the unfixed source)
|
|
1923
|
-
const start = /\{%\s*schema\s*%\}/m.exec(source);
|
|
1924
|
-
const end = /\{%\s*endschema\s*%\}/m.exec(source);
|
|
1925
|
-
if (!start || !end) return [];
|
|
1926
|
-
const schema = source.slice(start.index + start[0].length, end.index);
|
|
1927
|
-
const json = parseJSON(schema);
|
|
1928
|
-
if (isError(json) || !('settings' in json) || !Array.isArray(json.settings)) return [];
|
|
1929
|
-
const result: ObjectEntry[] = [];
|
|
1930
|
-
const inputSettings = json.settings.filter(isInputSetting);
|
|
1931
|
-
for (const setting of inputSettings) {
|
|
1932
|
-
result.push({
|
|
1933
|
-
name: setting.id,
|
|
1934
|
-
summary: '', // TODO, this should lookup the locale file for settings... setting.label
|
|
1935
|
-
description: '', // TODO , this should lookup the locale file as well... setting.info,
|
|
1936
|
-
return_type: settingReturnType(setting),
|
|
1937
|
-
access: {
|
|
1938
|
-
global: false,
|
|
1939
|
-
parents: [],
|
|
1940
|
-
template: [],
|
|
1941
|
-
},
|
|
1942
|
-
});
|
|
1943
|
-
}
|
|
1944
|
-
return result;
|
|
1945
|
-
} catch (_) {
|
|
1946
|
-
return [];
|
|
1947
|
-
}
|
|
1948
|
-
}
|