@xmachines/play-solid-router 2.0.0-alpha.1 → 2.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.
@@ -1,14 +1,16 @@
1
1
  /**
2
- * SolidJS Router bridge implementing RouterBridge protocol via RouterBridgeBase
2
+ * The SolidJS Router bridge. It implements the RouterBridge protocol through RouterBridgeBase
3
3
  *
4
- * Extends RouterBridgeBase to handle all common lifecycle and sync logic.
5
- * Uses Solid's native reactive primitives (createEffect) for router→actor direction.
4
+ * The class extends RouterBridgeBase, and the base class does all the common
5
+ * lifecycle work and synchronization work. This class uses the native reactive
6
+ * primitives of Solid (createEffect) for the router-to-actor direction.
6
7
  *
7
- * **IMPORTANT:** `connect()` MUST be called inside a Solid reactive owner (component
8
- * or createRoot). The `createEffect()` in `watchRouterChanges()` runs inside
9
- * `createRoot()`, which deliberately isolates it from any parent owner — automatic
10
- * cleanup on component unmount does NOT happen. You MUST call `disconnect()` (or
11
- * `dispose()`) explicitly, typically in `onCleanup()`.
8
+ * **IMPORTANT:** call `connect()` inside a Solid reactive owner: a component, or
9
+ * createRoot. The `createEffect()` call in `watchRouterChanges()` runs inside
10
+ * `createRoot()`, which keeps the effect separate from every parent owner on
11
+ * purpose. Therefore the effect does NOT clean up by itself when the component
12
+ * unmounts. You MUST call `disconnect()` or `dispose()` yourself, usually in
13
+ * `onCleanup()`.
12
14
  *
13
15
  * @example
14
16
  * ```tsx
@@ -24,7 +26,7 @@
24
26
  * const routeMap = new RouteMap([...]);
25
27
  * const bridge = new SolidRouterBridge(navigate, location, params, actor, routeMap);
26
28
  *
27
- * // connect() MUST be called inside a Solid reactive owner
29
+ * // Call connect() inside a Solid reactive owner
28
30
  * bridge.connect();
29
31
  * onCleanup(() => bridge.disconnect());
30
32
  *
@@ -38,97 +40,110 @@ import type { LocationLike } from "@xmachines/play-router";
38
40
  import type { RoutableActor } from "@xmachines/play-router";
39
41
  import type { RouteMap } from "@xmachines/play-router";
40
42
  /**
41
- * SolidJS Router integration bridge extending RouterBridgeBase
43
+ * The SolidJS Router integration bridge. It extends RouterBridgeBase
42
44
  *
43
- * Implements RouterBridge protocol for SolidJS Router using Solid's reactive
44
- * primitives. The actorrouter direction uses TC39 Signal watcher (from base class).
45
- * The routeractor direction uses Solid's createEffect for native reactivity.
45
+ * The class implements the RouterBridge protocol for SolidJS Router with the
46
+ * reactive primitives of Solid. The actor-to-router direction uses the TC39 Signal
47
+ * watcher of the base class. The router-to-actor direction uses the Solid
48
+ * createEffect function for a native reactivity.
46
49
  *
47
- * Path parameters are extracted from Solid's `useParams()` reactive proxy rather than
48
- * re-parsing the URL with URLPattern. This means parameterized routes work without the
49
- * URLPattern polyfill Solid's router has already extracted the values.
50
+ * The bridge reads each path parameter from the reactive `useParams()` proxy of
51
+ * Solid. It does not parse the URL again with URLPattern. Therefore a
52
+ * parameterized route works without the URLPattern polyfill, because the router of
53
+ * Solid holds the values already.
50
54
  */
