Skip to content

Commit 6f07541

Browse files
committed
fix(no-navigation-without-resolve): do not report relative upper directory urls in <a>
refactor(no-navigation-without-resolve): combine `expressionIsAbsolute()` and `expressionIsFragment()` into `expressionIsIgnored()` traversing once to reduce duplication
1 parent 2d72bf1 commit 6f07541

File tree

2 files changed

+34
-68
lines changed

2 files changed

+34
-68
lines changed

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

Lines changed: 25 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -97,11 +97,9 @@ export default createRule('no-navigation-without-resolve', {
9797
}
9898
if (
9999
(node.value[0].type === 'SvelteLiteral' &&
100-
!expressionIsAbsolute(new FindVariableContext(context), node.value[0]) &&
101-
!expressionIsFragment(new FindVariableContext(context), node.value[0])) ||
100+
!expressionIsIgnored(new FindVariableContext(context), node.value[0])) ||
102101
(node.value[0].type === 'SvelteMustacheTag' &&
103-
!expressionIsAbsolute(new FindVariableContext(context), node.value[0].expression) &&
104-
!expressionIsFragment(new FindVariableContext(context), node.value[0].expression) &&
102+
!expressionIsIgnored(new FindVariableContext(context), node.value[0].expression) &&
105103
!isResolveCall(
106104
new FindVariableContext(context),
107105
node.value[0].expression,
@@ -263,37 +261,37 @@ function expressionIsEmpty(url: TSESTree.CallExpressionArgument): boolean {
263261
);
264262
}
265263

266-
function expressionIsAbsolute(
264+
function expressionIsIgnored(
267265
ctx: FindVariableContext,
268266
url: AST.SvelteLiteral | TSESTree.Expression
269267
): boolean {
270268
switch (url.type) {
271269
case 'BinaryExpression':
272-
return binaryExpressionIsAbsolute(ctx, url);
270+
return binaryExpressionIsIgnored(ctx, url);
273271
case 'Identifier':
274-
return identifierIsAbsolute(ctx, url);
272+
return identifierIsIgnored(ctx, url);
275273
case 'Literal':
276-
return typeof url.value === 'string' && urlValueIsAbsolute(url.value);
274+
return typeof url.value === 'string' && urlValueIsIgnored(url.value);
277275
case 'SvelteLiteral':
278-
return urlValueIsAbsolute(url.value);
276+
return urlValueIsIgnored(url.value);
279277
case 'TemplateLiteral':
280-
return templateLiteralIsAbsolute(ctx, url);
278+
return templateLiteralIsIgnored(ctx, url);
281279
default:
282280
return false;
283281
}
284282
}
285283

286-
function binaryExpressionIsAbsolute(
284+
function binaryExpressionIsIgnored(
287285
ctx: FindVariableContext,
288286
url: TSESTree.BinaryExpression
289287
): boolean {
290288
return (
291-
(url.left.type !== 'PrivateIdentifier' && expressionIsAbsolute(ctx, url.left)) ||
292-
expressionIsAbsolute(ctx, url.right)
289+
(url.left.type !== 'PrivateIdentifier' && expressionIsIgnored(ctx, url.left)) ||
290+
expressionIsIgnored(ctx, url.right)
293291
);
294292
}
295293

296-
function identifierIsAbsolute(ctx: FindVariableContext, url: TSESTree.Identifier): boolean {
294+
function identifierIsIgnored(ctx: FindVariableContext, url: TSESTree.Identifier): boolean {
297295
const variable = ctx.findVariable(url);
298296
if (
299297
variable === null ||
@@ -303,73 +301,32 @@ function identifierIsAbsolute(ctx: FindVariableContext, url: TSESTree.Identifier
303301
) {
304302
return false;
305303
}
306-
return expressionIsAbsolute(ctx, variable.identifiers[0].parent.init);
304+
305+
return expressionIsIgnored(ctx, variable.identifiers[0].parent.init);
307306
}
308307

309-
function templateLiteralIsAbsolute(
308+
function templateLiteralIsIgnored(
310309
ctx: FindVariableContext,
311310
url: TSESTree.TemplateLiteral
312311
): boolean {
313312
return (
314-
url.expressions.some((expression) => expressionIsAbsolute(ctx, expression)) ||
315-
url.quasis.some((quasi) => urlValueIsAbsolute(quasi.value.raw))
313+
url.expressions.some((expression) => expressionIsIgnored(ctx, expression)) ||
314+
url.quasis.some((quasi) => urlValueIsIgnored(quasi.value.raw))
316315
);
317316
}
318317

319-
function urlValueIsAbsolute(url: string): boolean {
320-
return /^[+a-z]*:/i.test(url);
321-
}
322-
323-
function expressionIsFragment(
324-
ctx: FindVariableContext,
325-
url: AST.SvelteLiteral | TSESTree.Expression
326-
): boolean {
327-
switch (url.type) {
328-
case 'BinaryExpression':
329-
return binaryExpressionIsFragment(ctx, url);
330-
case 'Identifier':
331-
return identifierIsFragment(ctx, url);
332-
case 'Literal':
333-
return typeof url.value === 'string' && urlValueIsFragment(url.value);
334-
case 'SvelteLiteral':
335-
return urlValueIsFragment(url.value);
336-
case 'TemplateLiteral':
337-
return templateLiteralIsFragment(ctx, url);
338-
default:
339-
return false;
340-
}
341-
}
342-
343-
function binaryExpressionIsFragment(
344-
ctx: FindVariableContext,
345-
url: TSESTree.BinaryExpression
346-
): boolean {
347-
return url.left.type !== 'PrivateIdentifier' && expressionIsFragment(ctx, url.left);
348-
}
349-
350-
function identifierIsFragment(ctx: FindVariableContext, url: TSESTree.Identifier): boolean {
351-
const variable = ctx.findVariable(url);
352-
if (
353-
variable === null ||
354-
variable.identifiers.length === 0 ||
355-
variable.identifiers[0].parent.type !== 'VariableDeclarator' ||
356-
variable.identifiers[0].parent.init === null
357-
) {
358-
return false;
359-
}
360-
return expressionIsFragment(ctx, variable.identifiers[0].parent.init);
318+
function urlValueIsIgnored(url: string): boolean {
319+
return urlValueIsAbsolute(url) || urlValueIsFragment(url) || urlValueIsUpperDirectory(url);
361320
}
362321

363-
function templateLiteralIsFragment(
364-
ctx: FindVariableContext,
365-
url: TSESTree.TemplateLiteral
366-
): boolean {
367-
return (
368-
(url.expressions.length >= 1 && expressionIsFragment(ctx, url.expressions[0])) ||
369-
(url.quasis.length >= 1 && urlValueIsFragment(url.quasis[0].value.raw))
370-
);
322+
function urlValueIsAbsolute(url: string): boolean {
323+
return /^[+a-z]*:/i.test(url);
371324
}
372325

373326
function urlValueIsFragment(url: string): boolean {
374327
return url.startsWith('#');
375328
}
329+
330+
function urlValueIsUpperDirectory(url: string): boolean {
331+
return url.startsWith('..');
332+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<script>
2+
const value = '../';
3+
</script>
4+
5+
<a href="..">Click me!</a>
6+
<a href="../">Click me!</a>
7+
<a href="../other">Click me!</a>
8+
<a href={'..' + '/other'}>Click me!</a>
9+
<a href={value}>Click me!</a>

0 commit comments

Comments
 (0)