reactive-route 0.0.1-alpha.3 → 0.0.1-alpha.31

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.
Files changed (163) hide show
  1. package/adapters/kr-observable-preact.cjs +39 -0
  2. package/adapters/kr-observable-preact.d.ts +3 -0
  3. package/adapters/kr-observable-preact.d.ts.map +1 -0
  4. package/adapters/kr-observable-preact.mjs +18 -0
  5. package/adapters/kr-observable-react.cjs +39 -0
  6. package/adapters/kr-observable-react.d.ts +3 -0
  7. package/adapters/kr-observable-react.d.ts.map +1 -0
  8. package/adapters/kr-observable-react.mjs +18 -0
  9. package/adapters/kr-observable-solid.cjs +37 -0
  10. package/adapters/kr-observable-solid.d.ts +3 -0
  11. package/adapters/kr-observable-solid.d.ts.map +1 -0
  12. package/adapters/kr-observable-solid.mjs +16 -0
  13. package/adapters/mobx-preact.cjs +43 -0
  14. package/adapters/mobx-preact.d.ts +3 -0
  15. package/adapters/mobx-preact.d.ts.map +1 -0
  16. package/adapters/mobx-preact.mjs +22 -0
  17. package/adapters/mobx-react.cjs +43 -0
  18. package/adapters/mobx-react.d.ts +3 -0
  19. package/adapters/mobx-react.d.ts.map +1 -0
  20. package/adapters/mobx-react.mjs +22 -0
  21. package/adapters/mobx-solid.cjs +42 -0
  22. package/adapters/mobx-solid.d.ts +3 -0
  23. package/adapters/mobx-solid.d.ts.map +1 -0
  24. package/adapters/mobx-solid.mjs +21 -0
  25. package/adapters/solid.cjs +45 -0
  26. package/adapters/solid.d.ts +3 -0
  27. package/adapters/solid.d.ts.map +1 -0
  28. package/adapters/solid.mjs +24 -0
  29. package/adapters/vue.cjs +40 -0
  30. package/adapters/vue.d.ts +3 -0
  31. package/adapters/vue.d.ts.map +1 -0
  32. package/adapters/vue.mjs +19 -0
  33. package/core/createRouter.d.ts +9 -0
  34. package/core/createRouter.d.ts.map +1 -0
  35. package/{dist/types/core/createRouterConfig.d.ts → core/createRoutes.d.ts} +4 -4
  36. package/core/createRoutes.d.ts.map +1 -0
  37. package/core/index.d.ts +14 -0
  38. package/core/index.d.ts.map +1 -0
  39. package/core/types/TypeAdapters.d.ts +9 -0
  40. package/core/types/TypeAdapters.d.ts.map +1 -0
  41. package/{dist/types/core → core}/types/TypeCurrentRoute.d.ts +1 -1
  42. package/core/types/TypeCurrentRoute.d.ts.map +1 -0
  43. package/{dist/types/core → core}/types/TypeLifecycleConfig.d.ts +2 -0
  44. package/core/types/TypeLifecycleConfig.d.ts.map +1 -0
  45. package/{dist/types/core → core}/types/TypePropsRouter.d.ts +2 -3
  46. package/core/types/TypePropsRouter.d.ts.map +1 -0
  47. package/{dist/types/core/types/TypeRedirectToParams.d.ts → core/types/TypeRedirectParams.d.ts} +6 -10
  48. package/core/types/TypeRedirectParams.d.ts.map +1 -0
  49. package/{dist/types/core → core}/types/TypeRoute.d.ts +0 -1
  50. package/core/types/TypeRoute.d.ts.map +1 -0
  51. package/{dist/types/core → core}/types/TypeRouteRaw.d.ts +1 -0
  52. package/core/types/TypeRouteRaw.d.ts.map +1 -0
  53. package/core/types/TypeRouter.d.ts +22 -0
  54. package/core/types/TypeRouter.d.ts.map +1 -0
  55. package/core/types/TypeValidator.d.ts.map +1 -0
  56. package/core/utils/PreventError.d.ts +4 -0
  57. package/core/utils/PreventError.d.ts.map +1 -0
  58. package/core/utils/RedirectError.d.ts +4 -0
  59. package/core/utils/RedirectError.d.ts.map +1 -0
  60. package/core/utils/addNames.d.ts.map +1 -0
  61. package/{dist/types/core → core}/utils/constants.d.ts +0 -2
  62. package/core/utils/constants.d.ts.map +1 -0
  63. package/core/utils/dynamic.d.ts.map +1 -0
  64. package/core/utils/findRouteByPathname.d.ts.map +1 -0
  65. package/core/utils/getDynamicValues.d.ts.map +1 -0
  66. package/core/utils/getInitialRoute.d.ts +7 -0
  67. package/core/utils/getInitialRoute.d.ts.map +1 -0
  68. package/core/utils/getQueryValues.d.ts.map +1 -0
  69. package/core/utils/getTypedEntries.d.ts.map +1 -0
  70. package/core/utils/history.d.ts.map +1 -0
  71. package/core/utils/loadComponentToConfig.d.ts.map +1 -0
  72. package/core/utils/queryString.d.ts +8 -0
  73. package/core/utils/queryString.d.ts.map +1 -0
  74. package/{dist/types/core → core}/utils/replaceDynamicValues.d.ts +1 -2
  75. package/core/utils/replaceDynamicValues.d.ts.map +1 -0
  76. package/core/utils/routerSetLoadedComponent.d.ts +7 -0
  77. package/core/utils/routerSetLoadedComponent.d.ts.map +1 -0
  78. package/index.cjs +406 -0
  79. package/index.mjs +383 -0
  80. package/package.json +67 -57
  81. package/preact/Router.d.ts +3 -0
  82. package/preact/Router.d.ts.map +1 -0
  83. package/preact/index.cjs +80 -0
  84. package/preact/index.d.ts.map +1 -0
  85. package/preact/index.mjs +57 -0
  86. package/react/Router.d.ts +5 -0
  87. package/react/Router.d.ts.map +1 -0
  88. package/react/index.cjs +79 -0
  89. package/react/index.d.ts.map +1 -0
  90. package/react/index.mjs +56 -0
  91. package/solid/Router.d.ts.map +1 -0
  92. package/solid/index.cjs +80 -0
  93. package/solid/index.d.ts +2 -0
  94. package/solid/index.d.ts.map +1 -0
  95. package/solid/index.mjs +57 -0
  96. package/vue/index.cjs +0 -0
  97. package/vue/index.d.ts +2 -0
  98. package/vue/index.d.ts.map +1 -0
  99. package/vue/index.mjs +0 -0
  100. package/LICENSE +0 -21
  101. package/README.md +0 -10
  102. package/dist/cjs/index.js +0 -381
  103. package/dist/cjs/package.json +0 -1
  104. package/dist/cjs/react/index.js +0 -149
  105. package/dist/cjs/react/package.json +0 -1
  106. package/dist/cjs/solid/index.js +0 -130
  107. package/dist/cjs/solid/package.json +0 -1
  108. package/dist/esm/index.js +0 -348
  109. package/dist/esm/package.json +0 -1
  110. package/dist/esm/react/index.js +0 -126
  111. package/dist/esm/react/package.json +0 -1
  112. package/dist/esm/solid/index.js +0 -107
  113. package/dist/esm/solid/package.json +0 -1
  114. package/dist/types/core/createRouterConfig.d.ts.map +0 -1
  115. package/dist/types/core/createRouterStore.d.ts +0 -4
  116. package/dist/types/core/createRouterStore.d.ts.map +0 -1
  117. package/dist/types/core/index.d.ts +0 -14
  118. package/dist/types/core/index.d.ts.map +0 -1
  119. package/dist/types/core/types/InterfaceRouterStore.d.ts +0 -24
  120. package/dist/types/core/types/InterfaceRouterStore.d.ts.map +0 -1
  121. package/dist/types/core/types/TypeCurrentRoute.d.ts.map +0 -1
  122. package/dist/types/core/types/TypeLifecycleConfig.d.ts.map +0 -1
  123. package/dist/types/core/types/TypePropsRouter.d.ts.map +0 -1
  124. package/dist/types/core/types/TypeRedirectToParams.d.ts.map +0 -1
  125. package/dist/types/core/types/TypeRoute.d.ts.map +0 -1
  126. package/dist/types/core/types/TypeRouteRaw.d.ts.map +0 -1
  127. package/dist/types/core/types/TypeRouteWithParams.d.ts +0 -6
  128. package/dist/types/core/types/TypeRouteWithParams.d.ts.map +0 -1
  129. package/dist/types/core/types/TypeValidator.d.ts.map +0 -1
  130. package/dist/types/core/utils/addNames.d.ts.map +0 -1
  131. package/dist/types/core/utils/constants.d.ts.map +0 -1
  132. package/dist/types/core/utils/dynamic.d.ts.map +0 -1
  133. package/dist/types/core/utils/findRouteByPathname.d.ts.map +0 -1
  134. package/dist/types/core/utils/getDynamicValues.d.ts.map +0 -1
  135. package/dist/types/core/utils/getInitialRoute.d.ts +0 -8
  136. package/dist/types/core/utils/getInitialRoute.d.ts.map +0 -1
  137. package/dist/types/core/utils/getQueryValues.d.ts.map +0 -1
  138. package/dist/types/core/utils/getTypedEntries.d.ts.map +0 -1
  139. package/dist/types/core/utils/history.d.ts.map +0 -1
  140. package/dist/types/core/utils/loadComponentToConfig.d.ts.map +0 -1
  141. package/dist/types/core/utils/replaceDynamicValues.d.ts.map +0 -1
  142. package/dist/types/react/Router.d.ts +0 -5
  143. package/dist/types/react/Router.d.ts.map +0 -1
  144. package/dist/types/react/index.d.ts.map +0 -1
  145. package/dist/types/react/useStore.d.ts +0 -8
  146. package/dist/types/react/useStore.d.ts.map +0 -1
  147. package/dist/types/solid/Router.d.ts.map +0 -1
  148. package/dist/types/solid/index.d.ts.map +0 -1
  149. package/dist/types/solid/useStore.d.ts +0 -7
  150. package/dist/types/solid/useStore.d.ts.map +0 -1
  151. package/dist/types/tsconfig.types.tsbuildinfo +0 -1
  152. /package/{dist/types/core → core}/types/TypeValidator.d.ts +0 -0
  153. /package/{dist/types/core → core}/utils/addNames.d.ts +0 -0
  154. /package/{dist/types/core → core}/utils/dynamic.d.ts +0 -0
  155. /package/{dist/types/core → core}/utils/findRouteByPathname.d.ts +0 -0
  156. /package/{dist/types/core → core}/utils/getDynamicValues.d.ts +0 -0
  157. /package/{dist/types/core → core}/utils/getQueryValues.d.ts +0 -0
  158. /package/{dist/types/core → core}/utils/getTypedEntries.d.ts +0 -0
  159. /package/{dist/types/core → core}/utils/history.d.ts +0 -0
  160. /package/{dist/types/core → core}/utils/loadComponentToConfig.d.ts +0 -0
  161. /package/{dist/types/react → preact}/index.d.ts +0 -0
  162. /package/{dist/types/solid → react}/index.d.ts +0 -0
  163. /package/{dist/types/solid → solid}/Router.d.ts +0 -0
