Skip to content

Commit

Permalink
Merge branch 'main' into release-v1.4.0
Browse files Browse the repository at this point in the history
  • Loading branch information
chaance committed Apr 12, 2022
2 parents a04f3d1 + 9d1b88b commit 1e08cf9
Show file tree
Hide file tree
Showing 9 changed files with 77 additions and 22 deletions.
5 changes: 5 additions & 0 deletions contributors.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
- aaronshaf
- abereghici
- abotsi
- accidentaldeveloper
- adicuco
- ahbruns
- ahmedeldessouki
Expand All @@ -11,6 +12,7 @@
- Alarid
- alex-ketch
- alexuxui
- amorriscode
- andrelandgraf
- andrewbrey
- AndrewIngram
Expand All @@ -28,12 +30,14 @@
- ascorbic
- ashleyryan
- ashocean
- athongsavath
- axel-habermaier
- BasixKOR
- BenMcH
- bmontalvo
- bogas04
- BogdanDevBst
- bolchowka
- brophdawg11
- bruno-oliveira
- bsharrow
Expand Down Expand Up @@ -140,6 +144,7 @@
- jmasson
- jo-ninja
- joaosamouco
- jodygeraldo
- johannesbraeunig
- johnson444
- johnson444
Expand Down
30 changes: 14 additions & 16 deletions docs/api/remix.md
Original file line number Diff line number Diff line change
Expand Up @@ -1270,9 +1270,9 @@ function SomeComponent() {

```js
[
{ pathname, data, params, handle }, // root route
{ pathname, data, params, handle }, // layout route
{ pathname, data, params, handle }, // child route
{ id, pathname, data, params, handle }, // root route
{ id, pathname, data, params, handle }, // layout route
{ id, pathname, data, params, handle }, // child route
// etc.
];
```
Expand Down Expand Up @@ -1555,14 +1555,13 @@ These are fully featured utilities for handling fairly simple use cases. It's no
**Example:**

```tsx
const uploadHandler = unstable_createFileUploadHandler({
maxFileSize: 5_000_000,
file: ({ filename }) => filename,
});

export const action: ActionFunction = async ({
request,
}) => {
const uploadHandler = unstable_createFileUploadHandler({
maxFileSize: 5_000_000,
file: ({ filename }) => filename,
});
const formData = await unstable_parseMultipartFormData(
request,
uploadHandler
Expand Down Expand Up @@ -1594,13 +1593,12 @@ The `filter` function accepts an `object` and returns a `boolean` (or a promise
**Example:**

```tsx
const uploadHandler = unstable_createMemoryUploadHandler({
maxFileSize: 500_000,
});

export const action: ActionFunction = async ({
request,
}) => {
const uploadHandler = unstable_createMemoryUploadHandler({
maxFileSize: 500_000,
});
const formData = await unstable_parseMultipartFormData(
request,
uploadHandler
Expand Down Expand Up @@ -1709,12 +1707,12 @@ Your job is to do whatever you need with the `stream` and return a value that's

We have the built-in `unstable_createFileUploadHandler` and `unstable_createMemoryUploadHandler` and we also expect more upload handler utilities to be developed in the future. If you have a form that needs to use different upload handlers, you can compose them together with a custom handler, here's a theoretical example:

```tsx
```tsx filename=file-upload-handler.server.tsx
import type { UploadHandler } from "@remix-run/{runtime}";
import { unstable_createFileUploadHandler } from "@remix-run/{runtime}";
import { createCloudinaryUploadHandler } from "some-handy-remix-util";

export const fileUploadHandler =
export const standardFileUploadHandler =
unstable_createFileUploadHandler({
directory: "public/calendar-events",
});
Expand All @@ -1724,9 +1722,9 @@ export const cloudinaryUploadHandler =
folder: "/my-site/avatars",
});

export const multHandler: UploadHandler = (args) => {
export const fileUploadHandler: UploadHandler = (args) => {
if (args.name === "calendarEvent") {
return fileUploadHandler(args);
return standardFileUploadHandler(args);
} else if (args.name === "eventBanner") {
return cloudinaryUploadHandler(args);
} else {
Expand Down
7 changes: 5 additions & 2 deletions docs/guides/constraints.md
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,10 @@ export function removeTrailingSlash(loader) {
return function (arg) {
const { request } = arg;
const url = new URL(request.url);
if (url.pathname.endsWith("/")) {
if (
url.pathname !== "/" &&
url.pathname.endsWith("/")
) {
return redirect(request.url.slice(0, -1), {
status: 308,
});
Expand Down Expand Up @@ -188,7 +191,7 @@ This type of abstraction is introduced to try to return a response early. Since
import { redirect } from "@remix-run/{runtime}";

export function removeTrailingSlash(url) {
if (url.pathname.endsWith("/")) {
if (url.pathname !== "/" && url.pathname.endsWith("/")) {
throw redirect(request.url.slice(0, -1), {
status: 308,
});
Expand Down
6 changes: 6 additions & 0 deletions docs/pages/faq.md
Original file line number Diff line number Diff line change
Expand Up @@ -228,3 +228,9 @@ Again, `formData.getAll()` is often all you need, we encourage you to give it a

[form-data]: https://developer.mozilla.org/en-US/docs/Web/API/FormData
[query-string]: https://www.npmjs.com/package/query-string

## What's the difference between `CatchBoundary` & `ErrorBoundary`?

Error boundaries render when your application throws an error and you had no clue it was going to happen. Most apps just go blank or have spinners spin forever. In remix the error boundary renders and you have granular control over it.

Catch boundaries render when you decide in a loader that you can't proceed down the happy path to render the UI you want (auth required, record not found, etc.), so you throw a response and let some catch boundary up the tree handle it.
37 changes: 37 additions & 0 deletions docs/pages/gotchas.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,31 @@ export default function SomeRoute() {

Even better, send a PR to the project to add `"sideEffects": false` to their package.json so that bundlers that tree shake know they can safely remove the code from browser bundles.

Similarly, you may run into the same error if you call a function at the top-level scope of your route module that depends on server-only code.

For example, [Remix upload handlers like `unstable_createFileUploadHandler` and `unstable_createMemoryUploadHandler`](../api/remix#uploadhandler) use Node globals under the hood and should only be called on the server. You can call either of these functions in a `*.server.js` or `*.server.ts` file, or you can move them into your route's `action` or `loader` function:

```tsx filename=app/routes/some-route.jsx
import { unstable_createFileUploadHandler } from "@remix-run/{runtime}";

// Instead of this…
const uploadHandler = unstable_createFileUploadHandler({
maxFileSize: 5_000_000,
file: ({ filename }) => filename,
});

// …do this

export async function action({ request }) {
const uploadHandler = unstable_createFileUploadHandler({
maxFileSize: 5_000_000,
file: ({ filename }) => filename,
});

// ...
}
```

> Why does this happen?
Remix uses "tree shaking" to remove server code from browser bundles. Anything inside of Route module `loader`, `action`, and `headers` exports will be removed. It's a great approach but suffers from ecosystem compatibility.
Expand Down Expand Up @@ -120,3 +145,15 @@ if (typeof document === "undefined") {
This will work for all JS environments (Node.js, Deno, Workers, etc.).

[esbuild]: https://esbuild.github.io/

## Browser extensions injecting code

You may run into this warning in the browser:

```
Warning: Did not expect server HTML to contain a <script> in <html>.
```

This is a hydration warning from React, and is most likely due to one of your browser extensions injecting scripts into the server-rendered HTML, creating a difference with the resulting HTML.

Check out the page in incognito mode, the warning should disappear.
2 changes: 1 addition & 1 deletion docs/pages/technical-explanation.md
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ Taking our route module from before, here are a few small, but useful UX improve
2. Focus the input when server side form validation fails
3. Animate in the error messages
```jsx nocopy lines=[4-6,8-12,21,22,28-30]
```jsx nocopy lines=[4-6,8-12,23-26,30-32]
export default function Projects() {
const projects = useLoaderData();
const actionData = useActionData();
Expand Down
8 changes: 6 additions & 2 deletions docs/tutorials/blog.md
Original file line number Diff line number Diff line change
Expand Up @@ -930,8 +930,12 @@ Let's add some validation before we create the post.

💿 Validate if the form data contains what we need, and return the errors if not

```tsx filename=app/routes/posts/admin/new.tsx lines=[3-9,19-29]
// ...
```tsx filename=app/routes/posts/admin/new.tsx lines=[3,8-14,24-34]
import type { ActionFunction } from "@remix-run/node";
import { json, redirect } from "@remix-run/node";
import { Form } from "@remix-run/react";

import { createPost } from "~/models/post.server";

type ActionData =
| {
Expand Down
1 change: 1 addition & 0 deletions docs/tutorials/jokes.md
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ R E M I X
💿 Welcome to Remix! Let's get you set up with a new project.
? Where would you like to create your app? remix-jokes
? What type of app do you want to create? Just the basics
? Where do you want to deploy? Choose Remix if you're unsure, it's easy to change deployment targets. Remix
App Server
? TypeScript or JavaScript? TypeScript
Expand Down
3 changes: 2 additions & 1 deletion packages/remix-dev/__tests__/readConfig-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ describe("readConfig", () => {
rootDirectory: expect.any(String),
appDirectory: expect.any(String),
cacheDirectory: expect.any(String),
devServerPort: expect.any(Number),
serverBuildPath: expect.any(String),
assetsBuildDirectory: expect.any(String),
},
Expand All @@ -26,7 +27,7 @@ describe("readConfig", () => {
"assetsBuildDirectory": Any<String>,
"cacheDirectory": Any<String>,
"devServerBroadcastDelay": 0,
"devServerPort": 8002,
"devServerPort": Any<Number>,
"entryClientFile": "entry.client.tsx",
"entryServerFile": "entry.server.tsx",
"mdx": undefined,
Expand Down

0 comments on commit 1e08cf9

Please sign in to comment.