51
55
  export declare class SolidRouterBridge extends RouterBridgeBase {
52
56
  private readonly solidNavigate;
53
57
  private readonly location;
54
58
  private disposeRouterWatcher;
55
59
  /**
56
- * Live reactive params object from Solid's `useParams()`.
57
- * Read inside the createEffect callback so it always reflects the current route.
60
+ * The live reactive params object from the `useParams()` function of Solid.
61
+ * The bridge reads it inside the createEffect callback, so the value always shows
62
+ * the current route.
58
63
  */
59
64
  private readonly solidParams;
60
65
  /**
61
- * Create a SolidJS Router bridge
66
+ * Creates a SolidJS Router bridge
62
67
  *
63
- * **CRITICAL:** `connect()` must be called inside a Solid component where hooks are available.
68
+ * **CRITICAL:** call `connect()` inside a Solid component, where the hooks are available.
64
69
  *
65
- * @param solidNavigate - Result of useNavigate() hook
66
- * @param location - Result of useLocation() hook
67
- * @param params - Result of useParams() hook used directly for path parameter extraction,
68
- * avoiding the URLPattern polyfill requirement for parameterized routes
69
- * @param actor - XMachines actor instance
70
- * @param routeMap - Bidirectional state ID ↔ path mapping
70
+ * @param solidNavigate - The result of the useNavigate() hook
71
+ * @param location - The result of the useLocation() hook
72
+ * @param params - The result of the useParams() hook. The bridge reads each path
73
+ * parameter directly from it. A parameterized route therefore does not need the
74
+ * URLPattern polyfill
75
+ * @param actor - The XMachines actor instance
76
+ * @param routeMap - The bidirectional map between the state IDs and the paths
71
77
  */
72
78
  constructor(solidNavigate: Navigator, location: LocationLike, params: Params, actor: RoutableActor, routeMap: RouteMap);
73
79
  /**
74
- * Extract path parameters using Solid's pre-parsed `useParams()` values.
80
+ * Reads each path parameter from the values that `useParams()` of Solid parsed before.
75
81
  *
76
- * Solid's router has already extracted all named parameters for the matched route
77
- * segment. Reading `this.solidParams` inside the createEffect callback that drives
78
- * `syncActorFromRouter` is safe the reactive proxy always reflects the current
79
- * route at the time the effect runs.
82
+ * The router of Solid holds every named parameter of the matched route segment
83
+ * already. A read of `this.solidParams` inside the createEffect callback that
84
+ * drives `syncActorFromRouter` is safe, because the reactive proxy always shows the
85
+ * route of the moment when the effect runs.
80
86
  *
81
- * Falls back to URLPattern-based extraction (base class) only when Solid provided
82
- * no params for this route (i.e. the route has no `:param` segments).
87
+ * The method returns to the URLPattern method of the base class only when Solid
88
+ * gives no param for this route, which means that the route has no `:param`
89
+ * segment.
83
90
  *
84
- * @param pathname - The actual URL path (unused params already extracted by Solid)
85
- * @param stateId - The matched state ID (unused — params already extracted by Solid)
86
- * @returns Normalized path parameters with undefined/empty values filtered out
91
+ * @param pathname - The real URL path. The method does not use it, because Solid
92
+ * parsed the params before
93
+ * @param stateId - The matched state ID. The method does not use it, because Solid
94
+ * parsed the params before
95
+ * @returns The normalized path parameters, without an undefined value and without
96
+ * an empty value
87
97
  */
88
98
  protected extractParams(pathname: string, stateId: string): Record<string, string>;
89
99
  /**
90
- * Navigate SolidJS Router to the given path.
100
+ * Navigates SolidJS Router to the given path.
91
101
  */
92
102
  protected navigateRouter(path: string): void;
93
103
  /**
94
- * Get the current router pathname for initial URL -> actor sync on connect.
104
+ * Returns the current pathname of the router, for the first URL-to-actor synchronization in connect().
95
105
  */
96
106
  protected getInitialRouterPath(): string | null;
97
107
  /**
98
- * Return the initial URL search string for query-param forwarding on `connect()`.
108
+ * Returns the initial URL search string, so that `connect()` can forward the query params.
99
109
  *
100
- * Reads `this.location.search` from Solid's `useLocation()` reactive object
101
- * the same source used by `getInitialRouterPath()`. An empty string (no query
102
- * params) returns `undefined` so `syncActorFromRouter` produces `query: {}`.
110
+ * The method reads `this.location.search` from the reactive `useLocation()` object
111
+ * of Solid, the same source as `getInitialRouterPath()`. For an empty string, which
112
+ * means no query param, it returns `undefined`. `syncActorFromRouter` then makes
113
+ * `query: {}`.
103
114
  */
104
115
  protected getInitialRouterSearch(): string | undefined;
105
116
  /**
106
- * Subscribe to SolidJS Router location changes using createEffect.
117
+ * Subscribes to each location change of SolidJS Router with createEffect.
107
118
  *
108
- * MUST be called inside a Solid reactive owner (component or createRoot).
119
+ * Call this method inside a Solid reactive owner: a component, or createRoot.
109
120
  *
110
- * The effect runs inside `createRoot()` to give it a stable owner independent
111
- * of the calling component's lifecycle this prevents the effect from being
112
- * disposed if the component re-renders while the bridge should stay active.
113
- * The trade-off is that component unmount does NOT automatically clean up the
114
- * effect; `disconnect()` (or `dispose()`) MUST be called explicitly to avoid a leak.
121
+ * The effect runs inside `createRoot()`. The effect then has a stable owner, and
122
+ * that owner is separate from the lifecycle of the component that calls the method.
123
+ * Solid therefore does not dispose of the effect when the component renders again
124
+ * while the bridge must stay active. The cost is that the unmount of the component
125
+ * does NOT clean up the effect. Call `disconnect()` or `dispose()` yourself, or the
126
+ * effect stays in memory.
115
127
  */
116
128
  protected watchRouterChanges(): void;
117
129
  /**
118
- * Stop watching SolidJS Router changes.
130
+ * Stops the watch of the SolidJS Router changes.
119
131
  *
120
- * Calls the `dispose` function returned by `createRoot()` in `watchRouterChanges()`,
121
- * tearing down the reactive effect and freeing the isolated owner. This is the only
122
- * cleanup path component unmount does NOT trigger this automatically.
132
+ * The method calls the `dispose` function that `createRoot()` returned in
133
+ * `watchRouterChanges()`. This removes the reactive effect and frees the separate
134
+ * owner. This is the only path that cleans up. The unmount of the component does
135
+ * NOT start it.
123
136
  */
124
137
  protected unwatchRouterChanges(): void;
125
138
  /**
126
- * Dispose the bridge (alias for disconnect).
139
+ * Disposes of the bridge. This method is the alias of disconnect.
127
140
  *
128
141
  * @example
129
142
  * ```tsx
130
143
  * onCleanup(() => bridge.dispose());
131
144
  * ```
145
+ *
146
+ * @deprecated Use {@link RouterBridgeBase.disconnect | disconnect}. Will be removed in the next major.
132
147
  */
133
148
  dispose(): void;
134
149
  }