package/index.mjs ADDED
@@ -0,0 +1,383 @@
1
+ // packages/core/utils/constants.ts
2
+ var constants = {
3
+ dynamicSeparator: ":",
4
+ pathPartSeparator: "/",
5
+ isClient: typeof window !== "undefined"
6
+ };
7
+
8
+ // packages/core/utils/dynamic.ts
9
+ function isDynamic(param) {
10
+ return param[0] === constants.dynamicSeparator;
11
+ }
12
+ function clearDynamic(param) {
13
+ return param.replace(new RegExp(`^${constants.dynamicSeparator}`), "");
14
+ }
15
+
16
+ // packages/core/utils/getDynamicValues.ts
17
+ function getDynamicValues(params) {
18
+ const { route, pathname } = params;
19
+ const pathnameArray = pathname.replace(/\?.+$/, "").split(constants.pathPartSeparator).filter(Boolean).map((str) => decodeURIComponent(str));
20
+ const routePathnameArray = route.path.split(constants.pathPartSeparator).filter(Boolean);
21
+ const dynamicParams = {};
22
+ for (let i = 0; i < routePathnameArray.length; i++) {
23
+ const paramName = routePathnameArray[i];
24
+ if (isDynamic(paramName)) dynamicParams[clearDynamic(paramName)] = pathnameArray[i];
25
+ }
26
+ return dynamicParams;
27
+ }
28
+
29
+ // packages/core/utils/findRouteByPathname.ts
30
+ function completeStaticMatch(pathname, path) {
31
+ return !path.includes(constants.dynamicSeparator) && (pathname === path || pathname === `${path}${constants.pathPartSeparator}`);
32
+ }
33
+ function findRouteByPathname({
34
+ pathname,
35
+ routes
36
+ }) {
37
+ let dynamicRouteMatch;
38
+ const pathnameArray = pathname.replace(/\?.+$/, "").split(constants.pathPartSeparator).filter(Boolean);
39
+ for (const routeName in routes) {
40
+ if (!Object.hasOwn(routes, routeName)) continue;
41
+ const route = routes[routeName];
42
+ if (completeStaticMatch(pathname, route.path)) return route;
43
+ if (dynamicRouteMatch) continue;
44
+ const routePathnameArray = route.path.split(constants.pathPartSeparator).filter(Boolean);
45
+ if (routePathnameArray.length !== pathnameArray.length) continue;
46
+ const someParamInvalid = routePathnameArray.some((paramName, i) => {
47
+ const paramFromUrl = pathnameArray[i];
48
+ if (!isDynamic(paramName)) return paramName !== paramFromUrl;
49
+ const validator = route.params?.[clearDynamic(paramName)];
50
+ if (typeof validator !== "function") {
51
+ throw new Error(`findRoute: missing validator for param "${paramName}"`);
52
+ }
53
+ return !validator(paramFromUrl);
54
+ });
55
+ if (!someParamInvalid) dynamicRouteMatch = route;
56
+ }
57
+ return dynamicRouteMatch;
58
+ }
59
+
60
+ // packages/core/utils/getTypedEntries.ts
61
+ var getTypedEntries = Object.entries;
62
+
63
+ // packages/core/utils/queryString.ts
64
+ function removeHash(input) {
65
+ const hashStart = input.indexOf("#");
66
+ return hashStart === -1 ? input : input.slice(0, hashStart);
67
+ }
68
+ var queryString = {
69
+ extract(input) {
70
+ const inputNoHash = removeHash(input);
71
+ const queryStart = inputNoHash.indexOf("?");
72
+ return queryStart === -1 ? "" : inputNoHash.slice(queryStart + 1);
73
+ },
74
+ parse(input) {
75
+ return Object.fromEntries(new URLSearchParams(input));
76
+ },
77
+ stringify(obj) {
78
+ return new URLSearchParams(obj).toString();
79
+ }
80
+ };
81
+
82
+ // packages/core/utils/getQueryValues.ts
83
+ function getQueryValues(params) {
84
+ const { route, pathname } = params;
85
+ const qs = queryString.extract(pathname);
86
+ if (!qs || !route.query) return {};
87
+ const query = queryString.parse(qs);
88
+ getTypedEntries(query).forEach(([key, value]) => {
89
+ const validator = route.query[key];
90
+ if (typeof validator !== "function" || !validator(value)) {
91
+ delete query[key];
92
+ }
93
+ });
94
+ return query;
95
+ }
96
+
97
+ // packages/core/utils/getInitialRoute.ts
98
+ function getInitialRoute(params) {
99
+ const route = findRouteByPathname({ pathname: params.pathname, routes: params.routes }) || params.routes.notFound;
100
+ return {
101
+ route: route.name,
102
+ query: getQueryValues({ route, pathname: params.pathname }),
103
+ params: getDynamicValues({ route, pathname: params.pathname })
104
+ };
105
+ }
106
+
107
+ // packages/core/utils/history.ts
108
+ import { createBrowserHistory } from "history";
109
+ var history = constants.isClient ? createBrowserHistory() : null;
110
+
111
+ // packages/core/utils/loadComponentToConfig.ts
112
+ function loadComponentToConfig(params) {
113
+ const { route } = params;
114
+ if (!route.component) {
115
+ return route.loader().then((module) => {
116
+ const { default: component, ...rest } = module;
117
+ route.component = component;
118
+ route.otherExports = rest;
119
+ });
120
+ }
121
+ return Promise.resolve();
122
+ }
123
+
124
+ // packages/core/utils/PreventError.ts
125
+ var PreventError = class extends Error {
126
+ constructor(message) {
127
+ super(message);
128
+ this.name = "PreventError";
129
+ }
130
+ };
131
+
132
+ // packages/core/utils/RedirectError.ts
133
+ var RedirectError = class extends Error {
134
+ constructor(message) {
135
+ super(message);
136
+ this.name = "RedirectError";
137
+ }
138
+ };
139
+
140
+ // packages/core/utils/replaceDynamicValues.ts
141
+ var re = new RegExp(`[^${constants.pathPartSeparator}]+`, "g");
142
+ function replaceDynamicValues({
143
+ route,
144
+ params = {}
145
+ }) {
146
+ return route.path.replace(re, (paramName) => {
147
+ if (!isDynamic(paramName)) return paramName;
148
+ const value = params[clearDynamic(paramName)];
149
+ if (!value) {
150
+ throw new Error(
151
+ `replaceDynamicValues: no param "${paramName}" passed for route ${route.name}`
152
+ );
153
+ }
154
+ return encodeURIComponent(value);
155
+ });
156
+ }
157
+
158
+ // packages/core/createRouter.ts
159
+ function createRouter(config) {
160
+ const router = config.adapters.makeObservable({
161
+ routesHistory: [],
162
+ currentRoute: {},
163
+ isRedirecting: false,
164
+ redirect: void 0,
165
+ restoreFromURL: void 0,
166
+ restoreFromServer: void 0,
167
+ get adapters() {
168
+ return config.adapters;
169
+ },
170
+ get routes() {
171
+ return config.routes;
172
+ },
173
+ get lifecycleParams() {
174
+ return config.lifecycleParams;
175
+ }
176
+ });
177
+ router.restoreFromServer = function restoreFromServer(obj) {
178
+ router.adapters.batch(() => {
179
+ router.routesHistory.push(...obj.routesHistory || []);
180
+ Object.assign(router.currentRoute, obj.currentRoute);
181
+ });
182
+ const preloadedRouteName = Object.keys(router.routes).find(
183
+ (routeName) => router.currentRoute.name === routeName
184
+ );
185
+ return loadComponentToConfig({ route: router.routes[preloadedRouteName] });
186
+ };
187
+ router.restoreFromURL = function restoreFromURL(params) {
188
+ return router.redirect(getInitialRoute({ routes: router.routes, ...params }));
189
+ };
190
+ router.redirect = async function redirect(config2) {
191
+ const { route: routeName, replace } = config2;
192
+ let currentRoute;
193
+ let currentPathname;
194
+ let currentUrl;
195
+ let currentSearch;
196
+ let currentQuery;
197
+ if (router.currentRoute?.name) {
198
+ currentRoute = router.routes[router.currentRoute.name];
199
+ currentPathname = replaceDynamicValues({
200
+ route: currentRoute,
201
+ params: router.currentRoute.params
202
+ });
203
+ currentQuery = router.currentRoute.query;
204
+ currentSearch = queryString.stringify(router.currentRoute.query);
205
+ currentUrl = `${currentPathname}${currentSearch ? `?${currentSearch}` : ""}`;
206
+ }
207
+ const nextRoute = router.routes[routeName];
208
+ const nextPathname = replaceDynamicValues({
209
+ route: nextRoute,
210
+ params: "params" in config2 ? config2.params : void 0
211
+ });
212
+ let nextQuery;
213
+ let nextUrl = nextPathname;
214
+ let nextSearch;
215
+ if ("query" in config2 && config2.query) {
216
+ const clearedQuery = getQueryValues({
217
+ route: nextRoute,
218
+ pathname: `${nextPathname}?${queryString.stringify(config2.query)}`
219
+ });
220
+ if (Object.keys(clearedQuery).length > 0) {
221
+ nextQuery = clearedQuery;
222
+ nextSearch = queryString.stringify(clearedQuery);
223
+ nextUrl = `${nextPathname}?${nextSearch}`;
224
+ }
225
+ }
226
+ if (currentUrl === nextUrl) return Promise.resolve();
227
+ if (currentPathname === nextPathname) {
228
+ if (currentSearch !== nextSearch) {
229
+ router.adapters.batch(() => {
230
+ router.adapters.replaceObject(router.currentRoute, {
231
+ ...router.currentRoute,
232
+ query: nextQuery || {}
233
+ });
234
+ router.routesHistory.push(nextUrl);
235
+ });
236
+ if (history && !replace) {
237
+ history.push({
238
+ hash: history.location.hash,
239
+ search: nextSearch,
240
+ pathname: nextPathname
241
+ });
242
+ }
243
+ }
244
+ return Promise.resolve();
245
+ }
246
+ router.adapters.batch(() => {
247
+ router.isRedirecting = true;
248
+ });
249
+ try {
250
+ const config3 = {
251
+ nextUrl,
252
+ nextRoute,
253
+ nextQuery,
254
+ nextSearch,
255
+ nextPathname,
256
+ currentUrl,
257
+ currentQuery,
258
+ currentRoute,
259
+ currentSearch,
260
+ currentPathname,
261
+ redirect: (redirectConfig2) => {
262
+ if (constants.isClient) return redirectConfig2;
263
+ const redirectRoute = router.routes[redirectConfig2.route];
264
+ const redirectParams = "params" in redirectConfig2 && redirectConfig2.params ? redirectConfig2.params : void 0;
265
+ let redirectUrl = replaceDynamicValues({
266
+ params: redirectParams,
267
+ route: redirectRoute
268
+ });
269
+ if ("query" in redirectConfig2 && redirectConfig2.query) {
270
+ const clearedQuery = getQueryValues({
271
+ route: nextRoute,
272
+ pathname: `${nextPathname}?${queryString.stringify(redirectConfig2.query)}`
273
+ });
274
+ if (Object.keys(clearedQuery).length > 0) {
275
+ redirectUrl = `${redirectUrl}?${queryString.stringify(clearedQuery)}`;
276
+ }
277
+ }
278
+ throw new RedirectError(redirectUrl);
279
+ },
280
+ preventRedirect: () => {
281
+ throw new PreventError(`Redirect to ${nextUrl} was prevented`);
282
+ }
283
+ };
284
+ await currentRoute?.beforeLeave?.(config3, ...router.lifecycleParams || []);
285
+ const redirectConfig = await nextRoute.beforeEnter?.(config3, ...router.lifecycleParams || []);
286
+ if (redirectConfig) return redirect(redirectConfig);
287
+ await loadComponentToConfig({ route: router.routes[nextRoute.name] });
288
+ } catch (error) {
289
+ if (error instanceof PreventError) {
290
+ return Promise.resolve();
291
+ }
292
+ if (error instanceof RedirectError) {
293
+ throw error;
294
+ }
295
+ console.error(error);
296
+ await loadComponentToConfig({ route: router.routes.internalError });
297
+ router.adapters.batch(() => {
298
+ router.adapters.replaceObject(router.currentRoute, {
299
+ name: router.routes.internalError.name,
300
+ path: router.routes.internalError.path,
301
+ props: router.routes[router.routes.internalError.name].props,
302
+ query: router.adapters.makeObservable({}),
303
+ params: router.adapters.makeObservable({}),
304
+ pageId: router.routes[router.routes.internalError.name].pageId
305
+ });
306
+ router.isRedirecting = false;
307
+ });
308
+ return Promise.resolve();
309
+ }
310
+ router.adapters.batch(() => {
311
+ router.adapters.replaceObject(router.currentRoute, {
312
+ name: nextRoute.name,
313
+ path: nextRoute.path,
314
+ props: router.routes[nextRoute.name].props,
315
+ query: getQueryValues({ route: nextRoute, pathname: nextUrl }),
316
+ params: getDynamicValues({ route: nextRoute, pathname: nextUrl }),
317
+ pageId: router.routes[nextRoute.name].pageId
318
+ });
319
+ const lastUrl = router.routesHistory[router.routesHistory.length - 1];
320
+ if (lastUrl !== nextUrl) {
321
+ router.routesHistory.push(nextUrl);
322
+ }
323
+ if (history && !replace) {
324
+ history.push({
325
+ hash: history.location.hash,
326
+ search: "query" in config2 ? `?${queryString.stringify(config2.query)}` : "",
327
+ pathname: nextPathname
328
+ });
329
+ }
330
+ router.isRedirecting = false;
331
+ });
332
+ return Promise.resolve();
333
+ };
334
+ return router;
335
+ }
336
+
337
+ // packages/core/utils/addNames.ts
338
+ function addNames(obj) {
339
+ Object.entries(obj).forEach(([key, value]) => {
340
+ value.name = key;
341
+ });
342
+ return obj;
343
+ }
344
+
345
+ // packages/core/createRoutes.ts
346
+ function createRoutes(config) {
347
+ return addNames(config);
348
+ }
349
+
350
+ // packages/core/utils/routerSetLoadedComponent.ts
351
+ function routerSetLoadedComponent(props, config) {
352
+ const currentRouteName = props.router.currentRoute.name;
353
+ const currentRoutePage = props.router.currentRoute.pageId;
354
+ const componentConfig = props.router.routes[currentRouteName];
355
+ let preventRedirect = false;
356
+ if (props.router.isRedirecting) preventRedirect = true;
357
+ else if (config.loadedComponentName === currentRouteName) preventRedirect = true;
358
+ else if (config.loadedComponentPage != null && currentRouteName != null) {
359
+ if (config.loadedComponentPage === currentRoutePage) {
360
+ props.router.adapters.batch(() => {
361
+ config.currentProps = "props" in componentConfig ? componentConfig.props || {} : {};
362
+ });
363
+ preventRedirect = true;
364
+ }
365
+ }
366
+ if (preventRedirect) return;
367
+ props.router.adapters.batch(() => {
368
+ if (config.loadedComponentName) props.beforeUpdatePageComponent?.();
369
+ props.beforeSetPageComponent?.(componentConfig);
370
+ config.currentProps = "props" in componentConfig ? componentConfig.props : {};
371
+ config.loadedComponentName = currentRouteName;
372
+ config.loadedComponentPage = componentConfig.pageId;
373
+ });
374
+ }
375
+ export {
376
+ RedirectError,
377
+ createRouter,
378
+ createRoutes,
379
+ history,
380
+ loadComponentToConfig,
381
+ replaceDynamicValues,
382
+ routerSetLoadedComponent
383
+ };
package/package.json CHANGED
@@ -2,76 +2,86 @@
2
2
  "name": "reactive-route",
