From 040f51ad3a30ea77e49f1037082ca1a050aad619 Mon Sep 17 00:00:00 2001
From: Nathan Brown <nathan@nkbrown.us>
Date: Sun, 28 Jan 2024 23:58:46 -0700
Subject: [PATCH 1/6] Use ternary on vite configuration building

---
 packages/remix-dev/vite/plugin.ts | 86 ++++++++++++++++---------------
 1 file changed, 44 insertions(+), 42 deletions(-)

diff --git a/packages/remix-dev/vite/plugin.ts b/packages/remix-dev/vite/plugin.ts
index b684b453d24..be22a3fe57d 100644
--- a/packages/remix-dev/vite/plugin.ts
+++ b/packages/remix-dev/vite/plugin.ts
@@ -938,48 +938,50 @@ export const remixVitePlugin: RemixVitePlugin = (remixUserConfig = {}) => {
             ],
           },
           base: viteUserConfig.base,
-          ...(viteCommand === "build" && {
-            build: {
-              cssMinify: viteUserConfig.build?.cssMinify ?? true,
-              ...(!viteConfigEnv.isSsrBuild
-                ? {
-                    manifest: true,
-                    outDir: getClientBuildDirectory(ctx.remixConfig),
-                    rollupOptions: {
-                      preserveEntrySignatures: "exports-only",
-                      input: [
-                        ctx.entryClientFilePath,
-                        ...Object.values(ctx.remixConfig.routes).map(
-                          (route) =>
-                            `${path.resolve(
-                              ctx.remixConfig.appDirectory,
-                              route.file
-                            )}${CLIENT_ROUTE_QUERY_STRING}`
-                        ),
-                      ],
-                    },
-                  }
-                : {
-                    // We move SSR-only assets to client assets. Note that the
-                    // SSR build can also emit code-split JS files (e.g. by
-                    // dynamic import) under the same assets directory
-                    // regardless of "ssrEmitAssets" option, so we also need to
-                    // keep these JS files have to be kept as-is.
-                    ssrEmitAssets: true,
-                    copyPublicDir: false, // Assets in the public directory are only used by the client
-                    manifest: true, // We need the manifest to detect SSR-only assets
-                    outDir: getServerBuildDirectory(ctx),
-                    rollupOptions: {
-                      preserveEntrySignatures: "exports-only",
-                      input: serverBuildId,
-                      output: {
-                        entryFileNames: ctx.remixConfig.serverBuildFile,
-                        format: ctx.remixConfig.serverModuleFormat,
-                      },
-                    },
-                  }),
-            },
-          }),
+          ...(viteCommand === "build"
+            ? {
+                build: {
+                  cssMinify: viteUserConfig.build?.cssMinify ?? true,
+                  ...(!viteConfigEnv.isSsrBuild
+                    ? {
+                        manifest: true,
+                        outDir: getClientBuildDirectory(ctx.remixConfig),
+                        rollupOptions: {
+                          preserveEntrySignatures: "exports-only",
+                          input: [
+                            ctx.entryClientFilePath,
+                            ...Object.values(ctx.remixConfig.routes).map(
+                              (route) =>
+                                `${path.resolve(
+                                  ctx.remixConfig.appDirectory,
+                                  route.file
+                                )}${CLIENT_ROUTE_QUERY_STRING}`
+                            ),
+                          ],
+                        },
+                      }
+                    : {
+                        // We move SSR-only assets to client assets. Note that the
+                        // SSR build can also emit code-split JS files (e.g. by
+                        // dynamic import) under the same assets directory
+                        // regardless of "ssrEmitAssets" option, so we also need to
+                        // keep these JS files have to be kept as-is.
+                        ssrEmitAssets: true,
+                        copyPublicDir: false, // Assets in the public directory are only used by the client
+                        manifest: true, // We need the manifest to detect SSR-only assets
+                        outDir: getServerBuildDirectory(ctx),
+                        rollupOptions: {
+                          preserveEntrySignatures: "exports-only",
+                          input: serverBuildId,
+                          output: {
+                            entryFileNames: ctx.remixConfig.serverBuildFile,
+                            format: ctx.remixConfig.serverModuleFormat,
+                          },
+                        },
+                      }),
+                },
+              }
+            : undefined),
         };
       },
       async configResolved(resolvedViteConfig) {

From 604c48d4624d840db04a7a107030adb93cc27267 Mon Sep 17 00:00:00 2001
From: Nathan Brown <nathan@nkbrown.us>
Date: Mon, 29 Jan 2024 00:00:23 -0700
Subject: [PATCH 2/6] Adjust vite config when runnning "vite preview" in case
 of unstable_ssr

Set appType: "spa"
Ensure outDir gets passed through
---
 packages/remix-dev/vite/plugin.ts | 15 ++++++++++++++-
 1 file changed, 14 insertions(+), 1 deletion(-)

diff --git a/packages/remix-dev/vite/plugin.ts b/packages/remix-dev/vite/plugin.ts
index be22a3fe57d..defadd62f22 100644
--- a/packages/remix-dev/vite/plugin.ts
+++ b/packages/remix-dev/vite/plugin.ts
@@ -897,7 +897,12 @@ export const remixVitePlugin: RemixVitePlugin = (remixUserConfig = {}) => {
 
         return {
           __remixPluginContext: ctx,
-          appType: "custom",
+          appType:
+            viteCommand === "serve" &&
+            viteConfigEnv.mode === "production" &&
+            ctx.remixConfig.unstable_ssr === false
+              ? "spa"
+              : "custom",
           optimizeDeps: {
             include: [
               // Pre-bundle React dependencies to avoid React duplicates,
@@ -981,6 +986,14 @@ export const remixVitePlugin: RemixVitePlugin = (remixUserConfig = {}) => {
                       }),
                 },
               }
+            : viteCommand === "serve" && ctx.remixConfig.unstable_ssr === false
+            ? {
+                base: ctx.remixConfig.publicPath,
+                build: {
+                  manifest: true,
+                  outDir: getClientBuildDirectory(ctx.remixConfig),
+                },
+              }
             : undefined),
         };
       },

