Skip to content

Commit

Permalink
Merge branch 'dev' into markdalgleish/fix-unresolved-css-import-warnings
Browse files Browse the repository at this point in the history
  • Loading branch information
markdalgleish committed Jun 2, 2023
2 parents 01517b8 + ce07e84 commit 7d8b792
Show file tree
Hide file tree
Showing 121 changed files with 1,830 additions and 815 deletions.
5 changes: 5 additions & 0 deletions .changeset/hdr-revalidation-retry.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@remix-run/react": patch
---

retry HDR revalidations in development mode to aid in 3rd party server race conditions
5 changes: 5 additions & 0 deletions .changeset/log-server-errors.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@remix-run/server-runtime": patch
---

Ensure un-sanitized server errors are logged on the server during document requests
22 changes: 22 additions & 0 deletions .changeset/on-unhandled-error.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
---
"@remix-run/server-runtime": minor
---

Add optional `onUnhandledError` export for custom server-side error processing. This is a new optional export from your `entry.server.tsx` that will be called with any encountered error on the Remix server (loader, action, or render error):

```ts
// entry.server.tsx
export function onUnhandledError(
error: unknown,
{ request, params, context }: DataFunctionArgs
): void {
if (error instanceof Error) {
sendErrorToBugReportingService(error);
console.error(formatError(error));
} else {
let unknownError = new Error("Unknown Server Error");
sendErrorToBugReportingService(unknownError);
console.error(unknownError);
}
}
```
5 changes: 5 additions & 0 deletions .changeset/postcss-cache-regular-stylesheets.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@remix-run/dev": major
---

Add caching to PostCSS for regular stylesheets
6 changes: 6 additions & 0 deletions .changeset/req-clone-instanceof.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"remix": patch
"@remix-run/node": patch
---

Fix `request.clone() instanceof Request` returning false.
1 change: 1 addition & 0 deletions contributors.yml
Original file line number Diff line number Diff line change
Expand Up @@ -491,6 +491,7 @@
- tombiju
- tombyrer
- TomerAberbach
- tomer-yechiel
- toyozaki
- turkerdev
- tvanantwerp
Expand Down
47 changes: 45 additions & 2 deletions docs/file-conventions/entry.server.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,53 @@ toc: false

# entry.server

By default, Remix will handle generating the HTTP Response for you. If you want to customize this behavior, you can run `npx remix reveal` to generate a `app/entry.server.tsx` (or `.jsx`) that will take precedence. The `default` export of this module is a function that lets you create the response, including HTTP status, headers, and HTML, giving you full control over the way the markup is generated and sent to the client.
By default, Remix will handle generating the HTTP Response for you. If you want to customize this behavior, you can run `npx remix reveal` to generate an `app/entry.server.tsx` (or `.jsx`) that will take precedence. The `default` export of this module is a function that lets you create the response, including HTTP status, headers, and HTML, giving you full control over the way the markup is generated and sent to the client.

This module should render the markup for the current page using a `<RemixServer>` element with the `context` and `url` for the current request. This markup will (optionally) be re-hydrated once JavaScript loads in the browser using the [browser entry module][browser-entry-module].

You can also export an optional `handleDataRequest` function that will allow you to modify the response of a data request. These are the requests that do not render HTML, but rather return the loader and action data to the browser once client-side hydration has occurred.
## `handleDataRequest`

You can export an optional `handleDataRequest` function that will allow you to modify the response of a data request. These are the requests that do not render HTML, but rather return the loader and action data to the browser once client-side hydration has occurred.

```tsx
export function handleDataRequest(
response: Response,
{ request, params, context }: DataFunctionArgs
) {
response.headers.set("X-Custom-Header", "value");
return response;
}
```

## `onUnhandledError`

By default, Remix will log encountered server-side errors to the console. If you'd like more control over the logging, or would like to also report these errors to an external service, then you can export an optional `onUnhandledError` function which will give you control (and will disable the built-in error logging).

```tsx
export function onUnhandledError(
error: unknown,
{ request, params, context }: DataFunctionArgs
) {
sendErrorToErrorReportingService(error);
console.error(formatErrorForJsonLogging(error));
}
```

### Streaming Rendering Errors

When you are streaming your HTML responses via [`renderToPipeableStream`][rendertopipeablestream] or [`renderToReadableStream`][rendertoreadablestream], your own `onUnhandledError` implementation will only handle errors encountered uring the initial shell render. If you encounter a rendering error during subsequent streamed rendering you will need handle these errors manually since the Remix server has already sent the Response by that point.

- For `renderToPipeableStream`, you can handle these errors in the `onError` callback function. You will need to toggle a boolean when the in `onShellReady` so you know if the error was a shell rendering error (and can be ignored) or an async rendering error (and must be handled).
- For an exmaple, please see the default [`entry.server.tsx`][node-streaming-entry-server] for Node.
- For `renderToReadableStream`, you can handle these errors in the `onError` callback function
- For an example, please see the default [`entry.server.tsx`][cloudflare-streaming-entry-server] for Cloudflare

