sv-router 0.0.8 → 0.1.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/README.md CHANGED
@@ -18,11 +18,14 @@ A feature-rich yet intuitive routing library for Svelte single-page apps.
18
18
  ## Features
19
19
 
20
20
  - 🔒 **Typesafe navigation**: Get autocomplete and type checking for your routes.
21
- - 🗂ïļ **File-based routing (optional)**: Enjoy the DX of a meta-framework-like approach.
21
+ - 🔄 **Flexibility**: Choose between code-based or file-based routing approaches.
22
22
  - ðŸŒŋ **Nested routes**: Create complex layouts with ease.
23
+ - 🔍 **Reactive search params**: For simpler state management in the URL.
24
+ - ðŸ›Ąïļ **Hooks**: For navigation guards, data loading, or analytics tracking.
23
25
  - ⚡ **Performance**: Optimized for speed with built-in code splitting and preloading.
24
- - ðŸ§Đ **Familiar API**: Follows established conventions from popular meta frameworks
25
- - 🚀 **Made for Svelte 5**: Benefit from faster performance and smaller bundle size.
26
+ - ðŸ§Đ **Familiar API**: Follows established conventions from popular meta frameworks.
27
+ - ðŸŠķ **Lightweight**: Minimal impact on your bundle size.
28
+ - 🚀 **Made for Svelte 5**: True Svelte 5 implementation with the latest features.
26
29
 
27
30
  ## Installation
28
31
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sv-router",
3
- "version": "0.0.8",
3
+ "version": "0.1.0",
4
4
  "description": "Modern Svelte Routing",
5
5
  "keywords": [
6
6
  "svelte",
@@ -113,6 +113,10 @@ export async function onNavigate(path, options = {}) {
113
113
  syncSearchParams();
114
114
  Object.assign(location, updatedLocation());
115
115
 
116
+ if (options.scrollToTop !== false) {
117
+ window.scrollTo({ top: 0, left: 0, behavior: options.scrollToTop });
118
+ }
119
+
116
120
  for (const { afterLoad } of hooks) {
117
121
  afterLoad?.();
118
122
  }
@@ -130,12 +134,13 @@ export function onGlobalClick(event) {
130
134
  if (url.origin !== currentOrigin) return;
131
135
 
132
136
  event.preventDefault();
133
- const { replace, state } = anchor.dataset;
137
+ const { replace, state, scrollToTop } = anchor.dataset;
134
138
  onNavigate(url.pathname, {
135
139
  replace: replace === '' || replace === 'true',
136
140
  search: url.search,
137
141
  state,
138
142
  hash: url.hash,
143
+ scrollToTop: scrollToTop === 'false' ? false : /** @type ScrollBehavior */ (scrollToTop),
139
144
  });
140
145
  }
141
146
 
@@ -9,11 +9,11 @@ import path from 'node:path';
9
9
  * }} GeneratedRoutes
10
10
  */
11
11
 
12
- const FILENAME_REGEX = /\(?([\w-]+)\)?(\.lazy)?\.svelte$/; // any.svelte, any.lazy.svelte, (any).svelte
13
- const PARAM_FILENAME_REGEX = /\(?\[(.*)\]\)?(\.lazy)?\.svelte$/; // [any].svelte, [any].lazy.svelte, ([any]).svelte
14
- const CATCH_ALL_FILENAME_REGEX = /\(?\[\.\.\.(.*)\]\)?(\.lazy)?\.svelte$/; // [...any].svelte, [...any].lazy.svelte, ([...any]).svelte
15
- const OUT_OF_LAYOUT_FILENAME_REGEX = /\(\[\.?\.?\.?(.*)\]\)(\.lazy)?\.svelte$/; // ([any]).svelte, ([...any]).lazy.svelte
16
- const HOOKS_FILENAME_REGEX = /(hooks)(\.svelte)?\.(js|ts)$/; // hooks.js, hooks.svelte.js, hooks.ts, hooks.svelte.ts
12
+ const FILENAME_REGEX = /(?<=[/.]|^)\(?([\w-]+)\)?(\.lazy)?\.svelte$/; // any.svelte, any.lazy.svelte, (any).svelte
13
+ const PARAM_FILENAME_REGEX = /(?<=[/.]|^)\(?\[([\w-]+)\]\)?(\.lazy)?\.svelte$/; // [any].svelte, [any].lazy.svelte, ([any]).svelte
14
+ const CATCH_ALL_FILENAME_REGEX = /(?<=[/.]|^)\(?\[\.\.\.([\w-]+)\]\)?(\.lazy)?\.svelte$/; // [...any].svelte, [...any].lazy.svelte, ([...any]).svelte
15
+ const OUT_OF_LAYOUT_FILENAME_REGEX = /(?<=[/.]|^)\(\[\.?\.?\.?([\w-]+)\]\)(\.lazy)?\.svelte$/; // ([any]).svelte, ([...any]).lazy.svelte
16
+ const HOOKS_FILENAME_REGEX = /(?<=[/.]|^)(hooks)(\.svelte)?\.(js|ts)$/; // hooks.js, hooks.svelte.js, hooks.ts, hooks.svelte.ts
17
17
 
