@rpcbase/router 0.30.0 → 0.32.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,4 +1,4 @@
1
- import { Request } from "express";
2
- import { StaticHandlerContext } from "./index";
1
+ import { Request } from 'express';
2
+ import { StaticHandlerContext } from './index';
3
3
  export declare function applyRouteLoaders(req: Request, dataRoutes: any[]): Promise<StaticHandlerContext>;
4
4
  //# sourceMappingURL=applyRouteLoaders.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"applyRouteLoaders.d.ts","sourceRoot":"","sources":["../../../../pkg/router/src/applyRouteLoaders.ts"],"names":[],"mappings":"AACA,OAAO,EAAC,OAAO,EAAC,MAAM,SAAS,CAAA;AAE/B,OAAO,EACL,oBAAoB,EAMrB,MAAM,SAAS,CAAA;AAsDhB,wBAAsB,iBAAiB,CACrC,GAAG,EAAE,OAAO,EACZ,UAAU,EAAE,GAAG,EAAE,GAChB,OAAO,CAAC,oBAAoB,CAAC,CAkG/B"}
1
+ {"version":3,"file":"applyRouteLoaders.d.ts","sourceRoot":"","sources":["../src/applyRouteLoaders.ts"],"names":[],"mappings":"AACA,OAAO,EAAC,OAAO,EAAC,MAAM,SAAS,CAAA;AAE/B,OAAO,EACL,oBAAoB,EAMrB,MAAM,SAAS,CAAA;AAsDhB,wBAAsB,iBAAiB,CACrC,GAAG,EAAE,OAAO,EACZ,UAAU,EAAE,GAAG,EAAE,GAChB,OAAO,CAAC,oBAAoB,CAAC,CAkG/B"}
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- export * from "react-router";
2
- export * from "./applyRouteLoaders";
3
- export * from "./loadRoute";
4
- export * from "./useApplyMeta";
1
+ export * from 'react-router';
2
+ export * from './applyRouteLoaders';
3
+ export * from './loadRoute';
4
+ export * from './useApplyMeta';
5
5
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../pkg/router/src/index.ts"],"names":[],"mappings":"AAAA,cAAc,cAAc,CAAA;AAC5B,cAAc,qBAAqB,CAAA;AACnC,cAAc,aAAa,CAAA;AAC3B,cAAc,gBAAgB,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,cAAc,CAAA;AAC5B,cAAc,qBAAqB,CAAA;AACnC,cAAc,aAAa,CAAA;AAC3B,cAAc,gBAAgB,CAAA"}
package/dist/index.js CHANGED
@@ -1,4 +1,178 @@
1
+ import { createPath, matchRoutes, parsePath, useLocation } from "react-router";
1
2
  export * from "react-router";
