Skip to content

Commit

Permalink
breaking: remove deprecated use:enhance callback values (#11282)
Browse files Browse the repository at this point in the history
  • Loading branch information
Rich-Harris authored Dec 13, 2023
1 parent 76006b6 commit 4dc6aec
Show file tree
Hide file tree
Showing 9 changed files with 27 additions and 214 deletions.
5 changes: 5 additions & 0 deletions .changeset/chilled-fireants-learn.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@sveltejs/kit': major
---

breaking: remove deprecated `use:enhance` callback values
5 changes: 5 additions & 0 deletions .changeset/mean-moose-smell.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@sveltejs/kit': major
---

breaking: error if form without multipart/form-data enctype contains a file input
10 changes: 10 additions & 0 deletions documentation/docs/60-appendix/30-migrating-to-sveltekit-2.md
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,16 @@ As such, SvelteKit 2 replaces `resolvePath` with a (slightly better named) funct

`svelte-migrate` will do the method replacement for you, though if you later prepend the result with `base`, you need to remove that yourself.

## `form` and `data` have been removed from `use:enhance` callbacks

If you provide a callback to [`use:enhance`](/docs/form-actions#progressive-enhancement-use-enhance), it will be called with an object containing various useful properties.

In SvelteKit 1, those properties included `form` and `data`. These were deprecated some time ago in favour of `formElement` and `formData`, and have been removed altogether in SvelteKit 2.

## Forms containing file inputs must use `multipart/form-data`

If a form contains an `<input type="file">` but does not have an `enctype="multipart/form-data"` attribute, non-JS submissions will omit the file. SvelteKit 2 will throw an error if it encounters a form like this during a `use:enhance` submission to ensure that your forms work correctly when JavaScript is not present.

## Updated dependency requirements

SvelteKit requires Node `18.13` or higher, Vite `^5.0`, vite-plugin-svelte `^3.0`, TypeScript `^5.0` and Svelte version 4 or higher. `svelte-migrate` will do the `package.json` bumps for you.
Expand Down
20 changes: 0 additions & 20 deletions packages/kit/src/exports/public.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1247,35 +1247,15 @@ export type SubmitFunction<
Failure extends Record<string, unknown> | undefined = Record<string, any>
> = (input: {
action: URL;
/**
* use `formData` instead of `data`
* @deprecated
*/
data: FormData;
formData: FormData;
/**
* use `formElement` instead of `form`
* @deprecated
*/
form: HTMLFormElement;
formElement: HTMLFormElement;
controller: AbortController;
submitter: HTMLElement | null;
cancel(): void;
}) => MaybePromise<
| void
| ((opts: {
/**
* use `formData` instead of `data`
* @deprecated
*/
data: FormData;
formData: FormData;
/**
* use `formElement` instead of `form`
* @deprecated
*/
form: HTMLFormElement;
formElement: HTMLFormElement;
action: URL;
result: ActionResult<Success, Failure>;
Expand Down
37 changes: 2 additions & 35 deletions packages/kit/src/runtime/app/forms.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,20 +49,6 @@ export function deserialize(result) {
return parsed;
}

/**
* @param {string} old_name
* @param {string} new_name
* @param {string} call_location
* @returns void
*/
function warn_on_access(old_name, new_name, call_location) {
if (!DEV) return;
// TODO 2.0: Remove this code
console.warn(
`\`${old_name}\` has been deprecated in favor of \`${new_name}\`. \`${old_name}\` will be removed in a future version. (Called from ${call_location})`
);
}

/**
* Shallow clone an element, so that we can access e.g. `form.action` without worrying
* that someone has added an `<input name="action">` (https://github.com/sveltejs/kit/issues/7593)
Expand Down Expand Up @@ -157,11 +143,9 @@ export function enhance(form_element, submit = () => {}) {
if (DEV && clone(form_element).enctype !== 'multipart/form-data') {
for (const value of form_data.values()) {
if (value instanceof File) {
// TODO 2.0: Upgrade to `throw Error`
console.warn(
'Your form contains <input type="file"> fields, but is missing the `enctype="multipart/form-data"` attribute. This will lead to inconsistent behavior between enhanced and native forms. For more details, see https://github.com/sveltejs/kit/issues/9819. This will be upgraded to an error in v2.0.'
throw new Error(
'Your form contains <input type="file"> fields, but is missing the necessary `enctype="multipart/form-data"` attribute. This will lead to inconsistent behavior between enhanced and native forms. For more details, see https://github.com/sveltejs/kit/issues/9819.'
);
break;
}
}
}
Expand All @@ -176,21 +160,12 @@ export function enhance(form_element, submit = () => {}) {
let cancelled = false;
const cancel = () => (cancelled = true);

// TODO 2.0: Remove `data` and `form`
const callback =
(await submit({
action,
cancel,
controller,
get data() {
warn_on_access('data', 'formData', 'use:enhance submit function');
return form_data;
},
formData: form_data,
get form() {
warn_on_access('form', 'formElement', 'use:enhance submit function');
return form_element;
},
formElement: form_element,
submitter: event.submitter
})) ?? fallback_callback;
Expand Down Expand Up @@ -220,15 +195,7 @@ export function enhance(form_element, submit = () => {}) {

callback({
action,
get data() {
warn_on_access('data', 'formData', 'callback returned from use:enhance submit function');
return form_data;
},
formData: form_data,
get form() {
warn_on_access('form', 'formElement', 'callback returned from use:enhance submit function');
return form_element;
},
formElement: form_element,
update: (opts) =>
fallback_callback({
Expand Down

This file was deleted.

This file was deleted.

58 changes: 5 additions & 53 deletions packages/kit/test/apps/basics/test/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -918,69 +918,21 @@ test.describe('Actions', () => {
expect(preSubmitContent).not.toBe(postSubmitContent);
});

test('Submitting a form with a file input but no enctype="multipart/form-data" logs a warning', async ({
test('Submitting a form with a file input but no enctype="multipart/form-data" throws an error', async ({
page,
javaScriptEnabled
}) => {
test.skip(!javaScriptEnabled, 'Skip when JavaScript is disabled');
test.skip(!process.env.DEV, 'Skip when not in dev mode');
await page.goto('/actions/file-without-enctype');
const log_promise = page.waitForEvent('console');
const error_promise = page.waitForEvent('pageerror');
await page.click('button');
const log = await log_promise;
expect(log.text()).toBe(
'Your form contains <input type="file"> fields, but is missing the `enctype="multipart/form-data"` attribute. This will lead to inconsistent behavior between enhanced and native forms. For more details, see https://github.com/sveltejs/kit/issues/9819. This will be upgraded to an error in v2.0.'
const error = await error_promise;
expect(error.message).toBe(
'Your form contains <input type="file"> fields, but is missing the necessary `enctype="multipart/form-data"` attribute. This will lead to inconsistent behavior between enhanced and native forms. For more details, see https://github.com/sveltejs/kit/issues/9819.'
);
});

test('Accessing v2 deprecated properties results in a warning log', async ({
page,
javaScriptEnabled
}) => {
test.skip(!javaScriptEnabled, 'skip when js is disabled');
test.skip(!process.env.DEV, 'skip when not in dev mode');
await page.goto('/actions/enhance/old-property-access');

for (const { id, old_name, new_name, call_location } of [
{
id: 'access-form-in-submit',
old_name: 'form',
new_name: 'formElement',
call_location: 'use:enhance submit function'
},
{
id: 'access-form-in-callback',
old_name: 'form',
new_name: 'formElement',
call_location: 'callback returned from use:enhance submit function'
},
{
id: 'access-data-in-submit',
old_name: 'data',
new_name: 'formData',
call_location: 'use:enhance submit function'
},
{
id: 'access-data-in-callback',
old_name: 'data',
new_name: 'formData',
call_location: 'callback returned from use:enhance submit function'
}
]) {
await test.step(id, async () => {
const log_promise = page.waitForEvent('console');
const button = page.locator(`#${id}`);
await button.click();
await expect(button).toHaveAttribute('data-processed', 'true');
const log = await log_promise;
expect(log.text()).toBe(
`\`${old_name}\` has been deprecated in favor of \`${new_name}\`. \`${old_name}\` will be removed in a future version. (Called from ${call_location})`
);
expect(log.type()).toBe('warning');
});
}
});

test('Error props are returned', async ({ page, javaScriptEnabled }) => {
await page.goto('/actions/form-errors');
await page.click('button');
Expand Down
20 changes: 0 additions & 20 deletions packages/kit/types/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1229,35 +1229,15 @@ declare module '@sveltejs/kit' {
Failure extends Record<string, unknown> | undefined = Record<string, any>
> = (input: {
action: URL;
/**
* use `formData` instead of `data`
* @deprecated
*/
data: FormData;
formData: FormData;
/**
* use `formElement` instead of `form`
* @deprecated
*/
form: HTMLFormElement;
formElement: HTMLFormElement;
controller: AbortController;
submitter: HTMLElement | null;
cancel(): void;
}) => MaybePromise<
| void
| ((opts: {
/**
* use `formData` instead of `data`
* @deprecated
*/
data: FormData;
formData: FormData;
/**
* use `formElement` instead of `form`
* @deprecated
*/
form: HTMLFormElement;
formElement: HTMLFormElement;
action: URL;
result: ActionResult<Success, Failure>;
Expand Down

0 comments on commit 4dc6aec

Please sign in to comment.