@solidjs/router 0.11.5 → 0.12.1

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
@@ -29,7 +29,6 @@ It supports all of Solid's SSR methods and has Solid's transitions baked in, so
29
29
  - [useLocation](#uselocation)
30
30
  - [useSearchParams](#usesearchparams)
31
31
  - [useIsRouting](#useisrouting)
32
- - [useRouteData](#useroutedata)
33
32
  - [useMatch](#usematch)
34
33
  - [useBeforeLeave](#usebeforeleave)
35
34
  - [SPAs in Deployed Environments](#spas-in-deployed-environments)
@@ -490,7 +489,7 @@ You can revalidate the cache using the `revalidate` method or you can set `reval
490
489
  This is light wrapper over `createResource` that aims to serve as stand-in for a future primitive we intend to bring to Solid core in 2.0. It is a simpler async primitive where the function tracks like `createMemo` and it expects a promise back that it turns into a Signal. Reading it before it is ready causes Suspense/Transitions to trigger.
491
490
 
492
491
  ```jsx
493
- const user = createAsync(() => getUser(params.id))
492
+ const user = createAsync((currentValue) => getUser(params.id))
494
493
  ```
495
494
 
496
495
  Using `cache` in `createResource` directly won't work properly as the fetcher is not reactive and it won't invalidate properly.
@@ -674,7 +673,7 @@ render(() => <Router>{route}</Router>, document.getElementById("app"));
674
673
 
675
674
  ### Hash Mode Router
676
675
 
677
- By default, Solid Router uses `location.pathname` as route path. You can simply switch to hash mode through the `source` property on `<Router>` component.
676
+ By default, Solid Router uses `location.pathname` as route path. You can simply switch to hash mode through using `<HashRouter>`.
678
677
 
679
678
  ```jsx
680
679
  import { HashRouter } from "@solidjs/router";
@@ -1,12 +1,11 @@
1
- import { type ReconcileOptions } from "solid-js/store";
2
1
  import { CacheEntry } from "../types.js";
3
2
  export declare function revalidate(key?: string | string[] | void, force?: boolean): Promise<void>;
4
3
  export declare function cacheKeyOp(key: string | string[] | void, fn: (cacheEntry: CacheEntry) => void): void;
5
- export type CachedFunction<T extends (...args: any) => U | Response, U> = T & {
6
- keyFor: (...args: Parameters<T>) => string;
4
+ export type CachedFunction<T extends (...args: any) => any> = T extends (...args: infer A) => infer R ? ([] extends A ? (...args: never[]) => R : T) & {
5
+ keyFor: (...args: A) => string;
7
6
  key: string;
8
- };
9
- export declare function cache<T extends (...args: any) => U | Response, U>(fn: T, name: string, options?: ReconcileOptions): CachedFunction<T, U>;
7
+ } : never;
8
+ export declare function cache<T extends (...args: any) => any>(fn: T, name: string): CachedFunction<T>;
10
9
  export declare namespace cache {
11
10
  var set: (key: string, value: any) => void;
12
11
  var clear: () => void;
@@ -1,5 +1,4 @@
1
1
  import { createSignal, getListener, getOwner, onCleanup, sharedConfig, startTransition } from "solid-js";
2
- import { createStore, reconcile } from "solid-js/store";
3
2
  import { getRequestEvent, isServer } from "solid-js/web";
4
3
  import { useNavigate, getIntent } from "../routing.js";
5
4
  const LocationHeader = "Location";
@@ -41,8 +40,7 @@ export function cacheKeyOp(key, fn) {
41
40
  fn(cacheMap.get(k));
42
41
  }
43
42
  }
44
- export function cache(fn, name, options) {
45
- const [store, setStore] = createStore({});
43
+ export function cache(fn, name) {
46
44
  // prioritize GET for server functions
47
45
  if (fn.GET)
48
46
  fn = fn.GET;
@@ -99,15 +97,6 @@ export function cache(fn, name, options) {
99
97
  let res = !isServer && sharedConfig.context && sharedConfig.has(key)
100
98
  ? sharedConfig.load(key) // hydrating
101
99
  : fn(...args);
102
- // serialize on server
103
- if (isServer &&
104
- sharedConfig.context &&
105
- sharedConfig.context.async &&
106
- !sharedConfig.context.noHydrate) {
107
- const e = getRequestEvent();
108
- e && e.router.dataOnly && (e.router.data[key] = res);
109
- (!e || !e.serverOnly) && sharedConfig.context.serialize(key, res);
110
- }
111
100
  if (cached) {
112
101
  cached[0] = now;
113
102
  cached[1] = res;
@@ -122,12 +111,25 @@ export function cache(fn, name, options) {
122
111
  cached[3].count++;
123
112
  cached[3][0](); // track
124
113
  }
114
+ if (isServer) {
115
+ const e = getRequestEvent();
116
+ e && e.router.dataOnly && (e.router.data[key] = res);
117
+ return res;
118
+ }
125
119
  if (intent !== "preload") {
126
120
  res =
127
121
  "then" in res
128
122
  ? res.then(handleResponse(false), handleResponse(true))
129
123
  : handleResponse(false)(res);
130
124
  }
125
+ // serialize on server
126
+ if (isServer &&
127
+ sharedConfig.context &&
128
+ sharedConfig.context.async &&
129
+ !sharedConfig.context.noHydrate) {
130
+ const e = getRequestEvent();
131
+ (!e || !e.serverOnly) && sharedConfig.context.serialize(key, res);
132
+ }
131
133
  return res;
132
134
  function handleResponse(error) {
133
135
  return async (v) => {
@@ -153,10 +155,7 @@ export function cache(fn, name, options) {
153
155
  }
154
156
  if (error)
155
157
  throw v;
156
- if (isServer)
157
- return v;
158
- setStore(key, reconcile(v, options));
159
- return store[key];
158
+ return v;
160
159
  };
161
160
  }
162
161
  });
@@ -2,13 +2,26 @@
2
2
  * This is mock of the eventual Solid 2.0 primitive. It is not fully featured.
3
3
  */
4
4
  import { type Accessor } from "solid-js";
5
- export declare function createAsync<T>(fn: () => Promise<T>, options: {
5
+ import { type ReconcileOptions } from "solid-js/store";
6
+ export declare function createAsync<T>(fn: (prev: T) => Promise<T>, options: {
6
7
  name?: string;
7
8
  initialValue: T;
8
9
  deferStream?: boolean;
9
10
  }): Accessor<T>;
10
- export declare function createAsync<T>(fn: () => Promise<T>, options?: {
11
+ export declare function createAsync<T>(fn: (prev: T | undefined) => Promise<T>, options?: {
11
12
  name?: string;
12
13
  initialValue?: T;
13
14
  deferStream?: boolean;
14
15
  }): Accessor<T | undefined>;
16
+ export declare function createAsyncStore<T>(fn: (prev: T) => Promise<T>, options: {
17
+ name?: string;
18
+ initialValue: T;
19
+ deferStream?: boolean;
20
+ reconcile?: ReconcileOptions;
21
+ }): Accessor<T>;
22
+ export declare function createAsyncStore<T>(fn: (prev: T | undefined) => Promise<T>, options?: {
23
+ name?: string;
24
+ initialValue?: T;
25
+ deferStream?: boolean;
26
+ reconcile?: ReconcileOptions;
27
+ }): Accessor<T | undefined>;
@@ -1,12 +1,37 @@
1
1
  /**
2
2
  * This is mock of the eventual Solid 2.0 primitive. It is not fully featured.
3
3
  */
4
- import { createResource, sharedConfig } from "solid-js";
4
+ import { createResource, sharedConfig, untrack } from "solid-js";
5
+ import { createStore, reconcile, unwrap } from "solid-js/store";
5
6
  import { isServer } from "solid-js/web";
6
7
  export function createAsync(fn, options) {
7
- const [resource] = createResource(() => subFetch(fn), v => v, options);
8
+ let resource;
9
+ let prev = () => !resource || resource.state === "unresolved" ? undefined : resource.latest;
10
+ [resource] = createResource(() => subFetch(fn, untrack(prev)), v => v, options);
8
11
  return () => resource();
9
12
  }
13
+ export function createAsyncStore(fn, options = {}) {
14
+ let resource;
15
+ let prev = () => !resource || resource.state === "unresolved" ? undefined : unwrap(resource.latest);
16
+ [resource] = createResource(() => subFetch(fn, untrack(prev)), v => v, {
17
+ ...options,
18
+ storage: (init) => createDeepSignal(init, options.reconcile)
19
+ });
20
+ return () => resource();
21
+ }
22
+ function createDeepSignal(value, options) {
23
+ const [store, setStore] = createStore({
24
+ value
25
+ });
26
+ return [
27
+ () => store.value,
28
+ (v) => {
29
+ typeof v === "function" && (v = v());
30
+ setStore("value", reconcile(v, options));
31
+ return store.value;
32
+ }
33
+ ];
34
+ }
10
35
  // mock promise while hydrating to prevent fetching
11
36
  class MockPromise {
12
37
  static all() {
@@ -37,15 +62,15 @@ class MockPromise {
37
62
  return new MockPromise();
38
63
  }
39
64
  }
40
- function subFetch(fn) {
65
+ function subFetch(fn, prev) {
41
66
  if (isServer || !sharedConfig.context)
42
- return fn();
67
+ return fn(prev);
43
68
  const ogFetch = fetch;
44
69
  const ogPromise = Promise;
45
70
  try {
46
71
  window.fetch = () => new MockPromise();
47
72
  Promise = MockPromise;
48
- return fn();
73
+ return fn(prev);
49
74
  }
50
75
  finally {
51
76
  window.fetch = ogFetch;
@@ -1,4 +1,4 @@
1
- export { createAsync } from "./createAsync.js";
1
+ export { createAsync, createAsyncStore } from "./createAsync.js";
2
2
  export { action, useSubmission, useSubmissions, useAction, type Action } from "./action.js";
3
3
  export { cache, revalidate, type CachedFunction } from "./cache.js";
4
4
  export { redirect, reload, json } from "./response.js";
@@ -1,4 +1,4 @@
1
- export { createAsync } from "./createAsync.js";
1
+ export { createAsync, createAsyncStore } from "./createAsync.js";
2
2
  export { action, useSubmission, useSubmissions, useAction } from "./action.js";
3
3
  export { cache, revalidate } from "./cache.js";
4
4
  export { redirect, reload, json } from "./response.js";
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { isServer, getRequestEvent, createComponent as createComponent$1, delegateEvents, spread, mergeProps as mergeProps$1, template } from 'solid-js/web';
2
2
  import { getOwner, runWithOwner, createMemo, createContext, onCleanup, useContext, untrack, createSignal, createRenderEffect, on, startTransition, resetErrorBoundaries, createComponent, children, mergeProps, createRoot, Show, getListener, sharedConfig, $TRACK, splitProps, createResource } from 'solid-js';
3
- import { createStore, reconcile } from 'solid-js/store';
3
+ import { createStore, reconcile, unwrap } from 'solid-js/store';
4
4
 
5
5
  function createBeforeLeave() {
6
6
  let listeners = new Set();
@@ -467,6 +467,7 @@ function createRouterContext(integration, getBranches, options = {}) {
467
467
  }
468
468
  });
469
469
  });
470
+ const owner = getOwner();
470
471
  return {
471
472
  base: baseRoute,
472
473
  location,
@@ -577,7 +578,10 @@ function createRouterContext(integration, getBranches, options = {}) {
577
578
  params
578
579
  } = matches[match];
579
580
  route.component && route.component.preload && route.component.preload();
580
- preloadData && route.load && route.load({
581
+ const {
582
+ load
583
+ } = route;
584
+ preloadData && load && runWithOwner(owner, () => load({
581
585
  params,
582
586
  location: {
583
587
  pathname: url.pathname,
@@ -588,7 +592,7 @@ function createRouterContext(integration, getBranches, options = {}) {
588
592
  key: ""
589
593
  },
590
594
  intent: "preload"
591
- });
595
+ }));
592
596
  }
593
597
  intent = prevIntent;
594
598
  }
@@ -879,8 +883,7 @@ function cacheKeyOp(key, fn) {
879
883
  if (key === undefined || matchKey(k, key)) fn(cacheMap.get(k));
880
884
  }
881
885
  }
882
- function cache(fn, name, options) {
883
- const [store, setStore] = createStore({});
886
+ function cache(fn, name) {
884
887
  // prioritize GET for server functions
885
888
  if (fn.GET) fn = fn.GET;
886
889
  const cachedFn = (...args) => {
@@ -927,13 +930,6 @@ function cache(fn, name, options) {
927
930
  }
928
931
  let res = !isServer && sharedConfig.context && sharedConfig.has(key) ? sharedConfig.load(key) // hydrating
929
932
  : fn(...args);
930
-
931
- // serialize on server
932
- if (isServer && sharedConfig.context && sharedConfig.context.async && !sharedConfig.context.noHydrate) {
933
- const e = getRequestEvent();
934
- e && e.router.dataOnly && (e.router.data[key] = res);
935
- (!e || !e.serverOnly) && sharedConfig.context.serialize(key, res);
936
- }
937
933
  if (cached) {
938
934
  cached[0] = now;
939
935
  cached[1] = res;
@@ -947,9 +943,19 @@ function cache(fn, name, options) {
947
943
  cached[3].count++;
948
944
  cached[3][0](); // track
949
945
  }
946
+ if (isServer) {
947
+ const e = getRequestEvent();
948
+ e && e.router.dataOnly && (e.router.data[key] = res);
949
+ return res;
950
+ }
950
951
  if (intent !== "preload") {
951
952
  res = "then" in res ? res.then(handleResponse(false), handleResponse(true)) : handleResponse(false)(res);
952
953
  }
954
+ // serialize on server
955
+ if (isServer && sharedConfig.context && sharedConfig.context.async && !sharedConfig.context.noHydrate) {
956
+ const e = getRequestEvent();
957
+ (!e || !e.serverOnly) && sharedConfig.context.serialize(key, res);
958
+ }
953
959
  return res;
954
960
  function handleResponse(error) {
955
961
  return async v => {
@@ -972,9 +978,7 @@ function cache(fn, name, options) {
972
978
  if (v.customBody) v = await v.customBody();
973
979
  }
974
980
  if (error) throw v;
975
- if (isServer) return v;
976
- setStore(key, reconcile(v, options));
977
- return store[key];
981
+ return v;
978
982
  };
979
983
  }
980
984
  };
@@ -1447,9 +1451,30 @@ function Navigate(props) {
1447
1451
  * This is mock of the eventual Solid 2.0 primitive. It is not fully featured.
1448
1452
  */
1449
1453
  function createAsync(fn, options) {
1450
- const [resource] = createResource(() => subFetch(fn), v => v, options);
1454
+ let resource;
1455
+ let prev = () => !resource || resource.state === "unresolved" ? undefined : resource.latest;
1456
+ [resource] = createResource(() => subFetch(fn, untrack(prev)), v => v, options);
1451
1457
  return () => resource();
1452
1458
  }
1459
+ function createAsyncStore(fn, options = {}) {
1460
+ let resource;
1461
+ let prev = () => !resource || resource.state === "unresolved" ? undefined : unwrap(resource.latest);
1462
+ [resource] = createResource(() => subFetch(fn, untrack(prev)), v => v, {
1463
+ ...options,
1464
+ storage: init => createDeepSignal(init, options.reconcile)
1465
+ });
1466
+ return () => resource();
1467
+ }
1468
+ function createDeepSignal(value, options) {
1469
+ const [store, setStore] = createStore({
1470
+ value
1471
+ });
1472
+ return [() => store.value, v => {
1473
+ typeof v === "function" && (v = v());
1474
+ setStore("value", reconcile(v, options));
1475
+ return store.value;
1476
+ }];
1477
+ }
1453
1478
 
1454
1479
  // mock promise while hydrating to prevent fetching
1455
1480
  class MockPromise {
@@ -1481,14 +1506,14 @@ class MockPromise {
1481
1506
  return new MockPromise();
1482
1507
  }
1483
1508
  }
1484
- function subFetch(fn) {
1485
- if (isServer || !sharedConfig.context) return fn();
1509
+ function subFetch(fn, prev) {
1510
+ if (isServer || !sharedConfig.context) return fn(prev);
1486
1511
  const ogFetch = fetch;
1487
1512
  const ogPromise = Promise;
1488
1513
  try {
1489
1514
  window.fetch = () => new MockPromise();
1490
1515
  Promise = MockPromise;
1491
- return fn();
1516
+ return fn(prev);
1492
1517
  } finally {
1493
1518
  window.fetch = ogFetch;
1494
1519
  Promise = ogPromise;
@@ -1548,4 +1573,4 @@ function json(data, init = {}) {
1548
1573
  return response;
1549
1574
  }
1550
1575
 
1551
- export { A, HashRouter, MemoryRouter, Navigate, Route, Router, StaticRouter, mergeSearchString as _mergeSearchString, action, cache, createAsync, createBeforeLeave, createMemoryHistory, createRouter, json, keepDepth, notifyIfNotBlocked, redirect, reload, revalidate, saveCurrentDepth, useAction, useBeforeLeave, useHref, useIsRouting, useLocation, useMatch, useNavigate, useParams, useResolvedPath, useSearchParams, useSubmission, useSubmissions };
1576
+ export { A, HashRouter, MemoryRouter, Navigate, Route, Router, StaticRouter, mergeSearchString as _mergeSearchString, action, cache, createAsync, createAsyncStore, createBeforeLeave, createMemoryHistory, createRouter, json, keepDepth, notifyIfNotBlocked, redirect, reload, revalidate, saveCurrentDepth, useAction, useBeforeLeave, useHref, useIsRouting, useLocation, useMatch, useNavigate, useParams, useResolvedPath, useSearchParams, useSubmission, useSubmissions };
package/dist/routing.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { getOwner, runWithOwner } from "solid-js";
1
2
  import { createComponent, createContext, createMemo, createRenderEffect, createSignal, on, onCleanup, untrack, useContext, startTransition, resetErrorBoundaries } from "solid-js";
2
3
  import { isServer, getRequestEvent } from "solid-js/web";
3
4
  import { createBeforeLeave } from "./lifecycle.js";
@@ -236,6 +237,7 @@ export function createRouterContext(integration, getBranches, options = {}) {
236
237
  }
237
238
  });
238
239
  });
240
+ const owner = getOwner();
239
241
  return {
240
242
  base: baseRoute,
241
243
  location,
@@ -331,9 +333,10 @@ export function createRouterContext(integration, getBranches, options = {}) {
331
333
  route.component &&
332
334
  route.component.preload &&
333
335
  route.component.preload();
336
+ const { load } = route;
334
337
  preloadData &&
335
- route.load &&
336
- route.load({
338
+ load &&
339
+ runWithOwner(owner, () => load({
337
340
  params,
338
341
  location: {
339
342
  pathname: url.pathname,
@@ -344,7 +347,7 @@ export function createRouterContext(integration, getBranches, options = {}) {
344
347
  key: ""
345
348
  },
346
349
  intent: "preload"
347
- });
350
+ }));
348
351
  }
349
352
  intent = prevIntent;
350
353
  }
package/package.json CHANGED
@@ -6,7 +6,7 @@
6
6
  "Ryan Turnquist"
7
7
  ],
8
8
  "license": "MIT",
9
- "version": "0.11.5",
9
+ "version": "0.12.1",
10
10
  "homepage": "https://github.com/solidjs/solid-router#readme",
11
11
  "repository": {
12
12
  "type": "git",