3
3
  "author": "Dmitry Kazakov",
4
4
  "license": "MIT",
5
- "version": "0.0.1-alpha.3",
5
+ "version": "0.0.1-alpha.31",
6
6
  "description": "Reactive Router for different frameworks",
7
7
  "repository": {
8
8
  "type": "git",
9
9
  "url": "git+https://github.com/dkazakov8/reactive-route.git"
10
10
  },
11
- "scripts": {
12
- "upd": "rm -rf ./node_modules&&pnpm i --prefer-offline",
13
- "build": "rm -rf ./dist&&node --import tsx ./scripts/build.ts&&tsc -b ./tsconfig.types.json",
14
- "test": "vitest run&&node --import tsx ./scripts/genCoverageBadge.ts",
15
- "test-watch": "vitest --coverage=false",
16
- "npm-publish": "pnpm run build&&pnpm run test&&npm publish --access public",
17
- "analyze:js": "biome check --no-errors-on-unmatched .",
18
- "format:js": "biome check --no-errors-on-unmatched --write",
19
- "check-types": "tsc --project tsconfig.json",
20
- "check-types-example": "tsc --project ./examples/simple_ssr/tsconfig.json",
21
- "prepare": "husky"
22
- },
23
11
  "dependencies": {
24
- "history": "5.3.0",
25
- "query-string": "7.1.3"
12
+ "history": "5.3.0"
26
13
  },