From 78e38f4d46aa843b89d09751631b560ea159873c Mon Sep 17 00:00:00 2001
From: Nathan Brown <nathan@nkbrown.us>
Date: Mon, 29 Jan 2024 00:12:59 -0700
Subject: [PATCH 3/6] Update spa template README.md to mention that server
 needs to support SPA mode

---
 templates/spa/README.md | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/templates/spa/README.md b/templates/spa/README.md
index e5e8d9b223a..cd17f67abf3 100644
--- a/templates/spa/README.md
+++ b/templates/spa/README.md
@@ -22,16 +22,16 @@ npm run dev
 
 ## Production
 
-When you are ready yo build a production version of your app, `npm run build` will generate your assets and an `index.html` for the SPA.
+When you are ready to build a production version of your app, `npm run build` will generate your assets and an `index.html` for the SPA.
 
 ```shellscript
 npm run build
 ```
 
-You can serve this from any server of your choosing, for a simple example, you could use [http-server](https://www.npmjs.com/package/http-server):
+You can serve this from any server of your choosing. The server should support SPA fallback. For a simple example, you could use [sirv-cli](https://www.npmjs.com/package/sirv-cli):
 
 ```shellscript
-npx http-server build/client/
+npx sirv-cli build/client/ --single --ignores "^/assets/"
 ```
 
 [remix-vite-docs]: https://remix.run/docs/en/main/future/vite

From 18228143d6987b56d0285cea48386c453737af2f Mon Sep 17 00:00:00 2001
From: Nathan Brown <nathan@nkbrown.us>
Date: Mon, 29 Jan 2024 00:14:40 -0700
Subject: [PATCH 4/6] Update spa template package.json script to use vite
 preview

---
 templates/spa/README.md    | 6 ++++++
 templates/spa/package.json | 3 +--
 2 files changed, 7 insertions(+), 2 deletions(-)

diff --git a/templates/spa/README.md b/templates/spa/README.md
index cd17f67abf3..700cb47d074 100644
--- a/templates/spa/README.md
+++ b/templates/spa/README.md
@@ -28,6 +28,12 @@ When you are ready to build a production version of your app, `npm run build` wi
 npm run build
 ```
 
+Preview build locally with [vite preview](https://vitejs.dev/guide/cli#vite-preview) to serve all routes via the single `index.html` file. Do not use this as a production server as it's not designed for it:
+
+```shellscript
+npm run preview
+```
+
 You can serve this from any server of your choosing. The server should support SPA fallback. For a simple example, you could use [sirv-cli](https://www.npmjs.com/package/sirv-cli):
 
 ```shellscript
diff --git a/templates/spa/package.json b/templates/spa/package.json
index 66c5be09320..b45ba399a00 100644
--- a/templates/spa/package.json
+++ b/templates/spa/package.json
@@ -6,13 +6,12 @@
     "build": "remix vite:build",
     "dev": "remix vite:dev",
     "lint": "eslint --ignore-path .gitignore --cache --cache-location ./node_modules/.cache/eslint .",
-    "start": "http-server build/client/",
+    "preview": "vite preview",
     "typecheck": "tsc"
   },
   "dependencies": {
     "@remix-run/node": "*",
     "@remix-run/react": "*",
-    "http-server": "^14.1.1",
     "react": "^18.2.0",
     "react-dom": "^18.2.0"
   },

From 31e043038ae2be7ffb713c9f9a9e490a755a93c9 Mon Sep 17 00:00:00 2001
From: Nathan Brown <nathan@nkbrown.us>
Date: Mon, 29 Jan 2024 00:22:33 -0700
Subject: [PATCH 5/6] Add changeset

---
 .changeset/tender-pens-tan.md | 8 ++++++++
 1 file changed, 8 insertions(+)
 create mode 100644 .changeset/tender-pens-tan.md

diff --git a/.changeset/tender-pens-tan.md b/.changeset/tender-pens-tan.md
new file mode 100644
index 00000000000..f143f4bba81
--- /dev/null
+++ b/.changeset/tender-pens-tan.md
@@ -0,0 +1,8 @@
+---
+"@remix-run/dev": patch
+---
+
+vite: Enable use of `vite preview` to preview built SPA sites
+
+In the SPA template, `start` script (`npm run start`) has been renamed to `preview` (`npm run preview`).
+In the SPA template, `npm run preview` uses `vite preview` for serving built SPA sites.

From 4a757cc92b1d9bad911b9f071bc9b835a0d2e3b2 Mon Sep 17 00:00:00 2001
From: Nathan Brown <nathan@nkbrown.us>
Date: Tue, 30 Jan 2024 10:41:30 -0700
Subject: [PATCH 6/6] Clarify what "SPA fallback" is in the README.md

---
 templates/spa/README.md | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/templates/spa/README.md b/templates/spa/README.md
index 700cb47d074..d60efb5b0c3 100644
--- a/templates/spa/README.md
+++ b/templates/spa/README.md
@@ -34,7 +34,9 @@ Preview build locally with [vite preview](https://vitejs.dev/guide/cli#vite-prev
 npm run preview
 ```
 
-You can serve this from any server of your choosing. The server should support SPA fallback. For a simple example, you could use [sirv-cli](https://www.npmjs.com/package/sirv-cli):
+You can serve this from any server of your choosing. The server should be configured to serve multiple paths from a single root `/index.html` file (commonly called "SPA fallback"). Other steps may be required if the host doesn't directly support this functionality.
+
+For a simple example, you could use [sirv-cli](https://www.npmjs.com/package/sirv-cli):
 
 ```shellscript
 npx sirv-cli build/client/ --single --ignores "^/assets/"