Skip to content

Commit 494e20d

Browse files
committed
feat(no-navigation-without-resolve): ignoring links with rel=external
1 parent ef87440 commit 494e20d

File tree

3 files changed

+59
-2
lines changed

3 files changed

+59
-2
lines changed

.changeset/afraid-falcons-hang.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): ignoring links with rel=external

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

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,8 @@ export default createRule('no-navigation-without-resolve', {
9898
node.parent.parent.name.type !== 'SvelteName' ||
9999
node.parent.parent.name.name !== 'a' ||
100100
node.key.name !== 'href' ||
101-
node.value.type !== 'Identifier'
101+
node.value.type !== 'Identifier' ||
102+
hasRelExternal(new FindVariableContext(context), node.parent)
102103
) {
103104
return;
104105
}
@@ -117,7 +118,8 @@ export default createRule('no-navigation-without-resolve', {
117118
node.parent.parent.kind !== 'html' ||
118119
node.parent.parent.name.type !== 'SvelteName' ||
119120
node.parent.parent.name.name !== 'a' ||
120-
node.key.name !== 'href'
121+
node.key.name !== 'href' ||
122+
hasRelExternal(new FindVariableContext(context), node.parent)
121123
) {
122124
return;
123125
}
@@ -431,3 +433,38 @@ function templateLiteralIsFragment(
431433
function urlValueIsFragment(url: string): boolean {
432434
return url.startsWith('#');
433435
}
436+
437+
function hasRelExternal(ctx: FindVariableContext, element: AST.SvelteStartTag): boolean {
438+
function identifierIsExternal(identifier: TSESTree.Identifier): boolean {
439+
const variable = ctx.findVariable(identifier);
440+
return (
441+
variable !== null &&
442+
variable.identifiers.length > 0 &&
443+
variable.identifiers[0].parent.type === 'VariableDeclarator' &&
444+
variable.identifiers[0].parent.init !== null &&
445+
variable.identifiers[0].parent.init.type === 'Literal' &&
446+
variable.identifiers[0].parent.init.value === 'external'
447+
);
448+
}
449+
450+
for (const attr of element.attributes) {
451+
if (
452+
(attr.type === 'SvelteAttribute' &&
453+
attr.key.name === 'rel' &&
454+
((attr.value[0].type === 'SvelteLiteral' &&
455+
attr.value[0].value.split(/\s+/).includes('external')) ||
456+
(attr.value[0].type === 'SvelteMustacheTag' &&
457+
((attr.value[0].expression.type === 'Literal' &&
458+
attr.value[0].expression.value?.toString().split(/\s+/).includes('external')) ||
459+
(attr.value[0].expression.type === 'Identifier' &&
460+
identifierIsExternal(attr.value[0].expression)))))) ||
461+
(attr.type === 'SvelteShorthandAttribute' &&
462+
attr.key.name === 'rel' &&
463+
attr.value.type === 'Identifier' &&
464+
identifierIsExternal(attr.value))
465+
) {
466+
return true;
467+
}
468+
}
469+
return false;
470+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<script>
2+
const value = 'whatever';
3+
const href = 'whatever';
4+
const external = 'external';
5+
const rel = 'external';
6+
</script>
7+
8+
<a href="whatever" rel="external">Click me!</a>
9+
<a href={'whatever'} rel="external">Click me!</a>
10+
<a href={value} rel="external">Click me!</a>
11+
<a {href} rel="external">Click me!</a>
12+
<a href="whatever" rel={'external'}>Click me!</a>
13+
<a href="whatever" rel={external}>Click me!</a>
14+
<a href="whatever" {rel}>Click me!</a>
15+
<a href="whatever" rel="noopener external noreferrer">Click me!</a>

0 commit comments

Comments
 (0)