Skip to content

Support react router middleware #98

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/adapter/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export { configure } from './configure.js'
export { adonisContext } from './src/context.js'
export { stubsRoot } from './stubs/index.js'
2 changes: 1 addition & 1 deletion packages/adapter/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@matstack/remix-adonisjs",
"description": "An adapter for using Remix with AdonisJS",
"version": "1.0.1",
"version": "1.0.2-middleware.0",
"engines": {
"node": ">=20.10.0"
},
Expand Down
13 changes: 9 additions & 4 deletions packages/adapter/providers/remix_provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import type { RequestHandler } from '../src/remix_adapter.js'

import { HttpContext } from '@adonisjs/core/http'
import type { ApplicationService } from '@adonisjs/core/types'
import { adonisContext } from '../src/context.js'
import '../src/types/main.js'

declare module '@adonisjs/core/types' {
Expand Down Expand Up @@ -42,10 +43,14 @@ export default class RemixProvider {

const requestHandler = createRequestHandler({
build,
getLoadContext: (context) => ({
http: context.http,
make: context.container.make.bind(context.container),
}),
getLoadContext: ({ container, http }) => {
const map = new Map()
map.set(adonisContext, {
http,
make: container.make.bind(container),
})
return map as any // TODO: don't use any here
},
})
const app = this.app
HttpContext.getter(
Expand Down
4 changes: 4 additions & 0 deletions packages/adapter/src/context.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { unstable_createContext } from 'react-router'
import { AdonisApplicationContext } from './remix_adapter.js'

export const adonisContext = unstable_createContext<AdonisApplicationContext>()
24 changes: 16 additions & 8 deletions packages/adapter/src/remix_adapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,17 @@ import type { Request as AdonisRequest, Response as AdonisResponse } from '@adon

import {
AppLoadContext,
UNSAFE_MiddlewareEnabled as MiddlewareEnabled,
ServerBuild,
createRequestHandler as createRemixRequestHandler,
unstable_InitialContext,
} from 'react-router'

import { createReadableStreamFromReadable } from '@react-router/node'
import debug from './debug.js'
import { ReadableWebToNodeStream } from './stream_conversion.js'

export type HandlerContext = {
export type AdonisAdapterContext = {
http: HttpContext
container: Container<ContainerBindings>
}
Expand All @@ -25,9 +27,15 @@ export type AdonisApplicationContext = {
make: Container<ContainerBindings>['make']
}

export type GetLoadContextFunction = (context: HandlerContext) => AppLoadContext
export type MaybePromise<T> = T | Promise<T>

export type RequestHandler = (context: HandlerContext) => Promise<void>
export type GetLoadContextFunction = (
context: AdonisAdapterContext
) => MiddlewareEnabled extends true
? MaybePromise<unstable_InitialContext>
: MaybePromise<AppLoadContext>

export type RequestHandler = (context: AdonisAdapterContext) => Promise<void>

/**
* Returns a request handler for AdonisJS that serves the response using Remix.
Expand All @@ -37,20 +45,20 @@ export function createRequestHandler({
getLoadContext,
mode = process.env.NODE_ENV,
}: {
build: ServerBuild
getLoadContext: GetLoadContextFunction
build: ServerBuild | (() => Promise<ServerBuild>)
getLoadContext?: GetLoadContextFunction
mode?: string
}): RequestHandler {
let handleRequest = createRemixRequestHandler(build, mode)

return async (context: HandlerContext) => {
return async (context: AdonisAdapterContext) => {
debug(`Creating remix request for ${context.http.request.parsedUrl}`)
const request = createRemixRequest(context.http.request, context.http.response)
const loadContext = getLoadContext(context)
const loadContext = await getLoadContext?.(context)

const response = await handleRequest(request, loadContext)

sendRemixResponse(context.http, response)
await sendRemixResponse(context.http, response)
}
}

Expand Down
12 changes: 11 additions & 1 deletion packages/reference-app/react-router.config.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,17 @@
import type { Config } from '@react-router/dev/config'
import type { Config } from '@react-router/dev/config';

declare module "react-router" {
interface Future {
unstable_middleware: true;
}
}

export default {
ssr: true,
appDirectory: 'resources/remix_app',
buildDirectory: 'build/remix',
serverBuildFile: 'server.js',
future: {
unstable_middleware: true
}
} satisfies Config
20 changes: 19 additions & 1 deletion packages/reference-app/resources/remix_app/root.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,22 @@
import { Links, Meta, Outlet, Scripts, ScrollRestoration } from 'react-router'
import { adonisContext } from '@matstack/remix-adonisjs';
import { Links, Meta, Outlet, Scripts, ScrollRestoration } from 'react-router';
import { Route } from './+types/root.js';

const serverLogger: Route.unstable_MiddlewareFunction = async (
{ request, context },
next
) => {
const { http } = context.get(adonisContext)
let start = performance.now();

let res = await next();

let duration = performance.now() - start;
http.logger.info(`Navigated to ${request.url} (${duration}ms)`);

return res;
};
export const unstable_middleware = [serverLogger]

export default function App() {
return (
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { adonisContext } from "@matstack/remix-adonisjs"
import type { Route } from "./+types/adonis_redirect.js"

export const loader = async ({ context }: Route.LoaderArgs) => {
const { http } = context
const { http } = context.get(adonisContext)

http.response.redirect('/login')

Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { adonisContext } from '@matstack/remix-adonisjs'
import { useLoaderData } from 'react-router'
import type { Route } from './+types/dashboard.js'

export const loader = ({ context }: Route.LoaderArgs) => {
const { http, make } = context
const { http } = context.get(adonisContext)

return http.session.all()
}
Expand Down
3 changes: 2 additions & 1 deletion packages/reference-app/resources/remix_app/routes/login.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { adonisContext } from '@matstack/remix-adonisjs'
import { Form, redirect } from 'react-router'
import type { Route } from './+types/login.js'

export const action = async ({ context }: Route.ActionArgs) => {
const { http } = context
const { http } = context.get(adonisContext)

const { password } = http.request.only(['password'])
if (password !== '123') {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { adonisContext } from '@matstack/remix-adonisjs'
import { useLoaderData } from 'react-router'
import type { Route } from './+types/resolve_container.js'

export const loader = async ({ context }: Route.LoaderArgs) => {
const app = await context.make('app')
const app = await context.get(adonisContext).make('app')
return {
env: app.getEnvironment(),
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { adonisContext } from '@matstack/remix-adonisjs'
import type { Route } from './+types/webhooks.payment.js'

export const action = async ({ request, context }: Route.ActionArgs) => {
const body = await request.text()
const { http } = context
const { http } = context.get(adonisContext)
http.logger.info('webhook body', body)
return Response.json({
status: 'ok',
Expand Down
4 changes: 4 additions & 0 deletions packages/reference-app/vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,8 @@ export default defineConfig({
],
},
},
ssr: {
// Avoid bundling adapter code
external: ['@matstack/remix-adonisjs']
}
})