2
- export * from "./applyRouteLoaders";
3
- export * from "./loadRoute";
4
- export * from "./useApplyMeta";
3
+ import { lazy, useEffect } from "react";
4
+ function createLocation(current, to, state = null, key) {
5
+ const location = {
6
+ pathname: current,
7
+ search: "",
8
+ hash: "",
9
+ ...typeof to === "string" ? parsePath(to) : to,
10
+ state,
11
+ // TODO: This could be cleaned up. push/replace should probably just take
12
+ // full Locations now and avoid the need to run through this flow at all
13
+ // But that's a pretty big refactor to the current test suite so going to
14
+ // keep as is for the time being and just let any incoming keys take precedence
15
+ key: to && to.key || key
16
+ };
17
+ return location;
18
+ }
19
+ function getShortCircuitMatches(routes) {
20
+ const route = routes.length === 1 ? routes[0] : routes.find((r) => r.index || !r.path || r.path === "/") || {
21
+ id: "__shim-error-route__"
22
+ };
23
+ return {
24
+ matches: [
25
+ {
26
+ params: {},
27
+ pathname: "",
28
+ pathnameBase: "",
29
+ route
30
+ }
31
+ ],
32
+ route
33
+ };
34
+ }
35
+ async function applyRouteLoaders(req, dataRoutes) {
36
+ const baseUrl = `${req.protocol}://${req.get("host")}`;
37
+ const url = new URL(req.originalUrl, baseUrl);
38
+ const method = req.method;
39
+ const location = createLocation("", createPath(url), null, "default");
40
+ const baseContext = {
41
+ basename: "",
42
+ location,
43
+ loaderHeaders: {},
44
+ actionHeaders: {}
45
+ };
46
+ const matches = matchRoutes(dataRoutes, location) || [];
47
+ if (!matches) {
48
+ const error = {
49
+ status: 404,
50
+ message: `No route matches URL: ${req.originalUrl}`
51
+ };
52
+ const { matches: notFoundMatches, route } = getShortCircuitMatches(dataRoutes);
53
+ return {
54
+ ...baseContext,
55
+ matches: notFoundMatches,
56
+ loaderData: {},
57
+ actionData: null,
58
+ errors: { [route.id]: error },
59
+ statusCode: 404
60
+ };
61
+ }
62
+ if (method !== "GET") {
63
+ return {
64
+ ...baseContext,
65
+ matches,
66
+ loaderData: {},
67
+ actionData: null,
68
+ errors: null,
69
+ statusCode: 200
70
+ };
71
+ }
72
+ const loaderPromisesResults = await Promise.allSettled(
73
+ matches.map(async (match) => {
74
+ const { route, params } = match;
75
+ if (!route.loader) return null;
76
+ try {
77
+ return {
78
+ id: route.id,
79
+ data: await route.loader({
80
+ params,
81
+ ctx: { req }
82
+ })
83
+ };
84
+ } catch (error) {
85
+ throw { id: route.id, reason: error };
86
+ }
87
+ })
88
+ );
89
+ const loaderData = {};
90
+ let errors = null;
91
+ for (const result of loaderPromisesResults) {
92
+ if (result.status === "fulfilled") {
93
+ if (result.value) {
94
+ loaderData[result.value.id] = result.value.data;
95
+ }
96
+ } else if (result.status === "rejected") {
97
+ const id = result.reason?.id;
98
+ if (!id) {
99
+ throw new Error(`missing route ID in error: ${result.reason}`);
100
+ }
101
+ if (!errors) {
102
+ errors = {};
103
+ }
104
+ errors[id] = result.reason;
105
+ }
106
+ }
107
+ return {
108
+ ...baseContext,
109
+ matches,
110
+ loaderData,
111
+ actionData: null,
112
+ errors,
113
+ statusCode: Object.keys(errors || {}).length > 0 ? 500 : 200
114
+ };
115
+ }
116
+ const loadRoute = (importPromise) => {
117
+ const Component = lazy(async () => {
118
+ const module = await importPromise;
119
+ return { default: module.default };
120
+ });
121
+ const loader = async (args) => {
122
+ const module = await importPromise;
123
+ if (!module.loader) return null;
124
+ return module.loader(args);
125
+ };
126
+ return { Component, loader };
127
+ };
128
+ const useApplyMeta = () => {
129
+ const location = useLocation();
130
+ useEffect(() => {
131
+ const loadMeta = async () => {
132
+ let defaultTitle = "";
133
+ let defaultMeta = [];
134
+ let pagesMeta = {};
135
+ try {
136
+ const importPath = "@/static/meta";
137
+ const module = await import(importPath);
138
+ defaultTitle = module.defaultTitle || defaultTitle;
139
+ defaultMeta = module.defaultMeta || defaultMeta;
140
+ pagesMeta = module.pagesMeta || pagesMeta;
141
+ } catch (error) {
142
+ return;
143
+ }
144
+ let pageMeta = pagesMeta[location.pathname];
145
+ if (!pageMeta) {
146
+ pageMeta = { title: defaultTitle, meta: defaultMeta };
147
+ }
148
+ document.title = pageMeta.title;
149
+ document.querySelectorAll("[data-react-meta]").forEach((tag) => tag.remove());
150
+ pageMeta.meta.forEach((meta) => {
151
+ const metaElement = document.createElement("meta");
152
+ metaElement.setAttribute("data-react-meta", "true");
153
+ Object.entries(meta).forEach(([key, value]) => {
154
+ if (value) {
155
+ metaElement.setAttribute(key, value.toString());
156
+ }
157
+ });
158
+ document.head.appendChild(metaElement);
159
+ });
160
+ const canonicalUrl = `${window.location.origin}${location.pathname}`;
161
+ const existingCanonical = document.querySelector('link[rel="canonical"]');
162
+ if (existingCanonical) {
163
+ existingCanonical.remove();
164
+ }
165
+ const canonicalLink = document.createElement("link");
166
+ canonicalLink.setAttribute("rel", "canonical");
167
+ canonicalLink.setAttribute("href", canonicalUrl);
168
+ canonicalLink.setAttribute("data-react-meta", "true");
169
+ document.head.appendChild(canonicalLink);
170
+ };
171
+ loadMeta();
172
+ }, [location.pathname]);
173
+ };
174
+ export {
175
+ applyRouteLoaders,
176
+ loadRoute,
177
+ useApplyMeta
178
+ };
@@ -1,5 +1,5 @@
1
- import { LoaderFunction } from "react-router";
2
- import { Loader } from "@rpcbase/client";
1
+ import { LoaderFunction } from 'react-router';
2
+ import { Loader } from '../../client/src';
3
3
  type RouteModule = {
4
4
  default: React.ComponentType<unknown>;
5
5
  loader?: Loader;
@@ -1 +1 @@
1
- {"version":3,"file":"loadRoute.d.ts","sourceRoot":"","sources":["../../../../pkg/router/src/loadRoute.tsx"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAA;AAC7C,OAAO,EAAE,MAAM,EAAc,MAAM,iBAAiB,CAAA;AAGpD,KAAK,WAAW,GAAG;IACjB,OAAO,EAAE,KAAK,CAAC,aAAa,CAAC,OAAO,CAAC,CAAA;IACrC,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB,CAAA;AAED,KAAK,eAAe,GAAG;IACrB,SAAS,EAAE,KAAK,CAAC,mBAAmB,CAAC,KAAK,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC,CAAA;IAClE,MAAM,CAAC,EAAE,cAAc,CAAA;CACxB,CAAA;AAED,eAAO,MAAM,SAAS,GAAI,eAAe,OAAO,CAAC,WAAW,CAAC,KAAG,eAa/D,CAAA"}
1
+ {"version":3,"file":"loadRoute.d.ts","sourceRoot":"","sources":["../src/loadRoute.tsx"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAA;AAC7C,OAAO,EAAE,MAAM,EAAc,MAAM,iBAAiB,CAAA;AAGpD,KAAK,WAAW,GAAG;IACjB,OAAO,EAAE,KAAK,CAAC,aAAa,CAAC,OAAO,CAAC,CAAA;IACrC,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB,CAAA;AAED,KAAK,eAAe,GAAG;IACrB,SAAS,EAAE,KAAK,CAAC,mBAAmB,CAAC,KAAK,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC,CAAA;IAClE,MAAM,CAAC,EAAE,cAAc,CAAA;CACxB,CAAA;AAED,eAAO,MAAM,SAAS,GAAI,eAAe,OAAO,CAAC,WAAW,CAAC,KAAG,eAa/D,CAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"useApplyMeta.d.ts","sourceRoot":"","sources":["../../../../pkg/router/src/useApplyMeta.tsx"],"names":[],"mappings":"AAkBA,eAAO,MAAM,YAAY,YAoExB,CAAA"}
1
+ {"version":3,"file":"useApplyMeta.d.ts","sourceRoot":"","sources":["../src/useApplyMeta.tsx"],"names":[],"mappings":"AAkBA,eAAO,MAAM,YAAY,YAoExB,CAAA"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rpcbase/router",
3
- "version": "0.30.0",
3
+ "version": "0.32.0",
4
4
  "type": "module",
5
5
  "files": [
6
6
  "dist"
@@ -13,12 +13,12 @@
13
13
  },
14
14
  "wireit": {
15
15
  "build": {
16
- "command": "node ../../scripts/build-package.js router",
16
+ "command": "../../node_modules/.bin/vite build",
17
17
  "files": [
18
18
  "src/**/*",
19
19
  "../../tsconfig.json",
20
20
  "../../tsconfig.base.json",
21
- "../../scripts/build-package.js"
21
+ "../../scripts/vite.config-pkg.js"
22
22
  ],
23
23
  "output": [
24
24
  "dist/"
@@ -41,8 +41,6 @@
41
41
  }
42
42
  }
43
43
  },
44
- "dependencies": {
45
- "react-router": "7.8.2"
46
- },
44
+ "dependencies": {},
47
45
  "devDependencies": {}
48
46
  }
@@ -1,126 +0,0 @@
1
- import { matchRoutes, createPath, parsePath, } from "./index";
2
- function createKey() {
3
- return Math.random().toString(36).substring(2, 10);
4
- }
5
- function createLocation(current, to, state = null, key) {
6
- const location = {
7
- pathname: typeof current === "string" ? current : current.pathname,
8
- search: "",
9
- hash: "",
10
- ...(typeof to === "string" ? parsePath(to) : to),
11
- state,
12
- // TODO: This could be cleaned up. push/replace should probably just take
13
- // full Locations now and avoid the need to run through this flow at all
14
- // But that's a pretty big refactor to the current test suite so going to
15
- // keep as is for the time being and just let any incoming keys take precedence
16
- key: (to && to.key) || key || createKey(),
17
- };
18
- return location;
19
- }
20
- function getShortCircuitMatches(routes) {
21
- // Prefer a root layout route if present, otherwise shim in a route object
22
- const route = routes.length === 1
23
- ? routes[0]
24
- : routes.find((r) => r.index || !r.path || r.path === "/") || {
25
- id: "__shim-error-route__",
26
- };
27
- return {
28
- matches: [
29
- {
30
- params: {},
31
- pathname: "",
32
- pathnameBase: "",
33
- route,
34
- },
35
- ],
36
- route,
37
- };
38
- }
39
- export async function applyRouteLoaders(req, dataRoutes) {
40
- const baseUrl = `${req.protocol}://${req.get("host")}`;
41
- const url = new URL(req.originalUrl, baseUrl);
42
- const method = req.method;
43
- const location = createLocation("", createPath(url), null, "default");
44
- const baseContext = {
45
- basename: "",
46
- location,
47
- loaderHeaders: {},
48
- actionHeaders: {},
49
- };
50
- // Match routes to the current location
51
- const matches = matchRoutes(dataRoutes, location) || [];
52
- // Handle 404 (no matches)
53
- if (!matches) {
54
- const error = {
55
- status: 404,
56
- message: `No route matches URL: ${req.originalUrl}`,
57
- };
58
- const { matches: notFoundMatches, route } = getShortCircuitMatches(dataRoutes);
59
- return {
60
- ...baseContext,
61
- matches: notFoundMatches,
62
- loaderData: {},
63
- actionData: null,
64
- errors: { [route.id]: error },
65
- statusCode: 404,
66
- };
67
- }
68
- // Skip if anything but GET
69
- if (method !== "GET") {
70
- return {
71
- ...baseContext,
72
- matches,
73
- loaderData: {},
74
- actionData: null,
75
- errors: null,
76
- statusCode: 200,
77
- };
78
- }
79
- // Collect loader data and errors
80
- const loaderPromisesResults = await Promise.allSettled(matches.map(async (match) => {
81
- const { route, params } = match;
82
- if (!route.loader)
83
- return null;
84
- try {
85
- return {
86
- id: route.id,
87
- data: await route.loader({
88
- params,
89
- ctx: { req },
90
- }),
91
- };
92
- }
93
- catch (error) {
94
- // Include the route ID in the error for better traceability
95
- throw { id: route.id, reason: error };
96
- }
97
- }));
98
- const loaderData = {};
99
- // TODO: add i18n error handling
100
- let errors = null;
101
- for (const result of loaderPromisesResults) {
102
- if (result.status === "fulfilled") {
103
- if (result.value) {
104
- loaderData[result.value.id] = result.value.data;
105
- }
106
- }
107
- else if (result.status === "rejected") {
108
- const id = result.reason?.id;
109
- if (!id) {
110
- throw new Error(`missing route ID in error: ${result.reason}`);
111
- }
112
- if (!errors) {
113
- errors = {};
114
- }
115
- errors[id] = result.reason;
116
- }
117
- }
118
- return {
119
- ...baseContext,
120
- matches,
121
- loaderData,
122
- actionData: null,
123
- errors,
124
- statusCode: Object.keys(errors || {}).length > 0 ? 500 : 200,
125
- };
126
- }
package/dist/loadRoute.js DELETED
@@ -1,14 +0,0 @@
1
- import { lazy } from "react";
2
- export const loadRoute = (importPromise) => {
3
- const Component = lazy(async () => {
4
- const module = await importPromise;
5
- return { default: module.default };
6
- });
7
- const loader = async (args) => {
8
- const module = await importPromise;
9
- if (!module.loader)
10
- return null;
11
- return module.loader(args);
12
- };
13
- return { Component, loader: loader };
14
- };
@@ -1,56 +0,0 @@
1
- import { useEffect } from "react";
2
- import { useLocation } from "react-router";
3
- export const useApplyMeta = () => {
4
- const location = useLocation();
5
- useEffect(() => {
6
- const loadMeta = async () => {
7
- let defaultTitle = "";
8
- let defaultMeta = [];
9
- let pagesMeta = {};
10
- try {
11
- const importPath = "@/static/meta";
12
- const module = (await import(importPath));
13
- defaultTitle = module.defaultTitle || defaultTitle;
14
- defaultMeta = module.defaultMeta || defaultMeta;
15
- pagesMeta = module.pagesMeta || pagesMeta;
16
- }
17
- catch (error) {
18
- if (import.meta.env.MODE !== "production") {
19
- console.warn("Failed to load meta data from '@/static/meta'.", error);
20
- }
21
- return;
22
- }
23
- let pageMeta = pagesMeta[location.pathname];
24
- if (!pageMeta) {
25
- pageMeta = { title: defaultTitle, meta: defaultMeta };
26
- }
27
- document.title = pageMeta.title;
28
- // Remove previous dynamically inserted tags
29
- document.querySelectorAll("[data-react-meta]").forEach((tag) => tag.remove());
30
- // Inject new tags
31
- pageMeta.meta.forEach((meta) => {
32
- const metaElement = document.createElement("meta");
33
- metaElement.setAttribute("data-react-meta", "true");
34
- // Set all attributes from the meta object
35
- Object.entries(meta).forEach(([key, value]) => {
36
- if (value) {
37
- metaElement.setAttribute(key, value.toString());
38
- }
39
- });
40
- document.head.appendChild(metaElement);
41
- });
42
- // Update canonical link
43
- const canonicalUrl = `${window.location.origin}${location.pathname}`;
44
- const existingCanonical = document.querySelector("link[rel=\"canonical\"]");
45
- if (existingCanonical) {
46
- existingCanonical.remove();
47
- }
48
- const canonicalLink = document.createElement("link");
49
- canonicalLink.setAttribute("rel", "canonical");
50
- canonicalLink.setAttribute("href", canonicalUrl);
51
- canonicalLink.setAttribute("data-react-meta", "true");
52
- document.head.appendChild(canonicalLink);
53
- };
54
- loadMeta();
55
- }, [location.pathname]);
56
- };