Repository: remix-run/remix
Pull request #976 (2021-12-09 18:33:22)

feat: allow ESM output of server builds
chore: updated package.json from "browser" to "module" field


Patch:
diff --git a/packages/remix-dev/compiler.ts b/packages/remix-dev/compiler.ts
index fede87c86..f08577318 100644
--- a/packages/remix-dev/compiler.ts
+++ b/packages/remix-dev/compiler.ts
@@ -345,7 +345,7 @@ async function createServerBuild(
     },
     outfile: path.resolve(config.serverBuildDirectory, "index.js"),
     platform: "node",
-    format: "cjs",
+    format: config.serverModuleFormat,
     target: options.target,
     inject: [reactShim],
     loader: loaders,
diff --git a/packages/remix-dev/config.ts b/packages/remix-dev/config.ts
index 219a439ad..34f7249f0 100644
--- a/packages/remix-dev/config.ts
+++ b/packages/remix-dev/config.ts
@@ -70,11 +70,19 @@ export interface AppConfig {
    */
   devServerPort?: number;
   /**
-   * The delay before the dev server broadcasts a reload event
+   * The delay before the dev server broadcasts a reload event.
    */
   devServerBroadcastDelay?: number;
 
+  /**
+   * Additional MDX remark / rehype plugins.
+   */
   mdx?: RemixMdxConfig | RemixMdxConfigFunction;
+
+  /**
+   * The output format of the server build. Defaults to "cjs".
+   */
+  serverModuleFormat: "esm" | "cjs";
 }
 
 /**
@@ -141,7 +149,15 @@ export interface RemixConfig {
    */
   devServerBroadcastDelay: number;
 
+  /**
+   * Additional MDX remark / rehype plugins.
+   */
   mdx?: RemixMdxConfig | RemixMdxConfigFunction;
+
+  /**
+   * The output format of the server build. Defaults to "cjs".
+   */
+  serverModuleFormat: "esm" | "cjs";
 }
 
 /**
@@ -170,6 +186,9 @@ export async function readConfig(
     throw new Error(`Error loading Remix config in ${configFile}`);
   }
 
+  let serverModuleFormat = appConfig.serverModuleFormat || "cjs";
+  let mdx = appConfig.mdx;
+
   let appDirectory = path.resolve(
     rootDirectory,
     appConfig.appDirectory || "app"
@@ -243,7 +262,8 @@ export async function readConfig(
     routes,
     serverBuildDirectory,
     serverMode,
-    mdx: appConfig.mdx
+    serverModuleFormat,
+    mdx
   };
 }
 
diff --git a/packages/remix-react/package.json b/packages/remix-react/package.json
index 9b113c41d..b2ded6733 100644
--- a/packages/remix-react/package.json
+++ b/packages/remix-react/package.json
@@ -12,7 +12,7 @@
     "url": "https://github.com/remix-run/remix/issues"
   },
   "main": "index.js",
-  "browser": "browser/index.js",
+  "module": "esm/index.js",
   "dependencies": {
     "react-router-dom": "^6.1.0"
   },
diff --git a/packages/remix-server-runtime/package.json b/packages/remix-server-runtime/package.json
index c3c91aa42..f1d7433a1 100644
--- a/packages/remix-server-runtime/package.json
+++ b/packages/remix-server-runtime/package.json
@@ -3,6 +3,8 @@
   "description": "Server runtime for Remix",
   "version": "1.0.6",
   "license": "MIT",
+  "main": "./index.js",
+  "module": "./esm/index.js",
   "repository": {
     "type": "git",
     "url": "https://github.com/remix-run/remix",
diff --git a/packages/remix/package.json b/packages/remix/package.json
index 26fadabb7..e2c5de670 100644
--- a/packages/remix/package.json
+++ b/packages/remix/package.json
@@ -13,7 +13,7 @@
     "url": "https://github.com/remix-run/remix/issues"
   },
   "main": "index.js",
-  "browser": "browser/index.js",
+  "module": "esm/index.js",
   "sideEffects": false,
   "dependencies": {
     "fs-extra": "^10.0.0"
diff --git a/rollup.config.js b/rollup.config.js
index b2691a2b0..235407585 100644
--- a/rollup.config.js
+++ b/rollup.config.js
@@ -105,7 +105,7 @@ function remix() {
       input: `${SOURCE_DIR}/index.ts`,
       output: {
         banner: createBanner("remix", version),
-        dir: `${OUTPUT_DIR}/browser`,
+        dir: `${OUTPUT_DIR}/esm`,
         format: "esm"
       },
       plugins: [
@@ -231,6 +231,26 @@ function remixServerRuntime() {
         })
       ]
     },
+    {
+      external(id) {
+        return isBareModuleId(id);
+      },
+      input: `${SOURCE_DIR}/index.ts`,
+      output: {
+        banner: createBanner("@remix-run/server-runtime", version),
+        dir: `${OUTPUT_DIR}/esm`,
+        format: "esm",
+        preserveModules: true
+      },
+      plugins: [
+        babel({
+          babelHelpers: "bundled",
+          exclude: /node_modules/,
+          extensions: [".ts", ".tsx"]
+        }),
+        nodeResolve({ extensions: [".ts", ".tsx"] })
+      ]
+    },
     {
       external() {
         return true;
@@ -256,7 +276,7 @@ function remixServerRuntime() {
       input: `${SOURCE_DIR}/magicExports/server.ts`,
       output: {
         banner: createBanner("@remix-run/server-runtime", version),
-        dir: `${OUTPUT_DIR}/magicExports/browser`,
+        dir: `${OUTPUT_DIR}/magicExports/esm`,
         format: "esm"
       },
       plugins: [
@@ -330,7 +350,7 @@ function remixNode() {
       input: `${SOURCE_DIR}/magicExports/platform.ts`,
       output: {
         banner: createBanner("@remix-run/node", version),
-        dir: `${OUTPUT_DIR}/magicExports/browser`,
+        dir: `${OUTPUT_DIR}/magicExports/esm`,
         format: "esm"
       },
       plugins: [
@@ -376,7 +396,7 @@ function remixCloudflareWorkers() {
       input: `${SOURCE_DIR}/magicExports/platform.ts`,
       output: {
         banner: createBanner("@remix-run/cloudflare-workers", version),
-        dir: `${OUTPUT_DIR}/magicExports/browser`,
+        dir: `${OUTPUT_DIR}/magicExports/esm`,
         format: "esm"
       },
       plugins: [
@@ -447,7 +467,7 @@ function remixReact() {
   /** @type {import("rollup").RollupOptions} */
   // This CommonJS build of remix-react is for node; both for use in running our
   // server and for 3rd party tools that work with node.
