From bdc45fdf7f4ee9ddfcb0dc0e875ebaca68a4f0df Mon Sep 17 00:00:00 2001
From: Rich Harris <rich.harris@vercel.com>
Date: Tue, 9 Jul 2024 15:43:57 -0700
Subject: [PATCH] breaking: rename `legacy.componentApi` to
 `compatibility.componentApi` (#12370)

* breaking: rename `legacy.componentApi` to `compatibility.legacyComponent`

closes #12112

* fix

* rename to compatibility.componentApi

* update changeset

* tidy up

* default to 5

---------

Co-authored-by: Simon Holthausen <simon.holthausen@vercel.com>
---
 .changeset/sour-tomatoes-knock.md                    |  5 +++++
 packages/svelte/messages/client-errors/errors.md     |  2 +-
 .../svelte/src/compiler/phases/2-analyze/index.js    |  2 +-
 .../phases/3-transform/client/transform-client.js    |  4 ++--
 .../phases/3-transform/server/transform-server.js    |  2 +-
 packages/svelte/src/compiler/types/index.d.ts        | 10 +++++-----
 packages/svelte/src/compiler/validate-options.js     |  8 ++++++--
 .../svelte/src/internal/client/dom/legacy/misc.js    |  4 ++--
 packages/svelte/src/internal/client/errors.js        |  4 ++--
 .../binding-this-legacy-component-api/_config.js     |  4 ++--
 .../samples/legacy-class-transformation/_config.js   |  4 ++--
 packages/svelte/types/index.d.ts                     | 12 ++++++------
 .../docs/content/03-appendix/02-breaking-changes.md  |  6 ++++--
 sites/svelte-5-preview/svelte.config.js              |  4 ++--
 14 files changed, 41 insertions(+), 30 deletions(-)
 create mode 100644 .changeset/sour-tomatoes-knock.md

diff --git a/.changeset/sour-tomatoes-knock.md b/.changeset/sour-tomatoes-knock.md
new file mode 100644
index 0000000000..069b7d3f88
--- /dev/null
+++ b/.changeset/sour-tomatoes-knock.md
@@ -0,0 +1,5 @@
+---
+'svelte': patch
+---
+
+breaking: rename `legacy.componentApi` to `compatibility.componentApi`
diff --git a/packages/svelte/messages/client-errors/errors.md b/packages/svelte/messages/client-errors/errors.md
index 3e64b90899..e51242d8f6 100644
--- a/packages/svelte/messages/client-errors/errors.md
+++ b/packages/svelte/messages/client-errors/errors.md
@@ -16,7 +16,7 @@
 
 ## component_api_invalid_new
 
-> Attempted to instantiate %component% with `new %name%`, which is no longer valid in Svelte 5. If this component is not under your control, set the `legacy.componentApi` compiler option to keep it working. See https://svelte-5-preview.vercel.app/docs/breaking-changes#components-are-no-longer-classes for more information
+> Attempted to instantiate %component% with `new %name%`, which is no longer valid in Svelte 5. If this component is not under your control, set the `compatibility.componentApi` compiler option to `4` to keep it working. See https://svelte-5-preview.vercel.app/docs/breaking-changes#components-are-no-longer-classes for more information
 
 ## each_key_duplicate
 
diff --git a/packages/svelte/src/compiler/phases/2-analyze/index.js b/packages/svelte/src/compiler/phases/2-analyze/index.js
index aab31869cd..5a44c30d17 100644
--- a/packages/svelte/src/compiler/phases/2-analyze/index.js
+++ b/packages/svelte/src/compiler/phases/2-analyze/index.js
@@ -383,7 +383,7 @@ export function analyze_component(root, source, options) {
 			? true
 			: (runes ? false : !!options.accessors) ||
 				// because $set method needs accessors
-				!!options.legacy?.componentApi,
+				options.compatibility?.componentApi === 4,
 		reactive_statements: new Map(),
 		binding_groups: new Map(),
 		slot_names: new Map(),
diff --git a/packages/svelte/src/compiler/phases/3-transform/client/transform-client.js b/packages/svelte/src/compiler/phases/3-transform/client/transform-client.js
index a8438c0aca..d8ceb45372 100644
--- a/packages/svelte/src/compiler/phases/3-transform/client/transform-client.js
+++ b/packages/svelte/src/compiler/phases/3-transform/client/transform-client.js
@@ -283,7 +283,7 @@ export function client_component(source, analysis, options) {
 		}
 	}
 
-	if (options.legacy.componentApi) {
+	if (options.compatibility.componentApi === 4) {
 		component_returned_object.push(
 			b.init('$set', b.id('$.update_legacy_props')),
 			b.init(
@@ -474,7 +474,7 @@ export function client_component(source, analysis, options) {
 		body.unshift(b.imports([], 'svelte/internal/disclose-version'));
 	}
 
-	if (options.legacy.componentApi) {
+	if (options.compatibility.componentApi === 4) {
 		body.unshift(b.imports([['createClassComponent', '$$_createClassComponent']], 'svelte/legacy'));
 		component_block.body.unshift(
 			b.if(
diff --git a/packages/svelte/src/compiler/phases/3-transform/server/transform-server.js b/packages/svelte/src/compiler/phases/3-transform/server/transform-server.js
index 9359f967df..969a88418e 100644
--- a/packages/svelte/src/compiler/phases/3-transform/server/transform-server.js
+++ b/packages/svelte/src/compiler/phases/3-transform/server/transform-server.js
@@ -2172,7 +2172,7 @@ export function server_component(analysis, options) {
 		should_inject_props ? [b.id('$$payload'), b.id('$$props')] : [b.id('$$payload')],
 		component_block
 	);
-	if (options.legacy.componentApi) {
+	if (options.compatibility.componentApi === 4) {
 		body.unshift(b.imports([['render', '$$_render']], 'svelte/server'));
 		body.push(
 			component_function,
diff --git a/packages/svelte/src/compiler/types/index.d.ts b/packages/svelte/src/compiler/types/index.d.ts
index 9574491756..dfcd6467a3 100644
--- a/packages/svelte/src/compiler/types/index.d.ts
+++ b/packages/svelte/src/compiler/types/index.d.ts
@@ -141,14 +141,14 @@ export interface CompileOptions extends ModuleCompileOptions {
 	/**
 	 * @deprecated Use these only as a temporary solution before migrating your code
 	 */
-	legacy?: {
+	compatibility?: {
 		/**
 		 * Applies a transformation so that the default export of Svelte files can still be instantiated the same way as in Svelte 4 —
 		 * as a class when compiling for the browser (as though using `createClassComponent(MyComponent, {...})` from `svelte/legacy`)
 		 * or as an object with a `.render(...)` method when compiling for the server
-		 * @default false
+		 * @default 5
 		 */
-		componentApi?: boolean;
+		componentApi?: 4 | 5;
 	};
 	/**
 	 * An initial sourcemap that will be merged into the final output sourcemap.
@@ -226,7 +226,7 @@ export type ValidatedCompileOptions = ValidatedModuleCompileOptions &
 		Required<CompileOptions>,
 		| keyof ModuleCompileOptions
 		| 'name'
-		| 'legacy'
+		| 'compatibility'
 		| 'outputFilename'
 		| 'cssOutputFilename'
 		| 'sourcemap'
@@ -236,7 +236,7 @@ export type ValidatedCompileOptions = ValidatedModuleCompileOptions &
 		outputFilename: CompileOptions['outputFilename'];
 		cssOutputFilename: CompileOptions['cssOutputFilename'];
 		sourcemap: CompileOptions['sourcemap'];
-		legacy: Required<Required<CompileOptions>['legacy']>;
+		compatibility: Required<Required<CompileOptions>['compatibility']>;
 		runes: CompileOptions['runes'];
 		customElementOptions: SvelteOptions['customElement'];
 		hmr: CompileOptions['hmr'];
diff --git a/packages/svelte/src/compiler/validate-options.js b/packages/svelte/src/compiler/validate-options.js
index bd997a36ab..b90f9427b1 100644
--- a/packages/svelte/src/compiler/validate-options.js
+++ b/packages/svelte/src/compiler/validate-options.js
@@ -78,8 +78,12 @@ export const validate_component_options =
 
 			immutable: deprecate(w.options_deprecated_immutable, boolean(false)),
 
-			legacy: object({
-				componentApi: boolean(false)
+			legacy: removed(
+				'The legacy option has been removed. If you are using this because of legacy.componentApi, use compatibility.componentApi instead'
+			),
+
+			compatibility: object({
+				componentApi: list([4, 5], 5)
 			}),
 
 			loopGuardTimeout: warn_removed(w.options_removed_loop_guard_timeout),
diff --git a/packages/svelte/src/internal/client/dom/legacy/misc.js b/packages/svelte/src/internal/client/dom/legacy/misc.js
index b9ef9fc53a..6048b45d30 100644
--- a/packages/svelte/src/internal/client/dom/legacy/misc.js
+++ b/packages/svelte/src/internal/client/dom/legacy/misc.js
@@ -41,7 +41,7 @@ export function bubble_event($$props, event) {
 }
 
 /**
- * Used to simulate `$on` on a component instance when `legacy.componentApi` is `true`
+ * Used to simulate `$on` on a component instance when `compatibility.componentApi === 4`
  * @param {Record<string, any>} $$props
  * @param {string} event_name
  * @param {Function} event_callback
@@ -53,7 +53,7 @@ export function add_legacy_event_listener($$props, event_name, event_callback) {
 }
 
 /**
- * Used to simulate `$set` on a component instance when `legacy.componentApi` is `true`.
+ * Used to simulate `$set` on a component instance when `compatibility.componentApi === 4`.
  * Needs component accessors so that it can call the setter of the prop. Therefore doesn't
  * work for updating props in `$$props` or `$$restProps`.
  * @this {Record<string, any>}
diff --git a/packages/svelte/src/internal/client/errors.js b/packages/svelte/src/internal/client/errors.js
index ba41a560c3..f7ab6597af 100644
--- a/packages/svelte/src/internal/client/errors.js
+++ b/packages/svelte/src/internal/client/errors.js
@@ -76,14 +76,14 @@ export function component_api_changed(parent, method, component) {
 }
 
 /**
- * Attempted to instantiate %component% with `new %name%`, which is no longer valid in Svelte 5. If this component is not under your control, set the `legacy.componentApi` compiler option to keep it working. See https://svelte-5-preview.vercel.app/docs/breaking-changes#components-are-no-longer-classes for more information
+ * Attempted to instantiate %component% with `new %name%`, which is no longer valid in Svelte 5. If this component is not under your control, set the `compatibility.componentApi` compiler option to `4` to keep it working. See https://svelte-5-preview.vercel.app/docs/breaking-changes#components-are-no-longer-classes for more information
  * @param {string} component
  * @param {string} name
  * @returns {never}
  */
 export function component_api_invalid_new(component, name) {
 	if (DEV) {
-		const error = new Error(`component_api_invalid_new\nAttempted to instantiate ${component} with \`new ${name}\`, which is no longer valid in Svelte 5. If this component is not under your control, set the \`legacy.componentApi\` compiler option to keep it working. See https://svelte-5-preview.vercel.app/docs/breaking-changes#components-are-no-longer-classes for more information`);
+		const error = new Error(`component_api_invalid_new\nAttempted to instantiate ${component} with \`new ${name}\`, which is no longer valid in Svelte 5. If this component is not under your control, set the \`compatibility.componentApi\` compiler option to \`4\` to keep it working. See https://svelte-5-preview.vercel.app/docs/breaking-changes#components-are-no-longer-classes for more information`);
 
 		error.name = 'Svelte error';
 		throw error;
diff --git a/packages/svelte/tests/runtime-legacy/samples/binding-this-legacy-component-api/_config.js b/packages/svelte/tests/runtime-legacy/samples/binding-this-legacy-component-api/_config.js
index a4ab247250..c3ba255930 100644
--- a/packages/svelte/tests/runtime-legacy/samples/binding-this-legacy-component-api/_config.js
+++ b/packages/svelte/tests/runtime-legacy/samples/binding-this-legacy-component-api/_config.js
@@ -3,8 +3,8 @@ import { test } from '../../test';
 
 export default test({
 	compileOptions: {
-		legacy: {
-			componentApi: true
+		compatibility: {
+			componentApi: 4
 		}
 	},
 	html: '<button>0</button>',
diff --git a/packages/svelte/tests/runtime-runes/samples/legacy-class-transformation/_config.js b/packages/svelte/tests/runtime-runes/samples/legacy-class-transformation/_config.js
index e271e4f9b2..95c57cc255 100644
--- a/packages/svelte/tests/runtime-runes/samples/legacy-class-transformation/_config.js
+++ b/packages/svelte/tests/runtime-runes/samples/legacy-class-transformation/_config.js
@@ -2,8 +2,8 @@ import { test } from '../../test';
 
 export default test({
 	compileOptions: {
-		legacy: {
-			componentApi: true
+		compatibility: {
+			componentApi: 4
 		}
 	},
 
diff --git a/packages/svelte/types/index.d.ts b/packages/svelte/types/index.d.ts
index 5055799a9d..85349af2c4 100644
--- a/packages/svelte/types/index.d.ts
+++ b/packages/svelte/types/index.d.ts
@@ -802,14 +802,14 @@ declare module 'svelte/compiler' {
 		/**
 		 * @deprecated Use these only as a temporary solution before migrating your code
 		 */
-		legacy?: {
+		compatibility?: {
 			/**
 			 * Applies a transformation so that the default export of Svelte files can still be instantiated the same way as in Svelte 4 —
 			 * as a class when compiling for the browser (as though using `createClassComponent(MyComponent, {...})` from `svelte/legacy`)
 			 * or as an object with a `.render(...)` method when compiling for the server
-			 * @default false
+			 * @default 5
 			 */
-			componentApi?: boolean;
+			componentApi?: 4 | 5;
 		};
 		/**
 		 * An initial sourcemap that will be merged into the final output sourcemap.
@@ -2610,14 +2610,14 @@ declare module 'svelte/types/compiler/interfaces' {
 		/**
 		 * @deprecated Use these only as a temporary solution before migrating your code
 		 */
-		legacy?: {
+		compatibility?: {
 			/**
 			 * Applies a transformation so that the default export of Svelte files can still be instantiated the same way as in Svelte 4 —
 			 * as a class when compiling for the browser (as though using `createClassComponent(MyComponent, {...})` from `svelte/legacy`)
 			 * or as an object with a `.render(...)` method when compiling for the server
-			 * @default false
+			 * @default 5
 			 */
-			componentApi?: boolean;
+			componentApi?: 4 | 5;
 		};
 		/**
 		 * An initial sourcemap that will be merged into the final output sourcemap.
diff --git a/sites/svelte-5-preview/src/routes/docs/content/03-appendix/02-breaking-changes.md b/sites/svelte-5-preview/src/routes/docs/content/03-appendix/02-breaking-changes.md
index 8de2bfbfe4..65300276ea 100644
--- a/sites/svelte-5-preview/src/routes/docs/content/03-appendix/02-breaking-changes.md
+++ b/sites/svelte-5-preview/src/routes/docs/content/03-appendix/02-breaking-changes.md
@@ -70,13 +70,15 @@ import App from './App.svelte'
 export default app;
 ```
 
-If this component is not under your control, you can use the `legacy.componentApi` compiler option for auto-applied backwards compatibility, which means code using `new Component(...)` keeps working without adjustments (note that this adds a bit of overhead to each component). This will also add `$set` and `$on` methods for all component instances you get through `bind:this`.
+If this component is not under your control, you can use the `compatibility.componentApi` compiler option for auto-applied backwards compatibility, which means code using `new Component(...)` keeps working without adjustments (note that this adds a bit of overhead to each component). This will also add `$set` and `$on` methods for all component instances you get through `bind:this`.
 
 ```js
 /// svelte.config.js
 export default {
 	compilerOptions: {
-		legacy: { componentApi: true }
+		compatibility: {
+			componentApi: 4
+		}
 	}
 };
 ```
diff --git a/sites/svelte-5-preview/svelte.config.js b/sites/svelte-5-preview/svelte.config.js
index de3e5cfa05..d2d2d8019d 100644
--- a/sites/svelte-5-preview/svelte.config.js
+++ b/sites/svelte-5-preview/svelte.config.js
@@ -3,9 +3,9 @@ import adapter from '@sveltejs/adapter-vercel';
 /** @type {import('@sveltejs/kit').Config} */
 export default {
 	compilerOptions: {
-		legacy: {
+		compatibility: {
 			// site-kit manually instantiates components inside an action
-			componentApi: true
+			componentApi: 4
 		}
 	},
 	kit: {