( action: string, formElement: HTMLFormElement )
| 49 | export type FormProps<RouteInferType = any> = InternalFormProps |
| 50 | |
| 51 | export function createFormSubmitDestinationUrl( |
| 52 | action: string, |
| 53 | formElement: HTMLFormElement |
| 54 | ) { |
| 55 | let targetUrl: URL |
| 56 | try { |
| 57 | // NOTE: It might be more correct to resolve URLs relative to `document.baseURI`, |
| 58 | // but we already do it relative to `location.href` elsewhere: |
| 59 | // (see e.g. https://github.com/vercel/next.js/blob/bb0e6722f87ceb2d43015f5b8a413d0072f2badf/packages/next/src/client/components/app-router.tsx#L146) |
| 60 | // so it's better to stay consistent. |
| 61 | const base = window.location.href |
| 62 | targetUrl = new URL(action, base) |
| 63 | } catch (err) { |
| 64 | throw new Error(`Cannot parse form action "${action}" as a URL`, { |
| 65 | cause: err, |
| 66 | }) |
| 67 | } |
| 68 | if (targetUrl.searchParams.size) { |
| 69 | // url-encoded HTML forms *overwrite* any search params in the `action` url: |
| 70 | // |
| 71 | // "Let `query` be the result of running the application/x-www-form-urlencoded serializer [...]" |
| 72 | // "Set parsed action's query component to `query`." |
| 73 | // https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#submit-mutate-action |
| 74 | // |
| 75 | // We need to match that. |
| 76 | // (note that all other parts of the URL, like `hash`, are preserved) |
| 77 | targetUrl.search = '' |
| 78 | } |
| 79 | |
| 80 | const formData = new FormData(formElement) |
| 81 | |
| 82 | for (let [name, value] of formData) { |
| 83 | if (typeof value !== 'string') { |
| 84 | // For file inputs, the native browser behavior is to use the filename as the value instead: |
| 85 | // |
| 86 | // "If entry's value is a File object, then let value be entry's value's name. Otherwise, let value be entry's value." |
| 87 | // https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#converting-an-entry-list-to-a-list-of-name-value-pairs |
| 88 | // |
| 89 | if (process.env.NODE_ENV === 'development') { |
| 90 | console.warn( |
| 91 | `<Form> only supports file inputs if \`action\` is a function. File inputs cannot be used if \`action\` is a string, ` + |
| 92 | `because files cannot be encoded as search params.` |
| 93 | ) |
| 94 | } |
| 95 | value = value.name |
| 96 | } |
| 97 | |
| 98 | targetUrl.searchParams.append(name, value) |
| 99 | } |
| 100 | return targetUrl |
| 101 | } |
| 102 | |
| 103 | export function checkFormActionUrl( |
| 104 | action: string, |
no test coverage detected