Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Generalized no-goto-without-base into no-navigation-without-base #900

Open
wants to merge 9 commits into
base: main
Choose a base branch
from
5 changes: 5 additions & 0 deletions .changeset/cold-starfishes-doubt.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'eslint-plugin-svelte': minor
---

feat: added the no-navigation-without-base rule
5 changes: 5 additions & 0 deletions .changeset/olive-melons-explain.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'eslint-plugin-svelte': major
---

chore: deprecated the no-goto-without-base rule
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -473,7 +473,7 @@ These rules relate to SvelteKit and its best Practices.

| Rule ID | Description | |
|:--------|:------------|:---|
| [svelte/no-goto-without-base](https://sveltejs.github.io/eslint-plugin-svelte/rules/no-goto-without-base/) | disallow using goto() without the base path | |
| [svelte/no-navigation-without-base](https://sveltejs.github.io/eslint-plugin-svelte/rules/no-navigation-without-base/) | disallow using navigation (links, goto, pushState, replaceState) without the base path | |

## Experimental

Expand Down Expand Up @@ -501,6 +501,7 @@ These rules relate to this plugin works:
| Rule ID | Replaced by |
|:--------|:------------|
| [svelte/@typescript-eslint/no-unnecessary-condition](https://sveltejs.github.io/eslint-plugin-svelte/rules/@typescript-eslint/no-unnecessary-condition/) | This rule is no longer needed when using svelte-eslint-parser>=v0.19.0. |
| [svelte/no-goto-without-base](https://sveltejs.github.io/eslint-plugin-svelte/rules/no-goto-without-base/) | [svelte/no-navigation-without-base](https://sveltejs.github.io/eslint-plugin-svelte/rules/no-navigation-without-base/) |

<!--RULES_TABLE_END-->
<!--RULES_SECTION_END-->
Expand Down
13 changes: 7 additions & 6 deletions docs/rules.md
Original file line number Diff line number Diff line change
Expand Up @@ -108,9 +108,9 @@ These rules extend the rules provided by ESLint itself, or other plugins to work

These rules relate to SvelteKit and its best Practices.

| Rule ID | Description | |
| :------------------------------------------------------------- | :------------------------------------------ | :-- |
| [svelte/no-goto-without-base](./rules/no-goto-without-base.md) | disallow using goto() without the base path | |
| Rule ID | Description | |
| :------------------------------------------------------------------------- | :------------------------------------------------------------------------------------- | :-- |
| [svelte/no-navigation-without-base](./rules/no-navigation-without-base.md) | disallow using navigation (links, goto, pushState, replaceState) without the base path | |

## Experimental

Expand All @@ -135,6 +135,7 @@ These rules relate to this plugin works:
- :warning: We're going to remove deprecated rules in the next major release. Please migrate to successor/new rules.
- :innocent: We don't fix bugs which are in deprecated rules since we don't have enough resources.

| Rule ID | Replaced by |
| :----------------------------------------------------------------------------------------------------------- | :---------------------------------------------------------------------- |
| [svelte/@typescript-eslint/no-unnecessary-condition](./rules/@typescript-eslint/no-unnecessary-condition.md) | This rule is no longer needed when using svelte-eslint-parser>=v0.19.0. |
| Rule ID | Replaced by |
| :----------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------- |
| [svelte/@typescript-eslint/no-unnecessary-condition](./rules/@typescript-eslint/no-unnecessary-condition.md) | This rule is no longer needed when using svelte-eslint-parser>=v0.19.0. |
| [svelte/no-goto-without-base](./rules/no-goto-without-base.md) | [svelte/no-navigation-without-base](./rules/no-navigation-without-base.md) |
2 changes: 2 additions & 0 deletions docs/rules/no-goto-without-base.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ since: 'v2.36.0-next.9'

> disallow using goto() without the base path
- :warning: This rule was **deprecated** and replaced by [svelte/no-navigation-without-base](no-navigation-without-base.md) rule.

## :book: Rule Details

This rule reports navigation using SvelteKit's `goto()` function without prefixing a relative URL with the base path. If a non-prefixed relative URL is used for navigation, the `goto` function navigates away from the base path, which is usually not what you wanted to do (for external URLs, `window.location = url` should be used instead).
Expand Down
98 changes: 98 additions & 0 deletions docs/rules/no-navigation-without-base.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
---
pageClass: 'rule-details'
sidebarDepth: 0
title: 'svelte/no-navigation-without-base'
description: 'disallow using navigation (links, goto, pushState, replaceState) without the base path'
since: 'v2.36.0-next.9'
---

# svelte/no-navigation-without-base

> disallow using navigation (links, goto, pushState, replaceState) without the base path

## :book: Rule Details

This rule reports navigation using HTML `<a>` tags, SvelteKit's `goto()`, `pushState()` and `replaceState()` functions without prefixing a relative URL with the base path. All four of these may be used for navigation, with `goto()`, `pushState()` and `replaceState()` being intended solely for iternal navigation (i.e. not leaving the site), while `<a>` tags may be used for both internal and external navigation. When using any way of internal navigation, the base path must be prepended, otherwise the site may break. For programmatic navigation to external URLs, using `window.location` is advised.

This rule checks all 4 navigation options for the presence of the base path, with an exception for `<a>` links to absolute URLs, which are assumed to be used for external navigation and so do not require the base path, and for shallow outing functions with an empty string as the path, which keeps the current URL.

<ESLintCodeBlock>

<!--eslint-skip-->

```svelte
<script>
/* eslint svelte/no-navigation-without-base: "error" */

import { goto, pushState, replaceState } from '$app/navigation';
import { base } from '$app/paths';

// ✓ GOOD
goto(base + '/foo/');
goto(`${base}/foo/`);

pushState(base + '/foo/', {});
pushState(`${base}/foo/`, {});
pushState('', {});

replaceState(base + '/foo/', {});
replaceState(`${base}/foo/`, {});
replaceState('', {});

// ✗ BAD
goto('/foo');
goto('/foo/' + base);

pushState('/foo', {});
replaceState('/foo', {});
</script>

<!-- ✓ GOOD -->
<a href={base + '/foo/'}>Click me!</a>
<a href={`${base}/foo/`}>Click me!</a>
<a href="https://svelte.dev">Click me!</a>

<!-- ✗ BAD -->
<a href="/foo">Click me!</a>
<a href={'/foo'}>Click me!</a>
```

</ESLintCodeBlock>

## :wrench: Options

```json
{
"svelte/no-navigation-without-base": [
"error",
{
"ignoreGoto": false,
"ignoreLinks": false,
"ignorePushState": false,
"ignoreReplaceState": false
}
]
}
```

- `ignoreGoto` ... Whether to ignore all `goto()` calls. Default `false`.
- `ignoreLinks` ... Whether to ignore all `<a>` tags. Default `false`.
- `ignorePushState` ... Whether to ignore all `pushState()` calls. Default `false`.
- `ignoreReplaceState` ... Whether to ignore all `replaceState()` calls. Default `false`.

## :books: Further Reading

- [`base` documentation](https://svelte.dev/docs/kit/$app-paths#base)
- [Shallow routing](https://svelte.dev/docs/kit/shallow-routing)
- [`goto()` documentation](https://svelte.dev/docs/kit/$app-navigation#goto)
- [`pushState()` documentation](https://svelte.dev/docs/kit/$app-navigation#pushState)
- [`replaceState()` documentation](https://svelte.dev/docs/kit/$app-navigation#replaceState)

## :rocket: Version

This rule was introduced in eslint-plugin-svelte v2.36.0-next.9

## :mag: Implementation

- [Rule source](https://github.com/sveltejs/eslint-plugin-svelte/blob/main/packages/eslint-plugin-svelte/src/rules/no-navigation-without-base.ts)
- [Test source](https://github.com/sveltejs/eslint-plugin-svelte/blob/main/packages/eslint-plugin-svelte/tests/src/rules/no-navigation-without-base.ts)
13 changes: 13 additions & 0 deletions packages/eslint-plugin-svelte/src/rule-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,7 @@ export interface RuleOptions {
/**
* disallow using goto() without the base path
* @see https://sveltejs.github.io/eslint-plugin-svelte/rules/no-goto-without-base/
* @deprecated
*/
'svelte/no-goto-without-base'?: Linter.RuleEntry<[]>
/**
Expand Down Expand Up @@ -174,6 +175,11 @@ export interface RuleOptions {
* @see https://sveltejs.github.io/eslint-plugin-svelte/rules/no-inspect/
*/
'svelte/no-inspect'?: Linter.RuleEntry<[]>
/**
* disallow using navigation (links, goto, pushState, replaceState) without the base path
* @see https://sveltejs.github.io/eslint-plugin-svelte/rules/no-navigation-without-base/
*/
'svelte/no-navigation-without-base'?: Linter.RuleEntry<SvelteNoNavigationWithoutBase>
/**
* disallow use of not function in event handler
* @see https://sveltejs.github.io/eslint-plugin-svelte/rules/no-not-function-handler/
Expand Down Expand Up @@ -437,6 +443,13 @@ type SvelteNoInlineStyles = []|[{
type SvelteNoInnerDeclarations = []|[("functions" | "both")]|[("functions" | "both"), {
blockScopedFunctions?: ("allow" | "disallow")
}]
// ----- svelte/no-navigation-without-base -----
type SvelteNoNavigationWithoutBase = []|[{
ignoreGoto?: boolean
ignoreLinks?: boolean
ignorePushState?: boolean
ignoreReplaceState?: boolean
}]
// ----- svelte/no-reactive-reassign -----
type SvelteNoReactiveReassign = []|[{
props?: boolean
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import type { RuleContext } from '../types';

export default createRule('no-goto-without-base', {
meta: {
deprecated: true,
replacedBy: ['no-navigation-without-base'],
docs: {
description: 'disallow using goto() without the base path',
category: 'SvelteKit',
Expand Down
Loading
Loading