18
18
  /**
19
19
  * @param {string} routesPath
@@ -9,10 +9,6 @@ export function writeRouterCode() {
9
9
  fs.mkdirSync(genConfig.genCodeDirPath);
10
10
  }
11
11
 
12
- // Write `.router/router.ts` file
13
- const routerCode = generateRouterCode(genConfig.routesPath);
14
- writeFileIfDifferent(genConfig.routerPath, routerCode);
15
-
16
12
  // Write `.router/tsconfig.json` file
17
13
  const tsConfig = {
18
14
  compilerOptions: {
@@ -35,7 +31,15 @@ export function writeRouterCode() {
35
31
  };
36
32
  writeFileIfDifferent(genConfig.tsconfigPath, JSON.stringify(tsConfig, undefined, 2));
37
33
 
38
- console.log('✅ïļ Routes generated');
34
+ // Write `.router/router.ts` file
35
+ const routerCode = generateRouterCode(genConfig.routesPath);
36
+ const written = writeFileIfDifferent(genConfig.routerPath, routerCode);
37
+
38
+ if (written) {
39
+ console.log('✅ïļ Routes generated');
40
+ } else {
41
+ console.log('✅ïļ Routes already up to date');
42
+ }
39
43
  } catch (error) {
40
44
  console.error(
41
45
  'Error during routes generation:',
@@ -51,5 +55,6 @@ export function writeRouterCode() {
51
55
  function writeFileIfDifferent(filePath, content) {
52
56
  if (!fs.existsSync(filePath) || fs.readFileSync(filePath, 'utf8') !== content) {
53
57
  fs.writeFileSync(filePath, content);
58
+ return true;
54
59
  }
55
60
  }
package/src/index.d.ts CHANGED
@@ -29,7 +29,7 @@ export const Router: Component;
29
29
  * The reactive search params of the URL. It is just a wrapper around `SvelteURLSearchParam` that
30
30
  * will update the url on change.
31
31
  */
32
- export const searchParams: URLSearchParams;
32
+ export const searchParams: SearchParams;
33
33
 
34
34
  // eslint-disable-next-line @typescript-eslint/no-empty-object-type
35
35
  type BaseProps = {};
@@ -162,9 +162,17 @@ export type NavigateOptions =
162
162
  search?: string;
163
163
  state?: string;
164
164
  hash?: string;
165
+ scrollToTop?: ScrollBehavior | false;
165
166
  }
166
167
  | undefined;
167
168
 
169
+ export type SearchParams = URLSearchParams & {
170
+ append: (name: string, value: string, options?: { replace?: boolean }) => void;
171
+ delete: (name: string, value?: string, options?: { replace?: boolean }) => void;
172
+ set: (name: string, value: string, options?: { replace?: boolean }) => void;
173
+ sort: (options?: { replace?: boolean }) => void;
174
+ };
175
+
168
176
  type NavigateArgs<T extends string> =
169
177
  | (PathParams<T> extends never
170
178
  ? [T] | [T, NavigateOptions]
@@ -2,15 +2,15 @@ import { SvelteURLSearchParams } from 'svelte/reactivity';
2
2
 
3
3
  let searchParams = new SvelteURLSearchParams(globalThis.location.search);
4
4
 
5
- /** @type {URLSearchParams} */
5
+ /** @type {import('./index.js').SearchParams} */
6
6
  const shell = {
7
- append(...args) {
8
- searchParams.append(...args);
9
- updateUrlSearchParams();
7
+ append(name, value, options) {
8
+ searchParams.append(name, value);
9
+ updateUrlSearchParams(options);
10
10
  },
11
- delete(...args) {
12
- searchParams.delete(...args);
13
- updateUrlSearchParams();
11
+ delete(name, value, options) {
12
+ searchParams.delete(name, value);
13
+ updateUrlSearchParams(options);
14
14
  },
15
15
  entries() {
16
16
  return searchParams.entries();
@@ -31,13 +31,13 @@ const shell = {
31
31
  keys() {
32
32
  return searchParams.keys();
33
33
  },
34
- set(...args) {
35
- searchParams.set(...args);
36
- updateUrlSearchParams();
34
+ set(name, value, options) {
35
+ searchParams.set(name, value);
36
+ updateUrlSearchParams(options);
37
37
  },
38
- sort() {
38
+ sort(options) {
39
39
  searchParams.sort();
40
- updateUrlSearchParams();
40
+ updateUrlSearchParams(options);
41
41
  },
42
42
  toString() {
43
43
  return searchParams.toString();
@@ -60,19 +60,17 @@ export function syncSearchParams() {
60
60
  if (searchParams.toString() === newSearchParams.toString()) {
61
61
  return;
62
62
  }
63
-
64
- for (const key of searchParams.keys()) {
65
- searchParams.delete(key);
66
- }
63
+ searchParams = new SvelteURLSearchParams();
67
64
  for (const [key, value] of newSearchParams.entries()) {
68
65
  searchParams.append(key, value);
69
66
  }
70
67
  }
71
68
 
72
- function updateUrlSearchParams() {
69
+ /** @param {{ replace?: boolean }} [options] */
70
+ function updateUrlSearchParams(options) {
73
71
  let url = globalThis.location.origin + globalThis.location.pathname;
74
72
  if (searchParams.size > 0) {
75
73
  url += '?' + searchParams.toString();
76
74
  }
77
- globalThis.history.pushState({}, '', url);
75
+ globalThis.history[options?.replace ? 'replaceState' : 'pushState']({}, '', url);
78
76
  }