Skip to content
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

fix(remix-dev/vite): use global Request class in Vite dev server handler #8062

Merged
Prev Previous commit
Next Next commit
fix createRequest call, refactor
  • Loading branch information
markdalgleish committed Nov 23, 2023
commit 1dd58a77780ad22a2cb2703ae6bfde9d7bb9381a
34 changes: 18 additions & 16 deletions packages/remix-dev/vite/node/adapter.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,23 @@
// @ts-nocheck
// adapted from https://github.com/solidjs/solid-start/blob/ccff60ce75e066f6613daf0272dbb43a196235a4/packages/start/node/fetch.js
import { once } from "events";
import { type IncomingMessage, type ServerResponse } from "http";
import type {
IncomingHttpHeaders,
IncomingMessage,
ServerResponse,
} from "node:http";
import { once } from "node:events";
import { Readable } from "node:stream";
import { splitCookiesString } from "set-cookie-parser";
import { Readable } from "stream";
import {
type ServerBuild,
installGlobals,
createReadableStreamFromReadable,
} from "@remix-run/node";
import { createRequestHandler as createBaseRequestHandler } from "@remix-run/server-runtime";

// polyfill should be also opt-in? (move to template?)
import invariant from "../../invariant";

installGlobals();
Copy link
Contributor Author

@hi-ogawa hi-ogawa Nov 24, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for proceeding with this PR!

I was wondering about what's the future plan of this forced installGlobals during vite dev.
Node 18 ships fetch, Request, etc... globals, so I thought forcing polyfills here might not be appropriate and better to be opt-in choice by individual users.

Looking at what happened in other adapters #7009 (and also unstable-vite-express currently has explicit installGlobals in server.mjs), the general direction seems to remove automatic/forced installGlobals.

Currently this polyfills kicks in whenever remix vite plugin is loaded, so I think it's not even possible for custom server (during dev) to opt-out from installGlobals currently.

Here is a quick repro:

https://stackblitz.com/edit/remix-run-remix-ws1uig?file=vite.config.ts

//
// vite.config.ts
//

let fetch1 = fetch;

import { unstable_vitePlugin as remix } from '@remix-run/dev';
import { defineConfig } from 'vite';
import tsconfigPaths from 'vite-tsconfig-paths';

let fetch2 = fetch;

export default defineConfig({
  clearScreen: false,
  plugins: [remix(), tsconfigPaths()],
});

let fetch3 = fetch;

// the log happens twice in parent and child compilers
//   parent: true false
//   child:  true true
console.log(fetch1 === fetch2, fetch1 === fetch3);

EDIT: Current behavior would be essentially same as installGlobals in vite.config.ts, so in order to allow users to opt-out, I think I would prefer doing it in the template's vite.config.ts #8119

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, I was wanting to follow this up separately. Thanks for looking into this!


function createHeaders(requestHeaders) {
function createHeaders(requestHeaders: IncomingHttpHeaders) {
let headers = new Headers();

for (let [key, values] of Object.entries(requestHeaders)) {
Expand All @@ -32,12 +35,13 @@ function createHeaders(requestHeaders) {
return headers;
}

// based on `createRemixRequest` in packages/remix-express/server.ts
// Based on `createRemixRequest` in packages/remix-express/server.ts
function createRequest(req: IncomingMessage, res: ServerResponse): Request {
let origin =
req.headers.origin && "null" !== req.headers.origin
? req.headers.origin
: `http://${req.headers.host}`;
invariant(req.url, 'Expected "req.url" to be defined');
let url = new URL(req.url, origin);

let controller = new AbortController();
Expand All @@ -57,7 +61,7 @@ function createRequest(req: IncomingMessage, res: ServerResponse): Request {
return new Request(url.href, init);
}

// Adapted from more recent version of `handleNodeResponse`:
// Adapted from solid-start's `handleNodeResponse`:
// https://github.com/solidjs/solid-start/blob/7398163869b489cce503c167e284891cf51a6613/packages/start/node/fetch.js#L162-L185
async function handleNodeResponse(webRes: Response, res: ServerResponse) {
res.statusCode = webRes.status;
Expand All @@ -76,7 +80,9 @@ async function handleNodeResponse(webRes: Response, res: ServerResponse) {
}

if (webRes.body) {
let readable = Readable.from(webRes.body);
// https://github.com/microsoft/TypeScript/issues/29867
let responseBody = webRes.body as unknown as AsyncIterable<Uint8Array>;
let readable = Readable.from(responseBody);
readable.pipe(res);
await once(readable, "end");
} else {
Expand All @@ -86,15 +92,11 @@ async function handleNodeResponse(webRes: Response, res: ServerResponse) {

export let createRequestHandler = (
build: ServerBuild,
{
mode = "production",
}: {
mode?: string;
}
{ mode = "production" }: { mode?: string }
) => {
let handler = createBaseRequestHandler(build, mode);
return async (req: IncomingMessage, res: ServerResponse) => {
let request = createRequest(req);
let request = createRequest(req, res);
let response = await handler(request, {});
handleNodeResponse(response, res);
};
Expand Down