27
- "devDependencies": {
28
- "@types/node": "22.14.1",
29
- "@types/lodash": "4.17.16",
30
- "vitest": "3.2.4",
31
- "@testing-library/react": "16.3.0",
32
- "@vitest/coverage-istanbul": "3.2.4",
33
- "vite-plugin-solid": "2.11.8",
34
- "global-jsdom": "26.0.0",
35
- "babel-preset-solid": "1.9.9",
36
- "@solidjs/testing-library": "0.8.10",
37
- "lodash": "4.17.21",
38
- "mobx": "6.13.7",
39
- "mobx-react-lite": "4.1.0",
40
- "react": "19.1.1",
41
- "solid-js": "1.9.9",
42
- "@babel/core": "7.28.3",
43
- "@babel/preset-env": "7.28.3",
44
- "@babel/preset-typescript": "7.27.1",
45
- "@espcom/esbuild-plugin-replace": "1.3.0",
46
- "badge-maker": "5.0.2",
47
- "esbuild": "0.25.9",
48
- "regenerator-runtime": "0.14.1",
49
- "@biomejs/biome": "2.2.2",
50
- "husky": "9.1.7",
51
- "lint-staged": "16.1.6",
52
- "tsx": "4.20.5",
53
- "typescript": "5.9.2",
54
- "xml-splitter": "1.2.1"
14
+ "engines": {
15
+ "node": ">=22"
55
16
  },
56
- "packageManager": "pnpm@10.9.0",
57
17
  "exports": {
58
18
  ".": {
59
- "types": "./dist/types/core/index.d.ts",
60
- "require": "./dist/cjs/index.js",
61
- "import": "./dist/esm/index.js"
19
+ "types": "./core/index.d.ts",
20
+ "require": "./index.cjs",
21
+ "import": "./index.mjs"
22
+ },
23
+ "./solid": {
24
+ "types": "./solid/index.d.ts",
25
+ "require": "./solid/index.cjs",
26
+ "import": "./solid/index.mjs"
62
27
  },
63
28
  "./react": {
64
- "types": "./dist/types/react/index.d.ts",
65
- "require": "./dist/cjs/react/index.js",
66
- "import": "./dist/esm/react/index.js"
29
+ "types": "./react/index.d.ts",
30
+ "require": "./react/index.cjs",
31
+ "import": "./react/index.mjs"
67
32
  },
68
- "./solid": {
69
- "types": "./dist/types/solid/index.d.ts",
70
- "require": "./dist/cjs/solid/index.js",
71
- "import": "./dist/esm/solid/index.js"
33
+ "./preact": {
34
+ "types": "./preact/index.d.ts",
35
+ "require": "./preact/index.cjs",
36
+ "import": "./preact/index.mjs"
37
+ },
38
+ "./vue": {
39
+ "types": "./vue/index.d.ts",
40
+ "require": "./vue/index.cjs",
41
+ "import": "./vue/index.mjs"
42
+ },
43
+ "./adapters/mobx-react": {
44
+ "types": "./adapters/mobx-react.d.ts",
45
+ "require": "./adapters/mobx-react.cjs",
46
+ "import": "./adapters/mobx-react.mjs"
47
+ },
48
+ "./adapters/mobx-preact": {
49
+ "types": "./adapters/mobx-preact.d.ts",
50
+ "require": "./adapters/mobx-preact.cjs",
51
+ "import": "./adapters/mobx-preact.mjs"
52
+ },
53
+ "./adapters/mobx-solid": {
54
+ "types": "./adapters/mobx-solid.d.ts",
55
+ "require": "./adapters/mobx-solid.cjs",
56
+ "import": "./adapters/mobx-solid.mjs"
57
+ },
58
+ "./adapters/solid": {
59
+ "types": "./adapters/solid.d.ts",
60
+ "require": "./adapters/solid.cjs",
61
+ "import": "./adapters/solid.mjs"
62
+ },
63
+ "./adapters/kr-observable-react": {
64
+ "types": "./adapters/kr-observable-react.d.ts",
65
+ "require": "./adapters/kr-observable-react.cjs",
66
+ "import": "./adapters/kr-observable-react.mjs"
67
+ },
68
+ "./adapters/kr-observable-preact": {
69
+ "types": "./adapters/kr-observable-preact.d.ts",
70
+ "require": "./adapters/kr-observable-preact.cjs",
71
+ "import": "./adapters/kr-observable-preact.mjs"
72
+ },
73
+ "./adapters/kr-observable-solid": {
74
+ "types": "./adapters/kr-observable-solid.d.ts",
75
+ "require": "./adapters/kr-observable-solid.cjs",
76
+ "import": "./adapters/kr-observable-solid.mjs"
77
+ },
78
+ "./adapters/vue": {
79
+ "types": "./adapters/vue.d.ts",
80
+ "require": "./adapters/vue.cjs",
81
+ "import": "./adapters/vue.mjs"
72
82
  }
73
83
  },
74
- "main": "dist/cjs/index.js",
75
- "module": "dist/esm/index.js",
76
- "types": "dist/types/core/index.d.ts"
84
+ "main": "./index.cjs",
85
+ "module": "./index.mjs",
86
+ "types": "./core/index.d.ts"
77
87
  }
@@ -0,0 +1,3 @@
1
+ import { TypePropsRouter, TypeRoute } from 'reactive-route';
2
+ export declare function Router<TRoutes extends Record<string, TypeRoute>>(props: TypePropsRouter<TRoutes>): any;
3
+ //# sourceMappingURL=Router.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Router.d.ts","sourceRoot":"","sources":["../../packages/preact/Router.tsx"],"names":[],"mappings":"AACA,OAAO,EAAqC,eAAe,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAmE/F,wBAAgB,MAAM,CAAC,OAAO,SAAS,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,EAAE,KAAK,EAAE,eAAe,CAAC,OAAO,CAAC,GAK1D,GAAG,CACzC"}
@@ -0,0 +1,80 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // packages/preact/index.ts
21
+ var preact_exports = {};
22
+ __export(preact_exports, {
23
+ Router: () => Router
24
+ });
25
+ module.exports = __toCommonJS(preact_exports);
26
+
27
+ // packages/preact/Router.tsx
28
+ var import_hooks = require("preact/hooks");
29
+ var import_reactive_route = require("reactive-route");
30
+ var import_jsx_runtime = require("preact/jsx-runtime");
31
+ function RouterInner(props) {
32
+ const disposerRef = (0, import_hooks.useRef)(null);
33
+ const redirectOnHistoryPop = (0, import_hooks.useCallback)(() => {
34
+ if (!import_reactive_route.history) return;
35
+ import_reactive_route.history.listen((params) => {
36
+ if (params.action !== "POP") return;
37
+ const previousRoutePathname = props.router.routesHistory[props.router.routesHistory.length - 2];
38
+ if (previousRoutePathname === params.location.pathname) {
39
+ props.router.adapters.batch(() => props.router.routesHistory.pop());
40
+ }
41
+ void props.router.restoreFromURL({
42
+ pathname: import_reactive_route.history.location.pathname,
43
+ replace: true
44
+ });
45
+ });
46
+ }, []);
47
+ const [config] = (0, import_hooks.useState)(
48
+ () => props.router.adapters.makeObservable({
49
+ loadedComponentName: void 0,
50
+ loadedComponentPage: void 0,
51
+ currentProps: {}
52
+ })
53
+ );
54
+ (0, import_hooks.useState)(() => {
55
+ props.router.adapters.batch(() => {
56
+ props.beforeMount?.();
57
+ redirectOnHistoryPop();
58
+ (0, import_reactive_route.routerSetLoadedComponent)(props, config);
59
+ disposerRef.current = props.router.adapters.autorun(
60
+ () => (0, import_reactive_route.routerSetLoadedComponent)(props, config)
61
+ );
62
+ });
63
+ });
64
+ (0, import_hooks.useEffect)(() => {
65
+ return () => {
66
+ disposerRef.current?.();
67
+ };
68
+ }, []);
69
+ if (!config.loadedComponentName) return null;
70
+ const LoadedComponent = props.router.routes[config.loadedComponentName]?.component || null;
71
+ if (LoadedComponent)
72
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(LoadedComponent, { ...config.currentProps, router: props.router });
73
+ return null;
74
+ }
75
+ function Router(props) {
76
+ const [Component] = (0, import_hooks.useState)(
77
+ () => props.router.adapters.observer ? props.router.adapters.observer(RouterInner) : RouterInner
78
+ );
79
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Component, { ...props });
80
+ }
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../packages/preact/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC"}
@@ -0,0 +1,57 @@
1
+ // packages/preact/Router.tsx
2
+ import { useCallback, useEffect, useRef, useState } from "preact/hooks";
3
+ import { history, routerSetLoadedComponent } from "reactive-route";
4
+ import { jsx } from "preact/jsx-runtime";
5
+ function RouterInner(props) {
6
+ const disposerRef = useRef(null);
7
+ const redirectOnHistoryPop = useCallback(() => {
8
+ if (!history) return;
9
+ history.listen((params) => {
10
+ if (params.action !== "POP") return;
11
+ const previousRoutePathname = props.router.routesHistory[props.router.routesHistory.length - 2];
12
+ if (previousRoutePathname === params.location.pathname) {
13
+ props.router.adapters.batch(() => props.router.routesHistory.pop());
14
+ }
15
+ void props.router.restoreFromURL({
16
+ pathname: history.location.pathname,
17
+ replace: true
18
+ });
19
+ });
20
+ }, []);
21
+ const [config] = useState(
22
+ () => props.router.adapters.makeObservable({
23
+ loadedComponentName: void 0,
24
+ loadedComponentPage: void 0,
25
+ currentProps: {}
26
+ })
27
+ );
28
+ useState(() => {
29
+ props.router.adapters.batch(() => {
30
+ props.beforeMount?.();
31
+ redirectOnHistoryPop();
32
+ routerSetLoadedComponent(props, config);
33
+ disposerRef.current = props.router.adapters.autorun(
34
+ () => routerSetLoadedComponent(props, config)
35
+ );
36
+ });
37
+ });
38
+ useEffect(() => {
39
+ return () => {
40
+ disposerRef.current?.();
41
+ };
42
+ }, []);
43
+ if (!config.loadedComponentName) return null;
44
+ const LoadedComponent = props.router.routes[config.loadedComponentName]?.component || null;
45
+ if (LoadedComponent)
46
+ return /* @__PURE__ */ jsx(LoadedComponent, { ...config.currentProps, router: props.router });
47
+ return null;
48
+ }
49
+ function Router(props) {
50
+ const [Component] = useState(
51
+ () => props.router.adapters.observer ? props.router.adapters.observer(RouterInner) : RouterInner
52
+ );
53
+ return /* @__PURE__ */ jsx(Component, { ...props });
54
+ }
55
+ export {
56
+ Router
57
+ };
@@ -0,0 +1,5 @@
1
+ import { TypePropsRouter, TypeRoute } from 'reactive-route';
2
+ declare function RouterWrapper<TRoutes extends Record<string, TypeRoute>>(props: TypePropsRouter<TRoutes>): any;
3
+ export declare const Router: typeof RouterWrapper;
4
+ export {};
5
+ //# sourceMappingURL=Router.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Router.d.ts","sourceRoot":"","sources":["../../packages/react/Router.tsx"],"names":[],"mappings":"AACA,OAAO,EAAqC,eAAe,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAmE/F,iBAAS,aAAa,CAAC,OAAO,SAAS,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,EAAE,KAAK,EAAE,eAAe,CAAC,OAAO,CAAC,GAK1D,GAAG,CACzC;AAED,eAAO,MAAM,MAAM,EAA0B,OAAO,aAAa,CAAC"}