From f77f51750f5d321fb9f1c15fce188a6fe3ab95e4 Mon Sep 17 00:00:00 2001 From: Matt Kane Date: Mon, 16 Oct 2023 17:25:34 +0100 Subject: [PATCH 01/22] wip: updating to v2 --- remix.init/entry.server.tsx | 32 ++++++++++++++++++++++---------- remix.init/remix.config.js | 11 +++-------- remix.init/root.tsx | 13 ++++++------- remix.init/server.js | 11 +++++++++-- server.js | 23 +++++++++++++++++++++-- 5 files changed, 61 insertions(+), 29 deletions(-) diff --git a/remix.init/entry.server.tsx b/remix.init/entry.server.tsx index d6d0f0e..f2cdab2 100644 --- a/remix.init/entry.server.tsx +++ b/remix.init/entry.server.tsx @@ -1,22 +1,34 @@ -import type { EntryContext } from "@remix-run/node"; +import type { AppLoadContext, EntryContext } from "@netlify/remix-runtime"; import { RemixServer } from "@remix-run/react"; -// Looking to use renderReadableStream? See https://github.com/netlify/remix-template/discussions/100 -import { renderToString } from "react-dom/server"; +import isbot from "isbot"; +import { renderToReadableStream } from "react-dom/server"; -export default function handleRequest( +export default async function handleRequest( request: Request, responseStatusCode: number, responseHeaders: Headers, - remixContext: EntryContext + remixContext: EntryContext, + loadContext: AppLoadContext, ) { - const markup = renderToString( - + const body = await renderToReadableStream( + , + { + signal: request.signal, + onError(error: unknown) { + // Log streaming rendering errors from inside the shell + console.error(error); + responseStatusCode = 500; + }, + }, ); - responseHeaders.set("Content-Type", "text/html"); + if (isbot(request.headers.get("user-agent"))) { + await body.allReady; + } - return new Response("" + markup, { + responseHeaders.set("Content-Type", "text/html"); + return new Response(body, { headers: responseHeaders, status: responseStatusCode, }); -} +} \ No newline at end of file diff --git a/remix.init/remix.config.js b/remix.init/remix.config.js index 031471b..079451f 100644 --- a/remix.init/remix.config.js +++ b/remix.init/remix.config.js @@ -1,16 +1,11 @@ -const { config } = require("@netlify/remix-edge-adapter"); -const baseConfig = - process.env.NODE_ENV === "production" - ? config - : { ignoredRouteFiles: ["**/.*"], future: config.future }; +import { config } from "@netlify/remix-edge-adapter"; /** * @type {import('@remix-run/dev').AppConfig} */ -module.exports = { - ...baseConfig, +export default { + ...config, // This works out of the box with the Netlify adapter, but you can // add your own custom config here if you want to. // - // See https://remix.run/docs/en/v1/file-conventions/remix-config }; diff --git a/remix.init/root.tsx b/remix.init/root.tsx index 95772f0..d360df5 100644 --- a/remix.init/root.tsx +++ b/remix.init/root.tsx @@ -1,4 +1,5 @@ -import type { MetaFunction } from "@netlify/remix-runtime"; +import { cssBundleHref } from "@remix-run/css-bundle"; +import type { LinksFunction } from "@netlify/remix-runtime"; import { Links, LiveReload, @@ -8,18 +9,16 @@ import { ScrollRestoration, } from "@remix-run/react"; -export const meta: MetaFunction = () => [ - { - charset: "utf-8", - title: "New Remix App", - viewport: "width=device-width,initial-scale=1", - } +export const links: LinksFunction = () => [ + ...(cssBundleHref ? [{ rel: "stylesheet", href: cssBundleHref }] : []), ]; export default function App() { return ( + + diff --git a/remix.init/server.js b/remix.init/server.js index 18dc1b5..18a3908 100644 --- a/remix.init/server.js +++ b/remix.init/server.js @@ -1,6 +1,6 @@ -// Import path interpreted by the Remix compiler import * as build from "@remix-run/dev/server-build"; import { createRequestHandler } from "@netlify/remix-edge-adapter"; +import { broadcastDevReady } from "@netlify/remix-runtime"; export default createRequestHandler({ build, @@ -8,6 +8,13 @@ export default createRequestHandler({ mode: process.env.NODE_ENV, }); + +if(process.env.NODE_ENV === "development") { + // Tell remix dev that the server is ready + broadcastDevReady(build); +} + + export const config = { cache: "manual", path: "/*", @@ -16,4 +23,4 @@ export const config = { // Add other exclusions here, e.g. "^/api/*$" for custom Netlify functions or // custom Netlify Edge Functions excluded_patterns: ["^/_assets/*$"], -}; +}; \ No newline at end of file diff --git a/server.js b/server.js index 6997ce3..4ed6a98 100644 --- a/server.js +++ b/server.js @@ -1,7 +1,26 @@ -import { createRequestHandler } from "@netlify/remix-adapter"; import * as build from "@remix-run/dev/server-build"; +import { createRequestHandler } from "@netlify/remix-adapter"; +import { broadcastDevReady } from "@netlify/remix-runtime"; -export const handler = createRequestHandler({ +export default createRequestHandler({ build, + // process.env.NODE_ENV is provided by Remix at compile time mode: process.env.NODE_ENV, }); + + +if(process.env.NODE_ENV === "development") { + // Tell remix dev that the server is ready + broadcastDevReady(build); +} + + +export const config = { + cache: "manual", + path: "/*", + // Let the CDN handle requests for static assets, i.e. ^/_assets/*$ + // + // Add other exclusions here, e.g. "^/api/*$" for custom Netlify functions or + // custom Netlify Edge Functions + excluded_patterns: ["^/_assets/*$"], +}; \ No newline at end of file From 5fb28b7e213c7828ea7cf72244a6b140e0577cba Mon Sep 17 00:00:00 2001 From: Matt Kane Date: Mon, 16 Oct 2023 21:24:03 +0100 Subject: [PATCH 02/22] feat!: update to Remix 2 --- package.json | 5 +++-- remix.config.js | 7 ++----- remix.init/README-edge.md | 13 ++++++------ remix.init/README.md | 7 ++++--- remix.init/index.js | 38 +++++++++++++++++------------------- remix.init/netlify-edge-toml | 11 ++++++++--- remix.init/netlify-toml | 10 ++++++---- server.js | 25 +++--------------------- 8 files changed, 51 insertions(+), 65 deletions(-) diff --git a/package.json b/package.json index 9b8ed36..b3a2396 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,7 @@ { "private": true, "sideEffects": false, + "type": "module", "scripts": { "build": "remix build", "predev": "rimraf ./public/_redirects", @@ -9,10 +10,10 @@ "typecheck": "tsc -b" }, "dependencies": { - "@netlify/functions": "^1.3.0", "@remix-run/node": "*", "@remix-run/react": "*", "cross-env": "^7.0.3", + "isbot": "^3.7.0", "react": "^18.2.0", "react-dom": "^18.2.0" }, @@ -24,7 +25,7 @@ "@types/react-dom": "^18.0.8", "eslint": "^8.27.0", "rimraf": "^4.1.4", - "typescript": "^5.1.0" + "typescript": "^5.2.2" }, "engines": { "node": ">=18" diff --git a/remix.config.js b/remix.config.js index 77dbc31..9a3c296 100644 --- a/remix.config.js +++ b/remix.config.js @@ -9,13 +9,10 @@ const baseConfig = undefined; /** @type {import('@remix-run/dev').AppConfig} */ -module.exports = { +export default { ...baseConfig, ignoredRouteFiles: ["**/.*"], - // See https://remix.run/docs/en/main/file-conventions/route-files-v2 - future: { - v2_routeConvention: true, - } + // add your own custom config here if you want to. // // See https://remix.run/docs/en/v1/file-conventions/remix-config diff --git a/remix.init/README-edge.md b/remix.init/README-edge.md index 010d272..3c06738 100644 --- a/remix.init/README-edge.md +++ b/remix.init/README-edge.md @@ -40,26 +40,26 @@ npm install Run ```sh -netlify dev +npm run dev ``` Open up [http://localhost:8888](http://localhost:8888), and you're ready to go! ### Serve your site locally -Run +To serve your site locally in a production-like environment, run ```sh -npm netlify serve +npm run start ``` -to serve your site locally at [http://localhost:8888](http://localhost:8888). +Your site will be available at [http://localhost:8888](http://localhost:8888). Note that it will not auto-reload when you make changes. ## Excluding routes You can exclude routes for non-Remix code such as custom Netlify Functions or Edge Functions. To do this, add an additional entry in the array like in the example below: -````diff +```diff export const config = { cache: "manual", path: "/*", @@ -70,6 +70,7 @@ export const config = { - excluded_patterns: ["/_assets/*"], + excluded_patterns: ["/_assets/*", "/api/*"], }; +``` ## Deployment @@ -81,4 +82,4 @@ netlify deploy --build # production deployment netlify deploy --build --prod -```` +``` diff --git a/remix.init/README.md b/remix.init/README.md index 7e2979b..07c0bdb 100644 --- a/remix.init/README.md +++ b/remix.init/README.md @@ -47,17 +47,18 @@ Open up [http://localhost:8888](http://localhost:8888), and you're ready to go! ### Adding Redirects and Rewrites -To add redirects and rewrites, add them to the `netlify.toml` file or to the [\_app_redirects](_app_redirects) file. For more information about redirects and rewrites, see the [Netlify docs](https://docs.netlify.com/routing/redirects/). +To add redirects and rewrites, add them to the `netlify.toml` file. For more information about redirects and rewrites, see the [Netlify docs](https://docs.netlify.com/routing/redirects/). ### Serve your site locally -Run +To serve your site locally in a production-like environment, run ```sh npm run start ``` -to serve your site locally at [http://localhost:8888](http://localhost:8888). +Your site will be available at [http://localhost:8888](http://localhost:8888). Note that it will not auto-reload when you make changes. + ## Deployment diff --git a/remix.init/index.js b/remix.init/index.js index 0a62acc..a012ad9 100644 --- a/remix.init/index.js +++ b/remix.init/index.js @@ -3,7 +3,7 @@ const fs = require("fs/promises"); const { join } = require("path"); const PackageJson = require("@npmcli/package-json"); const execa = require("execa"); -const { Command } = require('commander'); +const { Command } = require("commander"); const foldersToExclude = [".github", ".git"]; @@ -19,12 +19,7 @@ const edgeFilesToCopy = [ ]; // Netlify Functions template file changes -const filesToCopy = [ - ["README.md"], - ["netlify-toml", "netlify.toml"], - ["_app_redirects"], -]; - +const filesToCopy = [["README.md"], ["netlify-toml"], ["_app_redirects"]]; async function copyTemplateFiles({ files, rootDirectory }) { for (const [file, target] of files) { @@ -41,10 +36,7 @@ async function copyTemplateFiles({ files, rootDirectory }) { async function updatePackageJsonForEdge(directory) { const packageJson = await PackageJson.load(directory); const { - dependencies: { - "@remix-run/node": _node, - ...dependencies - }, + dependencies: { "@remix-run/node": _node, ...dependencies }, scripts, ...restOfPackageJson } = packageJson.content; @@ -54,12 +46,13 @@ async function updatePackageJsonForEdge(directory) { scripts: { ...scripts, predev: "rimraf ./.netlify/edge-functions/", + dev: 'remix dev --manual -c "ntl dev --framework=#static"', }, ...restOfPackageJson, dependencies: { ...dependencies, "@netlify/edge-functions": "^2.0.0", - "@netlify/remix-edge-adapter": "1.2.0", + "@netlify/remix-edge-adapter": "^2.0.0", }, }); @@ -69,10 +62,7 @@ async function updatePackageJsonForEdge(directory) { async function updatePackageJsonForFunctions(directory) { const packageJson = await PackageJson.load(directory); const { - dependencies: { - "@remix-run/node": _node, - ...dependencies - }, + dependencies: { "@remix-run/node": _node, ...dependencies }, scripts, ...restOfPackageJson } = packageJson.content; @@ -81,6 +71,7 @@ async function updatePackageJsonForFunctions(directory) { ...restOfPackageJson, dependencies: { ...dependencies, + "@netlify/functions": "^2.0.0", "@netlify/remix-adapter": "^1.0.0", }, }); @@ -139,17 +130,24 @@ async function main({ rootDirectory }) { } async function shouldUseEdge() { - // parse the top level command args to see if edge was passed in const program = new Command(); program - .option('--netlify-edge', 'explicitly use Netlify Edge Functions to serve this Remix site.', undefined) - .option('--no-netlify-edge', 'explicitly do NOT use Netlify Edge Functions to serve this Remix site - use Serverless Functions instead.', undefined) + .option( + "--netlify-edge", + "explicitly use Netlify Edge Functions to serve this Remix site.", + undefined + ) + .option( + "--no-netlify-edge", + "explicitly do NOT use Netlify Edge Functions to serve this Remix site - use Serverless Functions instead.", + undefined + ); program.allowUnknownOption().parse(); const passedEdgeOption = program.opts().netlifyEdge; - if(passedEdgeOption !== true && passedEdgeOption !== false){ + if (passedEdgeOption !== true && passedEdgeOption !== false) { const { edge } = await inquirer.prompt([ { name: "edge", diff --git a/remix.init/netlify-edge-toml b/remix.init/netlify-edge-toml index 13021f6..f1ecf95 100644 --- a/remix.init/netlify-edge-toml +++ b/remix.init/netlify-edge-toml @@ -2,6 +2,11 @@ command = "npm run build" publish = "public" -[dev] -command = "npm run dev" -targetPort = 3000 +# Set immutable caching for static files, because they have fingerprinted filenames + +[[headers]] +for = "/build/\*" + +[headers.values] + +"Cache-Control" = "public, max-age=31560000, immutable" diff --git a/remix.init/netlify-toml b/remix.init/netlify-toml index 630bab8..1db2269 100644 --- a/remix.init/netlify-toml +++ b/remix.init/netlify-toml @@ -1,14 +1,16 @@ [build] -command = "remix build && cp _app_redirects public/_redirects" +command = "remix build && cp \_app_redirects public/\_redirects" publish = "public" [dev] command = "npm run dev" targetPort = 3000 +# Set immutable caching for static files, because they have fingerprinted filenames + [[headers]] -for = "/build/*" +for = "/build/\*" [headers.values] -# Set to 60 seconds as an example. You can also add cache headers via Remix. See the documentation on [headers](https://remix.run/docs/en/v1/route/headers) in Remix. -"Cache-Control" = "public, max-age=60, s-maxage=60" + +"Cache-Control" = "public, max-age=31560000, immutable" diff --git a/server.js b/server.js index 4ed6a98..10f10d7 100644 --- a/server.js +++ b/server.js @@ -1,26 +1,7 @@ -import * as build from "@remix-run/dev/server-build"; import { createRequestHandler } from "@netlify/remix-adapter"; -import { broadcastDevReady } from "@netlify/remix-runtime"; +import * as build from "@remix-run/dev/server-build"; -export default createRequestHandler({ +export const handler = createRequestHandler({ build, - // process.env.NODE_ENV is provided by Remix at compile time mode: process.env.NODE_ENV, -}); - - -if(process.env.NODE_ENV === "development") { - // Tell remix dev that the server is ready - broadcastDevReady(build); -} - - -export const config = { - cache: "manual", - path: "/*", - // Let the CDN handle requests for static assets, i.e. ^/_assets/*$ - // - // Add other exclusions here, e.g. "^/api/*$" for custom Netlify functions or - // custom Netlify Edge Functions - excluded_patterns: ["^/_assets/*$"], -}; \ No newline at end of file +}); \ No newline at end of file From 1958dfc03a374801e80f9fd56283d172a2b662b7 Mon Sep 17 00:00:00 2001 From: ascorbic Date: Tue, 17 Oct 2023 21:15:47 +0100 Subject: [PATCH 03/22] fix: use streaming entrypoint --- app/entry.server.tsx | 77 +++++++++++++++++++++++++++++++++++++------- 1 file changed, 65 insertions(+), 12 deletions(-) diff --git a/app/entry.server.tsx b/app/entry.server.tsx index fb8ea87..bd54b8f 100644 --- a/app/entry.server.tsx +++ b/app/entry.server.tsx @@ -1,21 +1,74 @@ -import type { EntryContext } from "@remix-run/node"; -import { RemixServer } from "@remix-run/react"; -import { renderToString } from "react-dom/server"; +import { PassThrough } from 'node:stream' + +import type { AppLoadContext, EntryContext } from '@remix-run/node' +import { createReadableStreamFromReadable } from './stream' +import { RemixServer } from '@remix-run/react' +import isbot from 'isbot' +import { renderToPipeableStream } from 'react-dom/server' + +const ABORT_DELAY = 5_000 export default function handleRequest( request: Request, responseStatusCode: number, responseHeaders: Headers, - remixContext: EntryContext + remixContext: EntryContext, + loadContext: AppLoadContext, ) { - const markup = renderToString( - - ); + const bot = isbot(request.headers.get('user-agent')) + return new Promise((resolve, reject) => { + let shellRendered = false + const body = new PassThrough() + const stream = createReadableStreamFromReadable(body) - responseHeaders.set("Content-Type", "text/html"); + const { pipe, abort } = renderToPipeableStream( + , + { + onShellReady() { + if (!bot) { + shellRendered = true + responseHeaders.set('Content-Type', 'text/html') + resolve( + new Response(stream, { + headers: responseHeaders, + status: responseStatusCode, + }), + ) + pipe(body) + } + }, + onShellError(error: unknown) { + reject(error) + }, + onAllReady() { + clearTimeout(timer) + if (bot) { + shellRendered = true + responseHeaders.set('Content-Type', 'text/html') + resolve( + new Response(stream, { + headers: responseHeaders, + status: responseStatusCode, + }), + ) + pipe(body) + } + }, + onError(error: unknown) { + responseStatusCode = 500 + // Log streaming rendering errors from inside the shell. Don't log + // errors encountered during initial shell rendering since they'll + // reject and get logged in handleDocumentRequest. + if (shellRendered) { + console.error(error) + } + }, + }, + ) - return new Response("" + markup, { - headers: responseHeaders, - status: responseStatusCode, - }); + const timer = setTimeout(() => { + console.log('aborting') + abort() + }, ABORT_DELAY) + }) } From bbecd91e041b696133154326cab48a24525aa596 Mon Sep 17 00:00:00 2001 From: Matt Kane Date: Thu, 26 Oct 2023 16:26:08 +0100 Subject: [PATCH 04/22] chore: fix package versions --- package.json | 4 ++-- remix.init/index.js | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index b3a2396..123ee6b 100644 --- a/package.json +++ b/package.json @@ -21,8 +21,8 @@ "@remix-run/dev": "*", "@remix-run/eslint-config": "*", "@remix-run/serve": "*", - "@types/react": "^18.0.25", - "@types/react-dom": "^18.0.8", + "@types/react": "^18.2.0", + "@types/react-dom": "^18.2.0", "eslint": "^8.27.0", "rimraf": "^4.1.4", "typescript": "^5.2.2" diff --git a/remix.init/index.js b/remix.init/index.js index a012ad9..2c91cb1 100644 --- a/remix.init/index.js +++ b/remix.init/index.js @@ -52,7 +52,7 @@ async function updatePackageJsonForEdge(directory) { dependencies: { ...dependencies, "@netlify/edge-functions": "^2.0.0", - "@netlify/remix-edge-adapter": "^2.0.0", + "@netlify/remix-edge-adapter": "^3.0.0", }, }); From 5390cfaab437ca121b364218c846f46f07deec5f Mon Sep 17 00:00:00 2001 From: Matt Kane Date: Thu, 26 Oct 2023 16:31:46 +0100 Subject: [PATCH 05/22] chore: update config and entrypoints --- remix.config.js | 22 +++++----------------- remix.init/index.js | 2 +- remix.init/{server.js => server.ts} | 0 server.js => server.ts | 0 tsconfig.json | 2 -- 5 files changed, 6 insertions(+), 20 deletions(-) rename remix.init/{server.js => server.ts} (100%) rename server.js => server.ts (100%) diff --git a/remix.config.js b/remix.config.js index 9a3c296..f3f1517 100644 --- a/remix.config.js +++ b/remix.config.js @@ -1,19 +1,7 @@ -const baseConfig = - process.env.NODE_ENV === "production" - ? // when running the Netify CLI or building on Netlify, we want to use - { - server: "./server.js", - serverBuildPath: ".netlify/functions-internal/server.js", - } - : // otherwise support running remix dev, i.e. no custom server - undefined; - /** @type {import('@remix-run/dev').AppConfig} */ export default { - ...baseConfig, - ignoredRouteFiles: ["**/.*"], - - // add your own custom config here if you want to. - // - // See https://remix.run/docs/en/v1/file-conventions/remix-config -}; + ignoredRouteFiles: ['**/.*'], + server: './server.ts', + serverBuildPath: './.netlify/functions-internal/server.mjs', + serverModuleFormat: 'esm', +} diff --git a/remix.init/index.js b/remix.init/index.js index 2c91cb1..fbd8aeb 100644 --- a/remix.init/index.js +++ b/remix.init/index.js @@ -11,7 +11,7 @@ const foldersToExclude = [".github", ".git"]; const edgeFilesToCopy = [ ["README-edge.md", "README.md"], ["netlify-edge-toml", "netlify.toml"], - ["server.js"], + ["server.ts"], ["remix.config.js"], ["entry.server.tsx", "app/entry.server.tsx"], ["root.tsx", "app/root.tsx"], diff --git a/remix.init/server.js b/remix.init/server.ts similarity index 100% rename from remix.init/server.js rename to remix.init/server.ts diff --git a/server.js b/server.ts similarity index 100% rename from server.js rename to server.ts diff --git a/tsconfig.json b/tsconfig.json index d8ee23a..3f23e2a 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -3,8 +3,6 @@ "remix.env.d.ts", "**/*.ts", "**/*.tsx", - "server.js", - "remix.init/server.js" ], "compilerOptions": { "lib": ["DOM", "DOM.Iterable", "ES2019"], From d5381e2e6092f547e2999e495a31c07b1b53433b Mon Sep 17 00:00:00 2001 From: Matt Kane Date: Thu, 26 Oct 2023 16:57:48 +0100 Subject: [PATCH 06/22] chore: fix dep version --- remix.init/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/remix.init/index.js b/remix.init/index.js index fbd8aeb..7df4c1f 100644 --- a/remix.init/index.js +++ b/remix.init/index.js @@ -72,7 +72,7 @@ async function updatePackageJsonForFunctions(directory) { dependencies: { ...dependencies, "@netlify/functions": "^2.0.0", - "@netlify/remix-adapter": "^1.0.0", + "@netlify/remix-adapter": "^2.0.0", }, }); From 930e549c60a45610ced78972ee4573d826d43c79 Mon Sep 17 00:00:00 2001 From: Matt Kane Date: Thu, 26 Oct 2023 17:48:05 +0100 Subject: [PATCH 07/22] chore: fix entrypoints --- app/entry.client.tsx | 26 +++++--------- app/entry.server.tsx | 70 ++++++++++++++++++++----------------- remix.init/entry.server.tsx | 6 ++-- server.ts | 2 +- 4 files changed, 49 insertions(+), 55 deletions(-) diff --git a/app/entry.client.tsx b/app/entry.client.tsx index f1fed31..999c0a1 100644 --- a/app/entry.client.tsx +++ b/app/entry.client.tsx @@ -2,21 +2,11 @@ import { RemixBrowser } from "@remix-run/react"; import { startTransition, StrictMode } from "react"; import { hydrateRoot } from "react-dom/client"; -function hydrate() { - startTransition(() => { - hydrateRoot( - document, - - - - ); - }); -} - -if (window.requestIdleCallback) { - window.requestIdleCallback(hydrate); -} else { - // Safari doesn't support requestIdleCallback - // https://caniuse.com/requestidlecallback - window.setTimeout(hydrate, 1); -} +startTransition(() => { + hydrateRoot( + document, + + + + ); +}); diff --git a/app/entry.server.tsx b/app/entry.server.tsx index bd54b8f..03c55d6 100644 --- a/app/entry.server.tsx +++ b/app/entry.server.tsx @@ -1,74 +1,78 @@ -import { PassThrough } from 'node:stream' +import { PassThrough } from "node:stream"; -import type { AppLoadContext, EntryContext } from '@remix-run/node' -import { createReadableStreamFromReadable } from './stream' -import { RemixServer } from '@remix-run/react' -import isbot from 'isbot' -import { renderToPipeableStream } from 'react-dom/server' +import type { AppLoadContext, EntryContext } from "@remix-run/node"; +import { createReadableStreamFromReadable } from "@remix-run/node"; +import { RemixServer } from "@remix-run/react"; +import isbot from "isbot"; +import { renderToPipeableStream } from "react-dom/server"; -const ABORT_DELAY = 5_000 +const ABORT_DELAY = 5_000; export default function handleRequest( request: Request, responseStatusCode: number, responseHeaders: Headers, remixContext: EntryContext, - loadContext: AppLoadContext, + loadContext: AppLoadContext ) { - const bot = isbot(request.headers.get('user-agent')) + const bot = isbot(request.headers.get("user-agent")); return new Promise((resolve, reject) => { - let shellRendered = false - const body = new PassThrough() - const stream = createReadableStreamFromReadable(body) + let shellRendered = false; + const body = new PassThrough(); + const stream = createReadableStreamFromReadable(body); const { pipe, abort } = renderToPipeableStream( - , + , { onShellReady() { if (!bot) { - shellRendered = true - responseHeaders.set('Content-Type', 'text/html') + shellRendered = true; + responseHeaders.set("Content-Type", "text/html"); resolve( new Response(stream, { headers: responseHeaders, status: responseStatusCode, - }), - ) - pipe(body) + }) + ); + pipe(body); } }, onShellError(error: unknown) { - reject(error) + reject(error); }, onAllReady() { - clearTimeout(timer) + // Avoid a bug where responses aren't flushed if there's an outstanding timer. + clearTimeout(timer); if (bot) { - shellRendered = true - responseHeaders.set('Content-Type', 'text/html') + shellRendered = true; + responseHeaders.set("Content-Type", "text/html"); resolve( new Response(stream, { headers: responseHeaders, status: responseStatusCode, - }), - ) - pipe(body) + }) + ); + pipe(body); } }, onError(error: unknown) { - responseStatusCode = 500 + responseStatusCode = 500; // Log streaming rendering errors from inside the shell. Don't log // errors encountered during initial shell rendering since they'll // reject and get logged in handleDocumentRequest. if (shellRendered) { - console.error(error) + console.error(error); } }, - }, - ) + } + ); const timer = setTimeout(() => { - console.log('aborting') - abort() - }, ABORT_DELAY) - }) + abort(); + }, ABORT_DELAY); + }); } diff --git a/remix.init/entry.server.tsx b/remix.init/entry.server.tsx index f2cdab2..374bca8 100644 --- a/remix.init/entry.server.tsx +++ b/remix.init/entry.server.tsx @@ -8,7 +8,7 @@ export default async function handleRequest( responseStatusCode: number, responseHeaders: Headers, remixContext: EntryContext, - loadContext: AppLoadContext, + loadContext: AppLoadContext ) { const body = await renderToReadableStream( , @@ -19,7 +19,7 @@ export default async function handleRequest( console.error(error); responseStatusCode = 500; }, - }, + } ); if (isbot(request.headers.get("user-agent"))) { @@ -31,4 +31,4 @@ export default async function handleRequest( headers: responseHeaders, status: responseStatusCode, }); -} \ No newline at end of file +} diff --git a/server.ts b/server.ts index 10f10d7..6997ce3 100644 --- a/server.ts +++ b/server.ts @@ -4,4 +4,4 @@ import * as build from "@remix-run/dev/server-build"; export const handler = createRequestHandler({ build, mode: process.env.NODE_ENV, -}); \ No newline at end of file +}); From 3ec00e717e4b2ddcca233ba4b9007706f18bc50c Mon Sep 17 00:00:00 2001 From: ascorbic Date: Thu, 26 Oct 2023 18:52:42 +0100 Subject: [PATCH 08/22] chore: update config --- package.json | 3 +-- remix.init/README.md | 4 ++-- remix.init/_app_redirects | 7 ------- remix.init/index.js | 7 ++++--- remix.init/netlify-edge-toml | 2 +- remix.init/netlify-toml | 5 ++--- remix.init/remix.config.js | 9 ++++----- tsconfig.json | 13 +++++++++---- 8 files changed, 23 insertions(+), 27 deletions(-) delete mode 100644 remix.init/_app_redirects diff --git a/package.json b/package.json index 123ee6b..dd25b42 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,6 @@ "type": "module", "scripts": { "build": "remix build", - "predev": "rimraf ./public/_redirects", "dev": "remix dev", "start": "netlify serve", "typecheck": "tsc -b" @@ -30,4 +29,4 @@ "engines": { "node": ">=18" } -} +} \ No newline at end of file diff --git a/remix.init/README.md b/remix.init/README.md index 07c0bdb..6513be5 100644 --- a/remix.init/README.md +++ b/remix.init/README.md @@ -40,10 +40,10 @@ npm install Run ```sh -netlify dev +npm run dev ``` -Open up [http://localhost:8888](http://localhost:8888), and you're ready to go! +Open up [http://localhost:3000](http://localhost:3000), and you're ready to go! ### Adding Redirects and Rewrites diff --git a/remix.init/_app_redirects b/remix.init/_app_redirects deleted file mode 100644 index cf91099..0000000 --- a/remix.init/_app_redirects +++ /dev/null @@ -1,7 +0,0 @@ -# This template uses this file instead of the typicial Netlify _redirects file. -# For more information about redirects and rewrites, see https://docs.netlify.com/routing/redirects/. - -# Do not remove the line below. This is required to serve the site when deployed. -/* /.netlify/functions/server 200 - -# Add other redirects and rewrites here and/or in your netlify.toml diff --git a/remix.init/index.js b/remix.init/index.js index 7df4c1f..1994e71 100644 --- a/remix.init/index.js +++ b/remix.init/index.js @@ -19,7 +19,7 @@ const edgeFilesToCopy = [ ]; // Netlify Functions template file changes -const filesToCopy = [["README.md"], ["netlify-toml"], ["_app_redirects"]]; +const filesToCopy = [["README.md"], ["netlify-toml", "netlify.toml"]]; async function copyTemplateFiles({ files, rootDirectory }) { for (const [file, target] of files) { @@ -51,8 +51,9 @@ async function updatePackageJsonForEdge(directory) { ...restOfPackageJson, dependencies: { ...dependencies, - "@netlify/edge-functions": "^2.0.0", - "@netlify/remix-edge-adapter": "^3.0.0", + "@netlify/edge-functions": "^3.0.0", + "@netlify/remix-edge-adapter": "^2.0.0", + "@netlify/remix-runtime": "^2.0.0", }, }); diff --git a/remix.init/netlify-edge-toml b/remix.init/netlify-edge-toml index f1ecf95..3525653 100644 --- a/remix.init/netlify-edge-toml +++ b/remix.init/netlify-edge-toml @@ -5,7 +5,7 @@ publish = "public" # Set immutable caching for static files, because they have fingerprinted filenames [[headers]] -for = "/build/\*" +for = "/build/*" [headers.values] diff --git a/remix.init/netlify-toml b/remix.init/netlify-toml index 1db2269..71c812f 100644 --- a/remix.init/netlify-toml +++ b/remix.init/netlify-toml @@ -1,15 +1,14 @@ [build] -command = "remix build && cp \_app_redirects public/\_redirects" +command = "npm run build" publish = "public" [dev] command = "npm run dev" -targetPort = 3000 # Set immutable caching for static files, because they have fingerprinted filenames [[headers]] -for = "/build/\*" +for = "/build/*" [headers.values] diff --git a/remix.init/remix.config.js b/remix.init/remix.config.js index 079451f..6113a42 100644 --- a/remix.init/remix.config.js +++ b/remix.init/remix.config.js @@ -1,11 +1,10 @@ -import { config } from "@netlify/remix-edge-adapter"; +import { config } from '@netlify/remix-edge-adapter' -/** - * @type {import('@remix-run/dev').AppConfig} - */ +/** @type {import('@remix-run/dev').AppConfig} */ export default { ...config, // This works out of the box with the Netlify adapter, but you can // add your own custom config here if you want to. // -}; + // See https://remix.run/file-conventions/remix-config +} diff --git a/tsconfig.json b/tsconfig.json index 3f23e2a..2846d2f 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -5,7 +5,11 @@ "**/*.tsx", ], "compilerOptions": { - "lib": ["DOM", "DOM.Iterable", "ES2019"], + "lib": [ + "DOM", + "DOM.Iterable", + "ES2019" + ], "isolatedModules": true, "esModuleInterop": true, "jsx": "react-jsx", @@ -18,10 +22,11 @@ "forceConsistentCasingInFileNames": true, "baseUrl": ".", "paths": { - "~/*": ["./app/*"] + "~/*": [ + "./app/*" + ] }, - // Remix takes care of building everything in `remix build`. "noEmit": true } -} +} \ No newline at end of file From 91a03ff5d19c10c343a4bdbe76d8cc42284d50be Mon Sep 17 00:00:00 2001 From: ascorbic Date: Thu, 26 Oct 2023 19:00:35 +0100 Subject: [PATCH 09/22] chore: fix server entry --- server.ts | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/server.ts b/server.ts index 6997ce3..69ed801 100644 --- a/server.ts +++ b/server.ts @@ -1,7 +1,16 @@ -import { createRequestHandler } from "@netlify/remix-adapter"; -import * as build from "@remix-run/dev/server-build"; +import * as build from '@remix-run/dev/server-build' +import { createRequestHandler } from '@netlify/remix-adapter' +import type { Context, Config } from '@netlify/functions' -export const handler = createRequestHandler({ +const handle = createRequestHandler({ build, mode: process.env.NODE_ENV, -}); +}) + +export default function handler(request: Request, context: Context) { + return handle(request, context) +} + +export const config: Config = { + path: '/*', +} From d98ff130c9a2a13851f92015698168e1dcb236bb Mon Sep 17 00:00:00 2001 From: ascorbic Date: Thu, 26 Oct 2023 19:16:20 +0100 Subject: [PATCH 10/22] chore: go back to redirects rather than ISC for serverless --- remix.init/netlify-toml | 5 +++++ server.ts | 3 --- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/remix.init/netlify-toml b/remix.init/netlify-toml index 71c812f..44b49b3 100644 --- a/remix.init/netlify-toml +++ b/remix.init/netlify-toml @@ -5,6 +5,11 @@ publish = "public" [dev] command = "npm run dev" +[[redirects]] +from = "/*" +to = "/.netlify/functions/server" +status = 200 + # Set immutable caching for static files, because they have fingerprinted filenames [[headers]] diff --git a/server.ts b/server.ts index 69ed801..20c6e9e 100644 --- a/server.ts +++ b/server.ts @@ -11,6 +11,3 @@ export default function handler(request: Request, context: Context) { return handle(request, context) } -export const config: Config = { - path: '/*', -} From 290a1c5a10a5b0db63692acc480e0426d3fe6858 Mon Sep 17 00:00:00 2001 From: ascorbic Date: Thu, 26 Oct 2023 19:20:10 +0100 Subject: [PATCH 11/22] chore: fix version --- remix.init/index.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/remix.init/index.js b/remix.init/index.js index 1994e71..a52bd0f 100644 --- a/remix.init/index.js +++ b/remix.init/index.js @@ -51,8 +51,8 @@ async function updatePackageJsonForEdge(directory) { ...restOfPackageJson, dependencies: { ...dependencies, - "@netlify/edge-functions": "^3.0.0", - "@netlify/remix-edge-adapter": "^2.0.0", + "@netlify/edge-functions": "2.0.0", + "@netlify/remix-edge-adapter": "^3.0.0", "@netlify/remix-runtime": "^2.0.0", }, }); From 7694fdc56542be3fea7aad7db0290e0ff706e50d Mon Sep 17 00:00:00 2001 From: Matt Kane Date: Fri, 27 Oct 2023 15:35:06 +0100 Subject: [PATCH 12/22] chore: import default config --- remix.config.js | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/remix.config.js b/remix.config.js index f3f1517..7b2902d 100644 --- a/remix.config.js +++ b/remix.config.js @@ -1,7 +1,10 @@ +import { config } from "@netlify/remix-adapter"; + /** @type {import('@remix-run/dev').AppConfig} */ export default { - ignoredRouteFiles: ['**/.*'], - server: './server.ts', - serverBuildPath: './.netlify/functions-internal/server.mjs', - serverModuleFormat: 'esm', -} + ...config, + // This works out of the box with the Netlify adapter, but you can + // add your own custom config here if you want to. + // + // See https://remix.run/file-conventions/remix-config +}; From c2fc97d1f16fc749dd50b2b9c35ca348c4bdd8dc Mon Sep 17 00:00:00 2001 From: Matt Kane Date: Fri, 27 Oct 2023 16:31:41 +0100 Subject: [PATCH 13/22] fix: use default dev server for functions --- remix.config.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/remix.config.js b/remix.config.js index 7b2902d..bbe7ebd 100644 --- a/remix.config.js +++ b/remix.config.js @@ -2,7 +2,7 @@ import { config } from "@netlify/remix-adapter"; /** @type {import('@remix-run/dev').AppConfig} */ export default { - ...config, + ...(process.env.NODE_ENV === "production" ? config : undefined), // This works out of the box with the Netlify adapter, but you can // add your own custom config here if you want to. // From 19dd3fb8540751b4f4d4a2b73a3379ffc36549c3 Mon Sep 17 00:00:00 2001 From: Matt Kane Date: Fri, 27 Oct 2023 16:33:16 +0100 Subject: [PATCH 14/22] fix: make eslintrc esm --- .eslintrc.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.eslintrc.js b/.eslintrc.js index 2061cd2..0ef9764 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -1,4 +1,4 @@ /** @type {import('eslint').Linter.Config} */ -module.exports = { +export default { extends: ["@remix-run/eslint-config", "@remix-run/eslint-config/node"], }; From 8b77193e0096e35591b083a3014bad2006c53575 Mon Sep 17 00:00:00 2001 From: Matt Kane Date: Fri, 27 Oct 2023 16:37:27 +0100 Subject: [PATCH 15/22] chore: move timeout clear into onShellReady --- app/entry.server.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/entry.server.tsx b/app/entry.server.tsx index 03c55d6..a14b5f5 100644 --- a/app/entry.server.tsx +++ b/app/entry.server.tsx @@ -30,6 +30,8 @@ export default function handleRequest( { onShellReady() { if (!bot) { + // Avoid a bug where responses aren't flushed if there's an outstanding timer. + clearTimeout(timer); shellRendered = true; responseHeaders.set("Content-Type", "text/html"); resolve( @@ -45,8 +47,6 @@ export default function handleRequest( reject(error); }, onAllReady() { - // Avoid a bug where responses aren't flushed if there's an outstanding timer. - clearTimeout(timer); if (bot) { shellRendered = true; responseHeaders.set("Content-Type", "text/html"); From 3cc024019348d36c1b2e7851db8913984f48de63 Mon Sep 17 00:00:00 2001 From: Matt Kane Date: Mon, 30 Oct 2023 11:44:19 +0000 Subject: [PATCH 16/22] chore: switch to re-exported server entry --- app/entry.server.tsx | 79 +------------------------------------ remix.init/entry.server.tsx | 35 +--------------- 2 files changed, 2 insertions(+), 112 deletions(-) diff --git a/app/entry.server.tsx b/app/entry.server.tsx index a14b5f5..beacf24 100644 --- a/app/entry.server.tsx +++ b/app/entry.server.tsx @@ -1,78 +1 @@ -import { PassThrough } from "node:stream"; - -import type { AppLoadContext, EntryContext } from "@remix-run/node"; -import { createReadableStreamFromReadable } from "@remix-run/node"; -import { RemixServer } from "@remix-run/react"; -import isbot from "isbot"; -import { renderToPipeableStream } from "react-dom/server"; - -const ABORT_DELAY = 5_000; - -export default function handleRequest( - request: Request, - responseStatusCode: number, - responseHeaders: Headers, - remixContext: EntryContext, - loadContext: AppLoadContext -) { - const bot = isbot(request.headers.get("user-agent")); - return new Promise((resolve, reject) => { - let shellRendered = false; - const body = new PassThrough(); - const stream = createReadableStreamFromReadable(body); - - const { pipe, abort } = renderToPipeableStream( - , - { - onShellReady() { - if (!bot) { - // Avoid a bug where responses aren't flushed if there's an outstanding timer. - clearTimeout(timer); - shellRendered = true; - responseHeaders.set("Content-Type", "text/html"); - resolve( - new Response(stream, { - headers: responseHeaders, - status: responseStatusCode, - }) - ); - pipe(body); - } - }, - onShellError(error: unknown) { - reject(error); - }, - onAllReady() { - if (bot) { - shellRendered = true; - responseHeaders.set("Content-Type", "text/html"); - resolve( - new Response(stream, { - headers: responseHeaders, - status: responseStatusCode, - }) - ); - pipe(body); - } - }, - onError(error: unknown) { - responseStatusCode = 500; - // Log streaming rendering errors from inside the shell. Don't log - // errors encountered during initial shell rendering since they'll - // reject and get logged in handleDocumentRequest. - if (shellRendered) { - console.error(error); - } - }, - } - ); - - const timer = setTimeout(() => { - abort(); - }, ABORT_DELAY); - }); -} +export { handleRequest as default } from "@netlify/remix-adapter"; diff --git a/remix.init/entry.server.tsx b/remix.init/entry.server.tsx index 374bca8..0ebf826 100644 --- a/remix.init/entry.server.tsx +++ b/remix.init/entry.server.tsx @@ -1,34 +1 @@ -import type { AppLoadContext, EntryContext } from "@netlify/remix-runtime"; -import { RemixServer } from "@remix-run/react"; -import isbot from "isbot"; -import { renderToReadableStream } from "react-dom/server"; - -export default async function handleRequest( - request: Request, - responseStatusCode: number, - responseHeaders: Headers, - remixContext: EntryContext, - loadContext: AppLoadContext -) { - const body = await renderToReadableStream( - , - { - signal: request.signal, - onError(error: unknown) { - // Log streaming rendering errors from inside the shell - console.error(error); - responseStatusCode = 500; - }, - } - ); - - if (isbot(request.headers.get("user-agent"))) { - await body.allReady; - } - - responseHeaders.set("Content-Type", "text/html"); - return new Response(body, { - headers: responseHeaders, - status: responseStatusCode, - }); -} +export { handleRequest as default } from "@netlify/remix-edge-adapter"; From 7cf10f636c9e4c3772760c2614ba4b8675d054aa Mon Sep 17 00:00:00 2001 From: Matt Kane Date: Mon, 30 Oct 2023 12:18:34 +0000 Subject: [PATCH 17/22] chore: simplify and format server.ts --- remix.init/server.ts | 6 ++---- server.ts | 14 +++++--------- 2 files changed, 7 insertions(+), 13 deletions(-) diff --git a/remix.init/server.ts b/remix.init/server.ts index 18a3908..a816f35 100644 --- a/remix.init/server.ts +++ b/remix.init/server.ts @@ -8,13 +8,11 @@ export default createRequestHandler({ mode: process.env.NODE_ENV, }); - -if(process.env.NODE_ENV === "development") { +if (process.env.NODE_ENV === "development") { // Tell remix dev that the server is ready broadcastDevReady(build); } - export const config = { cache: "manual", path: "/*", @@ -23,4 +21,4 @@ export const config = { // Add other exclusions here, e.g. "^/api/*$" for custom Netlify functions or // custom Netlify Edge Functions excluded_patterns: ["^/_assets/*$"], -}; \ No newline at end of file +}; diff --git a/server.ts b/server.ts index 20c6e9e..d1d6d95 100644 --- a/server.ts +++ b/server.ts @@ -1,13 +1,9 @@ -import * as build from '@remix-run/dev/server-build' -import { createRequestHandler } from '@netlify/remix-adapter' -import type { Context, Config } from '@netlify/functions' +import * as build from "@remix-run/dev/server-build"; +import { createRequestHandler } from "@netlify/remix-adapter"; -const handle = createRequestHandler({ +const handler = createRequestHandler({ build, mode: process.env.NODE_ENV, -}) - -export default function handler(request: Request, context: Context) { - return handle(request, context) -} +}); +export default handler; From 605d4a1fb08cd135de76f748131d58160ca01fbb Mon Sep 17 00:00:00 2001 From: Matt Kane Date: Mon, 30 Oct 2023 16:59:32 +0000 Subject: [PATCH 18/22] chore: fix command for dev --- remix.init/README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/remix.init/README.md b/remix.init/README.md index 6513be5..52a6a52 100644 --- a/remix.init/README.md +++ b/remix.init/README.md @@ -40,7 +40,7 @@ npm install Run ```sh -npm run dev +netlify dev ``` Open up [http://localhost:3000](http://localhost:3000), and you're ready to go! @@ -59,7 +59,6 @@ npm run start Your site will be available at [http://localhost:8888](http://localhost:8888). Note that it will not auto-reload when you make changes. - ## Deployment There are two ways to deploy your app to Netlify, you can either link your app to your git repo and have it auto deploy changes to Netlify, or you can deploy your app manually. If you've followed the setup instructions already, all you need to do is run this: From 7846dc79ec489c41af160c9929c0f3c1729ebabc Mon Sep 17 00:00:00 2001 From: Matt Kane Date: Mon, 30 Oct 2023 16:59:53 +0000 Subject: [PATCH 19/22] chore: fixes to init script --- remix.init/index.js | 35 ++++++++++++++++++++++------------- 1 file changed, 22 insertions(+), 13 deletions(-) diff --git a/remix.init/index.js b/remix.init/index.js index a52bd0f..d51fc54 100644 --- a/remix.init/index.js +++ b/remix.init/index.js @@ -5,7 +5,7 @@ const PackageJson = require("@npmcli/package-json"); const execa = require("execa"); const { Command } = require("commander"); -const foldersToExclude = [".github", ".git"]; +const foldersToExclude = [".github"]; // Netlify Edge Functions template file changes const edgeFilesToCopy = [ @@ -51,7 +51,7 @@ async function updatePackageJsonForEdge(directory) { ...restOfPackageJson, dependencies: { ...dependencies, - "@netlify/edge-functions": "2.0.0", + "@netlify/edge-functions": "^2.0.0", "@netlify/remix-edge-adapter": "^3.0.0", "@netlify/remix-runtime": "^2.0.0", }, @@ -96,7 +96,24 @@ async function removeNonTemplateFiles({ rootDirectory, folders }) { } } -async function main({ rootDirectory }) { +async function installAdditionalDependencies({ + rootDirectory, + packageManager, +}) { + try { + console.log(`Installing additional dependencies with ${packageManager}.`); + const npmInstall = await execa(packageManager, ["install"], { + cwd: rootDirectory, + stdio: "inherit", + }); + } catch (e) { + console.log( + `Unable to install additional packages. Run ${packageManager} install in the root of the new project, "${rootDirectory}".` + ); + } +} + +async function main({ rootDirectory, packageManager }) { await removeNonTemplateFiles({ rootDirectory, folders: foldersToExclude, @@ -108,6 +125,7 @@ async function main({ rootDirectory }) { rootDirectory, }); await updatePackageJsonForFunctions(rootDirectory); + await installAdditionalDependencies({ rootDirectory, packageManager }); return; } @@ -118,16 +136,7 @@ async function main({ rootDirectory }) { await updatePackageJsonForEdge(rootDirectory); - // The Netlify Edge Functions template has different and additional dependencies to install. - try { - console.log("installing additional npm packages..."); - const npmInstall = await execa("npm", ["install"], { cwd: rootDirectory }); - console.log(npmInstall.stdout); - } catch (e) { - console.log( - `Unable to install additional packages. Run npm install in the root of the new project, "${rootDirectory}".` - ); - } + await installAdditionalDependencies({ rootDirectory, packageManager }); } async function shouldUseEdge() { From 445921c3dc2a4e76692077942fbf143ee65828a7 Mon Sep 17 00:00:00 2001 From: Matt Kane Date: Wed, 1 Nov 2023 10:30:00 +0000 Subject: [PATCH 20/22] chore: exclude correct static path --- remix.init/server.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/remix.init/server.ts b/remix.init/server.ts index a816f35..f73b344 100644 --- a/remix.init/server.ts +++ b/remix.init/server.ts @@ -20,5 +20,5 @@ export const config = { // // Add other exclusions here, e.g. "^/api/*$" for custom Netlify functions or // custom Netlify Edge Functions - excluded_patterns: ["^/_assets/*$"], + excludedPath: ["/build/*", "/favicon.ico"], }; From 2ddef1327109d8f48702f4050da5a3714df57780 Mon Sep 17 00:00:00 2001 From: Matt Kane Date: Thu, 2 Nov 2023 10:17:03 +0000 Subject: [PATCH 21/22] fix: handle redirects in dev --- package.json | 3 +-- remix.init/index.js | 15 +++++++++++++-- remix.init/netlify-toml | 6 +----- remix.init/redirects | 4 ++++ 4 files changed, 19 insertions(+), 9 deletions(-) create mode 100644 remix.init/redirects diff --git a/package.json b/package.json index dd25b42..bcd1b07 100644 --- a/package.json +++ b/package.json @@ -23,10 +23,9 @@ "@types/react": "^18.2.0", "@types/react-dom": "^18.2.0", "eslint": "^8.27.0", - "rimraf": "^4.1.4", "typescript": "^5.2.2" }, "engines": { "node": ">=18" } -} \ No newline at end of file +} diff --git a/remix.init/index.js b/remix.init/index.js index d51fc54..838aa22 100644 --- a/remix.init/index.js +++ b/remix.init/index.js @@ -19,7 +19,11 @@ const edgeFilesToCopy = [ ]; // Netlify Functions template file changes -const filesToCopy = [["README.md"], ["netlify-toml", "netlify.toml"]]; +const filesToCopy = [ + ["README.md"], + ["netlify-toml", "netlify.toml"], + ["redirects", ".redirects"], +]; async function copyTemplateFiles({ files, rootDirectory }) { for (const [file, target] of files) { @@ -45,7 +49,6 @@ async function updatePackageJsonForEdge(directory) { // dev script is the same as the start script for Netlify Edge, "cross-env NODE_ENV=production netlify dev" scripts: { ...scripts, - predev: "rimraf ./.netlify/edge-functions/", dev: 'remix dev --manual -c "ntl dev --framework=#static"', }, ...restOfPackageJson, @@ -70,10 +73,18 @@ async function updatePackageJsonForFunctions(directory) { packageJson.update({ ...restOfPackageJson, + scripts: { + ...scripts, + build: "npm run redirects:enable && remix build", + dev: "npm run redirects:disable && remix dev", + "redirects:enable": "shx cp .redirects public/_redirects", + "redirects:disable": "shx rm -f public/_redirects", + }, dependencies: { ...dependencies, "@netlify/functions": "^2.0.0", "@netlify/remix-adapter": "^2.0.0", + shx: "^0.3.4", }, }); diff --git a/remix.init/netlify-toml b/remix.init/netlify-toml index 44b49b3..e27db6b 100644 --- a/remix.init/netlify-toml +++ b/remix.init/netlify-toml @@ -4,11 +4,7 @@ publish = "public" [dev] command = "npm run dev" - -[[redirects]] -from = "/*" -to = "/.netlify/functions/server" -status = 200 +targetPort = 3000 # Set immutable caching for static files, because they have fingerprinted filenames diff --git a/remix.init/redirects b/remix.init/redirects new file mode 100644 index 0000000..eb6d445 --- /dev/null +++ b/remix.init/redirects @@ -0,0 +1,4 @@ +# This file is copied into the public folder during the build step and removed during dev. +# If you need to add your own redirects, add them in netlify.toml or they will be overwritten. +# Do not remove the line below. This is required to serve the site when deployed. +/* /.netlify/functions/server 200 \ No newline at end of file From 2e4e9905da32f7eeb0761e6ee6642b62b879d21c Mon Sep 17 00:00:00 2001 From: Matt Kane Date: Thu, 2 Nov 2023 10:38:16 +0000 Subject: [PATCH 22/22] chore: remove unneeded dep --- package.json | 1 - 1 file changed, 1 deletion(-) diff --git a/package.json b/package.json index bcd1b07..225ba5a 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,6 @@ "@remix-run/node": "*", "@remix-run/react": "*", "cross-env": "^7.0.3", - "isbot": "^3.7.0", "react": "^18.2.0", "react-dom": "^18.2.0" },