@alxia/react-router 0.0.0-stage → 0.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.
- package/LICENSE +21 -0
- package/README.md +289 -2
- package/dist/assets.d.ts +12 -0
- package/dist/assets.d.ts.map +1 -0
- package/dist/chunks/index-kfekhvgr.js +31 -0
- package/dist/chunks/index-kfekhvgr.js.map +10 -0
- package/dist/cli/index.d.ts +3 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +160 -0
- package/dist/cli/index.js.map +13 -0
- package/dist/cli/main.d.ts +9 -0
- package/dist/cli/main.d.ts.map +1 -0
- package/dist/cli/reveal.d.ts +18 -0
- package/dist/cli/reveal.d.ts.map +1 -0
- package/dist/cli/template.d.ts +8 -0
- package/dist/cli/template.d.ts.map +1 -0
- package/dist/context.d.ts +70 -0
- package/dist/context.d.ts.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +166 -0
- package/dist/index.js.map +13 -0
- package/dist/react-router.d.ts +61 -0
- package/dist/react-router.d.ts.map +1 -0
- package/dist/server.d.ts +99 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/vite/config.d.ts +33 -0
- package/dist/vite/config.d.ts.map +1 -0
- package/dist/vite/dev.d.ts +23 -0
- package/dist/vite/dev.d.ts.map +1 -0
- package/dist/vite/entry.d.ts +20 -0
- package/dist/vite/entry.d.ts.map +1 -0
- package/dist/vite/index.d.ts +33 -0
- package/dist/vite/index.d.ts.map +1 -0
- package/dist/vite/index.js +391 -0
- package/dist/vite/index.js.map +16 -0
- package/dist/vite/node.d.ts +16 -0
- package/dist/vite/node.d.ts.map +1 -0
- package/dist/vite/preview.d.ts +8 -0
- package/dist/vite/preview.d.ts.map +1 -0
- package/dist/vite/runtime.d.ts +8 -0
- package/dist/vite/runtime.d.ts.map +1 -0
- package/dist/vite/socket.d.ts +68 -0
- package/dist/vite/socket.d.ts.map +1 -0
- package/docs/README.md +12 -0
- package/docs/guide.md +843 -0
- package/docs/roadmap.md +96 -0
- package/docs/troubleshooting.md +832 -0
- package/package.json +77 -5
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The key under which `reactRouter()` hands every loader, action and
|
|
3
|
+
* middleware the request's alxia context, and the typed way to read it.
|
|
4
|
+
*/
|
|
5
|
+
import type { Alxia, AnyAlxia, ContextOf, Empty } from '@alxia/core';
|
|
6
|
+
import { type RouterContextProvider } from 'react-router';
|
|
7
|
+
import type { FreshApp, ReactRouterServer } from './server';
|
|
8
|
+
/**
|
|
9
|
+
* The key `reactRouter()` sets on React Router's context provider, on every
|
|
10
|
+
* request, to what alxia's hooks built for it. It lives in this package, so
|
|
11
|
+
* it is one object whichever way the server was built or loaded: a key made
|
|
12
|
+
* in the app's own `app/` folder is copied into React Router's build, and
|
|
13
|
+
* the server that imports it separately sets a different one.
|
|
14
|
+
*/
|
|
15
|
+
export declare const alxiaContext: import("react-router").RouterContext<unknown>;
|
|
16
|
+
/**
|
|
17
|
+
* Names the server whose context `alxiaOf(context)` reads when given no
|
|
18
|
+
* type argument. One React Router build has one server, so it may be
|
|
19
|
+
* declared once, beside it:
|
|
20
|
+
*
|
|
21
|
+
* ```ts
|
|
22
|
+
* // app/server.ts
|
|
23
|
+
* const server = createServer({ configure: (app) => app.use(session) });
|
|
24
|
+
* export default server;
|
|
25
|
+
*
|
|
26
|
+
* declare module '@alxia/react-router' {
|
|
27
|
+
* interface Register {
|
|
28
|
+
* server: typeof server;
|
|
29
|
+
* }
|
|
30
|
+
* }
|
|
31
|
+
* ```
|
|
32
|
+
*
|
|
33
|
+
* Unregistered, `alxiaOf(context)` reads `BaseContext`.
|
|
34
|
+
*/
|
|
35
|
+
export interface Register {
|
|
36
|
+
}
|
|
37
|
+
/** The app a server makes, or the app itself. */
|
|
38
|
+
export type AppOf<Server> = Server extends ReactRouterServer<infer App> ? App : Server;
|
|
39
|
+
/**
|
|
40
|
+
* What `Register` names when it is neither a server nor an app — the
|
|
41
|
+
* module, say, rather than its default export: an app whose context has
|
|
42
|
+
* nothing but this key, so that reading anything of it is a compile error.
|
|
43
|
+
*/
|
|
44
|
+
export type InvalidRegister = Alxia<{
|
|
45
|
+
readonly 'Register.server must be typeof server, the default export of createServer()': never;
|
|
46
|
+
}, Empty, '', never>;
|
|
47
|
+
/** The app `alxiaOf` reads for a `Register` interface: its server's, a fresh one, or `InvalidRegister`. */
|
|
48
|
+
export type RegisteredOf<R> = R extends {
|
|
49
|
+
readonly server: infer Server;
|
|
50
|
+
} ? Server extends AnyAlxia | ReactRouterServer<AnyAlxia> ? AppOf<Server> : InvalidRegister : FreshApp;
|
|
51
|
+
/** What `alxiaOf` reads with no type argument: the registered server's app, or a fresh one. */
|
|
52
|
+
export type RegisteredApp = RegisteredOf<Register>;
|
|
53
|
+
/**
|
|
54
|
+
* What alxia's hooks built for this request, read in a loader, an action or
|
|
55
|
+
* a middleware, typed by the app: the server `Register` names, or the one
|
|
56
|
+
* given as the type argument — `typeof server`, or an app *before* the
|
|
57
|
+
* catch-all. With neither, `BaseContext`.
|
|
58
|
+
*
|
|
59
|
+
* ```ts
|
|
60
|
+
* export async function loader({ context }: Route.LoaderArgs) {
|
|
61
|
+
* const { user, log } = alxiaOf(context);
|
|
62
|
+
* }
|
|
63
|
+
* ```
|
|
64
|
+
*
|
|
65
|
+
* Throws when the request did not come through `reactRouter()`: under
|
|
66
|
+
* `react-router dev` without the plugin, or a test that calls a loader
|
|
67
|
+
* directly.
|
|
68
|
+
*/
|
|
69
|
+
export declare function alxiaOf<App extends AnyAlxia | ReactRouterServer<AnyAlxia> = RegisteredApp>(context: Readonly<RouterContextProvider>): ContextOf<AppOf<App>>;
|
|
70
|
+
//# sourceMappingURL=context.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AACrE,OAAO,EAAiB,KAAK,qBAAqB,EAAE,MAAM,cAAc,CAAC;AACzE,OAAO,KAAK,EAAE,QAAQ,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAC;AAK5D;;;;;;GAMG;AACH,eAAO,MAAM,YAAY,+CAAkC,CAAC;AAE5D;;;;;;;;;;;;;;;;;;GAkBG;AAEH,MAAM,WAAW,QAAQ;CAAG;AAE5B,iDAAiD;AACjD,MAAM,MAAM,KAAK,CAAC,MAAM,IACvB,MAAM,SAAS,iBAAiB,CAAC,MAAM,GAAG,CAAC,GAAG,GAAG,GAAG,MAAM,CAAC;AAE5D;;;;GAIG;AACH,MAAM,MAAM,eAAe,GAAG,KAAK,CAClC;IACC,QAAQ,CAAC,6EAA6E,EAAE,KAAK,CAAC;CAC9F,EACD,KAAK,EACL,EAAE,EACF,KAAK,CACL,CAAC;AAEF,2GAA2G;AAC3G,MAAM,MAAM,YAAY,CAAC,CAAC,IAAI,CAAC,SAAS;IAAE,QAAQ,CAAC,MAAM,EAAE,MAAM,MAAM,CAAA;CAAE,GACtE,MAAM,SAAS,QAAQ,GAAG,iBAAiB,CAAC,QAAQ,CAAC,GACpD,KAAK,CAAC,MAAM,CAAC,GACb,eAAe,GAChB,QAAQ,CAAC;AAEZ,+FAA+F;AAC/F,MAAM,MAAM,aAAa,GAAG,YAAY,CAAC,QAAQ,CAAC,CAAC;AAEnD;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,OAAO,CACtB,GAAG,SAAS,QAAQ,GAAG,iBAAiB,CAAC,QAAQ,CAAC,GAAG,aAAa,EACjE,OAAO,EAAE,QAAQ,CAAC,qBAAqB,CAAC,GAAG,SAAS,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAQjE"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { type AppOf, alxiaContext, alxiaOf, type InvalidRegister, type Register, type RegisteredApp, type RegisteredOf, } from './context';
|
|
2
|
+
export { isReactRouterRoute, type ReactRouterOptions, reactRouter, } from './react-router';
|
|
3
|
+
export { createServer, type FreshApp, type ReactRouterServer, type ServerOptions, type ServerWiring, } from './server';
|
|
4
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,KAAK,KAAK,EACV,YAAY,EACZ,OAAO,EACP,KAAK,eAAe,EACpB,KAAK,QAAQ,EACb,KAAK,aAAa,EAClB,KAAK,YAAY,GACjB,MAAM,WAAW,CAAC;AACnB,OAAO,EACN,kBAAkB,EAClB,KAAK,kBAAkB,EACvB,WAAW,GACX,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACN,YAAY,EACZ,KAAK,QAAQ,EACb,KAAK,iBAAiB,EACtB,KAAK,aAAa,EAClB,KAAK,YAAY,GACjB,MAAM,UAAU,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
// src/context.ts
|
|
2
|
+
import { createContext } from "react-router";
|
|
3
|
+
var MISSING = Symbol("alxia context missing");
|
|
4
|
+
var alxiaContext = createContext(MISSING);
|
|
5
|
+
function alxiaOf(context) {
|
|
6
|
+
const value = context.get(alxiaContext);
|
|
7
|
+
if (value === MISSING) {
|
|
8
|
+
throw new Error("alxiaOf(): this request has no alxia context. Serve the React Router app through alxia: add alxia() from @alxia/react-router/vite to vite.config.ts's plugins, or, with a server of your own, serve the build through reactRouter() from @alxia/react-router.");
|
|
9
|
+
}
|
|
10
|
+
return value;
|
|
11
|
+
}
|
|
12
|
+
// src/react-router.ts
|
|
13
|
+
import { fileURLToPath } from "node:url";
|
|
14
|
+
import {
|
|
15
|
+
createRequestHandler,
|
|
16
|
+
RouterContextProvider
|
|
17
|
+
} from "react-router";
|
|
18
|
+
|
|
19
|
+
// src/assets.ts
|
|
20
|
+
import { readdirSync, statSync } from "node:fs";
|
|
21
|
+
import { join } from "node:path";
|
|
22
|
+
var IMMUTABLE = "public, max-age=31536000, immutable";
|
|
23
|
+
var AN_HOUR = "public, max-age=3600";
|
|
24
|
+
function serveClient(app, client) {
|
|
25
|
+
if (!isDirectory(client)) {
|
|
26
|
+
throw new TypeError(`reactRouter(): client is ${client}, which is not a directory. Pass the client build, build/client by default.`);
|
|
27
|
+
}
|
|
28
|
+
for (const name of readdirSync(client).sort()) {
|
|
29
|
+
if (name.startsWith("."))
|
|
30
|
+
continue;
|
|
31
|
+
const path = join(client, name);
|
|
32
|
+
const cacheControl = name === "assets" ? IMMUTABLE : AN_HOUR;
|
|
33
|
+
const route = new URL(`/${name}`, "http://localhost").pathname;
|
|
34
|
+
try {
|
|
35
|
+
if (isDirectory(path))
|
|
36
|
+
app.static(route, path, { cacheControl });
|
|
37
|
+
else
|
|
38
|
+
app.file(route, path, { cacheControl });
|
|
39
|
+
} catch (error) {
|
|
40
|
+
throw new TypeError(`reactRouter(): ${path} cannot be served at a path of its own name; rename it. ${error.message}`);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
function isDirectory(path) {
|
|
45
|
+
try {
|
|
46
|
+
return statSync(path).isDirectory();
|
|
47
|
+
} catch {
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// src/react-router.ts
|
|
53
|
+
var METHODS = ["get", "post", "put", "patch", "delete"];
|
|
54
|
+
var declared = new WeakSet;
|
|
55
|
+
function isReactRouterRoute(route) {
|
|
56
|
+
return declared.has(route.handler);
|
|
57
|
+
}
|
|
58
|
+
function reactRouter(app, options) {
|
|
59
|
+
const mode = options.mode ?? "production";
|
|
60
|
+
const handle = requestHandler(options.build, mode);
|
|
61
|
+
const getLoadContext = options.getLoadContext;
|
|
62
|
+
const handler = async (ctx) => {
|
|
63
|
+
const context = new RouterContextProvider;
|
|
64
|
+
context.set(alxiaContext, ctx);
|
|
65
|
+
await getLoadContext?.(ctx, context);
|
|
66
|
+
const request = ctx.request.method === "HEAD" ? new Request(ctx.request, { method: "GET" }) : ctx.request;
|
|
67
|
+
const response = await handle(request, context);
|
|
68
|
+
return ctx.reply(response.status, response.body ?? undefined, { headers: response.headers });
|
|
69
|
+
};
|
|
70
|
+
const routes = asRoutes(app);
|
|
71
|
+
declaring(app, () => {
|
|
72
|
+
if (options.client !== undefined && mode === "production") {
|
|
73
|
+
serveClient(routes, pathOf(options.client));
|
|
74
|
+
}
|
|
75
|
+
for (const method of METHODS)
|
|
76
|
+
routes[method]("/*", handler);
|
|
77
|
+
});
|
|
78
|
+
return app;
|
|
79
|
+
}
|
|
80
|
+
function declareClient(app, client) {
|
|
81
|
+
declaring(app, () => serveClient(asRoutes(app), pathOf(client)));
|
|
82
|
+
}
|
|
83
|
+
function declaring(app, declare) {
|
|
84
|
+
const before = new Set(app.routes);
|
|
85
|
+
declare();
|
|
86
|
+
for (const route of app.routes) {
|
|
87
|
+
if (!before.has(route))
|
|
88
|
+
declared.add(route.handler);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
function asRoutes(app) {
|
|
92
|
+
return app;
|
|
93
|
+
}
|
|
94
|
+
function requestHandler(build, mode) {
|
|
95
|
+
if (typeof build !== "function" || mode === "development") {
|
|
96
|
+
return createRequestHandler(build, mode);
|
|
97
|
+
}
|
|
98
|
+
let resolved;
|
|
99
|
+
return async (request, context) => {
|
|
100
|
+
resolved ??= Promise.resolve(build()).then((server) => createRequestHandler(server, mode), (error) => {
|
|
101
|
+
resolved = undefined;
|
|
102
|
+
throw error;
|
|
103
|
+
});
|
|
104
|
+
return (await resolved)(request, context);
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
function pathOf(client) {
|
|
108
|
+
return client instanceof URL ? fileURLToPath(client) : client;
|
|
109
|
+
}
|
|
110
|
+
// src/server.ts
|
|
111
|
+
import {
|
|
112
|
+
alxia
|
|
113
|
+
} from "@alxia/core";
|
|
114
|
+
function createServer(options = {}) {
|
|
115
|
+
return {
|
|
116
|
+
create(wiring) {
|
|
117
|
+
const mode = options.mode ?? wiring.mode ?? "production";
|
|
118
|
+
const client = options.client === false ? undefined : options.client ?? wiring.client;
|
|
119
|
+
const fresh = alxia();
|
|
120
|
+
const before = options.beforeAll?.(fresh) ?? fresh;
|
|
121
|
+
if (client !== undefined && mode === "production") {
|
|
122
|
+
declareClient(before, client);
|
|
123
|
+
}
|
|
124
|
+
const app = options.configure?.(before) ?? before;
|
|
125
|
+
const getLoadContext = options.getLoadContext;
|
|
126
|
+
reactRouter(app, {
|
|
127
|
+
build: options.build ?? wiring.build,
|
|
128
|
+
mode,
|
|
129
|
+
...getLoadContext === undefined ? {} : {
|
|
130
|
+
getLoadContext: (ctx, context) => getLoadContext(ctx, context)
|
|
131
|
+
}
|
|
132
|
+
});
|
|
133
|
+
return app;
|
|
134
|
+
},
|
|
135
|
+
start(app) {
|
|
136
|
+
const server = app.listen({
|
|
137
|
+
port: Number(process.env["PORT"] || 3000),
|
|
138
|
+
hostname: process.env["HOST"] || "0.0.0.0",
|
|
139
|
+
...options.listen
|
|
140
|
+
});
|
|
141
|
+
(options.onListen ?? announce)(server);
|
|
142
|
+
for (const signal of ["SIGINT", "SIGTERM"]) {
|
|
143
|
+
process.once(signal, () => {
|
|
144
|
+
app.stop().then(() => process.exit(0), (error) => {
|
|
145
|
+
console.error(error);
|
|
146
|
+
process.exit(1);
|
|
147
|
+
});
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
return server;
|
|
151
|
+
}
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
function announce(server) {
|
|
155
|
+
console.log(`alxia listening on ${server.url}`);
|
|
156
|
+
}
|
|
157
|
+
export {
|
|
158
|
+
alxiaContext,
|
|
159
|
+
alxiaOf,
|
|
160
|
+
createServer,
|
|
161
|
+
isReactRouterRoute,
|
|
162
|
+
reactRouter
|
|
163
|
+
};
|
|
164
|
+
|
|
165
|
+
//# debugId=CFABDD0A38AACF7C64756E2164756E21
|
|
166
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/context.ts", "../src/react-router.ts", "../src/assets.ts", "../src/server.ts"],
|
|
4
|
+
"sourcesContent": [
|
|
5
|
+
"/**\n * The key under which `reactRouter()` hands every loader, action and\n * middleware the request's alxia context, and the typed way to read it.\n */\nimport type { Alxia, AnyAlxia, ContextOf, Empty } from '@alxia/core';\nimport { createContext, type RouterContextProvider } from 'react-router';\nimport type { FreshApp, ReactRouterServer } from './server';\n\n/** The default of the key: no catch-all set it. */\nconst MISSING: unique symbol = Symbol('alxia context missing');\n\n/**\n * The key `reactRouter()` sets on React Router's context provider, on every\n * request, to what alxia's hooks built for it. It lives in this package, so\n * it is one object whichever way the server was built or loaded: a key made\n * in the app's own `app/` folder is copied into React Router's build, and\n * the server that imports it separately sets a different one.\n */\nexport const alxiaContext = createContext<unknown>(MISSING);\n\n/**\n * Names the server whose context `alxiaOf(context)` reads when given no\n * type argument. One React Router build has one server, so it may be\n * declared once, beside it:\n *\n * ```ts\n * // app/server.ts\n * const server = createServer({ configure: (app) => app.use(session) });\n * export default server;\n *\n * declare module '@alxia/react-router' {\n * interface Register {\n * server: typeof server;\n * }\n * }\n * ```\n *\n * Unregistered, `alxiaOf(context)` reads `BaseContext`.\n */\n// biome-ignore lint/suspicious/noEmptyInterface: an app augments it\nexport interface Register {}\n\n/** The app a server makes, or the app itself. */\nexport type AppOf<Server> =\n\tServer extends ReactRouterServer<infer App> ? App : Server;\n\n/**\n * What `Register` names when it is neither a server nor an app — the\n * module, say, rather than its default export: an app whose context has\n * nothing but this key, so that reading anything of it is a compile error.\n */\nexport type InvalidRegister = Alxia<\n\t{\n\t\treadonly 'Register.server must be typeof server, the default export of createServer()': never;\n\t},\n\tEmpty,\n\t'',\n\tnever\n>;\n\n/** The app `alxiaOf` reads for a `Register` interface: its server's, a fresh one, or `InvalidRegister`. */\nexport type RegisteredOf<R> = R extends { readonly server: infer Server }\n\t? Server extends AnyAlxia | ReactRouterServer<AnyAlxia>\n\t\t? AppOf<Server>\n\t\t: InvalidRegister\n\t: FreshApp;\n\n/** What `alxiaOf` reads with no type argument: the registered server's app, or a fresh one. */\nexport type RegisteredApp = RegisteredOf<Register>;\n\n/**\n * What alxia's hooks built for this request, read in a loader, an action or\n * a middleware, typed by the app: the server `Register` names, or the one\n * given as the type argument — `typeof server`, or an app *before* the\n * catch-all. With neither, `BaseContext`.\n *\n * ```ts\n * export async function loader({ context }: Route.LoaderArgs) {\n * const { user, log } = alxiaOf(context);\n * }\n * ```\n *\n * Throws when the request did not come through `reactRouter()`: under\n * `react-router dev` without the plugin, or a test that calls a loader\n * directly.\n */\nexport function alxiaOf<\n\tApp extends AnyAlxia | ReactRouterServer<AnyAlxia> = RegisteredApp,\n>(context: Readonly<RouterContextProvider>): ContextOf<AppOf<App>> {\n\tconst value = context.get(alxiaContext);\n\tif (value === MISSING) {\n\t\tthrow new Error(\n\t\t\t\"alxiaOf(): this request has no alxia context. Serve the React Router app through alxia: add alxia() from @alxia/react-router/vite to vite.config.ts's plugins, or, with a server of your own, serve the build through reactRouter() from @alxia/react-router.\",\n\t\t);\n\t}\n\treturn value as ContextOf<AppOf<App>>;\n}\n",
|
|
6
|
+
"import { fileURLToPath } from 'node:url';\nimport type {\n\tAlxia,\n\tAnyAlxia,\n\tAnyReply,\n\tBaseContext,\n\tMaybePromise,\n\tRouteDefinition,\n\tStatusCode,\n} from '@alxia/core';\nimport {\n\tcreateRequestHandler,\n\ttype RequestHandler,\n\tRouterContextProvider,\n\ttype ServerBuild,\n} from 'react-router';\nimport { serveClient } from './assets';\nimport { alxiaContext } from './context';\n\nexport interface ReactRouterOptions<Ctx> {\n\t/**\n\t * React Router's server build: the module itself, or a function that\n\t * returns it — `() => import('virtual:react-router/server-build')` in a\n\t * server entry Vite builds, `() => import('./build/server/index.js')`\n\t * beside a build. A function is called on every request in\n\t * `development`, so an edit is picked up, and once in `production`.\n\t */\n\treadonly build: ServerBuild | (() => MaybePromise<ServerBuild>);\n\t/** React Router's server mode: `production` by default. */\n\treadonly mode?: 'development' | 'production';\n\t/**\n\t * Sets the app's own keys on React Router's context provider, from the\n\t * context alxia's hooks built. `ctx` is typed by the app at the point of\n\t * `use`: reading what no hook before it derives is a compile error.\n\t * `alxiaContext` is always set, whether or not this is given.\n\t */\n\treadonly getLoadContext?: (\n\t\tctx: BaseContext & Ctx,\n\t\tcontext: RouterContextProvider,\n\t) => MaybePromise<void>;\n\t/**\n\t * The client build's folder, `build/client`: a path or a `file:` URL.\n\t * Its `assets/` is served immutable, and every other top-level file or\n\t * folder — the copies of `public/` — with an hour's cache, before the\n\t * catch-all. Ignored in `development`, where Vite serves them.\n\t */\n\treadonly client?: string | URL;\n}\n\n/** The methods the catch-all takes. A `HEAD` reaches its `GET`. */\nconst METHODS = ['get', 'post', 'put', 'patch', 'delete'] as const;\n\n/** The handlers `reactRouter()` declared: the catch-all's and the client build's. */\nconst declared = new WeakSet<RouteDefinition['handler']>();\n\n/**\n * Whether `reactRouter()` declared this route: the catch-all, or one of the\n * client build's files. For `@alxia/openapi`'s `exclude`, so the document\n * lists the app's API and not its pages.\n *\n * ```ts\n * app.use(docs(app, { info, exclude: isReactRouterRoute }));\n * ```\n */\nexport function isReactRouterRoute(route: RouteDefinition): boolean {\n\treturn declared.has(route.handler);\n}\n\n/**\n * A React Router framework app, server rendered on `app`: `GET`, `POST`,\n * `PUT`, `PATCH` and `DELETE` at `/*`, behind every hook declared on `app`\n * before it. Each loader, action and middleware reads what those hooks\n * built through `alxiaOf<App>(context)`. The app's own routes — an `/api`\n * — answer their paths, declared before it or after; the client build's\n * files are served when `client` is given. The catch-all adds nothing to\n * the app's route table: pages are not something the typed client calls.\n *\n * ```ts\n * const app = base.use((app) =>\n * reactRouter(app, { build: () => import('./build/server/index.js'), client: 'build/client' }),\n * );\n * ```\n */\nexport function reactRouter<\n\tCtx extends object,\n\tRoutes extends object,\n\tPrefix extends string,\n\tShortcuts extends AnyReply,\n>(\n\tapp: Alxia<Ctx, Routes, Prefix, Shortcuts>,\n\toptions: ReactRouterOptions<Ctx>,\n): Alxia<Ctx, Routes, Prefix, Shortcuts> {\n\tconst mode = options.mode ?? 'production';\n\tconst handle = requestHandler(options.build, mode);\n\tconst getLoadContext = options.getLoadContext;\n\tconst handler = async (ctx: BaseContext & Ctx) => {\n\t\tconst context = new RouterContextProvider();\n\t\tcontext.set(alxiaContext, ctx);\n\t\tawait getLoadContext?.(ctx, context);\n\t\t// React Router answers a HEAD with no headers at all: hand it the GET,\n\t\t// and the core drops the body.\n\t\tconst request =\n\t\t\tctx.request.method === 'HEAD'\n\t\t\t\t? new Request(ctx.request, { method: 'GET' })\n\t\t\t\t: ctx.request;\n\t\tconst response = await handle(request, context);\n\t\treturn ctx.reply(\n\t\t\tresponse.status as StatusCode,\n\t\t\tresponse.body ?? undefined,\n\t\t\t{ headers: response.headers },\n\t\t);\n\t};\n\n\tconst routes = asRoutes(app);\n\tdeclaring(app, () => {\n\t\tif (options.client !== undefined && mode === 'production') {\n\t\t\tserveClient(routes, pathOf(options.client));\n\t\t}\n\t\tfor (const method of METHODS) routes[method]('/*', handler);\n\t});\n\treturn app;\n}\n\n/**\n * Declares the client build's files on `app`, marked for\n * `isReactRouterRoute`: what `reactRouter()` does with `client`, apart, so\n * that `createServer()` serves them before the hooks of `configure`.\n */\nexport function declareClient(app: AnyAlxia, client: string | URL): void {\n\tdeclaring(app, () => serveClient(asRoutes(app), pathOf(client)));\n}\n\n/** Runs `declare`, and marks every route it added to `app` as ours. */\nfunction declaring(app: AnyAlxia, declare: () => void): void {\n\tconst before = new Set(app.routes);\n\tdeclare();\n\tfor (const route of app.routes) {\n\t\tif (!before.has(route)) declared.add(route.handler);\n\t}\n}\n\n/** The app's route methods, untyped: the catch-all's handler is not one a route type admits. */\nfunction asRoutes(app: AnyAlxia) {\n\treturn app as unknown as {\n\t\tstatic(path: string, source: string, options: object): unknown;\n\t\tfile(path: string, file: string, options: object): unknown;\n\t} & Record<\n\t\t(typeof METHODS)[number],\n\t\t(path: string, handler: unknown) => unknown\n\t>;\n}\n\n/** A handler that resolves a function build once in production, on every request in development. */\nfunction requestHandler(\n\tbuild: ReactRouterOptions<never>['build'],\n\tmode: 'development' | 'production',\n): RequestHandler {\n\tif (typeof build !== 'function' || mode === 'development') {\n\t\treturn createRequestHandler(build, mode);\n\t}\n\tlet resolved: Promise<RequestHandler> | undefined;\n\treturn async (request, context) => {\n\t\tresolved ??= Promise.resolve(build()).then(\n\t\t\t(server) => createRequestHandler(server, mode),\n\t\t\t(error: unknown) => {\n\t\t\t\tresolved = undefined;\n\t\t\t\tthrow error;\n\t\t\t},\n\t\t);\n\t\treturn (await resolved)(request, context);\n\t};\n}\n\nfunction pathOf(client: string | URL): string {\n\treturn client instanceof URL ? fileURLToPath(client) : client;\n}\n",
|
|
7
|
+
"/**\n * The client build's files, served before the catch-all: the hashed\n * `assets/` immutable, every other top-level file or folder — what React\n * Router copied from `public/` — with an hour's cache.\n */\nimport { readdirSync, statSync } from 'node:fs';\nimport { join } from 'node:path';\n\n/** A hashed asset never changes under its name. */\nexport const IMMUTABLE = 'public, max-age=31536000, immutable';\n/** A public file keeps its name across builds: revalidated after an hour. */\nexport const AN_HOUR = 'public, max-age=3600';\n\ninterface Serving {\n\tstatic(path: string, source: string, options: object): unknown;\n\tfile(path: string, file: string, options: object): unknown;\n}\n\n/** Declares the client build's routes on `app`. */\nexport function serveClient(app: Serving, client: string): void {\n\tif (!isDirectory(client)) {\n\t\tthrow new TypeError(\n\t\t\t`reactRouter(): client is ${client}, which is not a directory. Pass the client build, build/client by default.`,\n\t\t);\n\t}\n\tfor (const name of readdirSync(client).sort()) {\n\t\tif (name.startsWith('.')) continue;\n\t\tconst path = join(client, name);\n\t\tconst cacheControl = name === 'assets' ? IMMUTABLE : AN_HOUR;\n\t\t// Declared as a request's URL carries the name: `my file.pdf` is\n\t\t// `/my%20file.pdf`, `café.png` is `/caf%C3%A9.png`.\n\t\tconst route = new URL(`/${name}`, 'http://localhost').pathname;\n\t\ttry {\n\t\t\tif (isDirectory(path)) app.static(route, path, { cacheControl });\n\t\t\telse app.file(route, path, { cacheControl });\n\t\t} catch (error) {\n\t\t\tthrow new TypeError(\n\t\t\t\t`reactRouter(): ${path} cannot be served at a path of its own name; rename it. ${(error as Error).message}`,\n\t\t\t);\n\t\t}\n\t}\n}\n\nfunction isDirectory(path: string): boolean {\n\ttry {\n\t\treturn statSync(path).isDirectory();\n\t} catch {\n\t\treturn false;\n\t}\n}\n",
|
|
8
|
+
"/**\n * `createServer()`: the alxia server of a React Router app, as\n * `@alxia/react-router/vite` builds it. The plugin hands it React Router's\n * build, the mode and the client folder; the app's own `app/server.ts`\n * customises the rest, or is not written at all.\n */\nimport {\n\ttype Alxia,\n\ttype AnyAlxia,\n\talxia,\n\ttype ContextOf,\n\ttype Empty,\n\ttype ListenOptions,\n\ttype MaybePromise,\n} from '@alxia/core';\nimport type { RouterContextProvider, ServerBuild } from 'react-router';\nimport { declareClient, reactRouter } from './react-router';\n\n/** An app as `alxia()` makes it: what `beforeAll`, or `configure`, receives. */\nexport type FreshApp = Alxia<Empty, Empty, '', never>;\n\n/**\n * What the Vite plugin hands the server, and a test passes to `create`:\n * React Router's server build, its mode, and the client build's folder.\n */\nexport interface ServerWiring {\n\t/**\n\t * React Router's server build, or a function that returns it: called on\n\t * every request in `development`, once in `production`.\n\t */\n\treadonly build: ServerBuild | (() => MaybePromise<ServerBuild>);\n\t/** `development` under `react-router dev`, `production` in a build, and by default. */\n\treadonly mode?: 'development' | 'production';\n\t/** The client build's folder, a path or a `file:` URL: served in `production`. */\n\treadonly client?: string | URL | undefined;\n}\n\nexport interface ServerOptions<Before extends AnyAlxia, App extends AnyAlxia> {\n\t/**\n\t * Runs first, on a new app: what is declared here runs before the\n\t * client's files too — a guard, a rate limit, a logger that should see\n\t * every request. Returns the app, so its types flow to `configure`.\n\t */\n\treadonly beforeAll?: (app: FreshApp) => Before;\n\t/**\n\t * The app the pages run behind: its plugins, its hooks, its `/api`.\n\t * Declared after the client's files and before the catch-all. Returns\n\t * the app: what it builds is what the loaders read through `alxiaOf`.\n\t */\n\treadonly configure?: (app: Before) => App;\n\t/**\n\t * Sets the app's own keys on React Router's context provider, `ctx`\n\t * typed by `configure`'s app. `alxiaContext` is always set.\n\t */\n\treadonly getLoadContext?: (\n\t\tctx: ContextOf<App>,\n\t\tcontext: RouterContextProvider,\n\t) => MaybePromise<void>;\n\t/** Overrides the plugin's server build: rarely wanted. */\n\treadonly build?: ServerWiring['build'];\n\t/** Overrides the plugin's mode, `development` in dev and `production` in a build. */\n\treadonly mode?: 'development' | 'production';\n\t/**\n\t * Overrides the plugin's client folder, `build/client` beside the built\n\t * server. `false` serves none of it: declare the files yourself, in\n\t * `beforeAll` or `configure`.\n\t */\n\treadonly client?: string | URL | false;\n\t/**\n\t * `listen`'s options for `bun build/server/index.js`; a `port` or `hostname` here wins over `PORT` (3000)\n\t * and `HOST` (`0.0.0.0`) from the environment.\n\t */\n\treadonly listen?: ListenOptions;\n\t/** Called once the built server listens. Prints `alxia listening on <url>` by default. */\n\treadonly onListen?: (server: Bun.Server<unknown>) => void;\n}\n\n/**\n * The server `createServer()` describes, made into an app by the Vite\n * plugin, by a test, or by a server file of your own.\n */\nexport interface ReactRouterServer<App extends AnyAlxia> {\n\t/**\n\t * The app, serving React Router's build: `build/server/index.js`'s\n\t * default export. A test drives it with `app.request`.\n\t *\n\t * ```ts\n\t * const app = server.create({ build: await import('./build/server/index.js') });\n\t * ```\n\t */\n\tcreate(wiring: ServerWiring): App;\n\t/**\n\t * Listens with `app`, on `listen`, `PORT` and `HOST`, and stops it on\n\t * `SIGINT` or `SIGTERM`: its `onStop` hooks run, and the process exits.\n\t * What `bun build/server/index.js` runs.\n\t */\n\tstart(app: App): Bun.Server<unknown>;\n}\n\n/**\n * The alxia server of a React Router app, for `app/server.ts`. Every option\n * is optional: the Vite plugin wires React Router's build, the mode and the\n * client folder, and without the file uses `createServer()` as it is.\n *\n * ```ts\n * const server = createServer({\n * configure: (app) => app.use(logger()).get('/api/health', ({ reply }) => reply.ok({ ok: true })),\n * });\n * export default server;\n * ```\n *\n * The request runs through `beforeAll`, the client's files, `configure`,\n * then the pages.\n */\nexport function createServer<\n\tBefore extends AnyAlxia = FreshApp,\n\tApp extends AnyAlxia = Before,\n>(options: ServerOptions<Before, App> = {}): ReactRouterServer<App> {\n\treturn {\n\t\tcreate(wiring) {\n\t\t\tconst mode = options.mode ?? wiring.mode ?? 'production';\n\t\t\tconst client =\n\t\t\t\toptions.client === false\n\t\t\t\t\t? undefined\n\t\t\t\t\t: (options.client ?? wiring.client);\n\t\t\tconst fresh = alxia();\n\t\t\t// Without beforeAll or configure, Before and App are their defaults,\n\t\t\t// the app passed through: a type argument given by hand is believed.\n\t\t\tconst before = (options.beforeAll?.(fresh) ?? fresh) as Before;\n\t\t\tif (client !== undefined && mode === 'production') {\n\t\t\t\tdeclareClient(before, client);\n\t\t\t}\n\t\t\tconst app = (options.configure?.(before) ?? before) as App;\n\t\t\tconst getLoadContext = options.getLoadContext;\n\t\t\treactRouter(app, {\n\t\t\t\tbuild: options.build ?? wiring.build,\n\t\t\t\tmode,\n\t\t\t\t...(getLoadContext === undefined\n\t\t\t\t\t? {}\n\t\t\t\t\t: {\n\t\t\t\t\t\t\tgetLoadContext: (ctx, context) =>\n\t\t\t\t\t\t\t\tgetLoadContext(ctx as ContextOf<App>, context),\n\t\t\t\t\t\t}),\n\t\t\t});\n\t\t\treturn app;\n\t\t},\n\t\tstart(app) {\n\t\t\tconst server = app.listen({\n\t\t\t\tport: Number(process.env['PORT'] || 3000),\n\t\t\t\thostname: process.env['HOST'] || '0.0.0.0',\n\t\t\t\t...options.listen,\n\t\t\t});\n\t\t\t(options.onListen ?? announce)(server);\n\t\t\t// Stop as the platform asks: the app's onStop hooks run, and the process ends.\n\t\t\tfor (const signal of ['SIGINT', 'SIGTERM'] as const) {\n\t\t\t\tprocess.once(signal, () => {\n\t\t\t\t\tvoid app.stop().then(\n\t\t\t\t\t\t() => process.exit(0),\n\t\t\t\t\t\t(error: unknown) => {\n\t\t\t\t\t\t\tconsole.error(error);\n\t\t\t\t\t\t\tprocess.exit(1);\n\t\t\t\t\t\t},\n\t\t\t\t\t);\n\t\t\t\t});\n\t\t\t}\n\t\t\treturn server;\n\t\t},\n\t};\n}\n\nfunction announce(server: Bun.Server<unknown>): void {\n\tconsole.log(`alxia listening on ${server.url}`);\n}\n"
|
|
9
|
+
],
|
|
10
|
+
"mappings": "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",
|
|
11
|
+
"debugId": "CFABDD0A38AACF7C64756E2164756E21",
|
|
12
|
+
"names": []
|
|
13
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import type { Alxia, AnyAlxia, AnyReply, BaseContext, MaybePromise, RouteDefinition } from '@alxia/core';
|
|
2
|
+
import { RouterContextProvider, type ServerBuild } from 'react-router';
|
|
3
|
+
export interface ReactRouterOptions<Ctx> {
|
|
4
|
+
/**
|
|
5
|
+
* React Router's server build: the module itself, or a function that
|
|
6
|
+
* returns it — `() => import('virtual:react-router/server-build')` in a
|
|
7
|
+
* server entry Vite builds, `() => import('./build/server/index.js')`
|
|
8
|
+
* beside a build. A function is called on every request in
|
|
9
|
+
* `development`, so an edit is picked up, and once in `production`.
|
|
10
|
+
*/
|
|
11
|
+
readonly build: ServerBuild | (() => MaybePromise<ServerBuild>);
|
|
12
|
+
/** React Router's server mode: `production` by default. */
|
|
13
|
+
readonly mode?: 'development' | 'production';
|
|
14
|
+
/**
|
|
15
|
+
* Sets the app's own keys on React Router's context provider, from the
|
|
16
|
+
* context alxia's hooks built. `ctx` is typed by the app at the point of
|
|
17
|
+
* `use`: reading what no hook before it derives is a compile error.
|
|
18
|
+
* `alxiaContext` is always set, whether or not this is given.
|
|
19
|
+
*/
|
|
20
|
+
readonly getLoadContext?: (ctx: BaseContext & Ctx, context: RouterContextProvider) => MaybePromise<void>;
|
|
21
|
+
/**
|
|
22
|
+
* The client build's folder, `build/client`: a path or a `file:` URL.
|
|
23
|
+
* Its `assets/` is served immutable, and every other top-level file or
|
|
24
|
+
* folder — the copies of `public/` — with an hour's cache, before the
|
|
25
|
+
* catch-all. Ignored in `development`, where Vite serves them.
|
|
26
|
+
*/
|
|
27
|
+
readonly client?: string | URL;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Whether `reactRouter()` declared this route: the catch-all, or one of the
|
|
31
|
+
* client build's files. For `@alxia/openapi`'s `exclude`, so the document
|
|
32
|
+
* lists the app's API and not its pages.
|
|
33
|
+
*
|
|
34
|
+
* ```ts
|
|
35
|
+
* app.use(docs(app, { info, exclude: isReactRouterRoute }));
|
|
36
|
+
* ```
|
|
37
|
+
*/
|
|
38
|
+
export declare function isReactRouterRoute(route: RouteDefinition): boolean;
|
|
39
|
+
/**
|
|
40
|
+
* A React Router framework app, server rendered on `app`: `GET`, `POST`,
|
|
41
|
+
* `PUT`, `PATCH` and `DELETE` at `/*`, behind every hook declared on `app`
|
|
42
|
+
* before it. Each loader, action and middleware reads what those hooks
|
|
43
|
+
* built through `alxiaOf<App>(context)`. The app's own routes — an `/api`
|
|
44
|
+
* — answer their paths, declared before it or after; the client build's
|
|
45
|
+
* files are served when `client` is given. The catch-all adds nothing to
|
|
46
|
+
* the app's route table: pages are not something the typed client calls.
|
|
47
|
+
*
|
|
48
|
+
* ```ts
|
|
49
|
+
* const app = base.use((app) =>
|
|
50
|
+
* reactRouter(app, { build: () => import('./build/server/index.js'), client: 'build/client' }),
|
|
51
|
+
* );
|
|
52
|
+
* ```
|
|
53
|
+
*/
|
|
54
|
+
export declare function reactRouter<Ctx extends object, Routes extends object, Prefix extends string, Shortcuts extends AnyReply>(app: Alxia<Ctx, Routes, Prefix, Shortcuts>, options: ReactRouterOptions<Ctx>): Alxia<Ctx, Routes, Prefix, Shortcuts>;
|
|
55
|
+
/**
|
|
56
|
+
* Declares the client build's files on `app`, marked for
|
|
57
|
+
* `isReactRouterRoute`: what `reactRouter()` does with `client`, apart, so
|
|
58
|
+
* that `createServer()` serves them before the hooks of `configure`.
|
|
59
|
+
*/
|
|
60
|
+
export declare function declareClient(app: AnyAlxia, client: string | URL): void;
|
|
61
|
+
//# sourceMappingURL=react-router.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"react-router.d.ts","sourceRoot":"","sources":["../src/react-router.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACX,KAAK,EACL,QAAQ,EACR,QAAQ,EACR,WAAW,EACX,YAAY,EACZ,eAAe,EAEf,MAAM,aAAa,CAAC;AACrB,OAAO,EAGN,qBAAqB,EACrB,KAAK,WAAW,EAChB,MAAM,cAAc,CAAC;AAItB,MAAM,WAAW,kBAAkB,CAAC,GAAG;IACtC;;;;;;OAMG;IACH,QAAQ,CAAC,KAAK,EAAE,WAAW,GAAG,CAAC,MAAM,YAAY,CAAC,WAAW,CAAC,CAAC,CAAC;IAChE,2DAA2D;IAC3D,QAAQ,CAAC,IAAI,CAAC,EAAE,aAAa,GAAG,YAAY,CAAC;IAC7C;;;;;OAKG;IACH,QAAQ,CAAC,cAAc,CAAC,EAAE,CACzB,GAAG,EAAE,WAAW,GAAG,GAAG,EACtB,OAAO,EAAE,qBAAqB,KAC1B,YAAY,CAAC,IAAI,CAAC,CAAC;IACxB;;;;;OAKG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,GAAG,CAAC;CAC/B;AAQD;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,eAAe,GAAG,OAAO,CAElE;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,WAAW,CAC1B,GAAG,SAAS,MAAM,EAClB,MAAM,SAAS,MAAM,EACrB,MAAM,SAAS,MAAM,EACrB,SAAS,SAAS,QAAQ,EAE1B,GAAG,EAAE,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,CAAC,EAC1C,OAAO,EAAE,kBAAkB,CAAC,GAAG,CAAC,GAC9B,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,CAAC,CA8BvC;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,GAAG,GAAG,GAAG,IAAI,CAEvE"}
|
package/dist/server.d.ts
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `createServer()`: the alxia server of a React Router app, as
|
|
3
|
+
* `@alxia/react-router/vite` builds it. The plugin hands it React Router's
|
|
4
|
+
* build, the mode and the client folder; the app's own `app/server.ts`
|
|
5
|
+
* customises the rest, or is not written at all.
|
|
6
|
+
*/
|
|
7
|
+
import { type Alxia, type AnyAlxia, type ContextOf, type Empty, type ListenOptions, type MaybePromise } from '@alxia/core';
|
|
8
|
+
import type { RouterContextProvider, ServerBuild } from 'react-router';
|
|
9
|
+
/** An app as `alxia()` makes it: what `beforeAll`, or `configure`, receives. */
|
|
10
|
+
export type FreshApp = Alxia<Empty, Empty, '', never>;
|
|
11
|
+
/**
|
|
12
|
+
* What the Vite plugin hands the server, and a test passes to `create`:
|
|
13
|
+
* React Router's server build, its mode, and the client build's folder.
|
|
14
|
+
*/
|
|
15
|
+
export interface ServerWiring {
|
|
16
|
+
/**
|
|
17
|
+
* React Router's server build, or a function that returns it: called on
|
|
18
|
+
* every request in `development`, once in `production`.
|
|
19
|
+
*/
|
|
20
|
+
readonly build: ServerBuild | (() => MaybePromise<ServerBuild>);
|
|
21
|
+
/** `development` under `react-router dev`, `production` in a build, and by default. */
|
|
22
|
+
readonly mode?: 'development' | 'production';
|
|
23
|
+
/** The client build's folder, a path or a `file:` URL: served in `production`. */
|
|
24
|
+
readonly client?: string | URL | undefined;
|
|
25
|
+
}
|
|
26
|
+
export interface ServerOptions<Before extends AnyAlxia, App extends AnyAlxia> {
|
|
27
|
+
/**
|
|
28
|
+
* Runs first, on a new app: what is declared here runs before the
|
|
29
|
+
* client's files too — a guard, a rate limit, a logger that should see
|
|
30
|
+
* every request. Returns the app, so its types flow to `configure`.
|
|
31
|
+
*/
|
|
32
|
+
readonly beforeAll?: (app: FreshApp) => Before;
|
|
33
|
+
/**
|
|
34
|
+
* The app the pages run behind: its plugins, its hooks, its `/api`.
|
|
35
|
+
* Declared after the client's files and before the catch-all. Returns
|
|
36
|
+
* the app: what it builds is what the loaders read through `alxiaOf`.
|
|
37
|
+
*/
|
|
38
|
+
readonly configure?: (app: Before) => App;
|
|
39
|
+
/**
|
|
40
|
+
* Sets the app's own keys on React Router's context provider, `ctx`
|
|
41
|
+
* typed by `configure`'s app. `alxiaContext` is always set.
|
|
42
|
+
*/
|
|
43
|
+
readonly getLoadContext?: (ctx: ContextOf<App>, context: RouterContextProvider) => MaybePromise<void>;
|
|
44
|
+
/** Overrides the plugin's server build: rarely wanted. */
|
|
45
|
+
readonly build?: ServerWiring['build'];
|
|
46
|
+
/** Overrides the plugin's mode, `development` in dev and `production` in a build. */
|
|
47
|
+
readonly mode?: 'development' | 'production';
|
|
48
|
+
/**
|
|
49
|
+
* Overrides the plugin's client folder, `build/client` beside the built
|
|
50
|
+
* server. `false` serves none of it: declare the files yourself, in
|
|
51
|
+
* `beforeAll` or `configure`.
|
|
52
|
+
*/
|
|
53
|
+
readonly client?: string | URL | false;
|
|
54
|
+
/**
|
|
55
|
+
* `listen`'s options for `bun build/server/index.js`; a `port` or `hostname` here wins over `PORT` (3000)
|
|
56
|
+
* and `HOST` (`0.0.0.0`) from the environment.
|
|
57
|
+
*/
|
|
58
|
+
readonly listen?: ListenOptions;
|
|
59
|
+
/** Called once the built server listens. Prints `alxia listening on <url>` by default. */
|
|
60
|
+
readonly onListen?: (server: Bun.Server<unknown>) => void;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* The server `createServer()` describes, made into an app by the Vite
|
|
64
|
+
* plugin, by a test, or by a server file of your own.
|
|
65
|
+
*/
|
|
66
|
+
export interface ReactRouterServer<App extends AnyAlxia> {
|
|
67
|
+
/**
|
|
68
|
+
* The app, serving React Router's build: `build/server/index.js`'s
|
|
69
|
+
* default export. A test drives it with `app.request`.
|
|
70
|
+
*
|
|
71
|
+
* ```ts
|
|
72
|
+
* const app = server.create({ build: await import('./build/server/index.js') });
|
|
73
|
+
* ```
|
|
74
|
+
*/
|
|
75
|
+
create(wiring: ServerWiring): App;
|
|
76
|
+
/**
|
|
77
|
+
* Listens with `app`, on `listen`, `PORT` and `HOST`, and stops it on
|
|
78
|
+
* `SIGINT` or `SIGTERM`: its `onStop` hooks run, and the process exits.
|
|
79
|
+
* What `bun build/server/index.js` runs.
|
|
80
|
+
*/
|
|
81
|
+
start(app: App): Bun.Server<unknown>;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* The alxia server of a React Router app, for `app/server.ts`. Every option
|
|
85
|
+
* is optional: the Vite plugin wires React Router's build, the mode and the
|
|
86
|
+
* client folder, and without the file uses `createServer()` as it is.
|
|
87
|
+
*
|
|
88
|
+
* ```ts
|
|
89
|
+
* const server = createServer({
|
|
90
|
+
* configure: (app) => app.use(logger()).get('/api/health', ({ reply }) => reply.ok({ ok: true })),
|
|
91
|
+
* });
|
|
92
|
+
* export default server;
|
|
93
|
+
* ```
|
|
94
|
+
*
|
|
95
|
+
* The request runs through `beforeAll`, the client's files, `configure`,
|
|
96
|
+
* then the pages.
|
|
97
|
+
*/
|
|
98
|
+
export declare function createServer<Before extends AnyAlxia = FreshApp, App extends AnyAlxia = Before>(options?: ServerOptions<Before, App>): ReactRouterServer<App>;
|
|
99
|
+
//# sourceMappingURL=server.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EACN,KAAK,KAAK,EACV,KAAK,QAAQ,EAEb,KAAK,SAAS,EACd,KAAK,KAAK,EACV,KAAK,aAAa,EAClB,KAAK,YAAY,EACjB,MAAM,aAAa,CAAC;AACrB,OAAO,KAAK,EAAE,qBAAqB,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAGvE,gFAAgF;AAChF,MAAM,MAAM,QAAQ,GAAG,KAAK,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK,CAAC,CAAC;AAEtD;;;GAGG;AACH,MAAM,WAAW,YAAY;IAC5B;;;OAGG;IACH,QAAQ,CAAC,KAAK,EAAE,WAAW,GAAG,CAAC,MAAM,YAAY,CAAC,WAAW,CAAC,CAAC,CAAC;IAChE,uFAAuF;IACvF,QAAQ,CAAC,IAAI,CAAC,EAAE,aAAa,GAAG,YAAY,CAAC;IAC7C,kFAAkF;IAClF,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,GAAG,GAAG,SAAS,CAAC;CAC3C;AAED,MAAM,WAAW,aAAa,CAAC,MAAM,SAAS,QAAQ,EAAE,GAAG,SAAS,QAAQ;IAC3E;;;;OAIG;IACH,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC,GAAG,EAAE,QAAQ,KAAK,MAAM,CAAC;IAC/C;;;;OAIG;IACH,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,GAAG,CAAC;IAC1C;;;OAGG;IACH,QAAQ,CAAC,cAAc,CAAC,EAAE,CACzB,GAAG,EAAE,SAAS,CAAC,GAAG,CAAC,EACnB,OAAO,EAAE,qBAAqB,KAC1B,YAAY,CAAC,IAAI,CAAC,CAAC;IACxB,0DAA0D;IAC1D,QAAQ,CAAC,KAAK,CAAC,EAAE,YAAY,CAAC,OAAO,CAAC,CAAC;IACvC,qFAAqF;IACrF,QAAQ,CAAC,IAAI,CAAC,EAAE,aAAa,GAAG,YAAY,CAAC;IAC7C;;;;OAIG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,GAAG,GAAG,KAAK,CAAC;IACvC;;;OAGG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,aAAa,CAAC;IAChC,0FAA0F;IAC1F,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,IAAI,CAAC;CAC1D;AAED;;;GAGG;AACH,MAAM,WAAW,iBAAiB,CAAC,GAAG,SAAS,QAAQ;IACtD;;;;;;;OAOG;IACH,MAAM,CAAC,MAAM,EAAE,YAAY,GAAG,GAAG,CAAC;IAClC;;;;OAIG;IACH,KAAK,CAAC,GAAG,EAAE,GAAG,GAAG,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;CACrC;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,YAAY,CAC3B,MAAM,SAAS,QAAQ,GAAG,QAAQ,EAClC,GAAG,SAAS,QAAQ,GAAG,MAAM,EAC5B,OAAO,GAAE,aAAa,CAAC,MAAM,EAAE,GAAG,CAAM,GAAG,iBAAiB,CAAC,GAAG,CAAC,CAmDlE"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { ResolvedConfig } from 'vite';
|
|
2
|
+
/** The server build's input, and in dev the module the requests go to. */
|
|
3
|
+
export declare const SERVER = "virtual:alxia-react-router/server";
|
|
4
|
+
/** The same, when there is no server file: `createServer()` as it is. */
|
|
5
|
+
export declare const DEFAULT = "virtual:alxia-react-router/server-default";
|
|
6
|
+
/** What the plugin reads of React Router's own config. */
|
|
7
|
+
export interface ReactRouterConfig {
|
|
8
|
+
readonly appDirectory: string;
|
|
9
|
+
readonly buildDirectory: string;
|
|
10
|
+
readonly serverBuildFile: string;
|
|
11
|
+
readonly ssr: boolean;
|
|
12
|
+
readonly serverBundles?: unknown;
|
|
13
|
+
}
|
|
14
|
+
/** React Router's config, which its plugin puts on Vite's. */
|
|
15
|
+
export declare function contextOf(config: object): ReactRouterConfig;
|
|
16
|
+
/** React Router's child compiler: a Vite server without its plugin, cached apart. */
|
|
17
|
+
export declare function isChildCompiler(config: ResolvedConfig): boolean;
|
|
18
|
+
/**
|
|
19
|
+
* The server file: `entry`, which must exist, or `app/server.ts` when it
|
|
20
|
+
* does, or `undefined` for the default server.
|
|
21
|
+
*/
|
|
22
|
+
export declare function serverFile(entry: string | undefined, config: ResolvedConfig): string | undefined;
|
|
23
|
+
/** The built server, absolute: `build/server/index.js`. */
|
|
24
|
+
export declare function serverBuildPath(config: ResolvedConfig): string;
|
|
25
|
+
/** `build/client`, relative to the built `build/server/index.js`. */
|
|
26
|
+
export declare function clientPath(config: ResolvedConfig): string;
|
|
27
|
+
/**
|
|
28
|
+
* The ssr build's options: one file, `build/server/index.js`, the server
|
|
29
|
+
* and React Router's build together. Rolldown, under Vite 8, names the
|
|
30
|
+
* option otherwise.
|
|
31
|
+
*/
|
|
32
|
+
export declare function serverBuildOptions(rolldown: boolean): object;
|
|
33
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/vite/config.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,MAAM,CAAC;AAG3C,0EAA0E;AAC1E,eAAO,MAAM,MAAM,sCAAsC,CAAC;AAC1D,yEAAyE;AACzE,eAAO,MAAM,OAAO,8CAAsB,CAAC;AAE3C,0DAA0D;AAC1D,MAAM,WAAW,iBAAiB;IACjC,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;IACjC,QAAQ,CAAC,GAAG,EAAE,OAAO,CAAC;IACtB,QAAQ,CAAC,aAAa,CAAC,EAAE,OAAO,CAAC;CACjC;AAED,8DAA8D;AAC9D,wBAAgB,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,iBAAiB,CAc3D;AAED,qFAAqF;AACrF,wBAAgB,eAAe,CAAC,MAAM,EAAE,cAAc,GAAG,OAAO,CAM/D;AAED;;;GAGG;AACH,wBAAgB,UAAU,CACzB,KAAK,EAAE,MAAM,GAAG,SAAS,EACzB,MAAM,EAAE,cAAc,GACpB,MAAM,GAAG,SAAS,CAYpB;AAED,2DAA2D;AAC3D,wBAAgB,eAAe,CAAC,MAAM,EAAE,cAAc,GAAG,MAAM,CAQ9D;AAED,qEAAqE;AACrE,wBAAgB,UAAU,CAAC,MAAM,EAAE,cAAc,GAAG,MAAM,CAOzD;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,OAAO,GAAG,MAAM,CAc5D"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Under `react-router dev`: a request Vite left, answered by the server,
|
|
3
|
+
* loaded through the ssr environment's module runner.
|
|
4
|
+
*/
|
|
5
|
+
import type { ViteDevServer } from 'vite';
|
|
6
|
+
import { toRequest } from './node';
|
|
7
|
+
/** What the dev server module exports: the alxia app, as `server.create` made it. */
|
|
8
|
+
export interface DevApp {
|
|
9
|
+
fetch(request: Request, server?: Bun.Server<unknown>): Promise<Response>;
|
|
10
|
+
readonly websocket: Bun.WebSocketHandler<never>;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* The app, as the server file is now: the runner keeps the module until an
|
|
14
|
+
* edit invalidates it, so an edit is a new app.
|
|
15
|
+
*/
|
|
16
|
+
export declare function loadApp(server: ViteDevServer, entry: string | undefined): Promise<DevApp>;
|
|
17
|
+
export declare function serveFromEntry(server: ViteDevServer, entry: string | undefined, req: Parameters<typeof toRequest>[0], res: Parameters<typeof toRequest>[1]): Promise<void>;
|
|
18
|
+
/**
|
|
19
|
+
* `react-router dev`: the app's sockets on Vite's server now, and, returned
|
|
20
|
+
* for after Vite's own middlewares, every request Vite leaves to the app.
|
|
21
|
+
*/
|
|
22
|
+
export declare function serveDev(server: ViteDevServer, entry: string | undefined): () => void;
|
|
23
|
+
//# sourceMappingURL=dev.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dev.d.ts","sourceRoot":"","sources":["../../src/vite/dev.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,MAAM,CAAC;AAG1C,OAAO,EAAQ,SAAS,EAAE,MAAM,QAAQ,CAAC;AAGzC,qFAAqF;AACrF,MAAM,WAAW,MAAM;IACtB,KAAK,CAAC,OAAO,EAAE,OAAO,EAAE,MAAM,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC;IACzE,QAAQ,CAAC,SAAS,EAAE,GAAG,CAAC,gBAAgB,CAAC,KAAK,CAAC,CAAC;CAChD;AAED;;;GAGG;AACH,wBAAsB,OAAO,CAC5B,MAAM,EAAE,aAAa,EACrB,KAAK,EAAE,MAAM,GAAG,SAAS,GACvB,OAAO,CAAC,MAAM,CAAC,CAoBjB;AAED,wBAAsB,cAAc,CACnC,MAAM,EAAE,aAAa,EACrB,KAAK,EAAE,MAAM,GAAG,SAAS,EACzB,GAAG,EAAE,UAAU,CAAC,OAAO,SAAS,CAAC,CAAC,CAAC,CAAC,EACpC,GAAG,EAAE,UAAU,CAAC,OAAO,SAAS,CAAC,CAAC,CAAC,CAAC,GAClC,OAAO,CAAC,IAAI,CAAC,CAGf;AAED;;;GAGG;AACH,wBAAgB,QAAQ,CACvB,MAAM,EAAE,aAAa,EACrB,KAAK,EAAE,MAAM,GAAG,SAAS,GACvB,MAAM,IAAI,CAeZ"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The source of the module the plugin serves in dev and builds as
|
|
3
|
+
* `build/server/index.js`: the server, made into the app with React
|
|
4
|
+
* Router's build, and in a build listening when run.
|
|
5
|
+
*/
|
|
6
|
+
export declare const NAME = "alxia-react-router";
|
|
7
|
+
/** The input when the plugin is disabled: React Router's build alone. */
|
|
8
|
+
export declare const PASS_THROUGH = "export * from 'virtual:react-router/server-build';\n";
|
|
9
|
+
export interface EntryOptions {
|
|
10
|
+
/** The server file, absolute, or `undefined` for `createServer()` as it is. */
|
|
11
|
+
readonly file: string | undefined;
|
|
12
|
+
/** How an error names the file: relative to Vite's root. */
|
|
13
|
+
readonly label: string;
|
|
14
|
+
/** Under `react-router dev`: the build on every request, no client, no listening. */
|
|
15
|
+
readonly dev: boolean;
|
|
16
|
+
/** The client build, relative to the built file: `../client`. */
|
|
17
|
+
readonly client: string | undefined;
|
|
18
|
+
}
|
|
19
|
+
export declare function serverEntry({ file, label, dev, client, }: EntryOptions): string;
|
|
20
|
+
//# sourceMappingURL=entry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"entry.d.ts","sourceRoot":"","sources":["../../src/vite/entry.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,eAAO,MAAM,IAAI,uBAAuB,CAAC;AAKzC,yEAAyE;AACzE,eAAO,MAAM,YAAY,yDAAuC,CAAC;AAEjE,MAAM,WAAW,YAAY;IAC5B,+EAA+E;IAC/E,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,CAAC;IAClC,4DAA4D;IAC5D,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,qFAAqF;IACrF,QAAQ,CAAC,GAAG,EAAE,OAAO,CAAC;IACtB,iEAAiE;IACjE,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;CACpC;AAED,wBAAgB,WAAW,CAAC,EAC3B,IAAI,EACJ,KAAK,EACL,GAAG,EACH,MAAM,GACN,EAAE,YAAY,GAAG,MAAM,CAyCvB"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { Plugin } from 'vite';
|
|
2
|
+
export interface AlxiaOptions {
|
|
3
|
+
/**
|
|
4
|
+
* The server file, relative to Vite's root: a module whose default export
|
|
5
|
+
* is `createServer()` from `@alxia/react-router`. By default `server.ts`
|
|
6
|
+
* in React Router's app directory, `app/server.ts`, when it exists, and
|
|
7
|
+
* otherwise `createServer()` with no options.
|
|
8
|
+
*/
|
|
9
|
+
readonly entry?: string;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* The Vite plugin that makes alxia the server of a React Router app,
|
|
13
|
+
* anywhere in `plugins`:
|
|
14
|
+
*
|
|
15
|
+
* ```ts
|
|
16
|
+
* export default defineConfig({ plugins: [alxia(), reactRouter()] });
|
|
17
|
+
* ```
|
|
18
|
+
*
|
|
19
|
+
* - **`react-router dev`**: every request Vite does not answer itself —
|
|
20
|
+
* pages, data, `/api` — goes to the server, loaded through Vite's SSR
|
|
21
|
+
* runner. HMR, an edit to the server, the app's own context keys and
|
|
22
|
+
* its WebSocket routes work, with no restart.
|
|
23
|
+
* - **`react-router build`**: `build/server/index.js` is the server, with
|
|
24
|
+
* React Router's build inside it. `bun build/server/index.js` listens on
|
|
25
|
+
* `PORT` (3000) and `HOST` (`0.0.0.0`); importing it starts nothing.
|
|
26
|
+
* - **`vite preview`**: every request goes to that built server, as it
|
|
27
|
+
* would answer it; so does React Router's prerendering.
|
|
28
|
+
*
|
|
29
|
+
* The server is `app/server.ts`'s default export, `createServer()` from
|
|
30
|
+
* `@alxia/react-router`, or without that file `createServer()` as it is.
|
|
31
|
+
*/
|
|
32
|
+
export declare function alxia(options?: AlxiaOptions): Plugin;
|
|
33
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/vite/index.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,MAAM,EAAkB,MAAM,MAAM,CAAC;AAenD,MAAM,WAAW,YAAY;IAC5B;;;;;OAKG;IACH,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;CACxB;AAKD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,KAAK,CAAC,OAAO,GAAE,YAAiB,GAAG,MAAM,CAwExD"}
|