diff --git a/.changeset/warm-plums-smile.md b/.changeset/warm-plums-smile.md
new file mode 100644
index 000000000..5475ec710
--- /dev/null
+++ b/.changeset/warm-plums-smile.md
@@ -0,0 +1,5 @@
+---
+'eslint-plugin-svelte': patch
+---
+
+fix(no-navigation-without-base): ignoring fragment links
diff --git a/packages/eslint-plugin-svelte/src/rules/no-navigation-without-base.ts b/packages/eslint-plugin-svelte/src/rules/no-navigation-without-base.ts
index 6f2a20994..aaeb4c6c6 100644
--- a/packages/eslint-plugin-svelte/src/rules/no-navigation-without-base.ts
+++ b/packages/eslint-plugin-svelte/src/rules/no-navigation-without-base.ts
@@ -96,14 +96,15 @@ export default createRule('no-navigation-without-base', {
}
const hrefValue = node.value[0];
if (hrefValue.type === 'SvelteLiteral') {
- if (!expressionIsAbsolute(hrefValue)) {
+ if (!expressionIsAbsolute(hrefValue) && !expressionIsFragment(hrefValue)) {
context.report({ loc: hrefValue.loc, messageId: 'linkNotPrefixed' });
}
return;
}
if (
!expressionStartsWithBase(context, hrefValue.expression, basePathNames) &&
- !expressionIsAbsolute(hrefValue.expression)
+ !expressionIsAbsolute(hrefValue.expression) &&
+ !expressionIsFragment(hrefValue.expression)
) {
context.report({ loc: hrefValue.loc, messageId: 'linkNotPrefixed' });
}
@@ -348,3 +349,33 @@ function templateLiteralIsAbsolute(url: TSESTree.TemplateLiteral): boolean {
function urlValueIsAbsolute(url: string): boolean {
return url.includes('://');
}
+
+function expressionIsFragment(url: SvelteLiteral | TSESTree.Expression): boolean {
+ switch (url.type) {
+ case 'BinaryExpression':
+ return binaryExpressionIsFragment(url);
+ case 'Literal':
+ return typeof url.value === 'string' && urlValueIsFragment(url.value);
+ case 'SvelteLiteral':
+ return urlValueIsFragment(url.value);
+ case 'TemplateLiteral':
+ return templateLiteralIsFragment(url);
+ default:
+ return false;
+ }
+}
+
+function binaryExpressionIsFragment(url: TSESTree.BinaryExpression): boolean {
+ return url.left.type !== 'PrivateIdentifier' && expressionIsFragment(url.left);
+}
+
+function templateLiteralIsFragment(url: TSESTree.TemplateLiteral): boolean {
+ return (
+ (url.expressions.length >= 1 && expressionIsFragment(url.expressions[0])) ||
+ (url.quasis.length >= 1 && urlValueIsFragment(url.quasis[0].value.raw))
+ );
+}
+
+function urlValueIsFragment(url: string): boolean {
+ return url.startsWith('#');
+}
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-navigation-without-base/invalid/link-with-fragment01-errors.yaml b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-navigation-without-base/invalid/link-with-fragment01-errors.yaml
new file mode 100644
index 000000000..e006d4d27
--- /dev/null
+++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-navigation-without-base/invalid/link-with-fragment01-errors.yaml
@@ -0,0 +1,16 @@
+- message: Found a link with a url that isn't prefixed with the base path.
+ line: 4
+ column: 10
+ suggestions: null
+- message: Found a link with a url that isn't prefixed with the base path.
+ line: 5
+ column: 9
+ suggestions: null
+- message: Found a link with a url that isn't prefixed with the base path.
+ line: 6
+ column: 9
+ suggestions: null
+- message: Found a link with a url that isn't prefixed with the base path.
+ line: 7
+ column: 9
+ suggestions: null
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-navigation-without-base/invalid/link-with-fragment01-input.svelte b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-navigation-without-base/invalid/link-with-fragment01-input.svelte
new file mode 100644
index 000000000..2e6a11df8
--- /dev/null
+++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-navigation-without-base/invalid/link-with-fragment01-input.svelte
@@ -0,0 +1,7 @@
+
+Click me!
+Click me!
+Click me!
+Click me!
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-navigation-without-base/valid/link-fragment-url01-input.svelte b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-navigation-without-base/valid/link-fragment-url01-input.svelte
new file mode 100644
index 000000000..eeaa16267
--- /dev/null
+++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-navigation-without-base/valid/link-fragment-url01-input.svelte
@@ -0,0 +1,10 @@
+
+
+Click me!
+Click me!
+Click me!
+Click me!
+Click me!
+Click me!