Skip to content

Commit dc92c50

Browse files
committed
feat(no-navigation-without-base): added support for urls defined in variables
1 parent 86898b2 commit dc92c50

13 files changed

+79
-9
lines changed

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

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ export default createRule('no-navigation-without-base', {
102102
return;
103103
}
104104
if (
105-
!urlStartsWithBase(hrefValue.expression, basePathNames) &&
105+
!urlStartsWithBase(context, hrefValue.expression, basePathNames) &&
106106
!urlIsAbsolute(hrefValue.expression)
107107
) {
108108
context.report({ loc: hrefValue.loc, messageId: 'linkNotPrefixed' });
@@ -183,7 +183,7 @@ function checkGotoCall(
183183
return;
184184
}
185185
const url = call.arguments[0];
186-
if (!urlStartsWithBase(url, basePathNames)) {
186+
if (!urlStartsWithBase(context, url, basePathNames)) {
187187
context.report({ loc: url.loc, messageId: 'gotoNotPrefixed' });
188188
}
189189
}
@@ -198,20 +198,23 @@ function checkShallowNavigationCall(
198198
return;
199199
}
200200
const url = call.arguments[0];
201-
if (!urlIsEmpty(url) && !urlStartsWithBase(url, basePathNames)) {
201+
if (!urlIsEmpty(url) && !urlStartsWithBase(context, url, basePathNames)) {
202202
context.report({ loc: url.loc, messageId });
203203
}
204204
}
205205

206206
// Helper functions
207207

208208
function urlStartsWithBase(
209+
context: RuleContext,
209210
url: TSESTree.CallExpressionArgument,
210211
basePathNames: Set<TSESTree.Identifier>
211212
): boolean {
212213
switch (url.type) {
213214
case 'BinaryExpression':
214215
return binaryExpressionStartsWithBase(url, basePathNames);
216+
case 'Identifier':
217+
return variableStartsWithBase(context, url, basePathNames);
215218
case 'TemplateLiteral':
216219
return templateLiteralStartsWithBase(url, basePathNames);
217220
default:
@@ -253,6 +256,23 @@ function extractLiteralStartingIdentifier(
253256
return undefined;
254257
}
255258

259+
function variableStartsWithBase(
260+
context: RuleContext,
261+
url: TSESTree.Identifier,
262+
basePathNames: Set<TSESTree.Identifier>
263+
): boolean {
264+
const variable = findVariable(context, url);
265+
if (
266+
variable === null ||
267+
variable.identifiers.length !== 1 ||
268+
variable.identifiers[0].parent.type !== 'VariableDeclarator' ||
269+
variable.identifiers[0].parent.init === null
270+
) {
271+
return false;
272+
}
273+
return urlStartsWithBase(context, variable.identifiers[0].parent.init, basePathNames);
274+
}
275+
256276
function urlIsEmpty(url: TSESTree.CallExpressionArgument): boolean {
257277
return (
258278
(url.type === 'Literal' && url.value === '') ||
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
11
- message: Found a goto() call with a url that isn't prefixed with the base path.
2-
line: 4
2+
line: 6
3+
column: 7
4+
suggestions: null
5+
- message: Found a goto() call with a url that isn't prefixed with the base path.
6+
line: 7
37
column: 7
48
suggestions: null
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
<script>
22
import { goto } from '$app/navigation';
33
4+
const value = "/foo";
5+
46
goto('/foo');
7+
goto(value);
58
</script>
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,16 @@
11
- message: Found a link with a url that isn't prefixed with the base path.
2-
line: 1
2+
line: 4
33
column: 10
44
suggestions: null
55
- message: Found a link with a url that isn't prefixed with the base path.
6-
line: 2
6+
line: 5
77
column: 9
88
suggestions: null
99
- message: Found a link with a url that isn't prefixed with the base path.
10-
line: 3
10+
line: 6
11+
column: 9
12+
suggestions: null
13+
- message: Found a link with a url that isn't prefixed with the base path.
14+
line: 7
1115
column: 9
1216
suggestions: null
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
<script>
2+
const value = "/foo";
3+
</script>
14
<a href="/foo">Click me!</a>
25
<a href={'/foo'}>Click me!</a>
36
<a href={'/' + 'foo'}>Click me!</a>
7+
<a href={value}>Click me!</a>
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
11
- message: Found a pushState() call with a url that isn't prefixed with the base path.
2-
line: 4
2+
line: 6
3+
column: 12
4+
suggestions: null
5+
- message: Found a pushState() call with a url that isn't prefixed with the base path.
6+
line: 7
37
column: 12
48
suggestions: null
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
<script>
22
import { pushState } from '$app/navigation';
33
4+
const value = "/foo";
5+
46
pushState('/foo');
7+
pushState(value);
58
</script>
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
- message: Found a replaceState() call with a url that isn't prefixed with the
22
base path.
3-
line: 4
3+
line: 6
4+
column: 15
5+
suggestions: null
6+
- message: Found a replaceState() call with a url that isn't prefixed with the
7+
base path.
8+
line: 7
49
column: 15
510
suggestions: null
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
<script>
22
import { replaceState } from '$app/navigation';
33
4+
const value = "/foo";
5+
46
replaceState('/foo');
7+
replaceState(value);
58
</script>

packages/eslint-plugin-svelte/tests/fixtures/rules/no-navigation-without-base/valid/goto-base-prefixed01-input.svelte

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,12 @@
22
import { base } from '$app/paths';
33
import { goto } from '$app/navigation';
44
5+
const value1 = base + '/foo/';
6+
const value2 = `${base}/foo/`;
7+
58
// eslint-disable-next-line prefer-template -- Testing both variants
69
goto(base + '/foo/');
710
goto(`${base}/foo/`);
11+
goto(value1);
12+
goto(value2);
813
</script>
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
<script>
22
import { base } from '$app/paths';
3+
4+
const value1 = base + '/foo/';
5+
const value2 = `${base}/foo/`;
36
</script>
47

58
<a href={base + '/foo/'}>Click me!</a>
69
<a href={`${base}/foo/`}>Click me!</a>
10+
<a href={value1}>Click me!</a>
11+
<a href={value2}>Click me!</a>

packages/eslint-plugin-svelte/tests/fixtures/rules/no-navigation-without-base/valid/pushState-base-prefixed01-input.svelte

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,12 @@
22
import { base } from '$app/paths';
33
import { pushState } from '$app/navigation';
44
5+
const value1 = base + '/foo/';
6+
const value2 = `${base}/foo/`;
7+
58
// eslint-disable-next-line prefer-template -- Testing both variants
69
pushState(base + '/foo/');
710
pushState(`${base}/foo/`);
11+
pushState(value1);
12+
pushState(value2);
813
</script>

packages/eslint-plugin-svelte/tests/fixtures/rules/no-navigation-without-base/valid/replaceState-base-prefixed01-input.svelte

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,12 @@
22
import { base } from '$app/paths';
33
import { replaceState } from '$app/navigation';
44
5+
const value1 = base + '/foo/';
6+
const value2 = `${base}/foo/`;
7+
58
// eslint-disable-next-line prefer-template -- Testing both variants
69
replaceState(base + '/foo/');
710
replaceState(`${base}/foo/`);
11+
replaceState(value1);
12+
replaceState(value2);
813
</script>

0 commit comments

Comments
 (0)