Skip to content

Commit 1c756ba

Browse files
committed
feat(no-navigation-without-resolve): not reporting pure query URLs
1 parent acf4777 commit 1c756ba

File tree

7 files changed

+84
-21
lines changed

7 files changed

+84
-21
lines changed

.changeset/eager-snails-follow.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'eslint-plugin-svelte': minor
3+
---
4+
5+
feat(no-navigation-without-resolve): not reporting pure query URLs

packages/eslint-plugin-svelte/src/rules/no-navigation-without-resolve.ts

Lines changed: 25 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -258,8 +258,9 @@ function isValueAllowed(
258258
if (
259259
(config.allowAbsolute && expressionIsAbsoluteUrl(ctx, value)) ||
260260
(config.allowEmpty && expressionIsEmpty(value)) ||
261-
(config.allowFragment && expressionIsFragment(ctx, value)) ||
261+
(config.allowFragment && expressionStartsWith(ctx, value, '#')) ||
262262
(config.allowNullish && expressionIsNullish(value)) ||
263+
expressionStartsWith(ctx, value, '?') ||
263264
expressionIsResolveCall(ctx, value, resolveReferences)
264265
) {
265266
return true;
@@ -365,34 +366,40 @@ function valueIsAbsoluteUrl(node: string): boolean {
365366
return /^[+a-z]*:/i.test(node);
366367
}
367368

368-
function expressionIsFragment(
369+
function expressionStartsWith(
369370
ctx: FindVariableContext,
370-
node: TSESTree.CallExpressionArgument | TSESTree.Expression | AST.SvelteLiteral
371+
node: TSESTree.CallExpressionArgument | TSESTree.Expression | AST.SvelteLiteral,
372+
prefix: string
371373
): boolean {
372374
switch (node.type) {
373375
case 'BinaryExpression':
374-
return binaryExpressionIsFragment(ctx, node);
376+
return binaryExpressionStartsWith(ctx, node, prefix);
375377
case 'Identifier':
376-
return identifierIsFragment(ctx, node);
378+
return identifierStartsWith(ctx, node, prefix);
377379
case 'Literal':
378-
return typeof node.value === 'string' && urlValueIsFragment(node.value);
380+
return typeof node.value === 'string' && node.value.startsWith(prefix);
379381
case 'SvelteLiteral':
380-
return urlValueIsFragment(node.value);
382+
return node.value.startsWith(prefix);
381383
case 'TemplateLiteral':
382-
return templateLiteralIsFragment(ctx, node);
384+
return templateLiteralStartsWith(ctx, node, prefix);
383385
default:
384386
return false;
385387
}
386388
}
387389

388-
function binaryExpressionIsFragment(
390+
function binaryExpressionStartsWith(
389391
ctx: FindVariableContext,
390-
node: TSESTree.BinaryExpression
392+
node: TSESTree.BinaryExpression,
393+
prefix: string
391394
): boolean {
392-
return node.left.type !== 'PrivateIdentifier' && expressionIsFragment(ctx, node.left);
395+
return node.left.type !== 'PrivateIdentifier' && expressionStartsWith(ctx, node.left, prefix);
393396
}
394397

395-
function identifierIsFragment(ctx: FindVariableContext, node: TSESTree.Identifier): boolean {
398+
function identifierStartsWith(
399+
ctx: FindVariableContext,
400+
node: TSESTree.Identifier,
401+
prefix: string
402+
): boolean {
396403
const variable = ctx.findVariable(node);
397404
if (
398405
variable === null ||
@@ -402,19 +409,16 @@ function identifierIsFragment(ctx: FindVariableContext, node: TSESTree.Identifie
402409
) {
403410
return false;
404411
}
405-
return expressionIsFragment(ctx, variable.identifiers[0].parent.init);
412+
return expressionStartsWith(ctx, variable.identifiers[0].parent.init, prefix);
406413
}
407414

408-
function templateLiteralIsFragment(
415+
function templateLiteralStartsWith(
409416
ctx: FindVariableContext,
410-
node: TSESTree.TemplateLiteral
417+
node: TSESTree.TemplateLiteral,
418+
prefix: string
411419
): boolean {
412420
return (
413-
(node.expressions.length >= 1 && expressionIsFragment(ctx, node.expressions[0])) ||
414-
(node.quasis.length >= 1 && urlValueIsFragment(node.quasis[0].value.raw))
421+
(node.expressions.length >= 1 && expressionStartsWith(ctx, node.expressions[0], prefix)) ||
422+
(node.quasis.length >= 1 && node.quasis[0].value.raw.startsWith(prefix))
415423
);
416424
}
417-
418-
function urlValueIsFragment(node: string): boolean {
419-
return node.startsWith('#');
420-
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<script>
2+
import { goto } from '$app/navigation';
3+
4+
const keyValue = 'bar=baz';
5+
const value = '?foo=bar';
6+
7+
goto('?');
8+
goto('?foo=bar');
9+
goto('?' + 'foo=bar');
10+
goto('?' + keyValue);
11+
goto(`?${keyValue}`);
12+
goto(value);
13+
</script>

packages/eslint-plugin-svelte/tests/fixtures/rules/no-navigation-without-resolve/valid/link-fragment-url01-input.svelte renamed to packages/eslint-plugin-svelte/tests/fixtures/rules/no-navigation-without-resolve/valid/link-fragment01-input.svelte

File renamed without changes.
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<script>
2+
const keyValue = 'bar=baz';
3+
4+
const value = '?foo=bar';
5+
const href = '?foo=bar';
6+
</script>
7+
8+
<a href="?">Click me!</a>
9+
<a href="?foo=bar">Click me!</a>
10+
<a href={'?foo=bar'}>Click me!</a>
11+
<a href={'?' + 'foo=bar'}>Click me!</a>
12+
<a href={'#' + keyValue}>Click me!</a>
13+
<a href={`#${keyValue}`}>Click me!</a>
14+
<a href={value}>Click me!</a>
15+
<a {href}>Click me!</a>
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<script>
2+
import { pushState } from '$app/navigation';
3+
4+
const keyValue = 'bar=baz';
5+
const value = '?foo=bar';
6+
7+
pushState('?');
8+
pushState('?foo=bar');
9+
pushState('?' + 'foo=bar');
10+
pushState('?' + keyValue);
11+
pushState(`?${keyValue}`);
12+
pushState(value);
13+
</script>
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<script>
2+
import { replaceState } from '$app/navigation';
3+
4+
const keyValue = 'bar=baz';
5+
const value = '?foo=bar';
6+
7+
replaceState('?');
8+
replaceState('?foo=bar');
9+
replaceState('?' + 'foo=bar');
10+
replaceState('?' + keyValue);
11+
replaceState(`?${keyValue}`);
12+
replaceState(value);
13+
</script>

0 commit comments

Comments
 (0)