diff --git a/.env.example b/.env.example
index 930bffd..3bfa08f 100644
--- a/.env.example
+++ b/.env.example
@@ -1 +1,2 @@
SESSION_SECRET="super-duper-s3cret"
+CYPRESS_ENV=true
diff --git a/.gitignore b/.gitignore
index d369341..3732997 100644
--- a/.gitignore
+++ b/.gitignore
@@ -8,11 +8,7 @@ yarn.lock
node_modules
-/public/build
-/server/index.mjs
-/server/index.mjs.map
-/server/metafile.*
-/server/version.txt
+/build
preferences.arc
sam.json
sam.yaml
diff --git a/app.arc b/app.arc
index 913e94d..fd95bd2 100644
--- a/app.arc
+++ b/app.arc
@@ -12,13 +12,10 @@ runtime nodejs18.x
@http
/*
method any
- src server
-
-@plugins
-plugin-remix
- src plugin-remix.js
+ src build/server
@static
+folder build/client
@tables
user
diff --git a/app/root.tsx b/app/root.tsx
index a33d370..605ecc3 100644
--- a/app/root.tsx
+++ b/app/root.tsx
@@ -1,4 +1,3 @@
-import { cssBundleHref } from "@remix-run/css-bundle";
import type { LinksFunction, LoaderFunctionArgs } from "@remix-run/node";
import { json } from "@remix-run/node";
import {
@@ -11,11 +10,10 @@ import {
} from "@remix-run/react";
import { getUser } from "~/session.server";
-import stylesheet from "~/tailwind.css";
+import stylesheet from "~/tailwind.css?url";
export const links: LinksFunction = () => [
{ rel: "stylesheet", href: stylesheet },
- ...(cssBundleHref ? [{ rel: "stylesheet", href: cssBundleHref }] : []),
];
export const loader = async ({ request }: LoaderFunctionArgs) => {
@@ -28,7 +26,7 @@ export default function App() {
-
+
diff --git a/app/utils.test.ts b/app/utils.test.ts
index 7ffd94a..7a1990c 100644
--- a/app/utils.test.ts
+++ b/app/utils.test.ts
@@ -1,3 +1,5 @@
+import { expect, test } from "vitest";
+
import { validateEmail } from "./utils";
test("validateEmail returns false for non-emails", () => {
diff --git a/handler.ts b/handler.ts
new file mode 100644
index 0000000..e813261
--- /dev/null
+++ b/handler.ts
@@ -0,0 +1,9 @@
+import { createRequestHandler } from "@remix-run/architect";
+// @ts-expect-error - Remix doesn't have types for this file
+// eslint-disable-next-line import/no-unresolved
+import * as build from "virtual:remix/server-build";
+
+export const handler = createRequestHandler({
+ build: build,
+ mode: process.env.NODE_ENV,
+});
diff --git a/package.json b/package.json
index 32e6c1d..03e9979 100644
--- a/package.json
+++ b/package.json
@@ -4,8 +4,8 @@
"sideEffects": false,
"type": "module",
"scripts": {
- "build": "remix build",
- "dev": "remix dev --manual -c \"arc sandbox -e testing\"",
+ "build": "remix vite:build",
+ "dev": "concurrently \"arc sandbox -e testing\" \"remix vite:dev\"",
"format": "prettier --write .",
"format:repo": "npm run format && npm run lint -- --fix",
"lint": "eslint --cache --cache-location ./node_modules/.cache/eslint .",
@@ -19,8 +19,7 @@
"prettier": {},
"eslintIgnore": [
"/node_modules",
- "/server/index.js",
- "/public/build"
+ "/build"
],
"dependencies": {
"@architect/architect": "^10.16.3",
@@ -52,6 +51,7 @@
"@vitejs/plugin-react": "^4.2.1",
"@vitest/coverage-v8": "^1.4.0",
"autoprefixer": "^10.4.19",
+ "concurrently": "^8.2.2",
"cross-env": "^7.0.3",
"cypress": "^13.7.1",
"esbuild": "^0.20.2",
@@ -74,10 +74,11 @@
"postcss": "^8.4.38",
"prettier": "3.2.5",
"prettier-plugin-tailwindcss": "^0.5.13",
+ "remix-development-tools": "^4.2.2",
"start-server-and-test": "^2.0.3",
"tailwindcss": "^3.4.2",
"typescript": "^5.4.3",
- "vite": "^5.2.6",
+ "vite": "^5.4.0",
"vite-tsconfig-paths": "^4.3.2",
"vitest": "^1.4.0"
},
diff --git a/postcss.config.cjs b/postcss.config.mjs
similarity index 77%
rename from postcss.config.cjs
rename to postcss.config.mjs
index 12a703d..2aa7205 100644
--- a/postcss.config.cjs
+++ b/postcss.config.mjs
@@ -1,4 +1,4 @@
-module.exports = {
+export default {
plugins: {
tailwindcss: {},
autoprefixer: {},
diff --git a/remix.config.js b/remix.config.js
deleted file mode 100644
index 73c6fb4..0000000
--- a/remix.config.js
+++ /dev/null
@@ -1,24 +0,0 @@
-import path from "node:path";
-
-/** @type {import('@remix-run/dev').AppConfig} */
-export default {
- cacheDirectory: "./node_modules/.cache/remix",
- ignoredRouteFiles: ["**/.*", "**/*.test.{js,jsx,ts,tsx}"],
- publicPath: "/_static/build/",
- server: "server.ts",
- serverBuildPath: "server/index.mjs",
- serverModuleFormat: "esm",
- routes: (defineRoutes) =>
- defineRoutes((route) => {
- if (process.env.NODE_ENV === "production") return;
-
- console.log("⚠️ Test routes enabled.");
-
- const appDir = path.join(process.cwd(), "app");
-
- route(
- "__tests/create-user",
- path.relative(appDir, "cypress/support/test-routes/create-user.ts"),
- );
- }),
-};
diff --git a/remix.env.d.ts b/remix.env.d.ts
deleted file mode 100644
index dcf8c45..0000000
--- a/remix.env.d.ts
+++ /dev/null
@@ -1,2 +0,0 @@
-///
-///
diff --git a/server.ts b/server.ts
deleted file mode 100644
index c0d04af..0000000
--- a/server.ts
+++ /dev/null
@@ -1,11 +0,0 @@
-import { createRequestHandler } from "@remix-run/architect";
-import * as build from "@remix-run/dev/server-build";
-
-if (process.env.NODE_ENV !== "production") {
- require("./mocks");
-}
-
-export const handler = createRequestHandler({
- build,
- mode: process.env.NODE_ENV,
-});
diff --git a/tsconfig.json b/tsconfig.json
index 17bce85..9758246 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -1,14 +1,14 @@
{
"exclude": ["./cypress", "./cypress.config.ts"],
- "include": ["remix.env.d.ts", "**/*.ts", "**/*.tsx"],
+ "include": ["**/*.ts", "**/*.tsx"],
"compilerOptions": {
"lib": ["DOM", "DOM.Iterable", "ES2020"],
- "types": ["vitest/globals"],
+ "types": ["@remix-run/node", "vite/client"],
"isolatedModules": true,
"esModuleInterop": true,
"jsx": "react-jsx",
- "module": "ES2020",
- "moduleResolution": "node",
+ "module": "ESNext",
+ "moduleResolution": "Bundler",
"resolveJsonModule": true,
"target": "ES2020",
"strict": true,
diff --git a/vite.config.ts b/vite.config.ts
new file mode 100644
index 0000000..fa48c44
--- /dev/null
+++ b/vite.config.ts
@@ -0,0 +1,60 @@
+import { vitePlugin as remix } from "@remix-run/dev";
+import { installGlobals } from "@remix-run/node";
+import { remixDevTools } from "remix-development-tools";
+import { defineConfig } from "vite";
+import tsconfigPaths from "vite-tsconfig-paths";
+
+installGlobals();
+
+export default defineConfig(({ mode }) => {
+ const isProduction = mode === "production";
+ const isCypress = process.env.CYPRESS_ENV === "true";
+
+ return {
+ base: isProduction || isCypress ? "/_static/" : "/",
+ server: {
+ port: 4000,
+ open: true,
+ },
+ build: {
+ outDir: "build",
+ rollupOptions: {
+ output: {
+ entryFileNames: "[name]-[hash].js",
+ chunkFileNames: "[name]-[hash].js",
+ assetFileNames: "assets/[name]-[hash].[ext]",
+ },
+ },
+ },
+ plugins: [
+ remixDevTools(),
+ remix({
+ ignoredRouteFiles: ["**/*.css"],
+ buildDirectory: "build",
+ serverBuildFile: "index.mjs",
+ basename: "/",
+ }),
+ tsconfigPaths(),
+ {
+ name: "architect-handler",
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
+ apply(_config, env): boolean {
+ return env.command === "build" && env?.isSsrBuild === true;
+ },
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
+ config: async (_config, _env) => {
+ return {
+ build: {
+ ssr: "handler.ts",
+ },
+ };
+ },
+ },
+ ],
+ test: {
+ globals: true,
+ environment: "happy-dom",
+ setupFiles: ["./test/setup-test-env.ts"],
+ },
+ };
+});
diff --git a/vitest.config.ts b/vitest.config.ts
deleted file mode 100644
index 898aeca..0000000
--- a/vitest.config.ts
+++ /dev/null
@@ -1,15 +0,0 @@
-///
-///
-
-import react from "@vitejs/plugin-react";
-import tsconfigPaths from "vite-tsconfig-paths";
-import { defineConfig } from "vitest/config";
-
-export default defineConfig({
- plugins: [react(), tsconfigPaths()],
- test: {
- globals: true,
- environment: "happy-dom",
- setupFiles: ["./test/setup-test-env.ts"],
- },
-});