sv-router 0.16.0 → 0.16.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -5,10 +5,11 @@
5
5
  # sv-router
6
6
 
7
7
  [![npm](https://badgen.net/npm/v/sv-router)](https://www.npmjs.com/package/sv-router)
8
- [![install size](https://packagephobia.com/badge?p=sv-router)](https://packagephobia.com/result?p=sv-router)
8
+ ![downloads](https://badgen.net/npm/dm/sv-router)
9
+ ![size](https://badgen.net/bundlephobia/minzip/sv-router)
9
10
  [![codecov](https://codecov.io/github/colinlienard/sv-router/graph/badge.svg?token=C9RBSEFO9S)](https://codecov.io/github/colinlienard/sv-router)
10
11
 
11
- A feature-rich yet intuitive routing library for Svelte single-page apps.
12
+ Flexible, ergonomic, and complete Svelte 5 router.
12
13
 
13
14
  [Documentation](https://sv-router.vercel.app/) • [Getting Started](https://sv-router.vercel.app/guide/getting-started) • [Reference](https://sv-router.vercel.app/reference)
14
15
 
@@ -18,16 +19,14 @@ A feature-rich yet intuitive routing library for Svelte single-page apps.
18
19
 
19
20
  ## Features
20
21
 
21
- - 🔒 **Typesafe navigation**: Get autocomplete and type checking for your routes.
22
- - 🔄 **Flexibility**: Choose between code-based or file-based routing approaches.
23
- - 🌿 **Nested routes**: Create complex layouts with ease.
22
+ - 🔒 **Type-safe navigation**: Catch broken links before you ship.
23
+ - 🗂️ **File-based routing (optional)**: DX of a meta-framework-like approach.
24
+ - 🪶 **Lightweight**: < 5kB gzipped.
25
+ - ⚡ **Performance**: Built-in code splitting and preloading.
24
26
  - 🔍 **Reactive search params**: For simpler state management in the URL.
27
+ - 🌿 **Nested routes**: Share layouts across pages.
25
28
  - 🛡️ **Hooks**: For navigation guards, data loading, or analytics tracking.
26
- - **Performance**: Optimized for speed with built-in code splitting and preloading.
27
- - 🧩 **Familiar API**: Follows established conventions from popular meta frameworks.
28
- - 🪶 **Lightweight**: Minimal impact on your bundle size.
29
- - 🚀 **Made for Svelte 5**: True Svelte 5 implementation with the latest features.
30
- - #️⃣ **Hash-based routing**: Hash-based routing enables usage in local environments.
29
+ - #️⃣ **Hash-based routing**: Works inside Electron/Tauri.
31
30
 
32
31
  ## Getting Started
33
32
 
package/package.json CHANGED
@@ -1,21 +1,24 @@
1
1
  {
2
2
  "name": "sv-router",
3
- "version": "0.16.0",
4
- "description": "Modern Svelte Routing",
3
+ "version": "0.16.2",
4
+ "description": "Type-safe routing for Svelte SPAs",
5
5
  "keywords": [
6
- "svelte",
7
6
  "router",
8
- "spa"
7
+ "spa",
8
+ "svelte"
9
9
  ],
10
10
  "homepage": "https://github.com/colinlienard/sv-router",
11
11
  "bugs": {
12
12
  "url": "https://github.com/colinlienard/sv-router/issues"
13
13
  },
14
+ "license": "MIT",
14
15
  "repository": {
15
16
  "type": "git",
16
17
  "url": "git+https://github.com/colinlienard/sv-router.git"
17
18
  },
18
- "license": "MIT",
19
+ "files": [
20
+ "src"
21
+ ],
19
22
  "type": "module",
20
23
  "exports": {
21
24
  ".": {
@@ -28,38 +31,32 @@
28
31
  "types": "./src/vite-plugin/index.d.ts"
29
32
  }
30
33
  },
31
- "bin": "./src/cli/index.js",
32
- "files": [
33
- "src"
34
- ],
35
34
  "dependencies": {
36
35
  "esm-env": "^1.2.2"
37
36
  },
38
37
  "devDependencies": {
39
- "@changesets/cli": "^2.30.0",
38
+ "@changesets/cli": "^2.31.0",
40
39
  "@eslint/js": "^10.0.1",
41
- "@sveltejs/vite-plugin-svelte": "^7.0.0",
40
+ "@sveltejs/vite-plugin-svelte": "^7.1.2",
42
41
  "@testing-library/jest-dom": "^6.9.1",
43
42
  "@testing-library/svelte": "^5.3.1",
44
43
  "@testing-library/user-event": "^14.6.1",
45
- "@types/node": "^24.12.0",
46
- "@vitest/coverage-v8": "^4.1.0",
47
- "eslint": "^10.0.3",
44
+ "@types/node": "^24.12.3",
45
+ "@vitest/coverage-v8": "^4.1.5",
46
+ "eslint": "^10.3.0",
48
47
  "eslint-config-prettier": "^10.1.8",
49
48
  "eslint-plugin-simple-import-sort": "^13.0.0",
50
- "eslint-plugin-svelte": "^3.15.2",
49
+ "eslint-plugin-svelte": "^3.17.1",
51
50
  "eslint-plugin-unicorn": "^64.0.0",
52
- "globals": "^17.4.0",
53
- "happy-dom": "^20.8.4",
54
- "prettier": "^3.8.1",
55
- "prettier-plugin-jsdoc": "^1.8.0",
56
- "prettier-plugin-svelte": "^3.5.1",
57
- "svelte-check": "^4.4.5",
51
+ "globals": "^17.6.0",
52
+ "happy-dom": "^20.9.0",
53
+ "oxfmt": "^0.49.0",
54
+ "svelte-check": "^4.4.8",
58
55
  "type-testing": "^0.2.0",
59
- "typescript": "^6.0.2",
60
- "typescript-eslint": "^8.57.1",
61
- "vite": "^8.0.0",
62
- "vitest": "^4.1.0"
56
+ "typescript": "^6.0.3",
57
+ "typescript-eslint": "^8.59.2",
58
+ "vite": "^8.0.11",
59
+ "vitest": "^4.1.5"
63
60
  },
64
61
  "peerDependencies": {
65
62
  "svelte": "^5"
@@ -75,8 +72,11 @@
75
72
  "check": "svelte-check && pnpm -r check",
76
73
  "lint": "eslint . --max-warnings 0",
77
74
  "lint:fix": "eslint . --fix",
78
- "format": "prettier . --check",
79
- "format:fix": "prettier . --write",
75
+ "format": "oxfmt --check",
76
+ "format:fix": "oxfmt",
80
77
  "changeset": "changeset"
78
+ },
79
+ "bin": {
80
+ "sv-router": "./src/cli/index.js"
81
81
  }
82
82
  }
package/src/Router.svelte CHANGED
@@ -1,4 +1,5 @@
1
1
  <script>
2
+ import { untrack } from 'svelte';
2
3
  import { on } from 'svelte/events';
3
4
  import {
4
5
  componentTree,
@@ -7,15 +8,19 @@
7
8
  onGlobalClick,
8
9
  onNavigate,
9
10
  } from './create-router.svelte.js';
11
+ import { getUserState } from './helpers/utils.js';
10
12
  import RecursiveComponentTree from './RecursiveComponentTree.svelte';
11
13
 
12
14
  /** @type {{ base?: string }} */
13
15
  let { base: basename } = $props();
14
16
 
15
- // svelte-ignore state_referenced_locally
16
- init(basename);
17
+ init(untrack(() => basename));
17
18
 
18
- onNavigate();
19
+ onNavigate(undefined, {
20
+ search: location.search,
21
+ hash: location.hash,
22
+ state: getUserState(history.state),
23
+ });
19
24
 
20
25
  $effect(() => {
21
26
  const cleanup = [
@@ -345,6 +345,8 @@ export function onGlobalClick(event) {
345
345
  const path = base.name === '#' ? url.hash : url.pathname;
346
346
  const hash = base.name === '#' ? undefined : url.hash;
347
347
 
348
+ if (base.name && base.name !== '#' && !path.startsWith(base.name)) return;
349
+
348
350
  event.preventDefault();
349
351
  const { replace, state, scrollToTop, viewTransition } = anchor.dataset;
350
352
 
@@ -68,7 +68,7 @@ function resolveRouteComponent(input) {
68
68
 
69
69
  /**
70
70
  * @param {unknown} input
71
- * @returns {input is import('../index.d.ts').LazyRouteComponent}
71
+ * @returns {input is import("../index.d.ts").LazyRouteComponent}
72
72
  */
73
73
  export function isLazyImport(input) {
74
74
  return (
@@ -104,7 +104,7 @@ export function stripBase(pathname) {
104
104
  }
105
105
 
106
106
  /** @param {any} state */
107
- function getUserState(state) {
107
+ export function getUserState(state) {
108
108
  if (state && '_userState' in state) {
109
109
  return state._userState;
110
110
  }
package/src/index.d.ts CHANGED
@@ -316,6 +316,7 @@ export type SearchParams = Omit<
316
316
  getAll(name: string): (string | number | boolean)[];
317
317
  set(name: string, value: string | number | boolean, options?: { replace?: boolean }): void;
318
318
  sort(options?: { replace?: boolean }): void;
319
+ toURLSearchParams(): URLSearchParams;
319
320
  values(): (string | number | boolean)[];
320
321
  };
321
322
 
@@ -47,6 +47,9 @@ const shell = {
47
47
  values() {
48
48
  return [...searchParams.values()].map(parseSearchValue);
49
49
  },
50
+ toURLSearchParams() {
51
+ return new URLSearchParams(searchParams);
52
+ },
50
53
  get size() {
51
54
  return searchParams.size;
52
55
  },
@@ -63,8 +66,11 @@ export function syncSearchParams(search) {
63
66
  return;
64
67
  }
65
68
  const newSearch = new URLSearchParams(search);
66
- for (const [key, value] of newSearch) {
67
- searchParams.set(key, value);
69
+ for (const key of newSearch.keys()) {
70
+ searchParams.delete(key);
71
+ for (const value of newSearch.getAll(key)) {
72
+ searchParams.append(key, value);
73
+ }
68
74
  }
69
75
  // eslint-disable-next-line unicorn/no-useless-spread
70
76
  for (const key of [...searchParams.keys()]) {