1.1.0 • Published 1 year ago

remix-bun v1.1.0

Weekly downloads
-
License
MIT
Repository
github
Last release
1 year ago

remix-bun

Bun server adapter for remix

bun + remix.run

Get Started

Prerequirements

Now either

Create a new remix app

Use the example in examples/basic (Easiest way!)

$ git clone git@github.com:patlux/remix-bun.git
$ cd remix-bun/examples/basic
$ bun install

You are ready to go!

Just run bun run dev to start the development server.

Use create-remix

Create a new project with the following commands:

$ bunx create-remix@latest --no-install --typescript

# Where would you like to create your app?
# -> my-remix-bun-app
# What type of app do you want to create?
# -> Just the basics
# Where do you want to deploy? Choose Remix App Server if you're unsure; it's easy to change deployment targets.
# -> Remix App Server
# 💿 That's it! `cd` into "<..>/my-remix-bun-app" and check the README for development and deploy instructions!

$ cd my-remix-bun-app

Continue with the steps below in Add to existing project

Add to existing project

Add remix-bun to your existing project with the following commands:

$ bun add remix-bun
$ bun add -d bun-types npm-run-all
# See below in "Setup project" for further instructions

Setup project

Make sure you followed the instructions above before you continue.

  1. Add server.ts in the root of your project:
// <root-folder>/server.ts

import type { ServeOptions } from "bun";
import { createRequestHandler } from "remix-bun";

setInterval(() => Bun.gc(true), 9000);

const bunServeOptions: ServeOptions = {
  port: parseInt(process.env.PORT ?? "3000", 10),
  fetch:
    process.env.NODE_ENV === "production"
      ? createRequestHandler({
          build: require("./build"),
          mode: "production",
        })
      : async (request: Request) => {
          const build = require("./build");
          const requestHandler = createRequestHandler({
            build,
            mode: "development",
          });
          return requestHandler(request);
        },
};

export default bunServeOptions;
  1. tsconfig.json

Make the following changes:

diff --git a/tsconfig.json b/tsconfig.json
index 20f8a38..92db692 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -1,13 +1,14 @@
 {
   "include": ["remix.env.d.ts", "**/*.ts", "**/*.tsx"],
   "compilerOptions": {
-    "lib": ["DOM", "DOM.Iterable", "ES2019"],
+    "lib": ["DOM", "DOM.Iterable", "ESNext"],
+    "module": "esnext",
+    "target": "esnext",
     "isolatedModules": true,
     "esModuleInterop": true,
     "jsx": "react-jsx",
     "moduleResolution": "node",
     "resolveJsonModule": true,
-    "target": "ES2019",
     "strict": true,
     "allowJs": true,
     "forceConsistentCasingInFileNames": true,
@@ -15,7 +16,6 @@
     "paths": {
       "~/*": ["./app/*"]
     },

     // Remix takes care of building everything in `remix build`.
     "noEmit": true
   }
  1. remix-env.d.ts

Make the following changes:

diff --git a/remix.env.d.ts b/remix.env.d.ts
index dcf8c45..a2385a1 100644
--- a/remix.env.d.ts
+++ b/remix.env.d.ts
@@ -1,2 +1,4 @@
+/// <reference lib="DOM" />
+/// <reference lib="DOM.Iterable" />
+/// <reference types="bun-types" />
 /// <reference types="@remix-run/dev" />
-/// <reference types="@remix-run/node" />
  1. package.json

Add the following scripts to your package.json:

diff --git a/package.json b/package.json
index fb04c8d..c1b8e17 100644
--- a/package.json
+++ b/package.json
@@ -2,8 +2,10 @@
   "private": true,
   "sideEffects": false,
   "scripts": {
+    "dev:remix": "bun run node_modules/@remix-run/dev/dist/cli.js watch",
+    "dev:server": "NODE_ENV=development bun --hot ./server.ts",
+    "dev": "run-p dev:*",
     "build": "remix build",
-    "dev": "remix dev",
-    "start": "remix-serve build",
+    "start": "NODE_ENV=production bun run ./server.ts",
     "typecheck": "tsc -b"
   },
  1. <root-folder>/app/entry.server.ts

Check the content of your <root-folder>/app/entry.server.ts.

If you are using renderToString, you are fine but you can replace it as below if you want.

If you are using renderToPipeableStream then replace your entry.server.ts with the following:

import type { EntryContext } from "@remix-run/node";
import { Response } from "@remix-run/node";
import { RemixServer } from "@remix-run/react";
import isbot from "isbot";
import { renderToReadableStream } from "react-dom/server";

export default function handleRequest(
  request: Request,
  responseStatusCode: number,
  responseHeaders: Headers,
  remixContext: EntryContext
) {
  return isbot(request.headers.get("user-agent"))
    ? handleBotRequest(
        request,
        responseStatusCode,
        responseHeaders,
        remixContext
      )
    : handleBrowserRequest(
        request,
        responseStatusCode,
        responseHeaders,
        remixContext
      );
}

function handleBotRequest(
  request: Request,
  responseStatusCode: number,
  responseHeaders: Headers,
  remixContext: EntryContext
) {
  return new Promise(async (resolve, reject) => {
    const stream = await renderToReadableStream(
      <RemixServer context={remixContext} url={request.url} />,
      {
        onError(error) {
          console.error(error);
          reject(error);
        },
      }
    );

    resolve(
      new Response(stream, {
        status: responseStatusCode,
        headers: responseHeaders,
      })
    );
  });
}

function handleBrowserRequest(
  request: Request,
  responseStatusCode: number,
  responseHeaders: Headers,
  remixContext: EntryContext
) {
  return new Promise(async (resolve, reject) => {
    const stream = await renderToReadableStream(
      <RemixServer context={remixContext} url={request.url} />,
      {
        onError(error) {
          console.error(error);
          reject(error);
        },
      }
    );

    resolve(
      new Response(stream, {
        status: responseStatusCode,
        headers: responseHeaders,
      })
    );
  });
}

Development

From your terminal:

bun run dev

This starts your app in development mode, rebuilding assets on file changes.

Troubleshooting

TypeError: undefined is not a function (near '...(0 , import_server.renderToPipeableStream)...')

You need to adjust the entry.server.ts as suggested in "Setup project".

TODO

Support remix's --template

bunx create-remix@latest --template patlux/remix-bun --typescript

Would be nice for a better way of bootstraping new remix projects with bun.

But it's currently not possible because bun doesn't support the bun config get command like npm or yarn.

$ bunx create-remix@latest --template patlux/remix-bun --typescript
# Where would you like to create your app? ./my-remix-app
# Do you want me to run `bun install`? Yes
# ⠏ Creating your app…error: script not found "config"
# Command failed: bun config get @remix-run:registry
# error: script not found "config"

# error: "create-remix" exited with code 1 (SIGHUP)

Release

  1. bunx changeset

Enter a summary for the changes.

  1. bunx changeset version
  2. git add .
  3. git commit -m "chore(release): vX.X.X"

Credits

Created by @de_patwoz