@platformos/platformos-language-server-common 0.0.2
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 +11 -0
- package/README.md +87 -0
- package/dist/ClientCapabilities.d.ts +14 -0
- package/dist/ClientCapabilities.js +52 -0
- package/dist/ClientCapabilities.js.map +1 -0
- package/dist/PropertyShapeInference.d.ts +57 -0
- package/dist/PropertyShapeInference.js +336 -0
- package/dist/PropertyShapeInference.js.map +1 -0
- package/dist/TypeSystem.d.ts +94 -0
- package/dist/TypeSystem.js +1481 -0
- package/dist/TypeSystem.js.map +1 -0
- package/dist/VariableShapeExtractor.d.ts +14 -0
- package/dist/VariableShapeExtractor.js +283 -0
- package/dist/VariableShapeExtractor.js.map +1 -0
- package/dist/codeActions/BaseCodeActionsProvider.d.ts +11 -0
- package/dist/codeActions/BaseCodeActionsProvider.js +14 -0
- package/dist/codeActions/BaseCodeActionsProvider.js.map +1 -0
- package/dist/codeActions/CodeActionsProvider.d.ts +9 -0
- package/dist/codeActions/CodeActionsProvider.js +22 -0
- package/dist/codeActions/CodeActionsProvider.js.map +1 -0
- package/dist/codeActions/index.d.ts +1 -0
- package/dist/codeActions/index.js +7 -0
- package/dist/codeActions/index.js.map +1 -0
- package/dist/codeActions/providers/FixAllProvider.d.ts +23 -0
- package/dist/codeActions/providers/FixAllProvider.js +52 -0
- package/dist/codeActions/providers/FixAllProvider.js.map +1 -0
- package/dist/codeActions/providers/FixProvider.d.ts +6 -0
- package/dist/codeActions/providers/FixProvider.js +72 -0
- package/dist/codeActions/providers/FixProvider.js.map +1 -0
- 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/codeActions/providers/SuggestionProvider.d.ts +6 -0
- package/dist/codeActions/providers/SuggestionProvider.js +41 -0
- package/dist/codeActions/providers/SuggestionProvider.js.map +1 -0
- package/dist/codeActions/providers/index.d.ts +3 -0
- package/dist/codeActions/providers/index.js +10 -0
- package/dist/codeActions/providers/index.js.map +1 -0
- package/dist/codeActions/providers/utils.d.ts +20 -0
- package/dist/codeActions/providers/utils.js +29 -0
- package/dist/codeActions/providers/utils.js.map +1 -0
- package/dist/commands/BaseExecuteCommandProvider.d.ts +13 -0
- package/dist/commands/BaseExecuteCommandProvider.js +13 -0
- package/dist/commands/BaseExecuteCommandProvider.js.map +1 -0
- package/dist/commands/ExecuteCommandProvider.d.ts +11 -0
- package/dist/commands/ExecuteCommandProvider.js +30 -0
- package/dist/commands/ExecuteCommandProvider.js.map +1 -0
- package/dist/commands/index.d.ts +2 -0
- package/dist/commands/index.js +9 -0
- package/dist/commands/index.js.map +1 -0
- package/dist/commands/providers/ApplyFixesProvider.d.ts +19 -0
- package/dist/commands/providers/ApplyFixesProvider.js +69 -0
- package/dist/commands/providers/ApplyFixesProvider.js.map +1 -0
- package/dist/commands/providers/ApplySuggestionProvider.d.ts +19 -0
- package/dist/commands/providers/ApplySuggestionProvider.js +73 -0
- package/dist/commands/providers/ApplySuggestionProvider.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/commands/providers/RunChecksProvider.d.ts +26 -0
- package/dist/commands/providers/RunChecksProvider.js +32 -0
- package/dist/commands/providers/RunChecksProvider.js.map +1 -0
- package/dist/commands/providers/index.d.ts +3 -0
- package/dist/commands/providers/index.js +12 -0
- package/dist/commands/providers/index.js.map +1 -0
- package/dist/completions/CompletionsProvider.d.ts +35 -0
- package/dist/completions/CompletionsProvider.js +55 -0
- package/dist/completions/CompletionsProvider.js.map +1 -0
- package/dist/completions/index.d.ts +1 -0
- package/dist/completions/index.js +6 -0
- package/dist/completions/index.js.map +1 -0
- package/dist/completions/params/LiquidCompletionParams.d.ts +24 -0
- package/dist/completions/params/LiquidCompletionParams.js +436 -0
- package/dist/completions/params/LiquidCompletionParams.js.map +1 -0
- package/dist/completions/params/fix.d.ts +11 -0
- package/dist/completions/params/fix.js +238 -0
- package/dist/completions/params/fix.js.map +1 -0
- package/dist/completions/params/index.d.ts +2 -0
- package/dist/completions/params/index.js +8 -0
- package/dist/completions/params/index.js.map +1 -0
- package/dist/completions/providers/ContentForBlockTypeCompletionProvider.d.ts +8 -0
- package/dist/completions/providers/ContentForBlockTypeCompletionProvider.js +36 -0
- package/dist/completions/providers/ContentForBlockTypeCompletionProvider.js.map +1 -0
- package/dist/completions/providers/ContentForCompletionProvider.d.ts +7 -0
- package/dist/completions/providers/ContentForCompletionProvider.js +87 -0
- package/dist/completions/providers/ContentForCompletionProvider.js.map +1 -0
- package/dist/completions/providers/ContentForParameterCompletionProvider.d.ts +23 -0
- package/dist/completions/providers/ContentForParameterCompletionProvider.js +127 -0
- package/dist/completions/providers/ContentForParameterCompletionProvider.js.map +1 -0
- package/dist/completions/providers/FilterCompletionProvider.d.ts +21 -0
- package/dist/completions/providers/FilterCompletionProvider.js +162 -0
- package/dist/completions/providers/FilterCompletionProvider.js.map +1 -0
- package/dist/completions/providers/FilterNamedParameterCompletionProvider.d.ts +15 -0
- package/dist/completions/providers/FilterNamedParameterCompletionProvider.js +86 -0
- package/dist/completions/providers/FilterNamedParameterCompletionProvider.js.map +1 -0
- package/dist/completions/providers/HtmlAttributeCompletionProvider.d.ts +13 -0
- package/dist/completions/providers/HtmlAttributeCompletionProvider.js +83 -0
- package/dist/completions/providers/HtmlAttributeCompletionProvider.js.map +1 -0
- package/dist/completions/providers/HtmlAttributeValueCompletionProvider.d.ts +7 -0
- package/dist/completions/providers/HtmlAttributeValueCompletionProvider.js +53 -0
- package/dist/completions/providers/HtmlAttributeValueCompletionProvider.js.map +1 -0
- package/dist/completions/providers/HtmlTagCompletionProvider.d.ts +7 -0
- package/dist/completions/providers/HtmlTagCompletionProvider.js +90 -0
- package/dist/completions/providers/HtmlTagCompletionProvider.js.map +1 -0
- package/dist/completions/providers/LiquidDocParamTypeCompletionProvider.d.ts +9 -0
- package/dist/completions/providers/LiquidDocParamTypeCompletionProvider.js +56 -0
- package/dist/completions/providers/LiquidDocParamTypeCompletionProvider.js.map +1 -0
- package/dist/completions/providers/LiquidDocTagCompletionProvider.d.ts +8 -0
- package/dist/completions/providers/LiquidDocTagCompletionProvider.js +62 -0
- package/dist/completions/providers/LiquidDocTagCompletionProvider.js.map +1 -0
- package/dist/completions/providers/LiquidTagsCompletionProvider.d.ts +9 -0
- package/dist/completions/providers/LiquidTagsCompletionProvider.js +367 -0
- package/dist/completions/providers/LiquidTagsCompletionProvider.js.map +1 -0
- package/dist/completions/providers/ObjectAttributeCompletionProvider.d.ts +9 -0
- package/dist/completions/providers/ObjectAttributeCompletionProvider.js +71 -0
- package/dist/completions/providers/ObjectAttributeCompletionProvider.js.map +1 -0
- package/dist/completions/providers/ObjectCompletionProvider.d.ts +9 -0
- package/dist/completions/providers/ObjectCompletionProvider.js +35 -0
- package/dist/completions/providers/ObjectCompletionProvider.js.map +1 -0
- package/dist/completions/providers/PartialCompletionProvider.d.ts +9 -0
- package/dist/completions/providers/PartialCompletionProvider.js +34 -0
- package/dist/completions/providers/PartialCompletionProvider.js.map +1 -0
- package/dist/completions/providers/RenderPartialParameterCompletionProvider.d.ts +10 -0
- package/dist/completions/providers/RenderPartialParameterCompletionProvider.js +55 -0
- package/dist/completions/providers/RenderPartialParameterCompletionProvider.js.map +1 -0
- package/dist/completions/providers/TranslationCompletionProvider.d.ts +11 -0
- package/dist/completions/providers/TranslationCompletionProvider.js +78 -0
- package/dist/completions/providers/TranslationCompletionProvider.js.map +1 -0
- package/dist/completions/providers/common/CompletionItemProperties.d.ts +7 -0
- package/dist/completions/providers/common/CompletionItemProperties.js +52 -0
- package/dist/completions/providers/common/CompletionItemProperties.js.map +1 -0
- package/dist/completions/providers/common/Provider.d.ts +5 -0
- package/dist/completions/providers/common/Provider.js +3 -0
- package/dist/completions/providers/common/Provider.js.map +1 -0
- package/dist/completions/providers/common/index.d.ts +7 -0
- package/dist/completions/providers/common/index.js +17 -0
- package/dist/completions/providers/common/index.js.map +1 -0
- package/dist/completions/providers/data/contentForParameterCompletionOptions.d.ts +7 -0
- package/dist/completions/providers/data/contentForParameterCompletionOptions.js +13 -0
- package/dist/completions/providers/data/contentForParameterCompletionOptions.js.map +1 -0
- package/dist/completions/providers/index.d.ts +17 -0
- package/dist/completions/providers/index.js +36 -0
- package/dist/completions/providers/index.js.map +1 -0
- package/dist/css/CSSLanguageService.d.ts +12 -0
- package/dist/css/CSSLanguageService.js +94 -0
- package/dist/css/CSSLanguageService.js.map +1 -0
- package/dist/css/test/test-helpers.d.ts +4 -0
- package/dist/css/test/test-helpers.js +19 -0
- package/dist/css/test/test-helpers.js.map +1 -0
- package/dist/definitions/BaseDefinitionProvider.d.ts +5 -0
- package/dist/definitions/BaseDefinitionProvider.js +3 -0
- package/dist/definitions/BaseDefinitionProvider.js.map +1 -0
- package/dist/definitions/DefinitionProvider.d.ts +8 -0
- package/dist/definitions/DefinitionProvider.js +29 -0
- package/dist/definitions/DefinitionProvider.js.map +1 -0
- package/dist/definitions/providers/SchemaTranslationStringDefinitionProvider.d.ts +10 -0
- package/dist/definitions/providers/SchemaTranslationStringDefinitionProvider.js +55 -0
- package/dist/definitions/providers/SchemaTranslationStringDefinitionProvider.js.map +1 -0
- package/dist/definitions/providers/TranslationStringDefinitionProvider.d.ts +10 -0
- package/dist/definitions/providers/TranslationStringDefinitionProvider.js +48 -0
- package/dist/definitions/providers/TranslationStringDefinitionProvider.js.map +1 -0
- package/dist/diagnostics/DiagnosticsManager.d.ts +24 -0
- package/dist/diagnostics/DiagnosticsManager.js +40 -0
- package/dist/diagnostics/DiagnosticsManager.js.map +1 -0
- package/dist/diagnostics/index.d.ts +2 -0
- package/dist/diagnostics/index.js +8 -0
- package/dist/diagnostics/index.js.map +1 -0
- package/dist/diagnostics/offenseToDiagnostic.d.ts +4 -0
- package/dist/diagnostics/offenseToDiagnostic.js +68 -0
- package/dist/diagnostics/offenseToDiagnostic.js.map +1 -0
- package/dist/diagnostics/runChecks.d.ts +9 -0
- package/dist/diagnostics/runChecks.js +103 -0
- package/dist/diagnostics/runChecks.js.map +1 -0
- package/dist/docset/HtmlDocset.d.ts +35 -0
- package/dist/docset/HtmlDocset.js +39 -0
- package/dist/docset/HtmlDocset.js.map +1 -0
- package/dist/docset/MarkdownRenderer.d.ts +7 -0
- package/dist/docset/MarkdownRenderer.js +125 -0
- package/dist/docset/MarkdownRenderer.js.map +1 -0
- package/dist/docset/index.d.ts +2 -0
- package/dist/docset/index.js +22 -0
- package/dist/docset/index.js.map +1 -0
- package/dist/documentHighlights/BaseDocumentHighlightsProvider.d.ts +5 -0
- package/dist/documentHighlights/BaseDocumentHighlightsProvider.js +3 -0
- package/dist/documentHighlights/BaseDocumentHighlightsProvider.js.map +1 -0
- package/dist/documentHighlights/DocumentHighlightsProvider.d.ts +22 -0
- package/dist/documentHighlights/DocumentHighlightsProvider.js +41 -0
- package/dist/documentHighlights/DocumentHighlightsProvider.js.map +1 -0
- package/dist/documentHighlights/index.d.ts +1 -0
- package/dist/documentHighlights/index.js +7 -0
- package/dist/documentHighlights/index.js.map +1 -0
- package/dist/documentHighlights/providers/HtmlTagNameDocumentHighlightsProvider.d.ts +11 -0
- package/dist/documentHighlights/providers/HtmlTagNameDocumentHighlightsProvider.js +20 -0
- package/dist/documentHighlights/providers/HtmlTagNameDocumentHighlightsProvider.js.map +1 -0
- package/dist/documentHighlights/providers/LiquidBlockTagDocumentHighlightsProvider.d.ts +11 -0
- package/dist/documentHighlights/providers/LiquidBlockTagDocumentHighlightsProvider.js +48 -0
- package/dist/documentHighlights/providers/LiquidBlockTagDocumentHighlightsProvider.js.map +1 -0
- package/dist/documentHighlights/providers/index.d.ts +2 -0
- package/dist/documentHighlights/providers/index.js +8 -0
- package/dist/documentHighlights/providers/index.js.map +1 -0
- package/dist/documentLinks/DocumentLinksProvider.d.ts +12 -0
- package/dist/documentLinks/DocumentLinksProvider.js +78 -0
- package/dist/documentLinks/DocumentLinksProvider.js.map +1 -0
- package/dist/documentLinks/index.d.ts +1 -0
- package/dist/documentLinks/index.js +6 -0
- package/dist/documentLinks/index.js.map +1 -0
- package/dist/documents/DocumentManager.d.ts +60 -0
- package/dist/documents/DocumentManager.js +167 -0
- package/dist/documents/DocumentManager.js.map +1 -0
- package/dist/documents/index.d.ts +2 -0
- package/dist/documents/index.js +21 -0
- package/dist/documents/index.js.map +1 -0
- package/dist/documents/types.d.ts +39 -0
- package/dist/documents/types.js +11 -0
- package/dist/documents/types.js.map +1 -0
- package/dist/formatting/OnTypeFormattingProvider.d.ts +10 -0
- package/dist/formatting/OnTypeFormattingProvider.js +24 -0
- package/dist/formatting/OnTypeFormattingProvider.js.map +1 -0
- package/dist/formatting/index.d.ts +1 -0
- package/dist/formatting/index.js +6 -0
- package/dist/formatting/index.js.map +1 -0
- package/dist/formatting/providers/BracketsAutoclosingOnTypeFormattingProvider.d.ts +49 -0
- package/dist/formatting/providers/BracketsAutoclosingOnTypeFormattingProvider.js +105 -0
- package/dist/formatting/providers/BracketsAutoclosingOnTypeFormattingProvider.js.map +1 -0
- package/dist/formatting/providers/HtmlElementAutoclosingOnTypeFormattingProvider.d.ts +41 -0
- package/dist/formatting/providers/HtmlElementAutoclosingOnTypeFormattingProvider.js +105 -0
- package/dist/formatting/providers/HtmlElementAutoclosingOnTypeFormattingProvider.js.map +1 -0
- package/dist/formatting/types.d.ts +7 -0
- package/dist/formatting/types.js +3 -0
- package/dist/formatting/types.js.map +1 -0
- package/dist/hover/BaseHoverProvider.d.ts +5 -0
- package/dist/hover/BaseHoverProvider.js +3 -0
- package/dist/hover/BaseHoverProvider.js.map +1 -0
- package/dist/hover/HoverProvider.d.ts +20 -0
- package/dist/hover/HoverProvider.js +55 -0
- package/dist/hover/HoverProvider.js.map +1 -0
- package/dist/hover/index.d.ts +1 -0
- package/dist/hover/index.js +6 -0
- package/dist/hover/index.js.map +1 -0
- package/dist/hover/providers/ContentForArgumentHoverProvider.d.ts +8 -0
- package/dist/hover/providers/ContentForArgumentHoverProvider.js +37 -0
- package/dist/hover/providers/ContentForArgumentHoverProvider.js.map +1 -0
- package/dist/hover/providers/ContentForTypeHoverProvider.d.ts +8 -0
- package/dist/hover/providers/ContentForTypeHoverProvider.js +32 -0
- package/dist/hover/providers/ContentForTypeHoverProvider.js.map +1 -0
- package/dist/hover/providers/HtmlAttributeHoverProvider.d.ts +6 -0
- package/dist/hover/providers/HtmlAttributeHoverProvider.js +37 -0
- package/dist/hover/providers/HtmlAttributeHoverProvider.js.map +1 -0
- package/dist/hover/providers/HtmlAttributeValueHoverProvider.d.ts +6 -0
- package/dist/hover/providers/HtmlAttributeValueHoverProvider.js +42 -0
- package/dist/hover/providers/HtmlAttributeValueHoverProvider.js.map +1 -0
- package/dist/hover/providers/HtmlTagHoverProvider.d.ts +6 -0
- package/dist/hover/providers/HtmlTagHoverProvider.js +37 -0
- package/dist/hover/providers/HtmlTagHoverProvider.js.map +1 -0
- package/dist/hover/providers/LiquidDocTagHoverProvider.d.ts +9 -0
- package/dist/hover/providers/LiquidDocTagHoverProvider.js +42 -0
- package/dist/hover/providers/LiquidDocTagHoverProvider.js.map +1 -0
- package/dist/hover/providers/LiquidFilterArgumentHoverProvider.d.ts +8 -0
- package/dist/hover/providers/LiquidFilterArgumentHoverProvider.js +36 -0
- package/dist/hover/providers/LiquidFilterArgumentHoverProvider.js.map +1 -0
- package/dist/hover/providers/LiquidFilterHoverProvider.d.ts +8 -0
- package/dist/hover/providers/LiquidFilterHoverProvider.js +29 -0
- package/dist/hover/providers/LiquidFilterHoverProvider.js.map +1 -0
- package/dist/hover/providers/LiquidObjectAttributeHoverProvider.d.ts +9 -0
- package/dist/hover/providers/LiquidObjectAttributeHoverProvider.js +113 -0
- package/dist/hover/providers/LiquidObjectAttributeHoverProvider.js.map +1 -0
- package/dist/hover/providers/LiquidObjectHoverProvider.d.ts +9 -0
- package/dist/hover/providers/LiquidObjectHoverProvider.js +72 -0
- package/dist/hover/providers/LiquidObjectHoverProvider.js.map +1 -0
- package/dist/hover/providers/LiquidTagHoverProvider.d.ts +8 -0
- package/dist/hover/providers/LiquidTagHoverProvider.js +31 -0
- package/dist/hover/providers/LiquidTagHoverProvider.js.map +1 -0
- package/dist/hover/providers/RenderPartialHoverProvider.d.ts +8 -0
- package/dist/hover/providers/RenderPartialHoverProvider.js +28 -0
- package/dist/hover/providers/RenderPartialHoverProvider.js.map +1 -0
- package/dist/hover/providers/RenderPartialParameterHoverProvider.d.ts +8 -0
- package/dist/hover/providers/RenderPartialParameterHoverProvider.js +33 -0
- package/dist/hover/providers/RenderPartialParameterHoverProvider.js.map +1 -0
- package/dist/hover/providers/TranslationHoverProvider.d.ts +13 -0
- package/dist/hover/providers/TranslationHoverProvider.js +45 -0
- package/dist/hover/providers/TranslationHoverProvider.js.map +1 -0
- package/dist/hover/providers/index.d.ts +11 -0
- package/dist/hover/providers/index.js +26 -0
- package/dist/hover/providers/index.js.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +33 -0
- package/dist/index.js.map +1 -0
- package/dist/internal-types.d.ts +1 -0
- package/dist/internal-types.js +3 -0
- package/dist/internal-types.js.map +1 -0
- package/dist/json/JSONContributions.d.ts +25 -0
- package/dist/json/JSONContributions.js +99 -0
- package/dist/json/JSONContributions.js.map +1 -0
- package/dist/json/JSONLanguageService.d.ts +27 -0
- package/dist/json/JSONLanguageService.js +171 -0
- package/dist/json/JSONLanguageService.js.map +1 -0
- package/dist/json/RequestContext.d.ts +22 -0
- package/dist/json/RequestContext.js +14 -0
- package/dist/json/RequestContext.js.map +1 -0
- package/dist/json/completions/JSONCompletionProvider.d.ts +30 -0
- package/dist/json/completions/JSONCompletionProvider.js +3 -0
- package/dist/json/completions/JSONCompletionProvider.js.map +1 -0
- package/dist/json/completions/providers/BlockSettingsPropertyCompletionProvider.d.ts +44 -0
- package/dist/json/completions/providers/BlockSettingsPropertyCompletionProvider.js +98 -0
- package/dist/json/completions/providers/BlockSettingsPropertyCompletionProvider.js.map +1 -0
- package/dist/json/completions/providers/BlockTypeCompletionProvider.d.ts +35 -0
- package/dist/json/completions/providers/BlockTypeCompletionProvider.js +97 -0
- package/dist/json/completions/providers/BlockTypeCompletionProvider.js.map +1 -0
- package/dist/json/completions/providers/ReferencedBlockTypeCompletionProvider.d.ts +34 -0
- package/dist/json/completions/providers/ReferencedBlockTypeCompletionProvider.js +95 -0
- package/dist/json/completions/providers/ReferencedBlockTypeCompletionProvider.js.map +1 -0
- package/dist/json/completions/providers/SchemaTranslationCompletionProvider.d.ts +9 -0
- package/dist/json/completions/providers/SchemaTranslationCompletionProvider.js +46 -0
- package/dist/json/completions/providers/SchemaTranslationCompletionProvider.js.map +1 -0
- package/dist/json/completions/providers/SettingsPropertyCompletionProvider.d.ts +33 -0
- package/dist/json/completions/providers/SettingsPropertyCompletionProvider.js +64 -0
- package/dist/json/completions/providers/SettingsPropertyCompletionProvider.js.map +1 -0
- package/dist/json/documentLinks/DocumentLinksProvider.d.ts +6 -0
- package/dist/json/documentLinks/DocumentLinksProvider.js +45 -0
- package/dist/json/documentLinks/DocumentLinksProvider.js.map +1 -0
- package/dist/json/hover/JSONHoverProvider.d.ts +7 -0
- package/dist/json/hover/JSONHoverProvider.js +3 -0
- package/dist/json/hover/JSONHoverProvider.js.map +1 -0
- package/dist/json/hover/providers/BlockSettingsHoverProvider.d.ts +12 -0
- package/dist/json/hover/providers/BlockSettingsHoverProvider.js +75 -0
- package/dist/json/hover/providers/BlockSettingsHoverProvider.js.map +1 -0
- package/dist/json/hover/providers/SchemaTranslationHoverProvider.d.ts +10 -0
- package/dist/json/hover/providers/SchemaTranslationHoverProvider.js +36 -0
- package/dist/json/hover/providers/SchemaTranslationHoverProvider.js.map +1 -0
- package/dist/json/hover/providers/SettingsHoverProvider.d.ts +10 -0
- package/dist/json/hover/providers/SettingsHoverProvider.js +62 -0
- package/dist/json/hover/providers/SettingsHoverProvider.js.map +1 -0
- package/dist/json/hover/providers/TranslationPathHoverProvider.d.ts +9 -0
- package/dist/json/hover/providers/TranslationPathHoverProvider.js +65 -0
- package/dist/json/hover/providers/TranslationPathHoverProvider.js.map +1 -0
- package/dist/json/schemaSettings.d.ts +4 -0
- package/dist/json/schemaSettings.js +45 -0
- package/dist/json/schemaSettings.js.map +1 -0
- package/dist/json/test/test-helpers.d.ts +8 -0
- package/dist/json/test/test-helpers.js +42 -0
- package/dist/json/test/test-helpers.js.map +1 -0
- package/dist/json/utils.d.ts +6 -0
- package/dist/json/utils.js +31 -0
- package/dist/json/utils.js.map +1 -0
- package/dist/linkedEditingRanges/BaseLinkedEditingRangesProvider.d.ts +5 -0
- package/dist/linkedEditingRanges/BaseLinkedEditingRangesProvider.js +3 -0
- package/dist/linkedEditingRanges/BaseLinkedEditingRangesProvider.js.map +1 -0
- package/dist/linkedEditingRanges/LinkedEditingRangesProvider.d.ts +8 -0
- package/dist/linkedEditingRanges/LinkedEditingRangesProvider.js +31 -0
- package/dist/linkedEditingRanges/LinkedEditingRangesProvider.js.map +1 -0
- package/dist/linkedEditingRanges/index.d.ts +1 -0
- package/dist/linkedEditingRanges/index.js +6 -0
- package/dist/linkedEditingRanges/index.js.map +1 -0
- package/dist/linkedEditingRanges/providers/EmptyHtmlTagLinkedRangesProvider.d.ts +9 -0
- package/dist/linkedEditingRanges/providers/EmptyHtmlTagLinkedRangesProvider.js +37 -0
- package/dist/linkedEditingRanges/providers/EmptyHtmlTagLinkedRangesProvider.js.map +1 -0
- package/dist/linkedEditingRanges/providers/HtmlTagNameLinkedRangesProvider.d.ts +12 -0
- package/dist/linkedEditingRanges/providers/HtmlTagNameLinkedRangesProvider.js +26 -0
- package/dist/linkedEditingRanges/providers/HtmlTagNameLinkedRangesProvider.js.map +1 -0
- package/dist/linkedEditingRanges/providers/index.d.ts +2 -0
- package/dist/linkedEditingRanges/providers/index.js +8 -0
- package/dist/linkedEditingRanges/providers/index.js.map +1 -0
- package/dist/linkedEditingRanges/wordPattern.d.ts +1 -0
- package/dist/linkedEditingRanges/wordPattern.js +7 -0
- package/dist/linkedEditingRanges/wordPattern.js.map +1 -0
- package/dist/plugins/css.d.ts +3 -0
- package/dist/plugins/css.js +12 -0
- package/dist/plugins/css.js.map +1 -0
- package/dist/progress.d.ts +42 -0
- package/dist/progress.js +74 -0
- package/dist/progress.js.map +1 -0
- package/dist/rename/BaseRenameProvider.d.ts +6 -0
- package/dist/rename/BaseRenameProvider.js +3 -0
- package/dist/rename/BaseRenameProvider.js.map +1 -0
- package/dist/rename/RenameProvider.d.ts +21 -0
- package/dist/rename/RenameProvider.js +58 -0
- package/dist/rename/RenameProvider.js.map +1 -0
- package/dist/rename/providers/HtmlTagNameRenameProvider.d.ts +10 -0
- package/dist/rename/providers/HtmlTagNameRenameProvider.js +39 -0
- package/dist/rename/providers/HtmlTagNameRenameProvider.js.map +1 -0
- package/dist/rename/providers/LiquidVariableRenameProvider.d.ts +16 -0
- package/dist/rename/providers/LiquidVariableRenameProvider.js +194 -0
- package/dist/rename/providers/LiquidVariableRenameProvider.js.map +1 -0
- package/dist/renamed/BaseRenameHandler.d.ts +4 -0
- package/dist/renamed/BaseRenameHandler.js +3 -0
- package/dist/renamed/BaseRenameHandler.js.map +1 -0
- package/dist/renamed/RenameHandler.d.ts +18 -0
- package/dist/renamed/RenameHandler.js +37 -0
- package/dist/renamed/RenameHandler.js.map +1 -0
- package/dist/renamed/handlers/AssetRenameHandler.d.ts +26 -0
- package/dist/renamed/handlers/AssetRenameHandler.js +99 -0
- package/dist/renamed/handlers/AssetRenameHandler.js.map +1 -0
- package/dist/renamed/handlers/BlockRenameHandler.d.ts +41 -0
- package/dist/renamed/handlers/BlockRenameHandler.js +304 -0
- package/dist/renamed/handlers/BlockRenameHandler.js.map +1 -0
- package/dist/renamed/handlers/PartialRenameHandler.d.ts +26 -0
- package/dist/renamed/handlers/PartialRenameHandler.js +99 -0
- package/dist/renamed/handlers/PartialRenameHandler.js.map +1 -0
- package/dist/renamed/handlers/SectionRenameHandler.d.ts +25 -0
- package/dist/renamed/handlers/SectionRenameHandler.js +176 -0
- package/dist/renamed/handlers/SectionRenameHandler.js.map +1 -0
- package/dist/renamed/handlers/utils.d.ts +3 -0
- package/dist/renamed/handlers/utils.js +20 -0
- package/dist/renamed/handlers/utils.js.map +1 -0
- package/dist/server/CachedFileSystem.d.ts +12 -0
- package/dist/server/CachedFileSystem.js +29 -0
- package/dist/server/CachedFileSystem.js.map +1 -0
- package/dist/server/Configuration.d.ts +25 -0
- package/dist/server/Configuration.js +77 -0
- package/dist/server/Configuration.js.map +1 -0
- package/dist/server/ThemeGraphManager.d.ts +35 -0
- package/dist/server/ThemeGraphManager.js +200 -0
- package/dist/server/ThemeGraphManager.js.map +1 -0
- package/dist/server/index.d.ts +1 -0
- package/dist/server/index.js +6 -0
- package/dist/server/index.js.map +1 -0
- package/dist/server/safe.d.ts +19 -0
- package/dist/server/safe.js +33 -0
- package/dist/server/safe.js.map +1 -0
- package/dist/server/startServer.d.ts +13 -0
- package/dist/server/startServer.js +598 -0
- package/dist/server/startServer.js.map +1 -0
- package/dist/settings/index.d.ts +29 -0
- package/dist/settings/index.js +11 -0
- package/dist/settings/index.js.map +1 -0
- package/dist/test/ApplyEditsAssertion.d.ts +14 -0
- package/dist/test/ApplyEditsAssertion.js +19 -0
- package/dist/test/ApplyEditsAssertion.js.map +1 -0
- package/dist/test/CompletionItemsAssertion.d.ts +25 -0
- package/dist/test/CompletionItemsAssertion.js +60 -0
- package/dist/test/CompletionItemsAssertion.js.map +1 -0
- package/dist/test/HoverAssertion.d.ts +16 -0
- package/dist/test/HoverAssertion.js +54 -0
- package/dist/test/HoverAssertion.js.map +1 -0
- package/dist/test/MockChecks.d.ts +2 -0
- package/dist/test/MockChecks.js +30 -0
- package/dist/test/MockChecks.js.map +1 -0
- package/dist/test/MockConnection.d.ts +44 -0
- package/dist/test/MockConnection.js +106 -0
- package/dist/test/MockConnection.js.map +1 -0
- package/dist/test/test-setup.d.ts +1 -0
- package/dist/test/test-setup.js +8 -0
- package/dist/test/test-setup.js.map +1 -0
- package/dist/translations.d.ts +19 -0
- package/dist/translations.js +73 -0
- package/dist/translations.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/dist/types.d.ts +149 -0
- package/dist/types.js +63 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/array.d.ts +1 -0
- package/dist/utils/array.js +11 -0
- package/dist/utils/array.js.map +1 -0
- package/dist/utils/debounce.d.ts +37 -0
- package/dist/utils/debounce.js +44 -0
- package/dist/utils/debounce.js.map +1 -0
- package/dist/utils/htmlTagNames.d.ts +5 -0
- package/dist/utils/htmlTagNames.js +42 -0
- package/dist/utils/htmlTagNames.js.map +1 -0
- package/dist/utils/index.d.ts +5 -0
- package/dist/utils/index.js +26 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/isCovered.d.ts +4 -0
- package/dist/utils/isCovered.js +7 -0
- package/dist/utils/isCovered.js.map +1 -0
- package/dist/utils/liquidDoc.d.ts +22 -0
- package/dist/utils/liquidDoc.js +78 -0
- package/dist/utils/liquidDoc.js.map +1 -0
- package/dist/utils/node.d.ts +24 -0
- package/dist/utils/node.js +52 -0
- package/dist/utils/node.js.map +1 -0
- package/dist/utils/uri.d.ts +12 -0
- package/dist/utils/uri.js +30 -0
- package/dist/utils/uri.js.map +1 -0
- package/dist/version.d.ts +1 -0
- package/dist/version.js +9 -0
- package/dist/version.js.map +1 -0
- package/package.json +40 -0
- package/src/ClientCapabilities.ts +63 -0
- package/src/PropertyShapeInference.ts +394 -0
- package/src/TypeSystem.spec.ts +1150 -0
- package/src/TypeSystem.ts +1948 -0
- package/src/VariableShapeExtractor.ts +380 -0
- package/src/codeActions/BaseCodeActionsProvider.ts +18 -0
- package/src/codeActions/CodeActionsProvider.ts +28 -0
- package/src/codeActions/index.ts +1 -0
- package/src/codeActions/providers/FixAllProvider.spec.ts +115 -0
- package/src/codeActions/providers/FixAllProvider.ts +60 -0
- package/src/codeActions/providers/FixProvider.spec.ts +330 -0
- package/src/codeActions/providers/FixProvider.ts +105 -0
- package/src/codeActions/providers/SuggestionProvider.spec.ts +143 -0
- package/src/codeActions/providers/SuggestionProvider.ts +75 -0
- package/src/codeActions/providers/index.ts +3 -0
- package/src/codeActions/providers/utils.ts +47 -0
- package/src/commands/BaseExecuteCommandProvider.ts +15 -0
- package/src/commands/ExecuteCommandProvider.ts +59 -0
- package/src/commands/index.ts +2 -0
- package/src/commands/providers/ApplyFixesProvider.spec.ts +133 -0
- package/src/commands/providers/ApplyFixesProvider.ts +86 -0
- package/src/commands/providers/ApplySuggestionProvider.spec.ts +135 -0
- package/src/commands/providers/ApplySuggestionProvider.ts +105 -0
- package/src/commands/providers/RunChecksProvider.spec.ts +55 -0
- package/src/commands/providers/RunChecksProvider.ts +36 -0
- package/src/commands/providers/index.ts +3 -0
- package/src/completions/CompletionsProvider.ts +128 -0
- package/src/completions/index.ts +1 -0
- package/src/completions/params/LiquidCompletionParams.spec.ts +319 -0
- package/src/completions/params/LiquidCompletionParams.ts +538 -0
- package/src/completions/params/fix.spec.ts +159 -0
- package/src/completions/params/fix.ts +269 -0
- package/src/completions/params/index.ts +2 -0
- package/src/completions/params/testcases.txt +104 -0
- package/src/completions/providers/ContentForBlockTypeCompletionProvider.spec.ts +41 -0
- package/src/completions/providers/ContentForBlockTypeCompletionProvider.ts +42 -0
- package/src/completions/providers/ContentForCompletionProvider.spec.ts +96 -0
- package/src/completions/providers/ContentForCompletionProvider.ts +113 -0
- package/src/completions/providers/ContentForParameterCompletionProvider.spec.ts +362 -0
- package/src/completions/providers/ContentForParameterCompletionProvider.ts +187 -0
- package/src/completions/providers/FilterCompletionProvider.spec.ts +377 -0
- package/src/completions/providers/FilterCompletionProvider.ts +225 -0
- package/src/completions/providers/FilterNamedParameterCompletionProvider.spec.ts +239 -0
- package/src/completions/providers/FilterNamedParameterCompletionProvider.ts +121 -0
- package/src/completions/providers/HtmlAttributeCompletionProvider.spec.ts +150 -0
- package/src/completions/providers/HtmlAttributeCompletionProvider.ts +117 -0
- package/src/completions/providers/HtmlAttributeValueCompletionProvider.spec.ts +38 -0
- package/src/completions/providers/HtmlAttributeValueCompletionProvider.ts +65 -0
- package/src/completions/providers/HtmlTagCompletionProvider.spec.ts +57 -0
- package/src/completions/providers/HtmlTagCompletionProvider.ts +119 -0
- package/src/completions/providers/LiquidDocParamTypeCompletionProvider.spec.ts +79 -0
- package/src/completions/providers/LiquidDocParamTypeCompletionProvider.ts +65 -0
- package/src/completions/providers/LiquidDocTagCompletionProvider.spec.ts +144 -0
- package/src/completions/providers/LiquidDocTagCompletionProvider.ts +74 -0
- package/src/completions/providers/LiquidTagsCompletionProvider.spec.ts +377 -0
- package/src/completions/providers/LiquidTagsCompletionProvider.ts +459 -0
- package/src/completions/providers/ObjectAttributeCompletionProvider.spec.ts +524 -0
- package/src/completions/providers/ObjectAttributeCompletionProvider.ts +95 -0
- package/src/completions/providers/ObjectCompletionProvider.spec.ts +232 -0
- package/src/completions/providers/ObjectCompletionProvider.ts +44 -0
- package/src/completions/providers/PartialCompletionProvider.spec.ts +107 -0
- package/src/completions/providers/PartialCompletionProvider.ts +50 -0
- package/src/completions/providers/RenderPartialParameterCompletionProvider.spec.ts +91 -0
- package/src/completions/providers/RenderPartialParameterCompletionProvider.ts +79 -0
- package/src/completions/providers/TranslationCompletionProvider.spec.ts +65 -0
- package/src/completions/providers/TranslationCompletionProvider.ts +103 -0
- package/src/completions/providers/common/CompletionItemProperties.ts +66 -0
- package/src/completions/providers/common/Provider.ts +6 -0
- package/src/completions/providers/common/index.ts +18 -0
- package/src/completions/providers/data/contentForParameterCompletionOptions.ts +9 -0
- package/src/completions/providers/index.ts +20 -0
- package/src/css/CSSLanguageService.spec.ts +161 -0
- package/src/css/CSSLanguageService.ts +113 -0
- package/src/css/test/test-helpers.ts +30 -0
- package/src/definitions/BaseDefinitionProvider.ts +10 -0
- package/src/definitions/DefinitionProvider.ts +48 -0
- package/src/definitions/providers/SchemaTranslationStringDefinitionProvider.spec.ts +96 -0
- package/src/definitions/providers/SchemaTranslationStringDefinitionProvider.ts +82 -0
- package/src/definitions/providers/TranslationStringDefinitionProvider.spec.ts +77 -0
- package/src/definitions/providers/TranslationStringDefinitionProvider.ts +72 -0
- package/src/diagnostics/DiagnosticsManager.spec.ts +79 -0
- package/src/diagnostics/DiagnosticsManager.ts +58 -0
- package/src/diagnostics/index.ts +2 -0
- package/src/diagnostics/offenseToDiagnostic.ts +80 -0
- package/src/diagnostics/runChecks.spec.ts +285 -0
- package/src/diagnostics/runChecks.ts +147 -0
- package/src/docset/HtmlDocset.ts +43 -0
- package/src/docset/MarkdownRenderer.spec.ts +46 -0
- package/src/docset/MarkdownRenderer.ts +179 -0
- package/src/docset/index.ts +2 -0
- package/src/documentHighlights/BaseDocumentHighlightsProvider.ts +10 -0
- package/src/documentHighlights/DocumentHighlightsProvider.ts +54 -0
- package/src/documentHighlights/index.ts +1 -0
- package/src/documentHighlights/providers/HtmlTagNameDocumentHighlightsProvider.spec.ts +94 -0
- package/src/documentHighlights/providers/HtmlTagNameDocumentHighlightsProvider.ts +22 -0
- package/src/documentHighlights/providers/LiquidBlockTagDocumentHighlightsProvider.spec.ts +188 -0
- package/src/documentHighlights/providers/LiquidBlockTagDocumentHighlightsProvider.ts +73 -0
- package/src/documentHighlights/providers/index.ts +2 -0
- package/src/documentLinks/DocumentLinksProvider.spec.ts +66 -0
- package/src/documentLinks/DocumentLinksProvider.ts +123 -0
- package/src/documentLinks/index.ts +1 -0
- package/src/documents/DocumentManager.spec.ts +276 -0
- package/src/documents/DocumentManager.ts +232 -0
- package/src/documents/index.ts +2 -0
- package/src/documents/types.ts +55 -0
- package/src/formatting/OnTypeFormattingProvider.spec.ts +74 -0
- package/src/formatting/OnTypeFormattingProvider.ts +26 -0
- package/src/formatting/index.ts +1 -0
- package/src/formatting/providers/BracketsAutoclosingOnTypeFormattingProvider.spec.ts +88 -0
- package/src/formatting/providers/BracketsAutoclosingOnTypeFormattingProvider.ts +114 -0
- package/src/formatting/providers/HtmlElementAutoclosingOnTypeFormattingProvider.spec.ts +183 -0
- package/src/formatting/providers/HtmlElementAutoclosingOnTypeFormattingProvider.ts +127 -0
- package/src/formatting/types.ts +12 -0
- package/src/hover/BaseHoverProvider.ts +10 -0
- package/src/hover/HoverProvider.ts +86 -0
- package/src/hover/index.ts +1 -0
- package/src/hover/providers/ContentForArgumentHoverProvider.spec.ts +76 -0
- package/src/hover/providers/ContentForArgumentHoverProvider.ts +56 -0
- package/src/hover/providers/ContentForTypeHoverProvider.spec.ts +91 -0
- package/src/hover/providers/ContentForTypeHoverProvider.ts +42 -0
- package/src/hover/providers/HtmlAttributeHoverProvider.spec.ts +50 -0
- package/src/hover/providers/HtmlAttributeHoverProvider.ts +48 -0
- package/src/hover/providers/HtmlAttributeValueHoverProvider.spec.ts +38 -0
- package/src/hover/providers/HtmlAttributeValueHoverProvider.ts +52 -0
- package/src/hover/providers/HtmlTagHoverProvider.spec.ts +39 -0
- package/src/hover/providers/HtmlTagHoverProvider.ts +42 -0
- package/src/hover/providers/LiquidDocTagHoverProvider.spec.ts +64 -0
- package/src/hover/providers/LiquidDocTagHoverProvider.ts +58 -0
- package/src/hover/providers/LiquidFilterArgumentHoverProvider.spec.ts +57 -0
- package/src/hover/providers/LiquidFilterArgumentHoverProvider.ts +42 -0
- package/src/hover/providers/LiquidFilterHoverProvider.spec.ts +50 -0
- package/src/hover/providers/LiquidFilterHoverProvider.ts +29 -0
- package/src/hover/providers/LiquidObjectAttributeHoverProvider.spec.ts +238 -0
- package/src/hover/providers/LiquidObjectAttributeHoverProvider.ts +156 -0
- package/src/hover/providers/LiquidObjectHoverProvider.spec.ts +278 -0
- package/src/hover/providers/LiquidObjectHoverProvider.ts +95 -0
- package/src/hover/providers/LiquidTagHoverProvider.spec.ts +51 -0
- package/src/hover/providers/LiquidTagHoverProvider.ts +33 -0
- package/src/hover/providers/RenderPartialHoverProvider.spec.ts +85 -0
- package/src/hover/providers/RenderPartialHoverProvider.ts +38 -0
- package/src/hover/providers/RenderPartialParameterHoverProvider.spec.ts +70 -0
- package/src/hover/providers/RenderPartialParameterHoverProvider.ts +47 -0
- package/src/hover/providers/TranslationHoverProvider.spec.ts +59 -0
- package/src/hover/providers/TranslationHoverProvider.ts +61 -0
- package/src/hover/providers/index.ts +11 -0
- package/src/index.spec.ts +8 -0
- package/src/index.ts +34 -0
- package/src/internal-types.ts +1 -0
- package/src/json/JSONContributions.ts +150 -0
- package/src/json/JSONLanguageService.spec.ts +424 -0
- package/src/json/JSONLanguageService.ts +239 -0
- package/src/json/RequestContext.ts +35 -0
- package/src/json/completions/JSONCompletionProvider.ts +33 -0
- package/src/json/completions/providers/BlockSettingsPropertyCompletionProvider.spec.ts +163 -0
- package/src/json/completions/providers/BlockSettingsPropertyCompletionProvider.ts +121 -0
- package/src/json/completions/providers/BlockTypeCompletionProvider.spec.ts +137 -0
- package/src/json/completions/providers/BlockTypeCompletionProvider.ts +108 -0
- package/src/json/completions/providers/ReferencedBlockTypeCompletionProvider.spec.ts +132 -0
- package/src/json/completions/providers/ReferencedBlockTypeCompletionProvider.ts +120 -0
- package/src/json/completions/providers/SchemaTranslationCompletionProvider.ts +51 -0
- package/src/json/completions/providers/SettingsPropertyCompletionProvider.spec.ts +211 -0
- package/src/json/completions/providers/SettingsPropertyCompletionProvider.ts +75 -0
- package/src/json/documentLinks/DocumentLinksProvider.spec.ts +223 -0
- package/src/json/documentLinks/DocumentLinksProvider.ts +61 -0
- package/src/json/hover/JSONHoverProvider.ts +8 -0
- package/src/json/hover/providers/BlockSettingsHoverProvider.spec.ts +292 -0
- package/src/json/hover/providers/BlockSettingsHoverProvider.ts +99 -0
- package/src/json/hover/providers/SchemaTranslationHoverProvider.ts +35 -0
- package/src/json/hover/providers/SettingsHoverProvider.spec.ts +139 -0
- package/src/json/hover/providers/SettingsHoverProvider.ts +79 -0
- package/src/json/hover/providers/TranslationPathHoverProvider.ts +71 -0
- package/src/json/schemaSettings.spec.ts +103 -0
- package/src/json/schemaSettings.ts +53 -0
- package/src/json/test/test-helpers.ts +68 -0
- package/src/json/utils.ts +30 -0
- package/src/linkedEditingRanges/BaseLinkedEditingRangesProvider.ts +10 -0
- package/src/linkedEditingRanges/LinkedEditingRangesProvider.ts +40 -0
- package/src/linkedEditingRanges/index.ts +1 -0
- package/src/linkedEditingRanges/providers/EmptyHtmlTagLinkedRangesProvider.spec.ts +74 -0
- package/src/linkedEditingRanges/providers/EmptyHtmlTagLinkedRangesProvider.ts +47 -0
- package/src/linkedEditingRanges/providers/HtmlTagNameLinkedRangesProvider.spec.ts +102 -0
- package/src/linkedEditingRanges/providers/HtmlTagNameLinkedRangesProvider.ts +28 -0
- package/src/linkedEditingRanges/providers/index.ts +2 -0
- package/src/linkedEditingRanges/wordPattern.ts +3 -0
- package/src/plugins/css.ts +12 -0
- package/src/progress.ts +83 -0
- package/src/rename/BaseRenameProvider.ts +21 -0
- package/src/rename/RenameProvider.ts +91 -0
- package/src/rename/providers/HtmlTagNameRenameProvider.spec.ts +84 -0
- package/src/rename/providers/HtmlTagNameRenameProvider.ts +59 -0
- package/src/rename/providers/LiquidVariableRenameProvider.spec.ts +511 -0
- package/src/rename/providers/LiquidVariableRenameProvider.ts +300 -0
- package/src/renamed/BaseRenameHandler.ts +5 -0
- package/src/renamed/RenameHandler.ts +45 -0
- package/src/renamed/handlers/AssetRenameHandler.spec.ts +217 -0
- package/src/renamed/handlers/AssetRenameHandler.ts +108 -0
- package/src/renamed/handlers/BlockRenameHandler.spec.ts +399 -0
- package/src/renamed/handlers/BlockRenameHandler.ts +402 -0
- package/src/renamed/handlers/PartialRenameHandler.spec.ts +153 -0
- package/src/renamed/handlers/PartialRenameHandler.ts +110 -0
- package/src/renamed/handlers/SectionRenameHandler.spec.ts +321 -0
- package/src/renamed/handlers/SectionRenameHandler.ts +224 -0
- package/src/renamed/handlers/utils.ts +22 -0
- package/src/server/CachedFileSystem.ts +39 -0
- package/src/server/Configuration.ts +89 -0
- package/src/server/ThemeGraphManager.ts +265 -0
- package/src/server/index.ts +1 -0
- package/src/server/safe.ts +30 -0
- package/src/server/startServer.spec.ts +423 -0
- package/src/server/startServer.ts +771 -0
- package/src/settings/index.ts +50 -0
- package/src/test/ApplyEditsAssertion.spec.ts +57 -0
- package/src/test/ApplyEditsAssertion.ts +34 -0
- package/src/test/CompletionItemsAssertion.spec.ts +48 -0
- package/src/test/CompletionItemsAssertion.ts +99 -0
- package/src/test/HoverAssertion.ts +84 -0
- package/src/test/MockChecks.ts +32 -0
- package/src/test/MockConnection.ts +158 -0
- package/src/test/test-setup.ts +6 -0
- package/src/translations.ts +83 -0
- package/src/types.ts +173 -0
- package/src/utils/array.ts +6 -0
- package/src/utils/debounce.ts +60 -0
- package/src/utils/htmlTagNames.ts +61 -0
- package/src/utils/index.ts +12 -0
- package/src/utils/isCovered.ts +3 -0
- package/src/utils/liquidDoc.spec.ts +167 -0
- package/src/utils/liquidDoc.ts +102 -0
- package/src/utils/node.ts +69 -0
- package/src/utils/uri.ts +22 -0
- package/src/version.ts +3 -0
- package/tsconfig.build.json +10 -0
- package/tsconfig.json +24 -0
|
@@ -0,0 +1,1948 @@
|
|
|
1
|
+
import {
|
|
2
|
+
AssignMarkup,
|
|
3
|
+
ComplexLiquidExpression,
|
|
4
|
+
FunctionMarkup,
|
|
5
|
+
LiquidDocParamNode,
|
|
6
|
+
LiquidExpression,
|
|
7
|
+
LiquidHtmlNode,
|
|
8
|
+
LiquidTag,
|
|
9
|
+
LiquidTagDecrement,
|
|
10
|
+
LiquidTagIncrement,
|
|
11
|
+
LiquidVariable,
|
|
12
|
+
LiquidVariableLookup,
|
|
13
|
+
NamedTags,
|
|
14
|
+
NodeTypes,
|
|
15
|
+
TextNode,
|
|
16
|
+
GraphQLMarkup,
|
|
17
|
+
GraphQLInlineMarkup,
|
|
18
|
+
LiquidString,
|
|
19
|
+
HashAssignMarkup,
|
|
20
|
+
toLiquidHtmlAST,
|
|
21
|
+
} from '@platformos/liquid-html-parser';
|
|
22
|
+
import {
|
|
23
|
+
ArrayReturnType,
|
|
24
|
+
DocsetEntry,
|
|
25
|
+
FilterEntry,
|
|
26
|
+
MetafieldDefinitionMap,
|
|
27
|
+
MetafieldDefinition,
|
|
28
|
+
ObjectEntry,
|
|
29
|
+
ReturnType,
|
|
30
|
+
SourceCodeType,
|
|
31
|
+
ThemeDocset,
|
|
32
|
+
isError,
|
|
33
|
+
parseJSON,
|
|
34
|
+
path,
|
|
35
|
+
FETCHED_METAFIELD_CATEGORIES,
|
|
36
|
+
BasicParamTypes,
|
|
37
|
+
getValidParamTypes,
|
|
38
|
+
parseParamType,
|
|
39
|
+
} from '@platformos/platformos-check-common';
|
|
40
|
+
import {
|
|
41
|
+
GetThemeSettingsSchemaForURI,
|
|
42
|
+
InputSetting,
|
|
43
|
+
isInputSetting,
|
|
44
|
+
isSettingsCategory,
|
|
45
|
+
} from './settings';
|
|
46
|
+
import { findLast, memo } from './utils';
|
|
47
|
+
import { visit } from '@platformos/platformos-check-common';
|
|
48
|
+
import {
|
|
49
|
+
PropertyShape,
|
|
50
|
+
inferShapeFromJSONString,
|
|
51
|
+
inferShapeFromGraphQL,
|
|
52
|
+
lookupPropertyPath,
|
|
53
|
+
shapeToTypeString,
|
|
54
|
+
shapeToDetailString,
|
|
55
|
+
} from './PropertyShapeInference';
|
|
56
|
+
import { AbstractFileSystem, DocumentsLocator } from '@platformos/platformos-common';
|
|
57
|
+
import { URI } from 'vscode-uri';
|
|
58
|
+
|
|
59
|
+
export class TypeSystem {
|
|
60
|
+
private graphqlSchemaCache: string | undefined;
|
|
61
|
+
private graphqlSchemaLoaded = false;
|
|
62
|
+
|
|
63
|
+
constructor(
|
|
64
|
+
private readonly themeDocset: ThemeDocset,
|
|
65
|
+
private readonly getThemeSettingsSchemaForURI: GetThemeSettingsSchemaForURI,
|
|
66
|
+
private readonly getMetafieldDefinitions: (rootUri: string) => Promise<MetafieldDefinitionMap>,
|
|
67
|
+
private readonly fs?: AbstractFileSystem,
|
|
68
|
+
private readonly documentsLocator?: DocumentsLocator,
|
|
69
|
+
private readonly findThemeRootURI?: (uri: string) => Promise<string | null>,
|
|
70
|
+
) {}
|
|
71
|
+
|
|
72
|
+
private async getGraphQLSchema(): Promise<string | undefined> {
|
|
73
|
+
if (!this.graphqlSchemaLoaded) {
|
|
74
|
+
this.graphqlSchemaCache = (await this.themeDocset.graphQL()) ?? undefined;
|
|
75
|
+
this.graphqlSchemaLoaded = true;
|
|
76
|
+
}
|
|
77
|
+
return this.graphqlSchemaCache;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
async inferType(
|
|
81
|
+
thing: Identifier | ComplexLiquidExpression | LiquidVariable | AssignMarkup,
|
|
82
|
+
partialAst: LiquidHtmlNode,
|
|
83
|
+
uri: string,
|
|
84
|
+
): Promise<PseudoType | ArrayType | ShapeType | UnionType> {
|
|
85
|
+
const [objectMap, filtersMap, symbolsTable] = await Promise.all([
|
|
86
|
+
this.objectMap(uri, partialAst),
|
|
87
|
+
this.filtersMap(),
|
|
88
|
+
this.symbolsTable(partialAst, uri),
|
|
89
|
+
]);
|
|
90
|
+
|
|
91
|
+
return inferType(thing, symbolsTable, objectMap, filtersMap);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
async availableVariables(
|
|
95
|
+
partialAst: LiquidHtmlNode,
|
|
96
|
+
partial: string,
|
|
97
|
+
node: LiquidVariableLookup,
|
|
98
|
+
uri: string,
|
|
99
|
+
): Promise<{ entry: DocsetEntry; type: PseudoType | ArrayType | ShapeType | UnionType }[]> {
|
|
100
|
+
const [objectMap, filtersMap, symbolsTable] = await Promise.all([
|
|
101
|
+
this.objectMap(uri, partialAst),
|
|
102
|
+
this.filtersMap(),
|
|
103
|
+
this.symbolsTable(partialAst, uri),
|
|
104
|
+
]);
|
|
105
|
+
|
|
106
|
+
return Object.entries(symbolsTable)
|
|
107
|
+
.filter(
|
|
108
|
+
([key, typeRanges]) =>
|
|
109
|
+
key.startsWith(partial) &&
|
|
110
|
+
typeRanges.some((typeRange) => isCorrectTypeRange(typeRange, node)),
|
|
111
|
+
)
|
|
112
|
+
.map(([identifier, typeRanges]) => {
|
|
113
|
+
const typeRange = findLast(typeRanges, (typeRange) => isCorrectTypeRange(typeRange, node))!;
|
|
114
|
+
const type = resolveTypeRangeType(typeRange.type, symbolsTable, objectMap, filtersMap);
|
|
115
|
+
const entryType = isArrayType(type)
|
|
116
|
+
? type.valueType
|
|
117
|
+
: isShapeType(type) || isUnionType(type)
|
|
118
|
+
? Untyped
|
|
119
|
+
: type;
|
|
120
|
+
const entry = objectMap[entryType] ?? {};
|
|
121
|
+
return {
|
|
122
|
+
entry: { ...entry, name: identifier },
|
|
123
|
+
type,
|
|
124
|
+
};
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
|
|
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
|
+
/**
|
|
152
|
+
* An indexed representation of objects.json by name
|
|
153
|
+
*
|
|
154
|
+
* e.g. objectMap['product'] returns the product ObjectEntry.
|
|
155
|
+
*/
|
|
156
|
+
public objectMap = async (uri: string, ast: LiquidHtmlNode): Promise<ObjectMap> => {
|
|
157
|
+
const [objectMap, themeSettingProperties, metafieldDefinitionsObjectMap] = await Promise.all([
|
|
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;
|
|
229
|
+
};
|
|
230
|
+
|
|
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
|
+
// This is the big one we reuse (memoized)
|
|
297
|
+
private _objectMap = memo(async (): Promise<ObjectMap> => {
|
|
298
|
+
const entries = await this.objectEntries();
|
|
299
|
+
return entries.reduce((map, entry) => {
|
|
300
|
+
map[entry.name] = entry;
|
|
301
|
+
return map;
|
|
302
|
+
}, {} as ObjectMap);
|
|
303
|
+
});
|
|
304
|
+
|
|
305
|
+
/** An indexed representation of filters.json by name */
|
|
306
|
+
public filtersMap = memo(async (): Promise<FiltersMap> => {
|
|
307
|
+
const entries = await this.filterEntries();
|
|
308
|
+
return entries.reduce((map, entry) => {
|
|
309
|
+
map[entry.name] = entry;
|
|
310
|
+
return map;
|
|
311
|
+
}, {} as FiltersMap);
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
public filterEntries = memo(async () => {
|
|
315
|
+
return this.themeDocset.filters();
|
|
316
|
+
});
|
|
317
|
+
|
|
318
|
+
public objectEntries = memo(async () => {
|
|
319
|
+
return this.themeDocset.objects();
|
|
320
|
+
});
|
|
321
|
+
|
|
322
|
+
private async symbolsTable(partialAst: LiquidHtmlNode, uri: string): Promise<SymbolsTable> {
|
|
323
|
+
const [seedSymbolsTable, liquidDrops, graphqlSchema, rootUri, objectMap, filtersMap] =
|
|
324
|
+
await Promise.all([
|
|
325
|
+
this.seedSymbolsTable(uri),
|
|
326
|
+
this.themeDocset.liquidDrops(),
|
|
327
|
+
this.getGraphQLSchema(),
|
|
328
|
+
this.findThemeRootURI?.(uri) ?? null,
|
|
329
|
+
this.objectMap(uri, partialAst),
|
|
330
|
+
this.filtersMap(),
|
|
331
|
+
]);
|
|
332
|
+
return await buildSymbolsTable(
|
|
333
|
+
partialAst,
|
|
334
|
+
seedSymbolsTable,
|
|
335
|
+
liquidDrops,
|
|
336
|
+
graphqlSchema,
|
|
337
|
+
this.fs,
|
|
338
|
+
this.documentsLocator,
|
|
339
|
+
rootUri ?? undefined,
|
|
340
|
+
undefined, // processingFiles
|
|
341
|
+
objectMap,
|
|
342
|
+
filtersMap,
|
|
343
|
+
);
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
/**
|
|
347
|
+
* The seedSymbolsTable contains all the global variables.
|
|
348
|
+
*
|
|
349
|
+
* This lets us have the ambient type of things first, but if someone
|
|
350
|
+
* reassigns product, then we'll be able to change the type of product on
|
|
351
|
+
* the appropriate range.
|
|
352
|
+
*
|
|
353
|
+
* This is not memo'ed because we would otherwise need to clone the thing.
|
|
354
|
+
*/
|
|
355
|
+
private seedSymbolsTable = async (uri: string) => {
|
|
356
|
+
const [globalVariables, contextualVariables] = await Promise.all([
|
|
357
|
+
this.globalVariables(),
|
|
358
|
+
this.contextualVariables(uri),
|
|
359
|
+
]);
|
|
360
|
+
return globalVariables.concat(contextualVariables).reduce((table, objectEntry) => {
|
|
361
|
+
table[objectEntry.name] ??= [];
|
|
362
|
+
table[objectEntry.name].push({
|
|
363
|
+
identifier: objectEntry.name,
|
|
364
|
+
type: objectEntryType(objectEntry),
|
|
365
|
+
range: [0],
|
|
366
|
+
});
|
|
367
|
+
return table;
|
|
368
|
+
}, {} as SymbolsTable);
|
|
369
|
+
};
|
|
370
|
+
|
|
371
|
+
private globalVariables = memo(async () => {
|
|
372
|
+
const entries = await this.objectEntries();
|
|
373
|
+
return entries.filter(
|
|
374
|
+
(entry) => !entry.access || entry.access.global === true || entry.access.template.length > 0,
|
|
375
|
+
);
|
|
376
|
+
});
|
|
377
|
+
|
|
378
|
+
private contextualVariables = async (uri: string) => {
|
|
379
|
+
const entries = await this.objectEntries();
|
|
380
|
+
const contextualEntries = getContextualEntries(uri);
|
|
381
|
+
return entries.filter((entry) => contextualEntries.includes(entry.name));
|
|
382
|
+
};
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
const SECTION_FILE_REGEX = /sections[\/\\][^.\\\/]*\.liquid$/;
|
|
386
|
+
const BLOCK_FILE_REGEX = /blocks[\/\\][^.\\\/]*\.liquid$/;
|
|
387
|
+
const PARTIAL_FILE_REGEX = /(views[\/\\]partials[\/\\]|[\/\\]lib[\/\\])[^.]*\.liquid$/;
|
|
388
|
+
const LAYOUT_FILE_REGEX = /layout[\/\\]checkout\.liquid$/;
|
|
389
|
+
|
|
390
|
+
function getContextualEntries(uri: string): string[] {
|
|
391
|
+
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
|
+
if (PARTIAL_FILE_REGEX.test(normalizedUri)) {
|
|
416
|
+
return ['app'];
|
|
417
|
+
}
|
|
418
|
+
return [];
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
/** An indexed representation on objects.json (by name) */
|
|
422
|
+
type ObjectMap = Record<ObjectEntryName, ObjectEntry>;
|
|
423
|
+
|
|
424
|
+
/** An indexed representation on filters.json (by name) */
|
|
425
|
+
type FiltersMap = Record<FilterEntryName, FilterEntry>;
|
|
426
|
+
|
|
427
|
+
/** An identifier refers to the name of a variable, e.g. `x`, `product`, etc. */
|
|
428
|
+
type Identifier = string;
|
|
429
|
+
|
|
430
|
+
type ObjectEntryName = ObjectEntry['name'];
|
|
431
|
+
type FilterEntryName = FilterEntry['name'];
|
|
432
|
+
|
|
433
|
+
/** Untyped is for declared variables without a type (like `any`) */
|
|
434
|
+
export const Untyped = 'untyped' as const;
|
|
435
|
+
export type Untyped = typeof Untyped;
|
|
436
|
+
|
|
437
|
+
/** Unknown is for variables that don't exist, type would come from context (e.g. snippet var without LiquidDoc) */
|
|
438
|
+
export const Unknown = 'unknown' as const;
|
|
439
|
+
export type Unknown = typeof Untyped;
|
|
440
|
+
|
|
441
|
+
const String = 'string' as const;
|
|
442
|
+
type String = typeof String;
|
|
443
|
+
|
|
444
|
+
/** A pseudo-type is the possible values of an ObjectEntry's return_type.type */
|
|
445
|
+
export type PseudoType = ObjectEntryName | String | Untyped | Unknown | 'number' | 'boolean';
|
|
446
|
+
|
|
447
|
+
/**
|
|
448
|
+
* A variable can have many types in the same file
|
|
449
|
+
*
|
|
450
|
+
* Just think of this:
|
|
451
|
+
*
|
|
452
|
+
* {{ x }} # unknown
|
|
453
|
+
* {% assign x = all_products['cool-handle'] %}
|
|
454
|
+
* {{ x }} # product
|
|
455
|
+
* {% assign x = x.featured_image %}
|
|
456
|
+
* {{ x }} # image
|
|
457
|
+
* [% assign x = x.src %}]
|
|
458
|
+
* {{ x }} # string
|
|
459
|
+
*/
|
|
460
|
+
interface TypeRange {
|
|
461
|
+
/** The name of the variable */
|
|
462
|
+
identifier: Identifier;
|
|
463
|
+
|
|
464
|
+
/** The type of the variable */
|
|
465
|
+
type:
|
|
466
|
+
| PseudoType
|
|
467
|
+
| ArrayType
|
|
468
|
+
| ShapeType
|
|
469
|
+
| UnionType
|
|
470
|
+
| LazyVariableType
|
|
471
|
+
| LazyDeconstructedExpression;
|
|
472
|
+
|
|
473
|
+
/**
|
|
474
|
+
* The range may be one of two things:
|
|
475
|
+
* - open ended (till end of file, end === undefined)
|
|
476
|
+
* - closed (inside for loop)
|
|
477
|
+
*/
|
|
478
|
+
range: [start: number, end?: number];
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
/** Some things can be an array type (e.g. product.images) */
|
|
482
|
+
export type ArrayType = {
|
|
483
|
+
kind: 'array';
|
|
484
|
+
valueType: PseudoType;
|
|
485
|
+
};
|
|
486
|
+
const arrayType = (valueType: PseudoType): ArrayType => ({
|
|
487
|
+
kind: 'array',
|
|
488
|
+
valueType,
|
|
489
|
+
});
|
|
490
|
+
|
|
491
|
+
/** ShapeType represents inferred types from parse_json, graphql, hash_assign */
|
|
492
|
+
export type ShapeType = {
|
|
493
|
+
kind: 'shape';
|
|
494
|
+
shape: PropertyShape;
|
|
495
|
+
};
|
|
496
|
+
const shapeType = (shape: PropertyShape): ShapeType => ({
|
|
497
|
+
kind: 'shape',
|
|
498
|
+
shape,
|
|
499
|
+
});
|
|
500
|
+
|
|
501
|
+
/** UnionType represents multiple possible types (e.g., from conditional returns) */
|
|
502
|
+
export type UnionType = {
|
|
503
|
+
kind: 'union';
|
|
504
|
+
types: (PseudoType | ArrayType | ShapeType)[];
|
|
505
|
+
};
|
|
506
|
+
const unionType = (types: (PseudoType | ArrayType | ShapeType)[]): UnionType => ({
|
|
507
|
+
kind: 'union',
|
|
508
|
+
types,
|
|
509
|
+
});
|
|
510
|
+
|
|
511
|
+
/**
|
|
512
|
+
* Because a type may depend on another, this represents the type of
|
|
513
|
+
* something as the type of a LiquidVariable chain.
|
|
514
|
+
* {% assign x = y.foo | filter1 | filter2 %}
|
|
515
|
+
*/
|
|
516
|
+
type LazyVariableType = {
|
|
517
|
+
kind: NodeTypes.LiquidVariable;
|
|
518
|
+
node: LiquidVariable;
|
|
519
|
+
offset: number;
|
|
520
|
+
};
|
|
521
|
+
const lazyVariable = (node: LiquidVariable, offset: number): LazyVariableType => ({
|
|
522
|
+
kind: NodeTypes.LiquidVariable,
|
|
523
|
+
node,
|
|
524
|
+
offset,
|
|
525
|
+
});
|
|
526
|
+
|
|
527
|
+
/**
|
|
528
|
+
* A thing may be the deconstruction of something else.
|
|
529
|
+
*
|
|
530
|
+
* examples
|
|
531
|
+
* - for thing in (0..2)
|
|
532
|
+
* - for thing in collection
|
|
533
|
+
* - for thing in parent.collection
|
|
534
|
+
* - for thing in 'string?'
|
|
535
|
+
*/
|
|
536
|
+
type LazyDeconstructedExpression = {
|
|
537
|
+
kind: 'deconstructed';
|
|
538
|
+
node: LiquidExpression;
|
|
539
|
+
offset: number;
|
|
540
|
+
};
|
|
541
|
+
const LazyDeconstructedExpression = (
|
|
542
|
+
node: LiquidExpression,
|
|
543
|
+
offset: number,
|
|
544
|
+
): LazyDeconstructedExpression => ({
|
|
545
|
+
kind: 'deconstructed',
|
|
546
|
+
node,
|
|
547
|
+
offset,
|
|
548
|
+
});
|
|
549
|
+
|
|
550
|
+
/**
|
|
551
|
+
* A symbols table is a map of identifiers to TypeRanges.
|
|
552
|
+
*
|
|
553
|
+
* It stores the mapping of variable name to type by position in the file.
|
|
554
|
+
*
|
|
555
|
+
* The ranges are sorted in range.start order.
|
|
556
|
+
*/
|
|
557
|
+
type SymbolsTable = Record<Identifier, TypeRange[]>;
|
|
558
|
+
|
|
559
|
+
async function buildSymbolsTable(
|
|
560
|
+
partialAst: LiquidHtmlNode,
|
|
561
|
+
seedSymbolsTable: SymbolsTable,
|
|
562
|
+
liquidDrops: ObjectEntry[],
|
|
563
|
+
graphqlSchema?: string,
|
|
564
|
+
fs?: AbstractFileSystem,
|
|
565
|
+
documentsLocator?: DocumentsLocator,
|
|
566
|
+
rootUri?: string,
|
|
567
|
+
processingFiles?: Set<string>,
|
|
568
|
+
objectMap?: ObjectMap,
|
|
569
|
+
filtersMap?: FiltersMap,
|
|
570
|
+
): Promise<SymbolsTable> {
|
|
571
|
+
// Track shapes for hash_assign merging
|
|
572
|
+
const variableShapes: Map<string, { shape: PropertyShape; rangeEnd: number }[]> = new Map();
|
|
573
|
+
|
|
574
|
+
const typeRanges = await visit<SourceCodeType.LiquidHtml, TypeRange | TypeRange[]>(partialAst, {
|
|
575
|
+
// {% assign x = foo.x | filter %}
|
|
576
|
+
// {% assign x = '{"a": 1}' | parse_json %}
|
|
577
|
+
async AssignMarkup(node) {
|
|
578
|
+
// Check if this has a parse_json or to_hash filter
|
|
579
|
+
const hasParseJsonFilter = node.value.filters?.some(
|
|
580
|
+
(f: { name: string }) => f.name === 'parse_json' || f.name === 'to_hash',
|
|
581
|
+
);
|
|
582
|
+
|
|
583
|
+
if (hasParseJsonFilter) {
|
|
584
|
+
let jsonString: string | undefined;
|
|
585
|
+
|
|
586
|
+
// Check if expression is a direct JSON string
|
|
587
|
+
if (node.value.expression.type === NodeTypes.String) {
|
|
588
|
+
jsonString = node.value.expression.value;
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
// Check if there's a default filter with a JSON string argument
|
|
592
|
+
if (!jsonString && node.value.filters) {
|
|
593
|
+
const defaultFilter = node.value.filters.find(
|
|
594
|
+
(f: { name: string }) => f.name === 'default',
|
|
595
|
+
);
|
|
596
|
+
if (defaultFilter && defaultFilter.args && defaultFilter.args.length > 0) {
|
|
597
|
+
const firstArg = defaultFilter.args[0];
|
|
598
|
+
if (firstArg.type === NodeTypes.String) {
|
|
599
|
+
jsonString = firstArg.value;
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
if (jsonString) {
|
|
605
|
+
const shape = inferShapeFromJSONString(jsonString);
|
|
606
|
+
if (shape) {
|
|
607
|
+
// Track shape for potential hash_assign merging
|
|
608
|
+
if (!variableShapes.has(node.name)) {
|
|
609
|
+
variableShapes.set(node.name, []);
|
|
610
|
+
}
|
|
611
|
+
variableShapes.get(node.name)!.push({ shape, rangeEnd: node.position.end });
|
|
612
|
+
|
|
613
|
+
return {
|
|
614
|
+
identifier: node.name,
|
|
615
|
+
type: shapeType(shape),
|
|
616
|
+
range: [node.position.end],
|
|
617
|
+
};
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
return {
|
|
623
|
+
identifier: node.name,
|
|
624
|
+
type: lazyVariable(node.value, node.position.start),
|
|
625
|
+
range: [node.position.end],
|
|
626
|
+
};
|
|
627
|
+
},
|
|
628
|
+
|
|
629
|
+
// {% doc %}
|
|
630
|
+
// @param {string} name - your name
|
|
631
|
+
// {% enddoc %}
|
|
632
|
+
async LiquidDocParamNode(node) {
|
|
633
|
+
return {
|
|
634
|
+
identifier: node.paramName.value,
|
|
635
|
+
type: inferLiquidDocParamType(node, liquidDrops),
|
|
636
|
+
range: [node.position.end],
|
|
637
|
+
};
|
|
638
|
+
},
|
|
639
|
+
|
|
640
|
+
// This also covers tablerow
|
|
641
|
+
async ForMarkup(node, ancestors) {
|
|
642
|
+
const parentNode = ancestors.at(-1)! as LiquidTag;
|
|
643
|
+
return {
|
|
644
|
+
identifier: node.variableName,
|
|
645
|
+
type: LazyDeconstructedExpression(node.collection, node.position.start),
|
|
646
|
+
range: [parentNode.blockStartPosition.end, end(parentNode.blockEndPosition?.end)],
|
|
647
|
+
};
|
|
648
|
+
},
|
|
649
|
+
|
|
650
|
+
// {% capture foo %}
|
|
651
|
+
// ...
|
|
652
|
+
// {% endcapture}
|
|
653
|
+
async LiquidTag(node) {
|
|
654
|
+
if (node.name === 'capture' && typeof node.markup !== 'string') {
|
|
655
|
+
return {
|
|
656
|
+
identifier: node.markup.name!,
|
|
657
|
+
type: String,
|
|
658
|
+
range: [node.position.end],
|
|
659
|
+
};
|
|
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
|
+
} else if (['for', 'tablerow'].includes(node.name)) {
|
|
667
|
+
return {
|
|
668
|
+
identifier: node.name + 'loop',
|
|
669
|
+
type: node.name + 'loop',
|
|
670
|
+
range: [node.blockStartPosition.end, end(node.blockEndPosition?.end)],
|
|
671
|
+
};
|
|
672
|
+
} else if (isLiquidTagIncrement(node) || isLiquidTagDecrement(node)) {
|
|
673
|
+
if (node.markup.name === null) return;
|
|
674
|
+
return {
|
|
675
|
+
identifier: node.markup.name,
|
|
676
|
+
type: 'number',
|
|
677
|
+
range: [node.position.start],
|
|
678
|
+
};
|
|
679
|
+
} else if (node.name === 'layout') {
|
|
680
|
+
return {
|
|
681
|
+
identifier: 'none',
|
|
682
|
+
type: 'keyword',
|
|
683
|
+
range: [node.position.start, node.position.end],
|
|
684
|
+
};
|
|
685
|
+
}
|
|
686
|
+
// {% parse_json x %}{"a": 1}{% endparse_json %}
|
|
687
|
+
else if (isLiquidTagParseJson(node)) {
|
|
688
|
+
const variableName = node.markup.name;
|
|
689
|
+
if (variableName && node.children) {
|
|
690
|
+
const textContent = node.children
|
|
691
|
+
.filter((c): c is TextNode => c.type === NodeTypes.TextNode)
|
|
692
|
+
.map((c) => c.value)
|
|
693
|
+
.join('');
|
|
694
|
+
const shape = inferShapeFromJSONString(textContent);
|
|
695
|
+
if (shape) {
|
|
696
|
+
// Track shape for potential hash_assign merging
|
|
697
|
+
if (!variableShapes.has(variableName)) {
|
|
698
|
+
variableShapes.set(variableName, []);
|
|
699
|
+
}
|
|
700
|
+
variableShapes.get(variableName)!.push({
|
|
701
|
+
shape,
|
|
702
|
+
rangeEnd: node.blockEndPosition?.end ?? node.position.end,
|
|
703
|
+
});
|
|
704
|
+
|
|
705
|
+
return {
|
|
706
|
+
identifier: variableName,
|
|
707
|
+
type: shapeType(shape),
|
|
708
|
+
range: [node.blockEndPosition?.end ?? node.position.end],
|
|
709
|
+
};
|
|
710
|
+
}
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
// {% graphql result %}...{% endgraphql %} (inline)
|
|
714
|
+
else if (isLiquidTagGraphQL(node) && isGraphQLInlineMarkup(node.markup)) {
|
|
715
|
+
const markup = node.markup;
|
|
716
|
+
if (node.children) {
|
|
717
|
+
const textContent = node.children
|
|
718
|
+
.filter((c): c is TextNode => c.type === NodeTypes.TextNode)
|
|
719
|
+
.map((c) => c.value)
|
|
720
|
+
.join('');
|
|
721
|
+
const shape = inferShapeFromGraphQL(textContent, graphqlSchema);
|
|
722
|
+
if (shape) {
|
|
723
|
+
// Track shape for potential hash_assign merging
|
|
724
|
+
if (!variableShapes.has(markup.name)) {
|
|
725
|
+
variableShapes.set(markup.name, []);
|
|
726
|
+
}
|
|
727
|
+
variableShapes.get(markup.name)!.push({
|
|
728
|
+
shape,
|
|
729
|
+
rangeEnd: node.blockEndPosition?.end ?? node.position.end,
|
|
730
|
+
});
|
|
731
|
+
|
|
732
|
+
return {
|
|
733
|
+
identifier: markup.name,
|
|
734
|
+
type: shapeType(shape),
|
|
735
|
+
range: [node.blockEndPosition?.end ?? node.position.end],
|
|
736
|
+
};
|
|
737
|
+
}
|
|
738
|
+
}
|
|
739
|
+
}
|
|
740
|
+
// {% graphql result = 'file' %} (file-based)
|
|
741
|
+
else if (isLiquidTagGraphQL(node) && isGraphQLFileMarkup(node.markup)) {
|
|
742
|
+
const markup = node.markup;
|
|
743
|
+
if (fs && documentsLocator && rootUri && isLiquidString(markup.graphql)) {
|
|
744
|
+
const graphqlFile = markup.graphql.value;
|
|
745
|
+
try {
|
|
746
|
+
const located = await documentsLocator.locate(
|
|
747
|
+
URI.parse(rootUri),
|
|
748
|
+
'graphql',
|
|
749
|
+
graphqlFile,
|
|
750
|
+
);
|
|
751
|
+
if (located) {
|
|
752
|
+
const content = await fs.readFile(located);
|
|
753
|
+
const shape = inferShapeFromGraphQL(content, graphqlSchema);
|
|
754
|
+
if (shape) {
|
|
755
|
+
// Track shape for potential hash_assign merging
|
|
756
|
+
if (!variableShapes.has(markup.name)) {
|
|
757
|
+
variableShapes.set(markup.name, []);
|
|
758
|
+
}
|
|
759
|
+
variableShapes.get(markup.name)!.push({
|
|
760
|
+
shape,
|
|
761
|
+
rangeEnd: node.position.end,
|
|
762
|
+
});
|
|
763
|
+
|
|
764
|
+
return {
|
|
765
|
+
identifier: markup.name,
|
|
766
|
+
type: shapeType(shape),
|
|
767
|
+
range: [node.position.end],
|
|
768
|
+
};
|
|
769
|
+
}
|
|
770
|
+
}
|
|
771
|
+
} catch {
|
|
772
|
+
// File read error - skip
|
|
773
|
+
}
|
|
774
|
+
}
|
|
775
|
+
}
|
|
776
|
+
// {% hash_assign x['key'] = value %}
|
|
777
|
+
else if (isLiquidTagHashAssign(node)) {
|
|
778
|
+
const markup = node.markup;
|
|
779
|
+
const variableName = markup.target.name;
|
|
780
|
+
if (!variableName) return;
|
|
781
|
+
|
|
782
|
+
const lookupPath = getHashAssignLookupPath(markup);
|
|
783
|
+
|
|
784
|
+
// Determine value shape - check if value is a JSON string with parse_json filter
|
|
785
|
+
let valueShape: PropertyShape | undefined;
|
|
786
|
+
if (markup.value.expression.type === NodeTypes.String) {
|
|
787
|
+
const hasParseJsonFilter = markup.value.filters.some(
|
|
788
|
+
(f) => f.name === 'parse_json' || f.name === 'to_hash',
|
|
789
|
+
);
|
|
790
|
+
if (hasParseJsonFilter) {
|
|
791
|
+
valueShape = inferShapeFromJSONString(markup.value.expression.value) ?? undefined;
|
|
792
|
+
}
|
|
793
|
+
}
|
|
794
|
+
|
|
795
|
+
// Check for existing type - first in variableShapes, then in seedSymbolsTable
|
|
796
|
+
const existingShapes = variableShapes.get(variableName) || [];
|
|
797
|
+
const existingShapeEntry = findLastApplicableShape(existingShapes, node.position.start);
|
|
798
|
+
|
|
799
|
+
// Check seedSymbolsTable for existing type if not in variableShapes
|
|
800
|
+
let existingType: PseudoType | ArrayType | ShapeType | UnionType | undefined;
|
|
801
|
+
if (!existingShapeEntry && seedSymbolsTable[variableName]) {
|
|
802
|
+
const typeRanges = seedSymbolsTable[variableName];
|
|
803
|
+
// Find the most recent type before this position
|
|
804
|
+
for (const tr of typeRanges) {
|
|
805
|
+
if (tr.range[0] < node.position.start) {
|
|
806
|
+
// Resolve lazy types
|
|
807
|
+
if (typeof tr.type === 'string') {
|
|
808
|
+
existingType = tr.type;
|
|
809
|
+
} else if (
|
|
810
|
+
tr.type.kind === 'shape' ||
|
|
811
|
+
tr.type.kind === 'array' ||
|
|
812
|
+
tr.type.kind === 'union'
|
|
813
|
+
) {
|
|
814
|
+
existingType = tr.type;
|
|
815
|
+
}
|
|
816
|
+
}
|
|
817
|
+
}
|
|
818
|
+
}
|
|
819
|
+
|
|
820
|
+
// Check if hash_assign is being applied to a non-object type (error case)
|
|
821
|
+
const nonObjectTypes = ['number', 'string', 'boolean'];
|
|
822
|
+
if (!existingShapeEntry && existingType) {
|
|
823
|
+
if (typeof existingType === 'string' && nonObjectTypes.includes(existingType)) {
|
|
824
|
+
// Can't hash_assign to a primitive - this is an error
|
|
825
|
+
// Return undefined to not change the type (let a check rule report the error)
|
|
826
|
+
return;
|
|
827
|
+
}
|
|
828
|
+
if (isArrayType(existingType)) {
|
|
829
|
+
// Can't hash_assign to an array - this is an error
|
|
830
|
+
return;
|
|
831
|
+
}
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
if (lookupPath && lookupPath.length > 0) {
|
|
835
|
+
// Nested property assignment: {% hash_assign a['key'] = value %}
|
|
836
|
+
let baseShape: PropertyShape;
|
|
837
|
+
if (existingShapeEntry) {
|
|
838
|
+
baseShape = existingShapeEntry.shape;
|
|
839
|
+
} else if (existingType && isShapeType(existingType)) {
|
|
840
|
+
baseShape = existingType.shape;
|
|
841
|
+
} else {
|
|
842
|
+
baseShape = { kind: 'object', properties: new Map() };
|
|
843
|
+
}
|
|
844
|
+
|
|
845
|
+
const newShape = mergeNestedPropertyIntoShape(
|
|
846
|
+
baseShape,
|
|
847
|
+
lookupPath,
|
|
848
|
+
valueShape ?? { kind: 'primitive' },
|
|
849
|
+
);
|
|
850
|
+
|
|
851
|
+
// Track the new shape
|
|
852
|
+
if (!variableShapes.has(variableName)) {
|
|
853
|
+
variableShapes.set(variableName, []);
|
|
854
|
+
}
|
|
855
|
+
variableShapes.get(variableName)!.push({ shape: newShape, rangeEnd: node.position.end });
|
|
856
|
+
|
|
857
|
+
return {
|
|
858
|
+
identifier: variableName,
|
|
859
|
+
type: shapeType(newShape),
|
|
860
|
+
range: [node.position.end],
|
|
861
|
+
};
|
|
862
|
+
} else if (valueShape) {
|
|
863
|
+
// Direct assignment: {% hash_assign a = value %} (works like assign)
|
|
864
|
+
if (!variableShapes.has(variableName)) {
|
|
865
|
+
variableShapes.set(variableName, []);
|
|
866
|
+
}
|
|
867
|
+
variableShapes
|
|
868
|
+
.get(variableName)!
|
|
869
|
+
.push({ shape: valueShape, rangeEnd: node.position.end });
|
|
870
|
+
|
|
871
|
+
return {
|
|
872
|
+
identifier: variableName,
|
|
873
|
+
type: shapeType(valueShape),
|
|
874
|
+
range: [node.position.end],
|
|
875
|
+
};
|
|
876
|
+
}
|
|
877
|
+
}
|
|
878
|
+
// {% function result = 'partial/path', args... %}
|
|
879
|
+
else if (isLiquidTagFunction(node)) {
|
|
880
|
+
const markup = node.markup;
|
|
881
|
+
let returnType: PseudoType | ArrayType | ShapeType | UnionType | undefined;
|
|
882
|
+
if (
|
|
883
|
+
fs &&
|
|
884
|
+
documentsLocator &&
|
|
885
|
+
rootUri &&
|
|
886
|
+
objectMap &&
|
|
887
|
+
filtersMap &&
|
|
888
|
+
isLiquidString(markup.partial)
|
|
889
|
+
) {
|
|
890
|
+
const partialPath = markup.partial.value;
|
|
891
|
+
try {
|
|
892
|
+
returnType = await inferFunctionReturnType(
|
|
893
|
+
partialPath,
|
|
894
|
+
fs,
|
|
895
|
+
documentsLocator,
|
|
896
|
+
rootUri,
|
|
897
|
+
seedSymbolsTable,
|
|
898
|
+
liquidDrops,
|
|
899
|
+
graphqlSchema,
|
|
900
|
+
processingFiles,
|
|
901
|
+
objectMap,
|
|
902
|
+
filtersMap,
|
|
903
|
+
);
|
|
904
|
+
} catch {
|
|
905
|
+
// File read/parse error - returnType stays undefined
|
|
906
|
+
}
|
|
907
|
+
}
|
|
908
|
+
|
|
909
|
+
// Track shape for potential hash_assign merging
|
|
910
|
+
if (returnType && isShapeType(returnType)) {
|
|
911
|
+
if (!variableShapes.has(markup.name)) {
|
|
912
|
+
variableShapes.set(markup.name, []);
|
|
913
|
+
}
|
|
914
|
+
variableShapes.get(markup.name)!.push({
|
|
915
|
+
shape: returnType.shape,
|
|
916
|
+
rangeEnd: node.position.end,
|
|
917
|
+
});
|
|
918
|
+
}
|
|
919
|
+
|
|
920
|
+
// Always add function variable to symbolsTable, using Untyped as fallback
|
|
921
|
+
return {
|
|
922
|
+
identifier: markup.name,
|
|
923
|
+
type: returnType ?? Untyped,
|
|
924
|
+
range: [node.position.end],
|
|
925
|
+
};
|
|
926
|
+
}
|
|
927
|
+
},
|
|
928
|
+
});
|
|
929
|
+
|
|
930
|
+
// Flatten array results (some visitors return TypeRange[])
|
|
931
|
+
const flattenedRanges = typeRanges.flat();
|
|
932
|
+
|
|
933
|
+
return flattenedRanges
|
|
934
|
+
.sort(({ range: [startA] }, { range: [startB] }) => startA - startB)
|
|
935
|
+
.reduce((table, typeRange) => {
|
|
936
|
+
table[typeRange.identifier] ??= [];
|
|
937
|
+
table[typeRange.identifier].push(typeRange);
|
|
938
|
+
return table;
|
|
939
|
+
}, seedSymbolsTable);
|
|
940
|
+
}
|
|
941
|
+
|
|
942
|
+
/**
|
|
943
|
+
* Given a TypeRange['type'] (which may be lazy), resolve its type recursively.
|
|
944
|
+
*
|
|
945
|
+
* The output is a fully resolved PseudoType | ArrayType | ShapeType. Which means we
|
|
946
|
+
* could use it to power completions.
|
|
947
|
+
*/
|
|
948
|
+
function resolveTypeRangeType(
|
|
949
|
+
typeRangeType: TypeRange['type'],
|
|
950
|
+
symbolsTable: SymbolsTable,
|
|
951
|
+
objectMap: ObjectMap,
|
|
952
|
+
filtersMap: FiltersMap,
|
|
953
|
+
): PseudoType | ArrayType | ShapeType | UnionType {
|
|
954
|
+
if (typeof typeRangeType === 'string') {
|
|
955
|
+
return typeRangeType;
|
|
956
|
+
}
|
|
957
|
+
|
|
958
|
+
switch (typeRangeType.kind) {
|
|
959
|
+
case 'array': {
|
|
960
|
+
return typeRangeType;
|
|
961
|
+
}
|
|
962
|
+
|
|
963
|
+
case 'shape': {
|
|
964
|
+
return typeRangeType;
|
|
965
|
+
}
|
|
966
|
+
|
|
967
|
+
case 'union': {
|
|
968
|
+
return typeRangeType;
|
|
969
|
+
}
|
|
970
|
+
|
|
971
|
+
case 'deconstructed': {
|
|
972
|
+
const deconstructedType = inferType(typeRangeType.node, symbolsTable, objectMap, filtersMap);
|
|
973
|
+
if (typeof deconstructedType === 'string') {
|
|
974
|
+
return Untyped;
|
|
975
|
+
} else if (isShapeType(deconstructedType)) {
|
|
976
|
+
// Deconstruct shape array
|
|
977
|
+
if (deconstructedType.shape.kind === 'array' && deconstructedType.shape.itemShape) {
|
|
978
|
+
return shapeType(deconstructedType.shape.itemShape);
|
|
979
|
+
}
|
|
980
|
+
return Untyped;
|
|
981
|
+
} else if (isUnionType(deconstructedType)) {
|
|
982
|
+
return Untyped;
|
|
983
|
+
} else {
|
|
984
|
+
return deconstructedType.valueType;
|
|
985
|
+
}
|
|
986
|
+
}
|
|
987
|
+
|
|
988
|
+
default: {
|
|
989
|
+
return inferType(typeRangeType.node, symbolsTable, objectMap, filtersMap);
|
|
990
|
+
}
|
|
991
|
+
}
|
|
992
|
+
}
|
|
993
|
+
|
|
994
|
+
function inferType(
|
|
995
|
+
thing: Identifier | ComplexLiquidExpression | LiquidVariable | AssignMarkup,
|
|
996
|
+
symbolsTable: SymbolsTable,
|
|
997
|
+
objectMap: ObjectMap,
|
|
998
|
+
filtersMap: FiltersMap,
|
|
999
|
+
): PseudoType | ArrayType | ShapeType | UnionType {
|
|
1000
|
+
if (typeof thing === 'string') {
|
|
1001
|
+
return objectMap[thing as PseudoType]?.name ?? Untyped;
|
|
1002
|
+
}
|
|
1003
|
+
|
|
1004
|
+
switch (thing.type) {
|
|
1005
|
+
case NodeTypes.Number: {
|
|
1006
|
+
return 'number';
|
|
1007
|
+
}
|
|
1008
|
+
|
|
1009
|
+
case NodeTypes.String: {
|
|
1010
|
+
return 'string';
|
|
1011
|
+
}
|
|
1012
|
+
|
|
1013
|
+
case NodeTypes.LiquidLiteral: {
|
|
1014
|
+
return 'boolean';
|
|
1015
|
+
}
|
|
1016
|
+
|
|
1017
|
+
case NodeTypes.BooleanExpression: {
|
|
1018
|
+
return 'boolean';
|
|
1019
|
+
}
|
|
1020
|
+
|
|
1021
|
+
case NodeTypes.Range: {
|
|
1022
|
+
return arrayType('number');
|
|
1023
|
+
}
|
|
1024
|
+
|
|
1025
|
+
// The type of the assign markup is the type of the right hand side.
|
|
1026
|
+
// {% assign x = y.property | filter1 | filter2 %}
|
|
1027
|
+
case NodeTypes.AssignMarkup: {
|
|
1028
|
+
return inferType(thing.value, symbolsTable, objectMap, filtersMap);
|
|
1029
|
+
}
|
|
1030
|
+
|
|
1031
|
+
// A variable lookup is expression[.lookup]*
|
|
1032
|
+
// {{ y.property }}
|
|
1033
|
+
case NodeTypes.VariableLookup: {
|
|
1034
|
+
return inferLookupType(thing, symbolsTable, objectMap, filtersMap);
|
|
1035
|
+
}
|
|
1036
|
+
|
|
1037
|
+
// A variable is the VariableLookup + Filters
|
|
1038
|
+
// The type is the return value of the last filter
|
|
1039
|
+
// {{ y.property | filter1 | filter2 }}
|
|
1040
|
+
case NodeTypes.LiquidVariable: {
|
|
1041
|
+
if (thing.filters.length > 0) {
|
|
1042
|
+
const lastFilter = thing.filters.at(-1)!;
|
|
1043
|
+
if (lastFilter.name === 'default') {
|
|
1044
|
+
// default filter is a special case, we need to return the type of the expression
|
|
1045
|
+
// instead of the filter.
|
|
1046
|
+
if (lastFilter.args.length > 0 && lastFilter.args[0].type !== NodeTypes.NamedArgument) {
|
|
1047
|
+
return inferType(lastFilter.args[0], symbolsTable, objectMap, filtersMap);
|
|
1048
|
+
}
|
|
1049
|
+
}
|
|
1050
|
+
const filterEntry = filtersMap[lastFilter.name];
|
|
1051
|
+
return filterEntry ? filterEntryReturnType(filterEntry) : Untyped;
|
|
1052
|
+
} else {
|
|
1053
|
+
return inferType(thing.expression, symbolsTable, objectMap, filtersMap);
|
|
1054
|
+
}
|
|
1055
|
+
}
|
|
1056
|
+
|
|
1057
|
+
default: {
|
|
1058
|
+
return Untyped;
|
|
1059
|
+
}
|
|
1060
|
+
}
|
|
1061
|
+
}
|
|
1062
|
+
|
|
1063
|
+
function inferLiquidDocParamType(node: LiquidDocParamNode, liquidDrops: ObjectEntry[]) {
|
|
1064
|
+
const paramTypeValue = node.paramType?.value;
|
|
1065
|
+
|
|
1066
|
+
if (!paramTypeValue) return Untyped;
|
|
1067
|
+
|
|
1068
|
+
const validParamTypes = getValidParamTypes(liquidDrops);
|
|
1069
|
+
|
|
1070
|
+
const parsedParamType = parseParamType(new Set(validParamTypes.keys()), paramTypeValue);
|
|
1071
|
+
|
|
1072
|
+
if (!parsedParamType) return Untyped;
|
|
1073
|
+
|
|
1074
|
+
const [type, isArray] = parsedParamType;
|
|
1075
|
+
|
|
1076
|
+
let transformedParamType;
|
|
1077
|
+
|
|
1078
|
+
// BasicParamTypes.Object does not map to any specific type in the type system.
|
|
1079
|
+
if (type === BasicParamTypes.Object) {
|
|
1080
|
+
transformedParamType = Untyped;
|
|
1081
|
+
} else {
|
|
1082
|
+
transformedParamType = type;
|
|
1083
|
+
}
|
|
1084
|
+
|
|
1085
|
+
if (isArray) {
|
|
1086
|
+
return arrayType(transformedParamType);
|
|
1087
|
+
}
|
|
1088
|
+
|
|
1089
|
+
return transformedParamType;
|
|
1090
|
+
}
|
|
1091
|
+
|
|
1092
|
+
function inferLookupType(
|
|
1093
|
+
thing: LiquidVariableLookup,
|
|
1094
|
+
symbolsTable: SymbolsTable,
|
|
1095
|
+
objectMap: ObjectMap,
|
|
1096
|
+
filtersMap: FiltersMap,
|
|
1097
|
+
): PseudoType | ArrayType | ShapeType | UnionType {
|
|
1098
|
+
// we return the type of the drop, so a.b.c
|
|
1099
|
+
const node = thing;
|
|
1100
|
+
|
|
1101
|
+
// We don't complete global lookups. It's too much of an edge case.
|
|
1102
|
+
if (node.name === null) return Untyped;
|
|
1103
|
+
|
|
1104
|
+
/**
|
|
1105
|
+
* curr stores the type of the variable lookup starting at the beginning.
|
|
1106
|
+
*
|
|
1107
|
+
* It starts as the type of the top-level identifier, and the we
|
|
1108
|
+
* recursively change it to the return type of the lookups.
|
|
1109
|
+
*
|
|
1110
|
+
* So, for x.images.first.src we do:
|
|
1111
|
+
* - curr = infer type of x | x
|
|
1112
|
+
* - curr = x.images -> ArrayType<image> | x.images
|
|
1113
|
+
* - curr = images.first -> image | x.images.first
|
|
1114
|
+
* - curr = first.src -> string | x.images.first.src
|
|
1115
|
+
*
|
|
1116
|
+
* Once were done iterating, the type of the lookup is curr.
|
|
1117
|
+
*/
|
|
1118
|
+
let curr: PseudoType | ArrayType | ShapeType | UnionType = inferIdentifierType(
|
|
1119
|
+
node,
|
|
1120
|
+
symbolsTable,
|
|
1121
|
+
objectMap,
|
|
1122
|
+
filtersMap,
|
|
1123
|
+
);
|
|
1124
|
+
|
|
1125
|
+
for (let lookup of node.lookups) {
|
|
1126
|
+
// Here we redefine curr to be the returnType of the lookup.
|
|
1127
|
+
|
|
1128
|
+
// e.g. images[0] -> image
|
|
1129
|
+
// e.g. images.first -> image
|
|
1130
|
+
// e.g. images.size -> number
|
|
1131
|
+
if (isArrayType(curr)) {
|
|
1132
|
+
curr = inferArrayTypeLookupType(curr, lookup);
|
|
1133
|
+
}
|
|
1134
|
+
|
|
1135
|
+
// Handle ShapeType from parse_json, graphql, hash_assign
|
|
1136
|
+
else if (isShapeType(curr)) {
|
|
1137
|
+
curr = inferShapeTypeLookupType(curr, lookup);
|
|
1138
|
+
}
|
|
1139
|
+
|
|
1140
|
+
// Handle UnionType - for now, treat as Untyped for lookups
|
|
1141
|
+
else if (isUnionType(curr)) {
|
|
1142
|
+
return Untyped;
|
|
1143
|
+
}
|
|
1144
|
+
|
|
1145
|
+
// e.g. product.featured_image -> image
|
|
1146
|
+
// e.g. product.images -> ArrayType<images>
|
|
1147
|
+
// e.g. product.name -> string
|
|
1148
|
+
else {
|
|
1149
|
+
curr = inferPseudoTypePropertyType(curr, lookup, objectMap);
|
|
1150
|
+
}
|
|
1151
|
+
|
|
1152
|
+
// Early return
|
|
1153
|
+
if (curr === Untyped) {
|
|
1154
|
+
return Untyped;
|
|
1155
|
+
}
|
|
1156
|
+
}
|
|
1157
|
+
|
|
1158
|
+
return curr;
|
|
1159
|
+
}
|
|
1160
|
+
|
|
1161
|
+
/**
|
|
1162
|
+
* Given a VariableLookup node, infer the type of its root (position-relative).
|
|
1163
|
+
*
|
|
1164
|
+
* e.g. for the following
|
|
1165
|
+
* {% assign x = product %}
|
|
1166
|
+
* {{ x.images.first }}
|
|
1167
|
+
*
|
|
1168
|
+
* This function infers the type of `x`.
|
|
1169
|
+
*/
|
|
1170
|
+
function inferIdentifierType(
|
|
1171
|
+
node: LiquidVariableLookup,
|
|
1172
|
+
symbolsTable: SymbolsTable,
|
|
1173
|
+
objectMap: ObjectMap,
|
|
1174
|
+
filtersMap: FiltersMap,
|
|
1175
|
+
): PseudoType | ArrayType | ShapeType | UnionType {
|
|
1176
|
+
// The name of a variable
|
|
1177
|
+
const identifier = node.name;
|
|
1178
|
+
|
|
1179
|
+
// We don't complete the global access edge case
|
|
1180
|
+
// e.g. {{ ['all_products'] }}
|
|
1181
|
+
if (!identifier) {
|
|
1182
|
+
return Untyped;
|
|
1183
|
+
}
|
|
1184
|
+
|
|
1185
|
+
const typeRanges = symbolsTable[identifier];
|
|
1186
|
+
if (!typeRanges) {
|
|
1187
|
+
return Unknown;
|
|
1188
|
+
}
|
|
1189
|
+
|
|
1190
|
+
const typeRange = findLast(typeRanges, (tr) => isCorrectTypeRange(tr, node));
|
|
1191
|
+
|
|
1192
|
+
return typeRange
|
|
1193
|
+
? resolveTypeRangeType(typeRange.type, symbolsTable, objectMap, filtersMap)
|
|
1194
|
+
: Unknown;
|
|
1195
|
+
}
|
|
1196
|
+
|
|
1197
|
+
/**
|
|
1198
|
+
* infers the type of a lookup on an ArrayType
|
|
1199
|
+
* - images[0] becomes 'image'
|
|
1200
|
+
* - images[index] becomes 'image'
|
|
1201
|
+
* - images.first becomes 'image'
|
|
1202
|
+
* - images.last becomes 'image'
|
|
1203
|
+
* - images.size becomes 'number'
|
|
1204
|
+
* - anything else becomes 'untyped'
|
|
1205
|
+
*/
|
|
1206
|
+
function inferArrayTypeLookupType(curr: ArrayType, lookup: LiquidExpression) {
|
|
1207
|
+
// images[0]
|
|
1208
|
+
// images[index]
|
|
1209
|
+
if (lookup.type === NodeTypes.Number || lookup.type === NodeTypes.VariableLookup) {
|
|
1210
|
+
return curr.valueType;
|
|
1211
|
+
}
|
|
1212
|
+
// images.first
|
|
1213
|
+
// images.last
|
|
1214
|
+
// images.size
|
|
1215
|
+
// anything else is undef
|
|
1216
|
+
else if (lookup.type === NodeTypes.String) {
|
|
1217
|
+
switch (lookup.value) {
|
|
1218
|
+
case 'first':
|
|
1219
|
+
case 'last': {
|
|
1220
|
+
return curr.valueType;
|
|
1221
|
+
}
|
|
1222
|
+
|
|
1223
|
+
case 'size': {
|
|
1224
|
+
return 'number';
|
|
1225
|
+
}
|
|
1226
|
+
|
|
1227
|
+
default: {
|
|
1228
|
+
return Unknown;
|
|
1229
|
+
}
|
|
1230
|
+
}
|
|
1231
|
+
}
|
|
1232
|
+
// images[true]
|
|
1233
|
+
// images[(0..2)]
|
|
1234
|
+
else {
|
|
1235
|
+
return Untyped;
|
|
1236
|
+
}
|
|
1237
|
+
}
|
|
1238
|
+
|
|
1239
|
+
/**
|
|
1240
|
+
* Infers the type of a lookup on a ShapeType (from parse_json, graphql, hash_assign)
|
|
1241
|
+
*/
|
|
1242
|
+
function inferShapeTypeLookupType(
|
|
1243
|
+
curr: ShapeType,
|
|
1244
|
+
lookup: LiquidExpression,
|
|
1245
|
+
): PseudoType | ArrayType | ShapeType {
|
|
1246
|
+
const shape = curr.shape;
|
|
1247
|
+
|
|
1248
|
+
// Handle array shape lookups
|
|
1249
|
+
if (shape.kind === 'array') {
|
|
1250
|
+
// array[0] or array[variable] -> item type
|
|
1251
|
+
if (lookup.type === NodeTypes.Number || lookup.type === NodeTypes.VariableLookup) {
|
|
1252
|
+
if (shape.itemShape) {
|
|
1253
|
+
return shapeToType(shape.itemShape);
|
|
1254
|
+
}
|
|
1255
|
+
return Untyped;
|
|
1256
|
+
}
|
|
1257
|
+
|
|
1258
|
+
// array.first, array.last, array.size
|
|
1259
|
+
if (lookup.type === NodeTypes.String) {
|
|
1260
|
+
switch (lookup.value) {
|
|
1261
|
+
case 'first':
|
|
1262
|
+
case 'last':
|
|
1263
|
+
if (shape.itemShape) {
|
|
1264
|
+
return shapeToType(shape.itemShape);
|
|
1265
|
+
}
|
|
1266
|
+
return Untyped;
|
|
1267
|
+
case 'size':
|
|
1268
|
+
return 'number';
|
|
1269
|
+
default:
|
|
1270
|
+
return Unknown;
|
|
1271
|
+
}
|
|
1272
|
+
}
|
|
1273
|
+
|
|
1274
|
+
return Untyped;
|
|
1275
|
+
}
|
|
1276
|
+
|
|
1277
|
+
// Handle object shape lookups
|
|
1278
|
+
if (shape.kind === 'object') {
|
|
1279
|
+
// Object lookups must be strings
|
|
1280
|
+
if (lookup.type !== NodeTypes.String) {
|
|
1281
|
+
return Untyped;
|
|
1282
|
+
}
|
|
1283
|
+
|
|
1284
|
+
const propertyName = lookup.value;
|
|
1285
|
+
const propertyShape = shape.properties?.get(propertyName);
|
|
1286
|
+
|
|
1287
|
+
if (propertyShape) {
|
|
1288
|
+
return shapeToType(propertyShape);
|
|
1289
|
+
}
|
|
1290
|
+
|
|
1291
|
+
return Unknown;
|
|
1292
|
+
}
|
|
1293
|
+
|
|
1294
|
+
// Primitive shapes don't support lookups (except string.size, string.first, string.last)
|
|
1295
|
+
if (shape.kind === 'primitive') {
|
|
1296
|
+
if (shape.primitiveType === 'string' && lookup.type === NodeTypes.String) {
|
|
1297
|
+
switch (lookup.value) {
|
|
1298
|
+
case 'first':
|
|
1299
|
+
case 'last':
|
|
1300
|
+
return 'string';
|
|
1301
|
+
case 'size':
|
|
1302
|
+
return 'number';
|
|
1303
|
+
default:
|
|
1304
|
+
return Unknown;
|
|
1305
|
+
}
|
|
1306
|
+
}
|
|
1307
|
+
return Unknown;
|
|
1308
|
+
}
|
|
1309
|
+
|
|
1310
|
+
return Untyped;
|
|
1311
|
+
}
|
|
1312
|
+
|
|
1313
|
+
/**
|
|
1314
|
+
* Convert a PropertyShape to a PseudoType, ArrayType, or ShapeType
|
|
1315
|
+
*/
|
|
1316
|
+
function shapeToType(shape: PropertyShape): PseudoType | ArrayType | ShapeType {
|
|
1317
|
+
if (shape.kind === 'primitive') {
|
|
1318
|
+
switch (shape.primitiveType) {
|
|
1319
|
+
case 'string':
|
|
1320
|
+
return 'string';
|
|
1321
|
+
case 'number':
|
|
1322
|
+
return 'number';
|
|
1323
|
+
case 'boolean':
|
|
1324
|
+
return 'boolean';
|
|
1325
|
+
default:
|
|
1326
|
+
return Untyped;
|
|
1327
|
+
}
|
|
1328
|
+
}
|
|
1329
|
+
|
|
1330
|
+
if (shape.kind === 'array') {
|
|
1331
|
+
// If array items are primitives, return ArrayType
|
|
1332
|
+
if (shape.itemShape?.kind === 'primitive') {
|
|
1333
|
+
const primitiveType = shape.itemShape.primitiveType;
|
|
1334
|
+
if (primitiveType === 'string' || primitiveType === 'number' || primitiveType === 'boolean') {
|
|
1335
|
+
return arrayType(primitiveType);
|
|
1336
|
+
}
|
|
1337
|
+
}
|
|
1338
|
+
// Otherwise return ShapeType to preserve nested structure
|
|
1339
|
+
return shapeType(shape);
|
|
1340
|
+
}
|
|
1341
|
+
|
|
1342
|
+
if (shape.kind === 'object') {
|
|
1343
|
+
return shapeType(shape);
|
|
1344
|
+
}
|
|
1345
|
+
|
|
1346
|
+
return Untyped;
|
|
1347
|
+
}
|
|
1348
|
+
|
|
1349
|
+
function inferPseudoTypePropertyType(
|
|
1350
|
+
curr: PseudoType, // settings
|
|
1351
|
+
lookup: LiquidExpression,
|
|
1352
|
+
objectMap: ObjectMap,
|
|
1353
|
+
) {
|
|
1354
|
+
const parentEntry: ObjectEntry | undefined = objectMap[curr];
|
|
1355
|
+
|
|
1356
|
+
// When doing a non string lookup, we don't really know the type. e.g.
|
|
1357
|
+
// products[0]
|
|
1358
|
+
// products[true]
|
|
1359
|
+
// products[(0..10)]
|
|
1360
|
+
if (lookup.type !== NodeTypes.String) {
|
|
1361
|
+
return Untyped;
|
|
1362
|
+
}
|
|
1363
|
+
|
|
1364
|
+
// When we don't have docs for the parent entry
|
|
1365
|
+
if (!parentEntry) {
|
|
1366
|
+
// It might be that the parent entry is a string.
|
|
1367
|
+
// We do support a couple of properties for those
|
|
1368
|
+
if (curr === 'string') {
|
|
1369
|
+
switch (lookup.value) {
|
|
1370
|
+
// some_string.first
|
|
1371
|
+
// some_string.last
|
|
1372
|
+
case 'first':
|
|
1373
|
+
case 'last':
|
|
1374
|
+
return 'string';
|
|
1375
|
+
|
|
1376
|
+
// some_string.size
|
|
1377
|
+
case 'size':
|
|
1378
|
+
return 'number';
|
|
1379
|
+
|
|
1380
|
+
default: {
|
|
1381
|
+
// For the string type, any property access other than first/last/size
|
|
1382
|
+
// is unknown. This is different from an untyped/any object where any
|
|
1383
|
+
// property access would return untyped.
|
|
1384
|
+
// String is a known type with specific properties, so accessing
|
|
1385
|
+
// undefined properties returns an unknown.
|
|
1386
|
+
return Unknown;
|
|
1387
|
+
}
|
|
1388
|
+
}
|
|
1389
|
+
}
|
|
1390
|
+
|
|
1391
|
+
// Or it might be that the parent entry is untyped, so its subproperty
|
|
1392
|
+
// could also be untyped (kind of like if `foo` is `any`, then `foo.bar` is `any`)
|
|
1393
|
+
return Untyped;
|
|
1394
|
+
}
|
|
1395
|
+
|
|
1396
|
+
const propertyName = lookup.value;
|
|
1397
|
+
const property = parentEntry.properties?.find((property) => property.name === propertyName);
|
|
1398
|
+
|
|
1399
|
+
// When the propety is not known, return Untyped. e.g.
|
|
1400
|
+
// product.foo
|
|
1401
|
+
// product.bar
|
|
1402
|
+
if (!property) {
|
|
1403
|
+
// Debating between returning Untyped or Unknown here
|
|
1404
|
+
// Might be that we have outdated docs. Prob better to return Untyped.
|
|
1405
|
+
return Untyped;
|
|
1406
|
+
}
|
|
1407
|
+
|
|
1408
|
+
// When the property is known & we have docs for it, return its type. e.g.
|
|
1409
|
+
// product.image
|
|
1410
|
+
// product.images
|
|
1411
|
+
return objectEntryType(property);
|
|
1412
|
+
}
|
|
1413
|
+
|
|
1414
|
+
function filterEntryReturnType(entry: FilterEntry): PseudoType | ArrayType {
|
|
1415
|
+
return docsetEntryReturnType(entry, 'string');
|
|
1416
|
+
}
|
|
1417
|
+
|
|
1418
|
+
function objectEntryType(entry: ObjectEntry): PseudoType | ArrayType {
|
|
1419
|
+
return docsetEntryReturnType(entry, entry.name);
|
|
1420
|
+
}
|
|
1421
|
+
|
|
1422
|
+
/**
|
|
1423
|
+
* This function converts the return_type property in one of the .json
|
|
1424
|
+
* files into a PseudoType or ArrayType.
|
|
1425
|
+
*/
|
|
1426
|
+
export function docsetEntryReturnType(
|
|
1427
|
+
entry: ObjectEntry | FilterEntry,
|
|
1428
|
+
defaultValue: PseudoType,
|
|
1429
|
+
): PseudoType | ArrayType {
|
|
1430
|
+
const returnTypes = entry.return_type;
|
|
1431
|
+
if (returnTypes && returnTypes.length > 0) {
|
|
1432
|
+
const returnType = returnTypes[0];
|
|
1433
|
+
if (isArrayReturnType(returnType)) {
|
|
1434
|
+
return arrayType(returnType.array_value);
|
|
1435
|
+
} else {
|
|
1436
|
+
return returnType.type;
|
|
1437
|
+
}
|
|
1438
|
+
}
|
|
1439
|
+
|
|
1440
|
+
return defaultValue;
|
|
1441
|
+
}
|
|
1442
|
+
|
|
1443
|
+
function isArrayReturnType(rt: ReturnType): rt is ArrayReturnType {
|
|
1444
|
+
return rt.type === 'array';
|
|
1445
|
+
}
|
|
1446
|
+
|
|
1447
|
+
export function isArrayType(
|
|
1448
|
+
thing: PseudoType | ArrayType | ShapeType | UnionType,
|
|
1449
|
+
): thing is ArrayType {
|
|
1450
|
+
return typeof thing !== 'string' && thing.kind === 'array';
|
|
1451
|
+
}
|
|
1452
|
+
|
|
1453
|
+
export function isShapeType(
|
|
1454
|
+
thing: PseudoType | ArrayType | ShapeType | UnionType,
|
|
1455
|
+
): thing is ShapeType {
|
|
1456
|
+
return typeof thing !== 'string' && thing.kind === 'shape';
|
|
1457
|
+
}
|
|
1458
|
+
|
|
1459
|
+
/** Assumes findLast */
|
|
1460
|
+
function isCorrectTypeRange(typeRange: TypeRange, node: LiquidVariableLookup): boolean {
|
|
1461
|
+
const [start, end] = typeRange.range;
|
|
1462
|
+
if (end && node.position.start > end) return false;
|
|
1463
|
+
return node.position.start > start;
|
|
1464
|
+
}
|
|
1465
|
+
|
|
1466
|
+
function end(offset: number | undefined): number | undefined {
|
|
1467
|
+
if (offset === -1) return undefined;
|
|
1468
|
+
return offset;
|
|
1469
|
+
}
|
|
1470
|
+
|
|
1471
|
+
function isLiquidTagIncrement(node: LiquidTag): node is LiquidTagIncrement {
|
|
1472
|
+
return node.name === NamedTags.increment && typeof node.markup !== 'string';
|
|
1473
|
+
}
|
|
1474
|
+
|
|
1475
|
+
function isLiquidTagDecrement(node: LiquidTag): node is LiquidTagDecrement {
|
|
1476
|
+
return node.name === NamedTags.decrement && typeof node.markup !== 'string';
|
|
1477
|
+
}
|
|
1478
|
+
|
|
1479
|
+
function isLiquidTagParseJson(
|
|
1480
|
+
node: LiquidTag,
|
|
1481
|
+
): node is LiquidTag & { markup: { name: string }; children: LiquidHtmlNode[] } {
|
|
1482
|
+
return node.name === NamedTags.parse_json && typeof node.markup !== 'string';
|
|
1483
|
+
}
|
|
1484
|
+
|
|
1485
|
+
function isLiquidTagGraphQL(
|
|
1486
|
+
node: LiquidTag,
|
|
1487
|
+
): node is LiquidTag & { markup: GraphQLMarkup | GraphQLInlineMarkup } {
|
|
1488
|
+
return node.name === NamedTags.graphql && typeof node.markup !== 'string';
|
|
1489
|
+
}
|
|
1490
|
+
|
|
1491
|
+
function isGraphQLInlineMarkup(
|
|
1492
|
+
markup: GraphQLMarkup | GraphQLInlineMarkup,
|
|
1493
|
+
): markup is GraphQLInlineMarkup {
|
|
1494
|
+
return markup.type === NodeTypes.GraphQLInlineMarkup;
|
|
1495
|
+
}
|
|
1496
|
+
|
|
1497
|
+
function isGraphQLFileMarkup(markup: GraphQLMarkup | GraphQLInlineMarkup): markup is GraphQLMarkup {
|
|
1498
|
+
return markup.type === NodeTypes.GraphQLMarkup;
|
|
1499
|
+
}
|
|
1500
|
+
|
|
1501
|
+
function isLiquidString(node: LiquidString | LiquidVariableLookup): node is LiquidString {
|
|
1502
|
+
return node.type === NodeTypes.String;
|
|
1503
|
+
}
|
|
1504
|
+
|
|
1505
|
+
function isLiquidTagHashAssign(node: LiquidTag): node is LiquidTag & { markup: HashAssignMarkup } {
|
|
1506
|
+
return node.name === NamedTags.hash_assign && typeof node.markup !== 'string';
|
|
1507
|
+
}
|
|
1508
|
+
|
|
1509
|
+
function isLiquidTagFunction(node: LiquidTag): node is LiquidTag & { markup: FunctionMarkup } {
|
|
1510
|
+
return node.name === NamedTags.function && typeof node.markup !== 'string';
|
|
1511
|
+
}
|
|
1512
|
+
|
|
1513
|
+
export function isUnionType(
|
|
1514
|
+
thing: PseudoType | ArrayType | ShapeType | UnionType,
|
|
1515
|
+
): thing is UnionType {
|
|
1516
|
+
return typeof thing !== 'string' && thing.kind === 'union';
|
|
1517
|
+
}
|
|
1518
|
+
|
|
1519
|
+
/**
|
|
1520
|
+
* Infer the return type of a function partial by analyzing its {% return %} statements.
|
|
1521
|
+
*/
|
|
1522
|
+
async function inferFunctionReturnType(
|
|
1523
|
+
partialPath: string,
|
|
1524
|
+
fs: AbstractFileSystem,
|
|
1525
|
+
documentsLocator: DocumentsLocator,
|
|
1526
|
+
rootUri: string,
|
|
1527
|
+
seedSymbolsTable: SymbolsTable,
|
|
1528
|
+
liquidDrops: ObjectEntry[],
|
|
1529
|
+
graphqlSchema: string | undefined,
|
|
1530
|
+
processingFiles: Set<string> | undefined,
|
|
1531
|
+
objectMap: ObjectMap,
|
|
1532
|
+
filtersMap: FiltersMap,
|
|
1533
|
+
): Promise<PseudoType | ArrayType | ShapeType | UnionType | undefined> {
|
|
1534
|
+
// 1. Locate the file
|
|
1535
|
+
const located = await documentsLocator.locate(URI.parse(rootUri), 'function', partialPath);
|
|
1536
|
+
if (!located) return undefined;
|
|
1537
|
+
|
|
1538
|
+
// 2. Check for circular references
|
|
1539
|
+
const trackingSet = processingFiles ?? new Set<string>();
|
|
1540
|
+
if (trackingSet.has(located)) return Untyped;
|
|
1541
|
+
trackingSet.add(located);
|
|
1542
|
+
|
|
1543
|
+
try {
|
|
1544
|
+
// 3. Read and parse the partial
|
|
1545
|
+
const content = await fs.readFile(located);
|
|
1546
|
+
const partialAst = toLiquidHtmlAST(content);
|
|
1547
|
+
|
|
1548
|
+
// 4. Build symbols table for the partial (recursive)
|
|
1549
|
+
const partialSymbolsTable = await buildSymbolsTable(
|
|
1550
|
+
partialAst,
|
|
1551
|
+
{ ...seedSymbolsTable }, // Clone to avoid pollution
|
|
1552
|
+
liquidDrops,
|
|
1553
|
+
graphqlSchema,
|
|
1554
|
+
fs,
|
|
1555
|
+
documentsLocator,
|
|
1556
|
+
rootUri,
|
|
1557
|
+
trackingSet,
|
|
1558
|
+
objectMap,
|
|
1559
|
+
filtersMap,
|
|
1560
|
+
);
|
|
1561
|
+
|
|
1562
|
+
// 5. Find all return statements and infer their types
|
|
1563
|
+
const returnTypes: (PseudoType | ArrayType | ShapeType)[] = [];
|
|
1564
|
+
|
|
1565
|
+
await visit<SourceCodeType.LiquidHtml, void>(partialAst, {
|
|
1566
|
+
async LiquidTag(node) {
|
|
1567
|
+
if (node.name === NamedTags.return && typeof node.markup !== 'string') {
|
|
1568
|
+
// markup is LiquidVariable - infer its type
|
|
1569
|
+
const type = inferType(node.markup, partialSymbolsTable, objectMap, filtersMap);
|
|
1570
|
+
// Flatten union types into individual types
|
|
1571
|
+
if (isUnionType(type)) {
|
|
1572
|
+
returnTypes.push(...type.types);
|
|
1573
|
+
} else {
|
|
1574
|
+
returnTypes.push(type);
|
|
1575
|
+
}
|
|
1576
|
+
}
|
|
1577
|
+
},
|
|
1578
|
+
});
|
|
1579
|
+
|
|
1580
|
+
if (returnTypes.length === 0) return undefined;
|
|
1581
|
+
if (returnTypes.length === 1) return returnTypes[0];
|
|
1582
|
+
|
|
1583
|
+
// Dedupe types (same type appearing multiple times)
|
|
1584
|
+
const uniqueTypes = dedupeTypes(returnTypes);
|
|
1585
|
+
if (uniqueTypes.length === 1) return uniqueTypes[0];
|
|
1586
|
+
|
|
1587
|
+
return unionType(uniqueTypes);
|
|
1588
|
+
} finally {
|
|
1589
|
+
trackingSet.delete(located);
|
|
1590
|
+
}
|
|
1591
|
+
}
|
|
1592
|
+
|
|
1593
|
+
/**
|
|
1594
|
+
* Deduplicate types by comparing their structure.
|
|
1595
|
+
*/
|
|
1596
|
+
function dedupeTypes(
|
|
1597
|
+
types: (PseudoType | ArrayType | ShapeType)[],
|
|
1598
|
+
): (PseudoType | ArrayType | ShapeType)[] {
|
|
1599
|
+
const seen = new Set<string>();
|
|
1600
|
+
const result: (PseudoType | ArrayType | ShapeType)[] = [];
|
|
1601
|
+
|
|
1602
|
+
for (const type of types) {
|
|
1603
|
+
const key = typeToKey(type);
|
|
1604
|
+
if (!seen.has(key)) {
|
|
1605
|
+
seen.add(key);
|
|
1606
|
+
result.push(type);
|
|
1607
|
+
}
|
|
1608
|
+
}
|
|
1609
|
+
|
|
1610
|
+
return result;
|
|
1611
|
+
}
|
|
1612
|
+
|
|
1613
|
+
/**
|
|
1614
|
+
* Convert a type to a string key for deduplication.
|
|
1615
|
+
*/
|
|
1616
|
+
function typeToKey(type: PseudoType | ArrayType | ShapeType): string {
|
|
1617
|
+
if (typeof type === 'string') return type;
|
|
1618
|
+
if (type.kind === 'array') return `array:${type.valueType}`;
|
|
1619
|
+
if (type.kind === 'shape') return `shape:${JSON.stringify(shapeToSimpleObject(type.shape))}`;
|
|
1620
|
+
return 'unknown';
|
|
1621
|
+
}
|
|
1622
|
+
|
|
1623
|
+
/**
|
|
1624
|
+
* Convert a PropertyShape to a simple object for JSON serialization.
|
|
1625
|
+
*/
|
|
1626
|
+
function shapeToSimpleObject(shape: PropertyShape): unknown {
|
|
1627
|
+
if (shape.kind === 'primitive') {
|
|
1628
|
+
return { kind: 'primitive', type: shape.primitiveType };
|
|
1629
|
+
}
|
|
1630
|
+
if (shape.kind === 'array') {
|
|
1631
|
+
return {
|
|
1632
|
+
kind: 'array',
|
|
1633
|
+
itemShape: shape.itemShape ? shapeToSimpleObject(shape.itemShape) : null,
|
|
1634
|
+
};
|
|
1635
|
+
}
|
|
1636
|
+
if (shape.kind === 'object') {
|
|
1637
|
+
const props: Record<string, unknown> = {};
|
|
1638
|
+
if (shape.properties) {
|
|
1639
|
+
for (const [key, value] of shape.properties) {
|
|
1640
|
+
props[key] = shapeToSimpleObject(value);
|
|
1641
|
+
}
|
|
1642
|
+
}
|
|
1643
|
+
return { kind: 'object', properties: props };
|
|
1644
|
+
}
|
|
1645
|
+
return { kind: 'unknown' };
|
|
1646
|
+
}
|
|
1647
|
+
|
|
1648
|
+
/**
|
|
1649
|
+
* Convert a type to a display string for hover/completions.
|
|
1650
|
+
*/
|
|
1651
|
+
export function typeToDisplayString(type: PseudoType | ArrayType | ShapeType | UnionType): string {
|
|
1652
|
+
if (typeof type === 'string') return type;
|
|
1653
|
+
if (type.kind === 'array') return `Array<${type.valueType}>`;
|
|
1654
|
+
if (type.kind === 'shape') return shapeToTypeString(type.shape);
|
|
1655
|
+
if (type.kind === 'union') return type.types.map((t) => typeToDisplayString(t)).join(' | ');
|
|
1656
|
+
return 'unknown';
|
|
1657
|
+
}
|
|
1658
|
+
|
|
1659
|
+
/**
|
|
1660
|
+
* Extract the lookup path from a hash_assign target.
|
|
1661
|
+
* For {% hash_assign a['key1']['key2'] = value %}, returns ['key1', 'key2']
|
|
1662
|
+
*/
|
|
1663
|
+
function getHashAssignLookupPath(markup: HashAssignMarkup): string[] | undefined {
|
|
1664
|
+
const path: string[] = [];
|
|
1665
|
+
|
|
1666
|
+
for (const lookup of markup.target.lookups) {
|
|
1667
|
+
if (lookup.type === NodeTypes.String) {
|
|
1668
|
+
path.push(lookup.value);
|
|
1669
|
+
} else if (lookup.type === NodeTypes.Number) {
|
|
1670
|
+
path.push(`${lookup.value}`);
|
|
1671
|
+
} else {
|
|
1672
|
+
// Dynamic lookup - can't determine statically
|
|
1673
|
+
return undefined;
|
|
1674
|
+
}
|
|
1675
|
+
}
|
|
1676
|
+
|
|
1677
|
+
return path.length > 0 ? path : undefined;
|
|
1678
|
+
}
|
|
1679
|
+
|
|
1680
|
+
/**
|
|
1681
|
+
* Merge a nested property into a shape following a path.
|
|
1682
|
+
* For path ['a', 'b'] and valueShape, creates/updates shape.a.b = valueShape
|
|
1683
|
+
*/
|
|
1684
|
+
function mergeNestedPropertyIntoShape(
|
|
1685
|
+
shape: PropertyShape,
|
|
1686
|
+
path: string[],
|
|
1687
|
+
valueShape: PropertyShape,
|
|
1688
|
+
): PropertyShape {
|
|
1689
|
+
if (path.length === 0) {
|
|
1690
|
+
return valueShape;
|
|
1691
|
+
}
|
|
1692
|
+
|
|
1693
|
+
const [key, ...rest] = path;
|
|
1694
|
+
|
|
1695
|
+
if (shape.kind !== 'object') {
|
|
1696
|
+
// Convert to object
|
|
1697
|
+
const properties = new Map<string, PropertyShape>();
|
|
1698
|
+
if (rest.length === 0) {
|
|
1699
|
+
properties.set(key, valueShape);
|
|
1700
|
+
} else {
|
|
1701
|
+
properties.set(
|
|
1702
|
+
key,
|
|
1703
|
+
mergeNestedPropertyIntoShape({ kind: 'object', properties: new Map() }, rest, valueShape),
|
|
1704
|
+
);
|
|
1705
|
+
}
|
|
1706
|
+
return { kind: 'object', properties };
|
|
1707
|
+
}
|
|
1708
|
+
|
|
1709
|
+
const newProperties = new Map(shape.properties);
|
|
1710
|
+
if (rest.length === 0) {
|
|
1711
|
+
newProperties.set(key, valueShape);
|
|
1712
|
+
} else {
|
|
1713
|
+
const existingNested = newProperties.get(key) || { kind: 'object', properties: new Map() };
|
|
1714
|
+
newProperties.set(key, mergeNestedPropertyIntoShape(existingNested, rest, valueShape));
|
|
1715
|
+
}
|
|
1716
|
+
return { kind: 'object', properties: newProperties };
|
|
1717
|
+
}
|
|
1718
|
+
|
|
1719
|
+
/**
|
|
1720
|
+
* Find the last applicable shape for a variable at a given position
|
|
1721
|
+
*/
|
|
1722
|
+
function findLastApplicableShape(
|
|
1723
|
+
shapes: { shape: PropertyShape; rangeEnd: number }[],
|
|
1724
|
+
position: number,
|
|
1725
|
+
): { shape: PropertyShape; rangeEnd: number } | undefined {
|
|
1726
|
+
let result: { shape: PropertyShape; rangeEnd: number } | undefined;
|
|
1727
|
+
for (const entry of shapes) {
|
|
1728
|
+
if (entry.rangeEnd < position) {
|
|
1729
|
+
result = entry;
|
|
1730
|
+
}
|
|
1731
|
+
}
|
|
1732
|
+
return result;
|
|
1733
|
+
}
|
|
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
|
+
}
|