@solidjs/router 0.10.0-beta.0 → 0.10.0-beta.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
@@ -372,14 +372,12 @@ This cache can be defined anywhere and then used inside your components with:
372
372
 
373
373
  ### `createAsync`
374
374
 
375
- 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 ready causes Suspense/Transitions to trigger.
375
+ 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.
376
376
 
377
377
  ```jsx
378
378
  const user = createAsync(() => getUser(params.id))
379
379
  ```
380
380
 
381
- `createAsync` is designed to only work with cached functions otherwise it will over fetch. If not using `cache`, continue using `createResource` instead.
382
-
383
381
  ### `action`
384
382
 
385
383
  Actions are data mutations that can trigger invalidations and further routing. A list of prebuilt response builders can be found below(TODO).
@@ -11,13 +11,8 @@ export const Router = (props) => {
11
11
  (isServer
12
12
  ? staticIntegration({ value: url || ((e = getRequestEvent()) && e.request.url) || "" })
13
13
  : pathIntegration());
14
- const routeDefs = children(() => props.root
15
- ? {
16
- component: props.root,
17
- children: props.children
18
- }
19
- : props.children);
20
- const branches = createMemo(() => createBranches(routeDefs(), props.base || ""));
14
+ const routeDefs = children(() => props.children);
15
+ const branches = createMemo(() => createBranches(props.root ? { component: props.root, children: routeDefs() } : routeDefs(), props.base || ""));
21
16
  const routerState = createRouterContext(integration, branches, base);
22
17
  return (<RouterContextObj.Provider value={routerState}>
23
18
  <Routes routerState={routerState} branches={branches()}/>
@@ -1,3 +1,3 @@
1
1
  import { type ReconcileOptions } from "solid-js/store";
2
2
  export declare function revalidate(key?: string | any[] | void): Promise<void>;
3
- export declare function cache<T extends (...args: any) => U | Response, U>(fn: T, name: string, options: ReconcileOptions): T;
3
+ export declare function cache<T extends (...args: any) => U | Response, U>(fn: T, name: string, options?: ReconcileOptions): T;
@@ -61,7 +61,13 @@ export function cache(fn, name, options) {
61
61
  }
62
62
  return cached[1];
63
63
  }
64
- let res = fn(...args);
64
+ let res = !isServer && sharedConfig.context && sharedConfig.load
65
+ ? sharedConfig.load(key) // hydrating
66
+ : fn(...args);
67
+ // serialize on server
68
+ if (isServer && sharedConfig.context && !sharedConfig.context.noHydrate) {
69
+ sharedConfig.context && sharedConfig.context.serialize(key, res);
70
+ }
65
71
  if (intent !== "preload") {
66
72
  res =
67
73
  "then" in res
@@ -99,6 +105,8 @@ export function cache(fn, name, options) {
99
105
  isServer ? handleRedirect(v) : setTimeout(() => handleRedirect(v), 0);
100
106
  return;
101
107
  }
108
+ if (isServer)
109
+ return v;
102
110
  setStore(key, reconcile(v, options));
103
111
  return store[key];
104
112
  }
@@ -1,5 +1,5 @@
1
1
  /**
2
2
  * This is mock of the eventual Solid 2.0 primitive. It is not fully featured.
3
3
  */
4
- import { Accessor } from "solid-js";
5
- export declare function createAsync<T>(fn: () => Promise<T>): Accessor<T>;
4
+ import { type Accessor } from "solid-js";
5
+ export declare function createAsync<T>(fn: () => Promise<T>): Accessor<T | undefined>;
@@ -1,8 +1,54 @@
1
1
  /**
2
2
  * This is mock of the eventual Solid 2.0 primitive. It is not fully featured.
3
3
  */
4
- import { createResource } from "solid-js";
4
+ import { createResource, sharedConfig } from "solid-js";
5
+ import { isServer } from "solid-js/web";
5
6
  export function createAsync(fn) {
6
- const [resource] = createResource(() => fn(), v => v);
7
+ const [resource] = createResource(() => subFetch(fn), v => v);
7
8
  return () => resource();
8
9
  }
10
+ // mock promise while hydrating to prevent fetching
11
+ class MockPromise {
12
+ static all() {
13
+ return new MockPromise();
14
+ }
15
+ static allSettled() {
16
+ return new MockPromise();
17
+ }
18
+ static any() {
19
+ return new MockPromise();
20
+ }
21
+ static race() {
22
+ return new MockPromise();
23
+ }
24
+ static reject() {
25
+ return new MockPromise();
26
+ }
27
+ static resolve() {
28
+ return new MockPromise();
29
+ }
30
+ catch() {
31
+ return new MockPromise();
32
+ }
33
+ then() {
34
+ return new MockPromise();
35
+ }
36
+ finally() {
37
+ return new MockPromise();
38
+ }
39
+ }
40
+ function subFetch(fn) {
41
+ if (isServer || !sharedConfig.context)
42
+ return fn();
43
+ const ogFetch = fetch;
44
+ const ogPromise = Promise;
45
+ try {
46
+ window.fetch = () => new MockPromise();
47
+ Promise = MockPromise;
48
+ return fn();
49
+ }
50
+ finally {
51
+ window.fetch = ogFetch;
52
+ Promise = ogPromise;
53
+ }
54
+ }
package/dist/index.d.ts CHANGED
@@ -4,4 +4,4 @@ export * from "./lifecycle";
4
4
  export { useHref, useIsRouting, useLocation, useMatch, useNavigate, useParams, useResolvedPath, useSearchParams, useBeforeLeave, } from "./routing";
5
5
  export { mergeSearchString as _mergeSearchString } from "./utils";
6
6
  export * from "./data";
7
- export type { Location, LocationChange, LocationChangeSignal, NavigateOptions, Navigator, OutputMatch, Params, RouteLoadFunc, RouteLoadFuncArgs, RouteDefinition, RouterIntegration, RouterOutput, RouterUtils, SetParams, BeforeLeaveEventArgs } from "./types";
7
+ export type { Location, LocationChange, LocationChangeSignal, NavigateOptions, Navigator, OutputMatch, Params, RouteSectionProps, RouteLoadFunc, RouteLoadFuncArgs, RouteDefinition, RouterIntegration, RouterOutput, RouterUtils, SetParams, BeforeLeaveEventArgs } from "./types";
package/dist/index.js CHANGED
@@ -501,7 +501,7 @@ function createLocation(path, state) {
501
501
  const pathname = createMemo(() => url().pathname);
502
502
  const search = createMemo(() => url().search, true);
503
503
  const hash = createMemo(() => url().hash);
504
- const key = createMemo(() => "");
504
+ const key = () => "";
505
505
  return {
506
506
  get pathname() {
507
507
  return pathname();
@@ -703,9 +703,9 @@ function createRouterContext(integration, getBranches, base = "") {
703
703
  state: state && JSON.parse(state)
704
704
  });
705
705
  }
706
- function doPreload(a, path) {
706
+ function doPreload(a, url) {
707
707
  const preload = a.getAttribute("preload") !== "false";
708
- const matches = getRouteMatches(getBranches(), path);
708
+ const matches = getRouteMatches(getBranches(), url.pathname);
709
709
  const prevIntent = intent;
710
710
  intent = "preload";
711
711
  for (let match in matches) {
@@ -716,7 +716,15 @@ function createRouterContext(integration, getBranches, base = "") {
716
716
  route.component && route.component.preload && route.component.preload();
717
717
  preload && route.load && route.load({
718
718
  params,
719
- location
719
+ location: {
720
+ pathname: url.pathname,
721
+ search: url.search,
722
+ hash: url.hash,
723
+ query: extractSearchParams(url),
724
+ state: null,
725
+ key: ""
726
+ },
727
+ intent
720
728
  });
721
729
  }
722
730
  intent = prevIntent;
@@ -725,7 +733,7 @@ function createRouterContext(integration, getBranches, base = "") {
725
733
  const res = handleAnchor(evt);
726
734
  if (!res) return;
727
735
  const [a, url] = res;
728
- if (!preloadTimeout[url.pathname]) doPreload(a, url.pathname);
736
+ if (!preloadTimeout[url.pathname]) doPreload(a, url);
729
737
  }
730
738
  function handleAnchorIn(evt) {
731
739
  const res = handleAnchor(evt);
@@ -733,9 +741,9 @@ function createRouterContext(integration, getBranches, base = "") {
733
741
  const [a, url] = res;
734
742
  if (preloadTimeout[url.pathname]) return;
735
743
  preloadTimeout[url.pathname] = setTimeout(() => {
736
- doPreload(a, url.pathname);
744
+ doPreload(a, url);
737
745
  delete preloadTimeout[url.pathname];
738
- }, 50);
746
+ }, 200);
739
747
  }
740
748
  function handleAnchorOut(evt) {
741
749
  const res = handleAnchor(evt);
@@ -827,7 +835,8 @@ function createRouteContext(router, parent, outlet, match, params) {
827
835
  component && component.preload && component.preload();
828
836
  load && load({
829
837
  params,
830
- location
838
+ location,
839
+ intent: intent || "navigate"
831
840
  });
832
841
  return route;
833
842
  }
@@ -843,11 +852,11 @@ const Router = props => {
843
852
  const integration = source || (isServer ? staticIntegration({
844
853
  value: url || (e = getRequestEvent()) && e.request.url || ""
845
854
  }) : pathIntegration());
846
- const routeDefs = children(() => props.root ? {
855
+ const routeDefs = children(() => props.children);
856
+ const branches = createMemo(() => createBranches(props.root ? {
847
857
  component: props.root,
848
- children: props.children
849
- } : props.children);
850
- const branches = createMemo(() => createBranches(routeDefs(), props.base || ""));
858
+ children: routeDefs()
859
+ } : routeDefs(), props.base || ""));
851
860
  const routerState = createRouterContext(integration, branches, base);
852
861
  return createComponent$1(RouterContextObj.Provider, {
853
862
  value: routerState,
@@ -998,10 +1007,54 @@ function Navigate(props) {
998
1007
  * This is mock of the eventual Solid 2.0 primitive. It is not fully featured.
999
1008
  */
1000
1009
  function createAsync(fn) {
1001
- const [resource] = createResource(() => fn(), v => v);
1010
+ const [resource] = createResource(() => subFetch(fn), v => v);
1002
1011
  return () => resource();
1003
1012
  }
1004
1013
 
1014
+ // mock promise while hydrating to prevent fetching
1015
+ class MockPromise {
1016
+ static all() {
1017
+ return new MockPromise();
1018
+ }
1019
+ static allSettled() {
1020
+ return new MockPromise();
1021
+ }
1022
+ static any() {
1023
+ return new MockPromise();
1024
+ }
1025
+ static race() {
1026
+ return new MockPromise();
1027
+ }
1028
+ static reject() {
1029
+ return new MockPromise();
1030
+ }
1031
+ static resolve() {
1032
+ return new MockPromise();
1033
+ }
1034
+ catch() {
1035
+ return new MockPromise();
1036
+ }
1037
+ then() {
1038
+ return new MockPromise();
1039
+ }
1040
+ finally() {
1041
+ return new MockPromise();
1042
+ }
1043
+ }
1044
+ function subFetch(fn) {
1045
+ if (isServer || !sharedConfig.context) return fn();
1046
+ const ogFetch = fetch;
1047
+ const ogPromise = Promise;
1048
+ try {
1049
+ window.fetch = () => new MockPromise();
1050
+ Promise = MockPromise;
1051
+ return fn();
1052
+ } finally {
1053
+ window.fetch = ogFetch;
1054
+ Promise = ogPromise;
1055
+ }
1056
+ }
1057
+
1005
1058
  const LocationHeader = "Location";
1006
1059
  const PRELOAD_TIMEOUT = 5000;
1007
1060
  let cacheMap = new Map();
@@ -1058,7 +1111,13 @@ function cache(fn, name, options) {
1058
1111
 
1059
1112
  return cached[1];
1060
1113
  }
1061
- let res = fn(...args);
1114
+ let res = !isServer && sharedConfig.context && sharedConfig.load ? sharedConfig.load(key) // hydrating
1115
+ : fn(...args);
1116
+
1117
+ // serialize on server
1118
+ if (isServer && sharedConfig.context && !sharedConfig.context.noHydrate) {
1119
+ sharedConfig.context && sharedConfig.context.serialize(key, res);
1120
+ }
1062
1121
  if (intent !== "preload") {
1063
1122
  res = "then" in res ? res.then(handleResponse) : handleResponse(res);
1064
1123
  }
@@ -1089,6 +1148,7 @@ function cache(fn, name, options) {
1089
1148
  if (navigate) isServer ? handleRedirect(v) : setTimeout(() => handleRedirect(v), 0);
1090
1149
  return;
1091
1150
  }
1151
+ if (isServer) return v;
1092
1152
  setStore(key, reconcile(v, options));
1093
1153
  return store[key];
1094
1154
  }
package/dist/routing.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import type { JSX, Accessor } from "solid-js";
2
- import type { BeforeLeaveEventArgs, Branch, Location, LocationChangeSignal, MatchFilters, NavigateOptions, Navigator, Params, Route, RouteContext, RouteDefinition, RouteMatch, RouterContext, RouterIntegration, SetParams } from "./types";
1
+ import { JSX, Accessor } from "solid-js";
2
+ import type { BeforeLeaveEventArgs, Branch, Intent, Location, LocationChangeSignal, MatchFilters, NavigateOptions, Navigator, Params, Route, RouteContext, RouteDefinition, RouteMatch, RouterContext, RouterIntegration, SetParams } from "./types";
3
3
  export declare const RouterContextObj: import("solid-js").Context<RouterContext | undefined>;
4
4
  export declare const RouteContextObj: import("solid-js").Context<RouteContext | undefined>;
5
5
  export declare const useRouter: () => RouterContext;
@@ -19,6 +19,6 @@ export declare function createBranches(routeDef: RouteDefinition | RouteDefiniti
19
19
  export declare function getRouteMatches(branches: Branch[], location: string): RouteMatch[];
20
20
  export declare function createLocation(path: Accessor<string>, state: Accessor<any>): Location;
21
21
  export declare function registerAction(url: string, fn: Function): void;
22
- export declare function getIntent(): "native" | "navigate" | "preload" | undefined;
22
+ export declare function getIntent(): Intent | undefined;
23
23
  export declare function createRouterContext(integration?: RouterIntegration | LocationChangeSignal, getBranches?: () => Branch[], base?: string): RouterContext;
24
24
  export declare function createRouteContext(router: RouterContext, parent: RouteContext, outlet: () => JSX.Element, match: () => RouteMatch, params: Params): RouteContext;
package/dist/routing.js CHANGED
@@ -153,7 +153,7 @@ export function createLocation(path, state) {
153
153
  const pathname = createMemo(() => url().pathname);
154
154
  const search = createMemo(() => url().search, true);
155
155
  const hash = createMemo(() => url().hash);
156
- const key = createMemo(() => "");
156
+ const key = () => "";
157
157
  return {
158
158
  get pathname() {
159
159
  return pathname();
@@ -352,9 +352,9 @@ export function createRouterContext(integration, getBranches, base = "") {
352
352
  state: state && JSON.parse(state)
353
353
  });
354
354
  }
355
- function doPreload(a, path) {
355
+ function doPreload(a, url) {
356
356
  const preload = a.getAttribute("preload") !== "false";
357
- const matches = getRouteMatches(getBranches(), path);
357
+ const matches = getRouteMatches(getBranches(), url.pathname);
358
358
  const prevIntent = intent;
359
359
  intent = "preload";
360
360
  for (let match in matches) {
@@ -362,7 +362,20 @@ export function createRouterContext(integration, getBranches, base = "") {
362
362
  route.component &&
363
363
  route.component.preload &&
364
364
  route.component.preload();
365
- preload && route.load && route.load({ params, location });
365
+ preload &&
366
+ route.load &&
367
+ route.load({
368
+ params,
369
+ location: {
370
+ pathname: url.pathname,
371
+ search: url.search,
372
+ hash: url.hash,
373
+ query: extractSearchParams(url),
374
+ state: null,
375
+ key: ""
376
+ },
377
+ intent
378
+ });
366
379
  }
367
380
  intent = prevIntent;
368
381
  }
@@ -372,7 +385,7 @@ export function createRouterContext(integration, getBranches, base = "") {
372
385
  return;
373
386
  const [a, url] = res;
374
387
  if (!preloadTimeout[url.pathname])
375
- doPreload(a, url.pathname);
388
+ doPreload(a, url);
376
389
  }
377
390
  function handleAnchorIn(evt) {
378
391
  const res = handleAnchor(evt);
@@ -382,9 +395,9 @@ export function createRouterContext(integration, getBranches, base = "") {
382
395
  if (preloadTimeout[url.pathname])
383
396
  return;
384
397
  preloadTimeout[url.pathname] = setTimeout(() => {
385
- doPreload(a, url.pathname);
398
+ doPreload(a, url);
386
399
  delete preloadTimeout[url.pathname];
387
- }, 50);
400
+ }, 200);
388
401
  }
389
402
  function handleAnchorOut(evt) {
390
403
  const res = handleAnchor(evt);
@@ -397,14 +410,13 @@ export function createRouterContext(integration, getBranches, base = "") {
397
410
  }
398
411
  }
399
412
  function handleFormSubmit(evt) {
400
- let actionRef = evt.submitter && evt.submitter.getAttribute("formaction") || evt.target.action;
413
+ let actionRef = (evt.submitter && evt.submitter.getAttribute("formaction")) || evt.target.action;
401
414
  if (actionRef && actionRef.startsWith("action:")) {
402
415
  const data = new FormData(evt.target);
403
416
  actions.get(actionRef.slice(7))(data);
404
417
  evt.preventDefault();
405
418
  }
406
419
  }
407
- ;
408
420
  // ensure delegated event run first
409
421
  delegateEvents(["click", "submit"]);
410
422
  document.addEventListener("click", handleAnchorClick);
@@ -429,11 +441,13 @@ export function createRouterContext(integration, getBranches, base = "") {
429
441
  return [];
430
442
  }
431
443
  const input = new Map(param.entries);
432
- return [{
444
+ return [
445
+ {
433
446
  url: param.url,
434
447
  result: param.error ? new Error(param.result.message) : param.result,
435
448
  input: input
436
- }];
449
+ }
450
+ ];
437
451
  }
438
452
  submissions = initFromFlash(location.query);
439
453
  }
@@ -473,6 +487,6 @@ export function createRouteContext(router, parent, outlet, match, params) {
473
487
  component &&
474
488
  component.preload &&
475
489
  component.preload();
476
- load && load({ params, location });
490
+ load && load({ params, location, intent: intent || "navigate" });
477
491
  return route;
478
492
  }
package/dist/types.d.ts CHANGED
@@ -39,16 +39,20 @@ export interface RouterIntegration {
39
39
  signal: LocationChangeSignal;
40
40
  utils?: Partial<RouterUtils>;
41
41
  }
42
+ export type Intent = "native" | "navigate" | "preload";
42
43
  export interface RouteLoadFuncArgs {
43
44
  params: Params;
44
45
  location: Location;
46
+ intent: Intent;
45
47
  }
46
48
  export type RouteLoadFunc = (args: RouteLoadFuncArgs) => void;
47
- export interface RouteSectionProps extends RouteLoadFuncArgs {
49
+ export interface RouteSectionProps {
50
+ params: Params;
51
+ location: Location;
48
52
  children?: JSX.Element;
49
53
  }
50
54
  export type RouteDefinition<S extends string | string[] = any> = {
51
- path: S;
55
+ path?: S;
52
56
  matchFilters?: MatchFilters<S>;
53
57
  load?: RouteLoadFunc;
54
58
  children?: RouteDefinition | RouteDefinition[];
package/package.json CHANGED
@@ -6,7 +6,7 @@
6
6
  "Ryan Turnquist"
7
7
  ],
8
8
  "license": "MIT",
9
- "version": "0.10.0-beta.0",
9
+ "version": "0.10.0-beta.1",
10
10
  "homepage": "https://github.com/solidjs/solid-router#readme",
11
11
  "repository": {
12
12
  "type": "git",