sv-router 0.1.0 → 0.2.0

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sv-router",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Modern Svelte Routing",
5
5
  "keywords": [
6
6
  "svelte",
@@ -37,23 +37,23 @@
37
37
  },
38
38
  "devDependencies": {
39
39
  "@changesets/cli": "^2.28.1",
40
- "@eslint/js": "^9.21.0",
41
- "@types/node": "^22.13.8",
42
- "eslint": "^9.21.0",
43
- "eslint-config-prettier": "^10.0.2",
40
+ "@eslint/js": "^9.23.0",
41
+ "@types/node": "^22.13.14",
42
+ "eslint": "^9.23.0",
43
+ "eslint-config-prettier": "^10.1.1",
44
44
  "eslint-plugin-simple-import-sort": "^12.1.1",
45
- "eslint-plugin-svelte": "^3.0.2",
46
- "eslint-plugin-unicorn": "^57.0.0",
45
+ "eslint-plugin-svelte": "^3.3.3",
46
+ "eslint-plugin-unicorn": "^58.0.0",
47
47
  "globals": "^16.0.0",
48
- "prettier": "^3.5.2",
48
+ "prettier": "^3.5.3",
49
49
  "prettier-plugin-jsdoc": "^1.3.2",
50
50
  "prettier-plugin-svelte": "^3.3.3",
51
- "svelte-check": "^4.1.4",
51
+ "svelte-check": "^4.1.5",
52
52
  "type-testing": "^0.2.0",
53
53
  "typescript": "^5.8.2",
54
- "typescript-eslint": "^8.25.0",
55
- "vite": "^6.2.0",
56
- "vitest": "^3.0.7"
54
+ "typescript-eslint": "^8.28.0",
55
+ "vite": "^6.2.3",
56
+ "vitest": "^3.0.9"
57
57
  },
58
58
  "peerDependencies": {
59
59
  "svelte": "^5"
@@ -64,7 +64,7 @@
64
64
  "docs:preview": "pnpm --filter docs preview",
65
65
  "test": "vitest",
66
66
  "check": "svelte-check && pnpm -r check",
67
- "lint": "eslint .",
67
+ "lint": "eslint . --max-warnings 0",
68
68
  "lint:fix": "eslint . --fix",
69
69
  "format": "prettier . --check",
70
70
  "format:fix": "prettier . --write",
package/src/Router.svelte CHANGED
@@ -1,8 +1,21 @@
1
1
  <script>
2
2
  import { on } from 'svelte/events';
3
- import { componentTree, onGlobalClick, onNavigate } from './create-router.svelte.js';
3
+ import { base, componentTree, onGlobalClick, onNavigate } from './create-router.svelte.js';
4
+ import { join } from './helpers/utils.js';
4
5
  import RecursiveComponentTree from './RecursiveComponentTree.svelte';
5
6
 
7
+ /** @type {{ base?: string }} */
8
+ let { base: basename } = $props();
9
+
10
+ if (basename) {
11
+ base.name = (basename.startsWith('/') ? '' : '/') + basename;
12
+ const url = new URL(globalThis.location.href);
13
+ if (!url.pathname.startsWith(base.name)) {
14
+ url.pathname = join(base.name, url.pathname);
15
+ history.replaceState(history.state || {}, '', url.href);
16
+ }
17
+ }
18
+
6
19
  onNavigate();
7
20
 
8
21
  $effect(() => {
@@ -2,11 +2,11 @@ import { BROWSER, DEV } from 'esm-env';
2
2
  import { isActive } from './helpers/is-active.js';
3
3
  import { matchRoute } from './helpers/match-route.js';
4
4
  import { preloadOnHover } from './helpers/preload-on-hover.js';
5
- import { constructPath, resolveRouteComponents } from './helpers/utils.js';
5
+ import { constructPath, join, resolveRouteComponents } from './helpers/utils.js';
6
6
  import { syncSearchParams } from './search-params.svelte.js';
7
7
 
8
8
  /** @type {import('./index.d.ts').Routes} */
9
- export let routes;
9
+ let routes;
10
10
 
11
11
  /** @type {{ value: import('svelte').Component[] }} */
12
12
  export let componentTree = $state({ value: [] });
@@ -16,6 +16,11 @@ export let params = $state({ value: {} });
16
16
 
17
17
  export let location = $state(updatedLocation());
18
18
 
19
+ /** @type {{ name?: string }} */
20
+ export const base = {
21
+ name: undefined,
22
+ };
23
+
19
24
  /**
20
25
  * @template {import('./index.d.ts').Routes} T
21
26
  * @param {T} r
@@ -85,12 +90,11 @@ export async function onNavigate(path, options = {}) {
85
90
  if (!routes) {
86
91
  throw new Error('Router not initialized: `createRouter` was not called.');
87
92
  }
88
- const {
89
- match,
90
- layouts,
91
- hooks,
92
- params: newParams,
93
- } = matchRoute(path || globalThis.location.pathname, routes);
93
+ let matchPath = path || globalThis.location.pathname;
94
+ if (base.name && matchPath.startsWith(base.name)) {
95
+ matchPath = matchPath.slice(base.name.length) || '/';
96
+ }
97
+ const { match, layouts, hooks, params: newParams } = matchRoute(matchPath, routes);
94
98
 
95
99
  for (const { beforeLoad } of hooks) {
96
100
  try {
@@ -107,7 +111,8 @@ export async function onNavigate(path, options = {}) {
107
111
  if (options.search) path += options.search;
108
112
  if (options.hash) path += options.hash;
109
113
  const historyMethod = options.replace ? 'replaceState' : 'pushState';
110
- globalThis.history[historyMethod](options.state || {}, '', path);
114
+ const to = base.name ? join(base.name, path) : path;
115
+ globalThis.history[historyMethod](options.state || {}, '', to);
111
116
  }
112
117
 
113
118
  syncSearchParams();
@@ -148,7 +153,7 @@ function updatedLocation() {
148
153
  return {
149
154
  pathname: globalThis.location.pathname,
150
155
  search: globalThis.location.search,
151
- state: globalThis.history.state,
156
+ state: history.state,
152
157
  hash: globalThis.location.hash,
153
158
  };
154
159
  }
@@ -44,3 +44,18 @@ export function resolveRouteComponent(input) {
44
44
  export function isLazyImport(input) {
45
45
  return typeof input === 'function' && !!/\(\)\s?=>\s?import\(.*\)/.test(String(input));
46
46
  }
47
+
48
+ /** @param {...string} parts */
49
+ export function join(...parts) {
50
+ let result = '';
51
+ for (let part of parts) {
52
+ if (!part.startsWith('/')) {
53
+ result += '/';
54
+ }
55
+ if (part.endsWith('/')) {
56
+ part = part.slice(0, -1);
57
+ }
58
+ result += part;
59
+ }
60
+ return result;
61
+ }
package/src/index.d.ts CHANGED
@@ -23,8 +23,11 @@ export const isActiveLink: IsActiveLink;
23
23
  * ```
24
24
  */
25
25
  export function createRouter<T extends Routes>(r: T): RouterApi<T>;
26
- /** The component that will render the current route. */
27
- export const Router: Component;
26
+ /**
27
+ * The component that will render the current route. You can pass a `base` prop to set the base path
28
+ * that is prepended to every url.
29
+ */
30
+ export const Router: Component<{ base?: string }>;
28
31
  /**
29
32
  * The reactive search params of the URL. It is just a wrapper around `SvelteURLSearchParam` that
30
33
  * will update the url on change.