### Thrown Responses

Note that this does not handle thrown `Response` instances from your `loader`/`action` functions. The intention of this handler is to find bugs in your code which result in unexpected thrown errors. If you are detecting a scenario and throwing a 401/404/etc. `Response` in your `loader`/`action` then it's an expected flow that is handled by your code. If you also wish to log, or send those to an external service, that should be done at the time you throw the response.

[browser-entry-module]: ./entry.client
[rendertopipeablestream]: https://react.dev/reference/react-dom/server/renderToPipeableStream
[rendertoreadablestream]: https://react.dev/reference/react-dom/server/renderToReadableStream
[node-streaming-entry-server]: https://github.com/remix-run/remix/blob/main/packages/remix-dev/config/defaults/node/entry.server.react-stream.tsx
[cloudflare-streaming-entry-server]: https://github.com/remix-run/remix/blob/main/packages/remix-dev/config/defaults/cloudflare/entry.server.react-stream.tsx
10 changes: 5 additions & 5 deletions docs/guides/styling.md
Original file line number Diff line number Diff line change
Expand Up @@ -432,11 +432,11 @@ Now we can tell it which files to generate classes from:
import type { Config } from "tailwindcss";

export default {
content: ["./app/**/*.{js,jsx,ts,tsx}"],
theme: {
extend: {},
},
plugins: [],
content: ["./app/**/*.{js,jsx,ts,tsx}"],
theme: {
extend: {},
},
plugins: [],
} satisfies Config;
```

Expand Down
4 changes: 3 additions & 1 deletion integration/abort-signal-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@ let appFixture: AppFixture;

test.beforeAll(async () => {
fixture = await createFixture({
future: { v2_routeConvention: true },
config: {
future: { v2_routeConvention: true },
},
files: {
"app/routes/_index.jsx": js`
import { json } from "@remix-run/node";
Expand Down
10 changes: 6 additions & 4 deletions integration/action-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,12 @@ test.describe("actions", () => {

test.beforeAll(async () => {
fixture = await createFixture({
future: {
v2_routeConvention: true,
v2_errorBoundary: true,
v2_normalizeFormMethod: true,
config: {
future: {
v2_routeConvention: true,
v2_errorBoundary: true,
v2_normalizeFormMethod: true,
},
},
files: {
"app/routes/urlencoded.jsx": js`
Expand Down
4 changes: 3 additions & 1 deletion integration/bug-report-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,9 @@ let appFixture: AppFixture;

test.beforeAll(async () => {
fixture = await createFixture({
future: { v2_routeConvention: true },
config: {
future: { v2_routeConvention: true },
},
////////////////////////////////////////////////////////////////////////////
// 💿 Next, add files to this object, just like files in a real app,
// `createFixture` will make an app and run your tests against it.
Expand Down
12 changes: 8 additions & 4 deletions integration/catch-boundary-data-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,9 @@ let LAYOUT_DATA = "root data";

test.beforeAll(async () => {
fixture = await createFixture({
future: { v2_routeConvention: true },
config: {
future: { v2_routeConvention: true },
},
files: {
"app/root.jsx": js`
import { json } from "@remix-run/node";
Expand Down Expand Up @@ -236,9 +238,11 @@ test("renders self boundary with layout data available on transition", async ({
test.describe("v2_errorBoundary", () => {
test.beforeAll(async () => {
fixture = await createFixture({
future: {
v2_routeConvention: true,
v2_errorBoundary: true,
config: {
future: {
v2_routeConvention: true,
v2_errorBoundary: true,
},
},
files: {
"app/root.jsx": js`
Expand Down
12 changes: 8 additions & 4 deletions integration/catch-boundary-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,9 @@ test.describe("CatchBoundary", () => {

test.beforeAll(async () => {
fixture = await createFixture({
future: { v2_routeConvention: true },
config: {
future: { v2_routeConvention: true },
},
files: {
"app/root.jsx": js`
import { json } from "@remix-run/node";
Expand Down Expand Up @@ -382,9 +384,11 @@ test.describe("v2_errorBoundary", () => {

test.beforeAll(async () => {
fixture = await createFixture({
future: {
v2_routeConvention: true,
v2_errorBoundary: true,
config: {
future: {
v2_routeConvention: true,
v2_errorBoundary: true,
},
},
files: {
"app/root.jsx": js`
Expand Down
4 changes: 3 additions & 1 deletion integration/cf-compiler-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,9 @@ test.describe("cloudflare compiler", () => {

test.beforeAll(async () => {
projectDir = await createFixtureProject({
future: { v2_routeConvention: true },
config: {
future: { v2_routeConvention: true },
},
setup: "cloudflare",
template: "cf-template",
files: {
Expand Down
18 changes: 8 additions & 10 deletions integration/compiler-mjs-output-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,15 @@ let projectDir: string;

test.beforeAll(async () => {
projectDir = await createFixtureProject({
future: { v2_routeConvention: true },
files: {
// Ensure the config is valid ESM
"remix.config.js": js`
export default {
serverModuleFormat: "esm",
serverBuildPath: "build/index.mjs",
future: { v2_routeConvention: true },
};
`,
"package.json": js`
{
"name": "remix-template-remix",
Expand Down Expand Up @@ -40,15 +47,6 @@ test.beforeAll(async () => {
}
}
`,
"remix.config.js": js`
export default {
serverModuleFormat: "esm",
serverBuildPath: "build/index.mjs",
future: {
v2_routeConvention: true,
},
};
`,
"app/routes/_index.jsx": js`
import { json } from "@remix-run/node";
import { useLoaderData, Link } from "@remix-run/react";
Expand Down
6 changes: 5 additions & 1 deletion integration/compiler-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ test.describe("compiler", () => {
fixture = await createFixture({
setup: "node",
files: {
// We need a custom config file here to test usage of `getDependenciesToBundle`
// since this can't be serialized from the fixture object.
"remix.config.js": js`
let { getDependenciesToBundle } = require("@remix-run/dev");
module.exports = {
Expand Down Expand Up @@ -386,7 +388,9 @@ test.describe("compiler", () => {

await expect(() =>
createFixtureProject({
future: { v2_routeConvention: true },
config: {
future: { v2_routeConvention: true },
},
buildStdio,
files: {
"app/routes/_index.jsx": js`
Expand Down
6 changes: 4 additions & 2 deletions integration/css-modules-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,10 @@ test.describe("CSS Modules", () => {

test.beforeAll(async () => {
fixture = await createFixture({
future: {
v2_routeConvention: true,
config: {
future: {
v2_routeConvention: true,
},
},
files: {
"app/root.jsx": js`
Expand Down
14 changes: 6 additions & 8 deletions integration/css-side-effect-imports-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,13 @@ test.describe("CSS side-effect imports", () => {

test.beforeAll(async () => {
fixture = await createFixture({
config: {
serverDependenciesToBundle: [/@test-package/],
future: {
v2_routeConvention: true,
},
},
files: {
"remix.config.js": js`
module.exports = {
serverDependenciesToBundle: [/@test-package/],
future: {
v2_routeConvention: true,
},
};
`,
"app/root.jsx": js`
import { Links, Outlet } from "@remix-run/react";
import { cssBundleHref } from "@remix-run/css-bundle";
Expand Down
4 changes: 3 additions & 1 deletion integration/custom-entry-server-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@ let appFixture: AppFixture;

test.beforeAll(async () => {
fixture = await createFixture({
future: { v2_routeConvention: true },
config: {
future: { v2_routeConvention: true },
},
files: {
"app/entry.server.jsx": js`
import * as React from "react";
Expand Down
4 changes: 3 additions & 1 deletion integration/defer-loader-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@ let appFixture: AppFixture;

test.beforeAll(async () => {
fixture = await createFixture({
future: { v2_routeConvention: true },
config: {
future: { v2_routeConvention: true },
},
files: {
"app/routes/_index.jsx": js`
import { useLoaderData, Link } from "@remix-run/react";
Expand Down
14 changes: 9 additions & 5 deletions integration/defer-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,12 @@ test.describe("non-aborted", () => {

test.beforeAll(async () => {
fixture = await createFixture({
future: {
v2_routeConvention: true,
v2_errorBoundary: true,
v2_normalizeFormMethod: true,
config: {
future: {
v2_routeConvention: true,
v2_errorBoundary: true,
v2_normalizeFormMethod: true,
},
},
files: {
"app/components/counter.tsx": js`
Expand Down Expand Up @@ -941,7 +943,9 @@ test.describe("aborted", () => {

test.beforeAll(async () => {
fixture = await createFixture({
future: { v2_routeConvention: true },
config: {
future: { v2_routeConvention: true },
},
////////////////////////////////////////////////////////////////////////////
// 💿 Next, add files to this object, just like files in a real app,
// `createFixture` will make an app and run your tests against it.
Expand Down
4 changes: 3 additions & 1 deletion integration/deno-compiler-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,9 @@ const searchFiles = async (pattern: string | RegExp, files: string[]) => {

test.beforeAll(async () => {
projectDir = await createFixtureProject({
future: { v2_routeConvention: true },
config: {
future: { v2_routeConvention: true },
},
template: "deno-template",
files: {
"package.json": json({
Expand Down
6 changes: 4 additions & 2 deletions integration/deterministic-build-output-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,10 @@ test("builds deterministically under different paths", async () => {
// * serverRouteModulesPlugin (implicitly tested by build)
// * vanillaExtractPlugin (via app/routes/foo.tsx' .css.ts file import)
let init: FixtureInit = {
future: {
v2_routeConvention: true,
config: {
future: {
v2_routeConvention: true,
},
},
files: {
"app/routes/_index.mdx": "# hello world",
Expand Down
Loading

0 comments on commit 7d8b792

Please sign in to comment.