-  let remixReactNode = {
+  let remixReactCJS = {
     external(id) {
       return isBareModuleId(id);
     },
@@ -478,14 +498,14 @@ function remixReact() {
 
   // The browser build of remix-react is ESM so we can treeshake it.
   /** @type {import("rollup").RollupOptions} */
-  let remixReactBrowser = {
+  let remixReactESM = {
     external(id) {
       return isBareModuleId(id);
     },
     input: `${SOURCE_DIR}/index.tsx`,
     output: {
       banner: createBanner("@remix-run/react", version),
-      dir: `${OUTPUT_DIR}/browser`,
+      dir: `${OUTPUT_DIR}/esm`,
       format: "esm",
       preserveModules: true
     },
@@ -500,7 +520,7 @@ function remixReact() {
   };
 
   /** @type {import("rollup").RollupOptions[]} */
-  let remixReactMagicExports = {
+  let remixReactMagicExportsCJS = {
     external() {
       return true;
     },
@@ -520,14 +540,14 @@ function remixReact() {
   };
 
   /** @type {import("rollup").RollupOptions[]} */
-  let remixReactMagicExportsBrowser = {
+  let remixReactMagicExportsESM = {
     external() {
       return true;
     },
     input: `${SOURCE_DIR}/magicExports/client.ts`,
     output: {
       banner: createBanner("@remix-run/react", version),
-      dir: `${OUTPUT_DIR}/magicExports/browser`,
+      dir: `${OUTPUT_DIR}/magicExports/esm`,
       format: "esm"
     },
     plugins: [
@@ -540,10 +560,10 @@ function remixReact() {
   };
 
   return [
-    remixReactNode,
-    remixReactBrowser,
-    remixReactMagicExports,
-    remixReactMagicExportsBrowser
+    remixReactCJS,
+    remixReactESM,
+    remixReactMagicExportsCJS,
+    remixReactMagicExportsESM
   ];
 }
 


Test patch:
diff --git a/packages/remix-dev/__tests__/readConfig-test.ts b/packages/remix-dev/__tests__/readConfig-test.ts
index 27f4bf7a1..b31af42d2 100644
--- a/packages/remix-dev/__tests__/readConfig-test.ts
+++ b/packages/remix-dev/__tests__/readConfig-test.ts
@@ -433,6 +433,7 @@ describe("readConfig", () => {
         },
         "serverBuildDirectory": Any<String>,
         "serverMode": "production",
+        "serverModuleFormat": "cjs",
       }
     `
     );