@@ -187,12 +187,11 @@ function checkGotoCall(
187187 call : TSESTree . CallExpression ,
188188 resolveReferences : Set < TSESTree . Identifier >
189189) : void {
190- if ( call . arguments . length < 1 ) {
191- return ;
192- }
193- const url = call . arguments [ 0 ] ;
194- if ( ! isResolveCall ( new FindVariableContext ( context ) , url , resolveReferences ) ) {
195- context . report ( { loc : url . loc , messageId : 'gotoWithoutResolve' } ) ;
190+ if (
191+ call . arguments . length > 0 &&
192+ ! isValueAllowed ( new FindVariableContext ( context ) , call . arguments [ 0 ] , resolveReferences , { } )
193+ ) {
194+ context . report ( { loc : call . arguments [ 0 ] . loc , messageId : 'gotoWithoutResolve' } ) ;
196195 }
197196}
198197
@@ -202,15 +201,13 @@ function checkShallowNavigationCall(
202201 resolveReferences : Set < TSESTree . Identifier > ,
203202 messageId : string
204203) : void {
205- if ( call . arguments . length < 1 ) {
206- return ;
207- }
208- const url = call . arguments [ 0 ] ;
209204 if (
210- ! expressionIsEmpty ( url ) &&
211- ! isResolveCall ( new FindVariableContext ( context ) , url , resolveReferences )
205+ call . arguments . length > 0 &&
206+ ! isValueAllowed ( new FindVariableContext ( context ) , call . arguments [ 0 ] , resolveReferences , {
207+ allowEmpty : true
208+ } )
212209 ) {
213- context . report ( { loc : url . loc , messageId } ) ;
210+ context . report ( { loc : call . arguments [ 0 ] . loc , messageId } ) ;
214211 }
215212}
216213
@@ -226,18 +223,53 @@ function checkLinkAttribute(
226223 attribute . parent . parent . name . type === 'SvelteName' &&
227224 attribute . parent . parent . name . name === 'a' &&
228225 attribute . key . name === 'href' &&
229- ! expressionIsNullish ( new FindVariableContext ( context ) , value ) &&
230- ! expressionIsAbsolute ( new FindVariableContext ( context ) , value ) &&
231- ! expressionIsFragment ( new FindVariableContext ( context ) , value ) &&
232- ! isResolveCall ( new FindVariableContext ( context ) , value , resolveReferences )
226+ ! isValueAllowed ( new FindVariableContext ( context ) , value , resolveReferences , {
227+ allowAbsolute : true ,
228+ allowFragment : true ,
229+ allowNullish : true
230+ } )
233231 ) {
234232 context . report ( { loc : attribute . loc , messageId : 'linkWithoutResolve' } ) ;
235233 }
236234}
237235
236+ function isValueAllowed (
237+ ctx : FindVariableContext ,
238+ value : TSESTree . CallExpressionArgument | TSESTree . Expression | AST . SvelteLiteral ,
239+ resolveReferences : Set < TSESTree . Identifier > ,
240+ config : {
241+ allowAbsolute ?: boolean ;
242+ allowEmpty ?: boolean ;
243+ allowFragment ?: boolean ;
244+ allowNullish ?: boolean ;
245+ }
246+ ) : boolean {
247+ if ( value . type === 'Identifier' ) {
248+ const variable = ctx . findVariable ( value ) ;
249+ if (
250+ variable !== null &&
251+ variable . identifiers . length > 0 &&
252+ variable . identifiers [ 0 ] . parent . type === 'VariableDeclarator' &&
253+ variable . identifiers [ 0 ] . parent . init !== null
254+ ) {
255+ return isValueAllowed ( ctx , variable . identifiers [ 0 ] . parent . init , resolveReferences , config ) ;
256+ }
257+ }
258+ if (
259+ ( config . allowEmpty && expressionIsEmpty ( value ) ) ||
260+ ( config . allowAbsolute && expressionIsAbsolute ( ctx , value ) ) ||
261+ ( config . allowFragment && expressionIsFragment ( ctx , value ) ) ||
262+ ( config . allowNullish && expressionIsNullish ( value ) ) ||
263+ expressionIsResolveCall ( ctx , value , resolveReferences )
264+ ) {
265+ return true ;
266+ }
267+ return false ;
268+ }
269+
238270// Helper functions
239271
240- function isResolveCall (
272+ function expressionIsResolveCall (
241273 ctx : FindVariableContext ,
242274 node : TSESTree . CallExpressionArgument | AST . SvelteLiteral ,
243275 resolveReferences : Set < TSESTree . Identifier >
@@ -263,10 +295,12 @@ function isResolveCall(
263295 ) {
264296 return false ;
265297 }
266- return isResolveCall ( ctx , variable . identifiers [ 0 ] . parent . init , resolveReferences ) ;
298+ return expressionIsResolveCall ( ctx , variable . identifiers [ 0 ] . parent . init , resolveReferences ) ;
267299}
268300
269- function expressionIsEmpty ( url : TSESTree . CallExpressionArgument ) : boolean {
301+ function expressionIsEmpty (
302+ url : TSESTree . CallExpressionArgument | TSESTree . Expression | AST . SvelteLiteral
303+ ) : boolean {
270304 return (
271305 ( url . type === 'Literal' && url . value === '' ) ||
272306 ( url . type === 'TemplateLiteral' &&
@@ -277,44 +311,25 @@ function expressionIsEmpty(url: TSESTree.CallExpressionArgument): boolean {
277311}
278312
279313function expressionIsNullish (
280- ctx : FindVariableContext ,
281- url : AST . SvelteLiteral | TSESTree . Expression
314+ url : TSESTree . CallExpressionArgument | TSESTree . Expression | AST . SvelteLiteral
282315) : boolean {
283316 switch ( url . type ) {
284317 case 'Identifier' :
285- return identifierIsNullish ( ctx , url ) ;
318+ return url . name === 'undefined' ;
286319 case 'Literal' :
287320 return url . value === null ; // Undefined is an Identifier in ESTree, null is a Literal
288321 default :
289322 return false ;
290323 }
291324}
292325
293- function identifierIsNullish ( ctx : FindVariableContext , url : TSESTree . Identifier ) : boolean {
294- if ( url . name === 'undefined' ) {
295- return true ;
296- }
297- const variable = ctx . findVariable ( url ) ;
298- if (
299- variable === null ||
300- variable . identifiers . length === 0 ||
301- variable . identifiers [ 0 ] . parent . type !== 'VariableDeclarator' ||
302- variable . identifiers [ 0 ] . parent . init === null
303- ) {
304- return false ;
305- }
306- return expressionIsNullish ( ctx , variable . identifiers [ 0 ] . parent . init ) ;
307- }
308-
309326function expressionIsAbsolute (
310327 ctx : FindVariableContext ,
311- url : AST . SvelteLiteral | TSESTree . Expression
328+ url : TSESTree . CallExpressionArgument | TSESTree . Expression | AST . SvelteLiteral
312329) : boolean {
313330 switch ( url . type ) {
314331 case 'BinaryExpression' :
315332 return binaryExpressionIsAbsolute ( ctx , url ) ;
316- case 'Identifier' :
317- return identifierIsAbsolute ( ctx , url ) ;
318333 case 'Literal' :
319334 return typeof url . value === 'string' && urlValueIsAbsolute ( url . value ) ;
320335 case 'SvelteLiteral' :
@@ -336,19 +351,6 @@ function binaryExpressionIsAbsolute(
336351 ) ;
337352}
338353
339- function identifierIsAbsolute ( ctx : FindVariableContext , url : TSESTree . Identifier ) : boolean {
340- const variable = ctx . findVariable ( url ) ;
341- if (
342- variable === null ||
343- variable . identifiers . length === 0 ||
344- variable . identifiers [ 0 ] . parent . type !== 'VariableDeclarator' ||
345- variable . identifiers [ 0 ] . parent . init === null
346- ) {
347- return false ;
348- }
349- return expressionIsAbsolute ( ctx , variable . identifiers [ 0 ] . parent . init ) ;
350- }
351-
352354function templateLiteralIsAbsolute (
353355 ctx : FindVariableContext ,
354356 url : TSESTree . TemplateLiteral
@@ -365,7 +367,7 @@ function urlValueIsAbsolute(url: string): boolean {
365367
366368function expressionIsFragment (
367369 ctx : FindVariableContext ,
368- url : AST . SvelteLiteral | TSESTree . Expression
370+ url : TSESTree . CallExpressionArgument | TSESTree . Expression | AST . SvelteLiteral
369371) : boolean {
370372 switch ( url . type ) {
371373 case 'BinaryExpression' :
0 commit comments