77 LiquidTagDecrement ,
88 LiquidTagFor ,
99 LiquidTagIncrement ,
10+ LiquidTagSnippet ,
1011 LiquidTagTablerow ,
1112 LiquidVariableLookup ,
1213 NamedTags ,
@@ -16,7 +17,7 @@ import {
1617import { LiquidCheckDefinition , Severity , SourceCodeType , ThemeDocset } from '../../types' ;
1718import { isError , last } from '../../utils' ;
1819import { hasLiquidDoc } from '../../liquid-doc/liquidDoc' ;
19- import { isWithinRawTagThatDoesNotParseItsContents } from '../utils' ;
20+ import { isWithinRawTagThatDoesNotParseItsContents , findInlineSnippetAncestor } from '../utils' ;
2021
2122type Scope = { start ?: number ; end ?: number } ;
2223
@@ -38,13 +39,14 @@ export const UndefinedObject: LiquidCheckDefinition = {
3839 create ( context ) {
3940 const relativePath = context . toRelativePath ( context . file . uri ) ;
4041 const ast = context . file . ast ;
42+ const isSnippetFile = relativePath . startsWith ( 'snippets/' ) ;
4143
4244 if ( isError ( ast ) ) return { } ;
4345
4446 /**
4547 * Skip this check when a snippet does not have the presence of doc tags.
4648 */
47- if ( relativePath . startsWith ( 'snippets/' ) && ! hasLiquidDoc ( ast ) ) return { } ;
49+ if ( isSnippetFile && ! hasLiquidDoc ( ast ) ) return { } ;
4850
4951 /**
5052 * Skip this check when definitions for global objects are unavailable.
@@ -56,7 +58,9 @@ export const UndefinedObject: LiquidCheckDefinition = {
5658 const themeDocset = context . themeDocset ;
5759 const scopedVariables : Map < string , Scope [ ] > = new Map ( ) ;
5860 const fileScopedVariables : Set < string > = new Set ( ) ;
59- const variables : LiquidVariableLookup [ ] = [ ] ;
61+ const variables : Array < { node : LiquidVariableLookup ; inlineSnippet : LiquidTag | null } > = [ ] ;
62+ const inlineSnippetsWithDocTags : Set < number > = new Set ( ) ;
63+ let snippetFileHasDocTag = false ;
6064
6165 function indexVariableScope ( variableName : string | null , scope : Scope ) {
6266 if ( ! variableName ) return ;
@@ -66,9 +70,27 @@ export const UndefinedObject: LiquidCheckDefinition = {
6670 }
6771
6872 return {
69- async LiquidDocParamNode ( node : LiquidDocParamNode ) {
73+ async LiquidRawTag ( node , ancestors ) {
74+ if ( ! isSnippetFile || node . name !== 'doc' ) return ;
75+
76+ const parent = last ( ancestors ) ;
77+ if ( isLiquidTag ( parent ) && isLiquidTagSnippet ( parent ) ) {
78+ inlineSnippetsWithDocTags . add ( parent . position . start ) ;
79+ } else {
80+ snippetFileHasDocTag = true ;
81+ }
82+ } ,
83+
84+ async LiquidDocParamNode ( node : LiquidDocParamNode , ancestors : LiquidHtmlNode [ ] ) {
7085 const paramName = node . paramName ?. value ;
71- if ( paramName ) {
86+ if ( ! paramName ) return ;
87+ const snippetAncestor = findInlineSnippetAncestor ( ancestors ) ;
88+ if ( snippetAncestor ) {
89+ indexVariableScope ( paramName , {
90+ start : snippetAncestor . blockStartPosition . end ,
91+ end : snippetAncestor . blockEndPosition ?. start ,
92+ } ) ;
93+ } else {
7294 fileScopedVariables . add ( paramName ) ;
7395 }
7496 } ,
@@ -134,6 +156,10 @@ export const UndefinedObject: LiquidCheckDefinition = {
134156 end : node . blockEndPosition ?. start ,
135157 } ) ;
136158 }
159+
160+ if ( isLiquidTagSnippet ( node ) && node . markup . name ) {
161+ fileScopedVariables . add ( node . markup . name ) ;
162+ }
137163 } ,
138164
139165 async VariableLookup ( node , ancestors ) {
@@ -142,21 +168,33 @@ export const UndefinedObject: LiquidCheckDefinition = {
142168 const parent = last ( ancestors ) ;
143169 if ( isLiquidTag ( parent ) && isLiquidTagCapture ( parent ) ) return ;
144170
145- if ( parent ?. type === NodeTypes . RenderMarkup && parent . snippet === node ) return ;
146-
147- if ( isLiquidTag ( parent ) && parent . name === 'snippet' && parent . markup === node ) return ;
171+ if ( isLiquidTag ( parent ) && isLiquidTagSnippet ( parent ) ) return ;
148172
149- variables . push ( node ) ;
173+ const inlineSnippet = findInlineSnippetAncestor ( ancestors ) ;
174+ variables . push ( { node, inlineSnippet } ) ;
150175 } ,
151176
152177 async onCodePathEnd ( ) {
153178 const objects = await globalObjects ( themeDocset , relativePath ) ;
154179
155180 objects . forEach ( ( obj ) => fileScopedVariables . add ( obj . name ) ) ;
156181
157- variables . forEach ( ( variable ) => {
182+ variables . forEach ( ( { node : variable , inlineSnippet } ) => {
158183 if ( ! variable . name ) return ;
159184
185+ // For snippet files: only check variables if they're in a scope with a doc tag.
186+ if ( isSnippetFile ) {
187+ if ( inlineSnippet ) {
188+ if ( ! inlineSnippetsWithDocTags . has ( inlineSnippet . position . start ) ) {
189+ return ;
190+ }
191+ } else {
192+ if ( ! snippetFileHasDocTag ) {
193+ return ;
194+ }
195+ }
196+ }
197+
160198 const isVariableDefined = isDefined (
161199 variable . name ,
162200 variable . position ,
@@ -272,6 +310,10 @@ function isLiquidTagCapture(node: LiquidTag): node is LiquidTagCapture {
272310 return node . name === NamedTags . capture ;
273311}
274312
313+ function isLiquidTagSnippet ( node : LiquidTag ) : node is LiquidTagSnippet {
314+ return node . name === NamedTags . snippet ;
315+ }
316+
275317function isLiquidTagAssign ( node : LiquidTag ) : node is LiquidTagAssign {
276318 return node . name === NamedTags . assign && typeof node . markup !== 'string' ;
277319}
0 commit comments