@@ -1 +1 @@
1
- {"version":3,"file":"solid-router-bridge.d.ts","sourceRoot":"","sources":["../src/solid-router-bridge.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAGH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AACzD,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC1D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAC3D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAC5D,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AAEvD;;;;;;;;;;GAUG;AACH,qBAAa,iBAAkB,SAAQ,gBAAgB;IAsBrD,OAAO,CAAC,QAAQ,CAAC,aAAa;IAC9B,OAAO,CAAC,QAAQ,CAAC,QAAQ;IAtB1B,OAAO,CAAC,oBAAoB,CAA6B;IAEzD;;;OAGG;IACH,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;IAErC;;;;;;;;;;;OAWG;gBAEe,aAAa,EAAE,SAAS,EACxB,QAAQ,EAAE,YAAY,EACvC,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,aAAa,EACpB,QAAQ,EAAE,QAAQ;IASnB;;;;;;;;;;;;;;OAcG;cACgB,aAAa,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC;IAY3F;;OAEG;IACH,SAAS,CAAC,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAI5C;;OAEG;cACgB,oBAAoB,IAAI,MAAM,GAAG,IAAI;IAIxD;;;;;;OAMG;cACgB,sBAAsB,IAAI,MAAM,GAAG,SAAS;IAI/D;;;;;;;;;;OAUG;IACH,SAAS,CAAC,kBAAkB,IAAI,IAAI;IAepC;;;;;;OAMG;IACH,SAAS,CAAC,oBAAoB,IAAI,IAAI;IAKtC;;;;;;;OAOG;IACH,OAAO,IAAI,IAAI;CAGf"}
1
+ {"version":3,"file":"solid-router-bridge.d.ts","sourceRoot":"","sources":["../src/solid-router-bridge.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AAGH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AACzD,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC1D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAC3D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAC5D,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AAEvD;;;;;;;;;;;;GAYG;AACH,qBAAa,iBAAkB,SAAQ,gBAAgB;IAwBrD,OAAO,CAAC,QAAQ,CAAC,aAAa;IAC9B,OAAO,CAAC,QAAQ,CAAC,QAAQ;IAxB1B,OAAO,CAAC,oBAAoB,CAA6B;IAEzD;;;;OAIG;IACH,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;IAErC;;;;;;;;;;;;OAYG;gBAEe,aAAa,EAAE,SAAS,EACxB,QAAQ,EAAE,YAAY,EACvC,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,aAAa,EACpB,QAAQ,EAAE,QAAQ;IASnB;;;;;;;;;;;;;;;;;;OAkBG;cACgB,aAAa,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC;IAY3F;;OAEG;IACH,SAAS,CAAC,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAI5C;;OAEG;cACgB,oBAAoB,IAAI,MAAM,GAAG,IAAI;IAIxD;;;;;;;OAOG;cACgB,sBAAsB,IAAI,MAAM,GAAG,SAAS;IAI/D;;;;;;;;;;;OAWG;IACH,SAAS,CAAC,kBAAkB,IAAI,IAAI;IAepC;;;;;;;OAOG;IACH,SAAS,CAAC,oBAAoB,IAAI,IAAI;IAKtC;;;;;;;;;OASG;IACH,OAAO,IAAI,IAAI;CAGf"}
@@ -1,166 +1,182 @@
1
- /**
2
- * SolidJS Router bridge implementing RouterBridge protocol via RouterBridgeBase
3
- *
4
- * Extends RouterBridgeBase to handle all common lifecycle and sync logic.
5
- * Uses Solid's native reactive primitives (createEffect) for router→actor direction.
6
- *
7
- * **IMPORTANT:** `connect()` MUST be called inside a Solid reactive owner (component
8
- * or createRoot). The `createEffect()` in `watchRouterChanges()` runs inside
9
- * `createRoot()`, which deliberately isolates it from any parent owner — automatic
10
- * cleanup on component unmount does NOT happen. You MUST call `disconnect()` (or
11
- * `dispose()`) explicitly, typically in `onCleanup()`.
12
- *
13
- * @example
14
- * ```tsx
15
- * import { useNavigate, useLocation, useParams } from '@solidjs/router';
16
- * import { onCleanup } from 'solid-js';
17
- * import { SolidRouterBridge, RouteMap } from '@xmachines/play-solid-router';
18
- *
19
- * function App() {
20
- * const navigate = useNavigate();
21
- * const location = useLocation();
22
- * const params = useParams();
23
- *
24
- * const routeMap = new RouteMap([...]);
25
- * const bridge = new SolidRouterBridge(navigate, location, params, actor, routeMap);
26
- *
27
- * // connect() MUST be called inside a Solid reactive owner
28
- * bridge.connect();
29
- * onCleanup(() => bridge.disconnect());
30
- *
31
- * return <div>...</div>;
32
- * }
33
- * ```
34
- */
35
1
  import { createEffect, createRoot, on } from "solid-js";
36
2
  import { RouterBridgeBase } from "@xmachines/play-router";
3
+ //#region packages/play-solid-router/src/solid-router-bridge.ts
4
+ /**
5
+ * The SolidJS Router bridge. It implements the RouterBridge protocol through RouterBridgeBase
6
+ *
7
+ * The class extends RouterBridgeBase, and the base class does all the common
8
+ * lifecycle work and synchronization work. This class uses the native reactive
9
+ * primitives of Solid (createEffect) for the router-to-actor direction.
10
+ *
11
+ * **IMPORTANT:** call `connect()` inside a Solid reactive owner: a component, or
12
+ * createRoot. The `createEffect()` call in `watchRouterChanges()` runs inside
13
+ * `createRoot()`, which keeps the effect separate from every parent owner on
14
+ * purpose. Therefore the effect does NOT clean up by itself when the component
15
+ * unmounts. You MUST call `disconnect()` or `dispose()` yourself, usually in
16
+ * `onCleanup()`.
17
+ *
18
+ * @example
19
+ * ```tsx
20
+ * import { useNavigate, useLocation, useParams } from '@solidjs/router';
21
+ * import { onCleanup } from 'solid-js';
22
+ * import { SolidRouterBridge, RouteMap } from '@xmachines/play-solid-router';
23
+ *
24
+ * function App() {
25
+ * const navigate = useNavigate();
26
+ * const location = useLocation();
27
+ * const params = useParams();
28
+ *
29
+ * const routeMap = new RouteMap([...]);
30
+ * const bridge = new SolidRouterBridge(navigate, location, params, actor, routeMap);
31
+ *
32
+ * // Call connect() inside a Solid reactive owner
33
+ * bridge.connect();
34
+ * onCleanup(() => bridge.disconnect());
35
+ *
36
+ * return <div>...</div>;
37
+ * }
38
+ * ```
39
+ */
37
40
  /**
38
- * SolidJS Router integration bridge extending RouterBridgeBase
39
- *
40
- * Implements RouterBridge protocol for SolidJS Router using Solid's reactive
41
- * primitives. The actorrouter direction uses TC39 Signal watcher (from base class).
42
- * The routeractor direction uses Solid's createEffect for native reactivity.
43
- *
44
- * Path parameters are extracted from Solid's `useParams()` reactive proxy rather than
45
- * re-parsing the URL with URLPattern. This means parameterized routes work without the
46
- * URLPattern polyfill Solid's router has already extracted the values.
47
- */
48
- export class SolidRouterBridge extends RouterBridgeBase {
49
- solidNavigate;
50
- location;
51
- disposeRouterWatcher = null;
52
- /**
53
- * Live reactive params object from Solid's `useParams()`.
54
- * Read inside the createEffect callback so it always reflects the current route.
55
- */
56
- solidParams;
57
- /**
58
- * Create a SolidJS Router bridge
59
- *
60
- * **CRITICAL:** `connect()` must be called inside a Solid component where hooks are available.
61
- *
62
- * @param solidNavigate - Result of useNavigate() hook
63
- * @param location - Result of useLocation() hook
64
- * @param params - Result of useParams() hook — used directly for path parameter extraction,
65
- * avoiding the URLPattern polyfill requirement for parameterized routes
66
- * @param actor - XMachines actor instance
67
- * @param routeMap - Bidirectional state ID path mapping
68
- */
69
- constructor(solidNavigate, location, params, actor, routeMap) {
70
- super(actor, {
71
- getStateIdByPath: (path) => routeMap.getStateIdByPath(path),
72
- getPathByStateId: (id) => routeMap.getPathByStateId(id),
73
- });
74
- this.solidNavigate = solidNavigate;
75
- this.location = location;
76
- this.solidParams = params;
77
- }
78
- /**
79
- * Extract path parameters using Solid's pre-parsed `useParams()` values.
80
- *
81
- * Solid's router has already extracted all named parameters for the matched route
82
- * segment. Reading `this.solidParams` inside the createEffect callback that drives
83
- * `syncActorFromRouter` is safe the reactive proxy always reflects the current
84
- * route at the time the effect runs.
85
- *
86
- * Falls back to URLPattern-based extraction (base class) only when Solid provided
87
- * no params for this route (i.e. the route has no `:param` segments).
88
- *
89
- * @param pathname - The actual URL path (unused — params already extracted by Solid)
90
- * @param stateId - The matched state ID (unused params already extracted by Solid)
91
- * @returns Normalized path parameters with undefined/empty values filtered out
92
- */
93
- extractParams(pathname, stateId) {
94
- const entries = Object.entries(this.solidParams).filter((entry) => entry[1] !== undefined && entry[1] !== null && entry[1] !== "");
95
- if (entries.length > 0) {
96
- return Object.fromEntries(entries);
97
- }
98
- // No params from Solid fall back to URLPattern for routes with no segments
99
- return super.extractParams(pathname, stateId);
100
- }
101
- /**
102
- * Navigate SolidJS Router to the given path.
103
- */
104
- navigateRouter(path) {
105
- this.solidNavigate(path);
106
- }
107
- /**
108
- * Get the current router pathname for initial URL -> actor sync on connect.
109
- */
110
- getInitialRouterPath() {
111
- return this.location.pathname ?? null;
112
- }
113
- /**
114
- * Return the initial URL search string for query-param forwarding on `connect()`.
115
- *
116
- * Reads `this.location.search` from Solid's `useLocation()` reactive object —
117
- * the same source used by `getInitialRouterPath()`. An empty string (no query
118
- * params) returns `undefined` so `syncActorFromRouter` produces `query: {}`.
119
- */
120
- getInitialRouterSearch() {
121
- return this.location.search || undefined;
122
- }
123
- /**
124
- * Subscribe to SolidJS Router location changes using createEffect.
125
- *
126
- * MUST be called inside a Solid reactive owner (component or createRoot).
127
- *
128
- * The effect runs inside `createRoot()` to give it a stable owner independent
129
- * of the calling component's lifecycle — this prevents the effect from being
130
- * disposed if the component re-renders while the bridge should stay active.
131
- * The trade-off is that component unmount does NOT automatically clean up the
132
- * effect; `disconnect()` (or `dispose()`) MUST be called explicitly to avoid a leak.
133
- */
134
- watchRouterChanges() {
135
- this.disposeRouterWatcher = createRoot((dispose) => {
136
- createEffect(on(() => this.location.pathname, (pathname) => {
137
- const search = this.location.search ?? "";
138
- this.syncActorFromRouter(pathname, search);
139
- }));
140
- return dispose;
141
- });
142
- }
143
- /**
144
- * Stop watching SolidJS Router changes.
145
- *
146
- * Calls the `dispose` function returned by `createRoot()` in `watchRouterChanges()`,
147
- * tearing down the reactive effect and freeing the isolated owner. This is the only
148
- * cleanup path — component unmount does NOT trigger this automatically.
149
- */
150
- unwatchRouterChanges() {
151
- this.disposeRouterWatcher?.();
152
- this.disposeRouterWatcher = null;
153
- }
154
- /**
155
- * Dispose the bridge (alias for disconnect).
156
- *
157
- * @example
158
- * ```tsx
159
- * onCleanup(() => bridge.dispose());
160
- * ```
161
- */
162
- dispose() {
163
- this.disconnect();
164
- }
165
- }
41
+ * The SolidJS Router integration bridge. It extends RouterBridgeBase
42
+ *
43
+ * The class implements the RouterBridge protocol for SolidJS Router with the
44
+ * reactive primitives of Solid. The actor-to-router direction uses the TC39 Signal
45
+ * watcher of the base class. The router-to-actor direction uses the Solid
46
+ * createEffect function for a native reactivity.
47
+ *
48
+ * The bridge reads each path parameter from the reactive `useParams()` proxy of
49
+ * Solid. It does not parse the URL again with URLPattern. Therefore a
50
+ * parameterized route works without the URLPattern polyfill, because the router of
51
+ * Solid holds the values already.
52
+ */
53
+ var SolidRouterBridge = class extends RouterBridgeBase {
54
+ solidNavigate;
55
+ location;
56
+ disposeRouterWatcher = null;
57
+ /**
58
+ * The live reactive params object from the `useParams()` function of Solid.
59
+ * The bridge reads it inside the createEffect callback, so the value always shows
60
+ * the current route.
61
+ */
62
+ solidParams;
63
+ /**
64
+ * Creates a SolidJS Router bridge
65
+ *
66
+ * **CRITICAL:** call `connect()` inside a Solid component, where the hooks are available.
67
+ *
68
+ * @param solidNavigate - The result of the useNavigate() hook
69
+ * @param location - The result of the useLocation() hook
70
+ * @param params - The result of the useParams() hook. The bridge reads each path
71
+ * parameter directly from it. A parameterized route therefore does not need the
72
+ * URLPattern polyfill
73
+ * @param actor - The XMachines actor instance
74
+ * @param routeMap - The bidirectional map between the state IDs and the paths
75
+ */
76
+ constructor(solidNavigate, location, params, actor, routeMap) {
77
+ super(actor, {
78
+ getStateIdByPath: (path) => routeMap.getStateIdByPath(path),
79
+ getPathByStateId: (id) => routeMap.getPathByStateId(id)
80
+ });
81
+ this.solidNavigate = solidNavigate;
82
+ this.location = location;
83
+ this.solidParams = params;
84
+ }
85
+ /**
86
+ * Reads each path parameter from the values that `useParams()` of Solid parsed before.
87
+ *
88
+ * The router of Solid holds every named parameter of the matched route segment
89
+ * already. A read of `this.solidParams` inside the createEffect callback that
90
+ * drives `syncActorFromRouter` is safe, because the reactive proxy always shows the
91
+ * route of the moment when the effect runs.
92
+ *
93
+ * The method returns to the URLPattern method of the base class only when Solid
94
+ * gives no param for this route, which means that the route has no `:param`
95
+ * segment.
96
+ *
97
+ * @param pathname - The real URL path. The method does not use it, because Solid
98
+ * parsed the params before
99
+ * @param stateId - The matched state ID. The method does not use it, because Solid
100
+ * parsed the params before
101
+ * @returns The normalized path parameters, without an undefined value and without
102
+ * an empty value
103
+ */
104
+ extractParams(pathname, stateId) {
105
+ const entries = Object.entries(this.solidParams).filter((entry) => entry[1] !== void 0 && entry[1] !== null && entry[1] !== "");
106
+ if (entries.length > 0) return Object.fromEntries(entries);
107
+ return super.extractParams(pathname, stateId);
108
+ }
109
+ /**
110
+ * Navigates SolidJS Router to the given path.
111
+ */
112
+ navigateRouter(path) {
113
+ this.solidNavigate(path);
114
+ }
115
+ /**
116
+ * Returns the current pathname of the router, for the first URL-to-actor synchronization in connect().
117
+ */
118
+ getInitialRouterPath() {
119
+ return this.location.pathname ?? null;
120
+ }
121
+ /**
122
+ * Returns the initial URL search string, so that `connect()` can forward the query params.
123
+ *
124
+ * The method reads `this.location.search` from the reactive `useLocation()` object
125
+ * of Solid, the same source as `getInitialRouterPath()`. For an empty string, which
126
+ * means no query param, it returns `undefined`. `syncActorFromRouter` then makes
127
+ * `query: {}`.
128
+ */
129
+ getInitialRouterSearch() {
130
+ return this.location.search || void 0;
131
+ }
132
+ /**
133
+ * Subscribes to each location change of SolidJS Router with createEffect.
134
+ *
135
+ * Call this method inside a Solid reactive owner: a component, or createRoot.
136
+ *
137
+ * The effect runs inside `createRoot()`. The effect then has a stable owner, and
138
+ * that owner is separate from the lifecycle of the component that calls the method.
139
+ * Solid therefore does not dispose of the effect when the component renders again
140
+ * while the bridge must stay active. The cost is that the unmount of the component
141
+ * does NOT clean up the effect. Call `disconnect()` or `dispose()` yourself, or the
142
+ * effect stays in memory.
143
+ */
144
+ watchRouterChanges() {
145
+ this.disposeRouterWatcher = createRoot((dispose) => {
146
+ createEffect(on(() => this.location.pathname, (pathname) => {
147
+ const search = this.location.search ?? "";
148
+ this.syncActorFromRouter(pathname, search);
149
+ }));
150
+ return dispose;
151
+ });
152
+ }
153
+ /**
154
+ * Stops the watch of the SolidJS Router changes.
155
+ *
156
+ * The method calls the `dispose` function that `createRoot()` returned in
157
+ * `watchRouterChanges()`. This removes the reactive effect and frees the separate
158
+ * owner. This is the only path that cleans up. The unmount of the component does
159
+ * NOT start it.
160
+ */
161
+ unwatchRouterChanges() {
162
+ this.disposeRouterWatcher?.();
163
+ this.disposeRouterWatcher = null;
164
+ }
165
+ /**
166
+ * Disposes of the bridge. This method is the alias of disconnect.
167
+ *
168
+ * @example
169
+ * ```tsx
170
+ * onCleanup(() => bridge.dispose());
171
+ * ```
172
+ *
173
+ * @deprecated Use {@link RouterBridgeBase.disconnect | disconnect}. Will be removed in the next major.
174
+ */
175
+ dispose() {
176
+ this.disconnect();
177
+ }
178
+ };
179
+ //#endregion
180
+ export { SolidRouterBridge };
181
+
166
182
  //# sourceMappingURL=solid-router-bridge.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"solid-router-bridge.js","sourceRoot":"","sources":["../src/solid-router-bridge.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAEH,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,EAAE,EAAE,MAAM,UAAU,CAAC;AAExD,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAK1D;;;;;;;;;;GAUG;AACH,MAAM,OAAO,iBAAkB,SAAQ,gBAAgB;IAsBpC;IACA;IAtBV,oBAAoB,GAAwB,IAAI,CAAC;IAEzD;;;OAGG;IACc,WAAW,CAAS;IAErC;;;;;;;;;;;OAWG;IACH,YACkB,aAAwB,EACxB,QAAsB,EACvC,MAAc,EACd,KAAoB,EACpB,QAAkB;QAElB,KAAK,CAAC,KAAK,EAAE;YACZ,gBAAgB,EAAE,CAAC,IAAY,EAAE,EAAE,CAAC,QAAQ,CAAC,gBAAgB,CAAC,IAAI,CAAC;YACnE,gBAAgB,EAAE,CAAC,EAAU,EAAE,EAAE,CAAC,QAAQ,CAAC,gBAAgB,CAAC,EAAE,CAAC;SAC/D,CAAC,CAAC;QATc,kBAAa,GAAb,aAAa,CAAW;QACxB,aAAQ,GAAR,QAAQ,CAAc;QASvC,IAAI,CAAC,WAAW,GAAG,MAAM,CAAC;IAC3B,CAAC;IAED;;;;;;;;;;;;;;OAcG;IACgB,aAAa,CAAC,QAAgB,EAAE,OAAe;QACjE,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,MAAM,CACtD,CAAC,KAAK,EAA6B,EAAE,CACpC,KAAK,CAAC,CAAC,CAAC,KAAK,SAAS,IAAI,KAAK,CAAC,CAAC,CAAC,KAAK,IAAI,IAAI,KAAK,CAAC,CAAC,CAAC,KAAK,EAAE,CAC/D,CAAC;QACF,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACxB,OAAO,MAAM,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC;QACpC,CAAC;QACD,6EAA6E;QAC7E,OAAO,KAAK,CAAC,aAAa,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;IAC/C,CAAC;IAED;;OAEG;IACO,cAAc,CAAC,IAAY;QACpC,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC;IAC1B,CAAC;IAED;;OAEG;IACgB,oBAAoB;QACtC,OAAO,IAAI,CAAC,QAAQ,CAAC,QAAQ,IAAI,IAAI,CAAC;IACvC,CAAC;IAED;;;;;;OAMG;IACgB,sBAAsB;QACxC,OAAO,IAAI,CAAC,QAAQ,CAAC,MAAM,IAAI,SAAS,CAAC;IAC1C,CAAC;IAED;;;;;;;;;;OAUG;IACO,kBAAkB;QAC3B,IAAI,CAAC,oBAAoB,GAAG,UAAU,CAAC,CAAC,OAAO,EAAE,EAAE;YAClD,YAAY,CACX,EAAE,CACD,GAAG,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,QAAQ,EAC5B,CAAC,QAAgB,EAAE,EAAE;gBACpB,MAAM,MAAM,GAAG,IAAI,CAAC,QAAQ,CAAC,MAAM,IAAI,EAAE,CAAC;gBAC1C,IAAI,CAAC,mBAAmB,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;YAC5C,CAAC,CACD,CACD,CAAC;YACF,OAAO,OAAO,CAAC;QAChB,CAAC,CAAC,CAAC;IACJ,CAAC;IAED;;;;;;OAMG;IACO,oBAAoB;QAC7B,IAAI,CAAC,oBAAoB,EAAE,EAAE,CAAC;QAC9B,IAAI,CAAC,oBAAoB,GAAG,IAAI,CAAC;IAClC,CAAC;IAED;;;;;;;OAOG;IACH,OAAO;QACN,IAAI,CAAC,UAAU,EAAE,CAAC;IACnB,CAAC;CACD"}
1
+ {"version":3,"file":"solid-router-bridge.js","names":[],"sources":["../src/solid-router-bridge.ts"],"sourcesContent":["/**\n * The SolidJS Router bridge. It implements the RouterBridge protocol through RouterBridgeBase\n *\n * The class extends RouterBridgeBase, and the base class does all the common\n * lifecycle work and synchronization work. This class uses the native reactive\n * primitives of Solid (createEffect) for the router-to-actor direction.\n *\n * **IMPORTANT:** call `connect()` inside a Solid reactive owner: a component, or\n * createRoot. The `createEffect()` call in `watchRouterChanges()` runs inside\n * `createRoot()`, which keeps the effect separate from every parent owner on\n * purpose. Therefore the effect does NOT clean up by itself when the component\n * unmounts. You MUST call `disconnect()` or `dispose()` yourself, usually in\n * `onCleanup()`.\n *\n * @example\n * ```tsx\n * import { useNavigate, useLocation, useParams } from '@solidjs/router';\n * import { onCleanup } from 'solid-js';\n * import { SolidRouterBridge, RouteMap } from '@xmachines/play-solid-router';\n *\n * function App() {\n * const navigate = useNavigate();\n * const location = useLocation();\n * const params = useParams();\n *\n * const routeMap = new RouteMap([...]);\n * const bridge = new SolidRouterBridge(navigate, location, params, actor, routeMap);\n *\n * // Call connect() inside a Solid reactive owner\n * bridge.connect();\n * onCleanup(() => bridge.disconnect());\n *\n * return <div>...</div>;\n * }\n * ```\n */\n\nimport { createEffect, createRoot, on } from \"solid-js\";\nimport type { Navigator, Params } from \"@solidjs/router\";\nimport { RouterBridgeBase } from \"@xmachines/play-router\";\nimport type { LocationLike } from \"@xmachines/play-router\";\nimport type { RoutableActor } from \"@xmachines/play-router\";\nimport type { RouteMap } from \"@xmachines/play-router\";\n\n/**\n * The SolidJS Router integration bridge. It extends RouterBridgeBase\n *\n * The class implements the RouterBridge protocol for SolidJS Router with the\n * reactive primitives of Solid. The actor-to-router direction uses the TC39 Signal\n * watcher of the base class. The router-to-actor direction uses the Solid\n * createEffect function for a native reactivity.\n *\n * The bridge reads each path parameter from the reactive `useParams()` proxy of\n * Solid. It does not parse the URL again with URLPattern. Therefore a\n * parameterized route works without the URLPattern polyfill, because the router of\n * Solid holds the values already.\n */\nexport class SolidRouterBridge extends RouterBridgeBase {\n\tprivate disposeRouterWatcher: (() => void) | null = null;\n\n\t/**\n\t * The live reactive params object from the `useParams()` function of Solid.\n\t * The bridge reads it inside the createEffect callback, so the value always shows\n\t * the current route.\n\t */\n\tprivate readonly solidParams: Params;\n\n\t/**\n\t * Creates a SolidJS Router bridge\n\t *\n\t * **CRITICAL:** call `connect()` inside a Solid component, where the hooks are available.\n\t *\n\t * @param solidNavigate - The result of the useNavigate() hook\n\t * @param location - The result of the useLocation() hook\n\t * @param params - The result of the useParams() hook. The bridge reads each path\n\t * parameter directly from it. A parameterized route therefore does not need the\n\t * URLPattern polyfill\n\t * @param actor - The XMachines actor instance\n\t * @param routeMap - The bidirectional map between the state IDs and the paths\n\t */\n\tconstructor(\n\t\tprivate readonly solidNavigate: Navigator,\n\t\tprivate readonly location: LocationLike,\n\t\tparams: Params,\n\t\tactor: RoutableActor,\n\t\trouteMap: RouteMap,\n\t) {\n\t\tsuper(actor, {\n\t\t\tgetStateIdByPath: (path: string) => routeMap.getStateIdByPath(path),\n\t\t\tgetPathByStateId: (id: string) => routeMap.getPathByStateId(id),\n\t\t});\n\t\tthis.solidParams = params;\n\t}\n\n\t/**\n\t * Reads each path parameter from the values that `useParams()` of Solid parsed before.\n\t *\n\t * The router of Solid holds every named parameter of the matched route segment\n\t * already. A read of `this.solidParams` inside the createEffect callback that\n\t * drives `syncActorFromRouter` is safe, because the reactive proxy always shows the\n\t * route of the moment when the effect runs.\n\t *\n\t * The method returns to the URLPattern method of the base class only when Solid\n\t * gives no param for this route, which means that the route has no `:param`\n\t * segment.\n\t *\n\t * @param pathname - The real URL path. The method does not use it, because Solid\n\t * parsed the params before\n\t * @param stateId - The matched state ID. The method does not use it, because Solid\n\t * parsed the params before\n\t * @returns The normalized path parameters, without an undefined value and without\n\t * an empty value\n\t */\n\tprotected override extractParams(pathname: string, stateId: string): Record<string, string> {\n\t\tconst entries = Object.entries(this.solidParams).filter(\n\t\t\t(entry): entry is [string, string] =>\n\t\t\t\tentry[1] !== undefined && entry[1] !== null && entry[1] !== \"\",\n\t\t);\n\t\tif (entries.length > 0) {\n\t\t\treturn Object.fromEntries(entries);\n\t\t}\n\t\t// Solid gives no param: use URLPattern for a route with no segment\n\t\treturn super.extractParams(pathname, stateId);\n\t}\n\n\t/**\n\t * Navigates SolidJS Router to the given path.\n\t */\n\tprotected navigateRouter(path: string): void {\n\t\tthis.solidNavigate(path);\n\t}\n\n\t/**\n\t * Returns the current pathname of the router, for the first URL-to-actor synchronization in connect().\n\t */\n\tprotected override getInitialRouterPath(): string | null {\n\t\treturn this.location.pathname ?? null;\n\t}\n\n\t/**\n\t * Returns the initial URL search string, so that `connect()` can forward the query params.\n\t *\n\t * The method reads `this.location.search` from the reactive `useLocation()` object\n\t * of Solid, the same source as `getInitialRouterPath()`. For an empty string, which\n\t * means no query param, it returns `undefined`. `syncActorFromRouter` then makes\n\t * `query: {}`.\n\t */\n\tprotected override getInitialRouterSearch(): string | undefined {\n\t\treturn this.location.search || undefined;\n\t}\n\n\t/**\n\t * Subscribes to each location change of SolidJS Router with createEffect.\n\t *\n\t * Call this method inside a Solid reactive owner: a component, or createRoot.\n\t *\n\t * The effect runs inside `createRoot()`. The effect then has a stable owner, and\n\t * that owner is separate from the lifecycle of the component that calls the method.\n\t * Solid therefore does not dispose of the effect when the component renders again\n\t * while the bridge must stay active. The cost is that the unmount of the component\n\t * does NOT clean up the effect. Call `disconnect()` or `dispose()` yourself, or the\n\t * effect stays in memory.\n\t */\n\tprotected watchRouterChanges(): void {\n\t\tthis.disposeRouterWatcher = createRoot((dispose) => {\n\t\t\tcreateEffect(\n\t\t\t\ton(\n\t\t\t\t\t() => this.location.pathname,\n\t\t\t\t\t(pathname: string) => {\n\t\t\t\t\t\tconst search = this.location.search ?? \"\";\n\t\t\t\t\t\tthis.syncActorFromRouter(pathname, search);\n\t\t\t\t\t},\n\t\t\t\t),\n\t\t\t);\n\t\t\treturn dispose;\n\t\t});\n\t}\n\n\t/**\n\t * Stops the watch of the SolidJS Router changes.\n\t *\n\t * The method calls the `dispose` function that `createRoot()` returned in\n\t * `watchRouterChanges()`. This removes the reactive effect and frees the separate\n\t * owner. This is the only path that cleans up. The unmount of the component does\n\t * NOT start it.\n\t */\n\tprotected unwatchRouterChanges(): void {\n\t\tthis.disposeRouterWatcher?.();\n\t\tthis.disposeRouterWatcher = null;\n\t}\n\n\t/**\n\t * Disposes of the bridge. This method is the alias of disconnect.\n\t *\n\t * @example\n\t * ```tsx\n\t * onCleanup(() => bridge.dispose());\n\t * ```\n\t *\n\t * @deprecated Use {@link RouterBridgeBase.disconnect | disconnect}. Will be removed in the next major.\n\t */\n\tdispose(): void {\n\t\tthis.disconnect();\n\t}\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAyDA,IAAa,oBAAb,cAAuC,iBAAiB;CAwBrC;CACA;CAxBlB,uBAAoD;;;;;;CAOpD;;;;;;;;;;;;;;CAeA,YACC,eACA,UACA,QACA,OACA,UACC;EACD,MAAM,OAAO;GACZ,mBAAmB,SAAiB,SAAS,iBAAiB,IAAI;GAClE,mBAAmB,OAAe,SAAS,iBAAiB,EAAE;EAC/D,CAAC;EATgB,KAAA,gBAAA;EACA,KAAA,WAAA;EASjB,KAAK,cAAc;CACpB;;;;;;;;;;;;;;;;;;;;CAqBA,cAAiC,UAAkB,SAAyC;EAC3F,MAAM,UAAU,OAAO,QAAQ,KAAK,WAAW,CAAC,CAAC,QAC/C,UACA,MAAM,OAAO,KAAA,KAAa,MAAM,OAAO,QAAQ,MAAM,OAAO,EAC9D;EACA,IAAI,QAAQ,SAAS,GACpB,OAAO,OAAO,YAAY,OAAO;EAGlC,OAAO,MAAM,cAAc,UAAU,OAAO;CAC7C;;;;CAKA,eAAyB,MAAoB;EAC5C,KAAK,cAAc,IAAI;CACxB;;;;CAKA,uBAAyD;EACxD,OAAO,KAAK,SAAS,YAAY;CAClC;;;;;;;;;CAUA,yBAAgE;EAC/D,OAAO,KAAK,SAAS,UAAU,KAAA;CAChC;;;;;;;;;;;;;CAcA,qBAAqC;EACpC,KAAK,uBAAuB,YAAY,YAAY;GACnD,aACC,SACO,KAAK,SAAS,WACnB,aAAqB;IACrB,MAAM,SAAS,KAAK,SAAS,UAAU;IACvC,KAAK,oBAAoB,UAAU,MAAM;GAC1C,CACD,CACD;GACA,OAAO;EACR,CAAC;CACF;;;;;;;;;CAUA,uBAAuC;EACtC,KAAK,uBAAuB;EAC5B,KAAK,uBAAuB;CAC7B;;;;;;;;;;;CAYA,UAAgB;EACf,KAAK,WAAW;CACjB;AACD"}
package/dist/types.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Type definitions for @xmachines/play-solid-router
2
+ * The type definitions of @xmachines/play-solid-router
3
3
  */
4
4
  export type { PlayRouteEvent, RouterBridge } from "@xmachines/play-router";
5
5
  export type { AbstractActor } from "@xmachines/play-actor";