From fe6e4e2b05b8099d27483b7b5f14c1d757c934d3 Mon Sep 17 00:00:00 2001 From: Craig Jennings <1683368+craig-jennings@users.noreply.github.com> Date: Mon, 22 Apr 2024 13:16:02 -0500 Subject: [PATCH 01/27] fix: avoid hoisting error by using 'let' instead of 'var' (#11291) Fixes #11284 --- .changeset/plenty-starfishes-dress.md | 5 +++++ .../src/internal/client/dom/elements/attributes.js | 7 ++++--- .../samples/event-attribute-after-spread/_config.js | 13 +++++++++++++ .../event-attribute-after-spread/main.svelte | 7 +++++++ 4 files changed, 29 insertions(+), 3 deletions(-) create mode 100644 .changeset/plenty-starfishes-dress.md create mode 100644 packages/svelte/tests/runtime-runes/samples/event-attribute-after-spread/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/event-attribute-after-spread/main.svelte diff --git a/.changeset/plenty-starfishes-dress.md b/.changeset/plenty-starfishes-dress.md new file mode 100644 index 0000000000..38b2201e9e --- /dev/null +++ b/.changeset/plenty-starfishes-dress.md @@ -0,0 +1,5 @@ +--- +"svelte": patch +--- + +fix: avoid hoisting error by using 'let' instead of 'var' diff --git a/packages/svelte/src/internal/client/dom/elements/attributes.js b/packages/svelte/src/internal/client/dom/elements/attributes.js index 67e336744f..58519d9baa 100644 --- a/packages/svelte/src/internal/client/dom/elements/attributes.js +++ b/packages/svelte/src/internal/client/dom/elements/attributes.js @@ -111,7 +111,8 @@ export function set_attributes(element, prev, next, lowercase_attributes, css_ha var events = []; for (key in next) { - var value = next[key]; + // let instead of var because referenced in a closure + let value = next[key]; if (value === prev?.[key]) continue; var prefix = key[0] + key[1]; // this is faster than key.slice(0, 2) @@ -119,8 +120,8 @@ export function set_attributes(element, prev, next, lowercase_attributes, css_ha if (prefix === 'on') { /** @type {{ capture?: true }} */ - var opts = {}; - var event_name = key.slice(2); + const opts = {}; + let event_name = key.slice(2); var delegated = DelegatedEvents.includes(event_name); if ( diff --git a/packages/svelte/tests/runtime-runes/samples/event-attribute-after-spread/_config.js b/packages/svelte/tests/runtime-runes/samples/event-attribute-after-spread/_config.js new file mode 100644 index 0000000000..09975947e6 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/event-attribute-after-spread/_config.js @@ -0,0 +1,13 @@ +import { test } from '../../test'; + +export default test({ + async test({ assert, target }) { + const input = target.querySelector('input'); + + input?.dispatchEvent(new Event('input', { bubbles: true })); + + await Promise.resolve(); + + assert.htmlEqual(target.innerHTML, 'true '); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/event-attribute-after-spread/main.svelte b/packages/svelte/tests/runtime-runes/samples/event-attribute-after-spread/main.svelte new file mode 100644 index 0000000000..c854071f3e --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/event-attribute-after-spread/main.svelte @@ -0,0 +1,7 @@ + + +{changed} + (changed = true)} class="hello" /> From e3c85897374a76812191464e7eca869e0aec3082 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 22 Apr 2024 16:46:28 -0400 Subject: [PATCH 02/27] Version Packages (next) (#11292) Co-authored-by: github-actions[bot] --- .changeset/pre.json | 1 + packages/svelte/CHANGELOG.md | 6 ++++++ packages/svelte/package.json | 2 +- packages/svelte/src/version.js | 2 +- 4 files changed, 9 insertions(+), 2 deletions(-) diff --git a/.changeset/pre.json b/.changeset/pre.json index 6a3cec722e..91b0d77715 100644 --- a/.changeset/pre.json +++ b/.changeset/pre.json @@ -262,6 +262,7 @@ "orange-yaks-protect", "pink-bikes-agree", "pink-mayflies-tie", + "plenty-starfishes-dress", "polite-dolphins-care", "polite-pumpkins-guess", "polite-ravens-study", diff --git a/packages/svelte/CHANGELOG.md b/packages/svelte/CHANGELOG.md index a5a654eedb..109238e281 100644 --- a/packages/svelte/CHANGELOG.md +++ b/packages/svelte/CHANGELOG.md @@ -1,5 +1,11 @@ # svelte +## 5.0.0-next.112 + +### Patch Changes + +- fix: avoid hoisting error by using 'let' instead of 'var' ([#11291](https://github.com/sveltejs/svelte/pull/11291)) + ## 5.0.0-next.111 ### Patch Changes diff --git a/packages/svelte/package.json b/packages/svelte/package.json index 709d74dd35..f8bee6890b 100644 --- a/packages/svelte/package.json +++ b/packages/svelte/package.json @@ -2,7 +2,7 @@ "name": "svelte", "description": "Cybernetically enhanced web apps", "license": "MIT", - "version": "5.0.0-next.111", + "version": "5.0.0-next.112", "type": "module", "types": "./types/index.d.ts", "engines": { diff --git a/packages/svelte/src/version.js b/packages/svelte/src/version.js index b13963eb4d..d17a9f204e 100644 --- a/packages/svelte/src/version.js +++ b/packages/svelte/src/version.js @@ -6,5 +6,5 @@ * https://svelte.dev/docs/svelte-compiler#svelte-version * @type {string} */ -export const VERSION = '5.0.0-next.111'; +export const VERSION = '5.0.0-next.112'; export const PUBLIC_VERSION = '5'; From 2d378bb76284d19ec843482086cf5d631ba238a7 Mon Sep 17 00:00:00 2001 From: Simon H <5968653+dummdidumm@users.noreply.github.com> Date: Tue, 23 Apr 2024 15:29:02 +0200 Subject: [PATCH 03/27] breaking: disallow binding to component exports in runes mode (#11238) * breaking: disallow binding to component exports in runes mode Svelte 4 allowed you to have `export const foo = ..` in component A and then do ``. This is confusing because it's not clear whether the binding is for a property or an export, and we have to sanitize rest props from the export bindings. This PR therefore introduces a breaking change in runes mode: You cannot bind to these exports anymore. Instead use `` and then do `a.foo` - makes things easier to reason about. * Update sites/svelte-5-preview/src/routes/docs/content/03-appendix/02-breaking-changes.md Co-authored-by: Rich Harris * tweak messages * fix tests * use component.name * oops --------- Co-authored-by: Rich Harris --- .changeset/beige-seas-share.md | 5 +++++ .../3-transform/client/transform-client.js | 14 ++++++++++-- .../client/visitors/javascript-runes.js | 3 ++- .../3-transform/server/transform-server.js | 22 ++----------------- .../svelte/src/internal/client/validate.js | 22 ++++++++++++++----- .../samples/export-binding/Counter.svelte | 12 ---------- .../samples/export-binding/_config.js | 15 ++++--------- .../export-binding/counter/index.svelte | 8 +++++++ .../samples/export-binding/main.svelte | 4 ++-- .../props-not-bindable-spread/_config.js | 5 +++-- .../samples/props-not-bindable/_config.js | 5 +++-- .../03-appendix/02-breaking-changes.md | 4 ++-- 12 files changed, 59 insertions(+), 60 deletions(-) create mode 100644 .changeset/beige-seas-share.md delete mode 100644 packages/svelte/tests/runtime-runes/samples/export-binding/Counter.svelte create mode 100644 packages/svelte/tests/runtime-runes/samples/export-binding/counter/index.svelte diff --git a/.changeset/beige-seas-share.md b/.changeset/beige-seas-share.md new file mode 100644 index 0000000000..3758385c55 --- /dev/null +++ b/.changeset/beige-seas-share.md @@ -0,0 +1,5 @@ +--- +"svelte": patch +--- + +breaking: disallow binding to component exports in runes mode 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 f1de213a9b..70f981d3e6 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 @@ -255,14 +255,24 @@ export function client_component(source, analysis, options) { ); if (analysis.runes && options.dev) { - const bindable = analysis.exports.map(({ name, alias }) => b.literal(alias ?? name)); + const exports = analysis.exports.map(({ name, alias }) => b.literal(alias ?? name)); + /** @type {import('estree').Literal[]} */ + const bindable = []; for (const [name, binding] of properties) { if (binding.kind === 'bindable_prop') { bindable.push(b.literal(binding.prop_alias ?? name)); } } instance.body.unshift( - b.stmt(b.call('$.validate_prop_bindings', b.id('$$props'), b.array(bindable))) + b.stmt( + b.call( + '$.validate_prop_bindings', + b.id('$$props'), + b.array(bindable), + b.array(exports), + b.id(`${analysis.name}`) + ) + ) ); } diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/javascript-runes.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/javascript-runes.js index 7646ae78b7..5c5b177917 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/javascript-runes.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/javascript-runes.js @@ -222,7 +222,8 @@ export const javascript_visitors_runes = { if (rune === '$props') { assert.equal(declarator.id.type, 'ObjectPattern'); - const seen = state.analysis.exports.map(({ name, alias }) => alias ?? name); + /** @type {string[]} */ + const seen = []; for (const property of declarator.id.properties) { if (property.type === 'Property') { 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 d5402351c7..4f631b8c70 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 @@ -692,8 +692,7 @@ const javascript_visitors_runes = { } if (rune === '$props') { - // remove $bindable() from props declaration and handle rest props - let uses_rest_props = false; + // remove $bindable() from props declaration const id = walk(declarator.id, null, { AssignmentPattern(node) { if ( @@ -705,26 +704,9 @@ const javascript_visitors_runes = { : b.id('undefined'); return b.assignment_pattern(node.left, right); } - }, - RestElement(node, { path }) { - if (path.at(-1) === declarator.id) { - uses_rest_props = true; - } } }); - - const exports = /** @type {import('../../types').ComponentAnalysis} */ ( - state.analysis - ).exports.map(({ name, alias }) => b.literal(alias ?? name)); - - declarations.push( - b.declarator( - id, - uses_rest_props && exports.length > 0 - ? b.call('$.rest_props', b.id('$$props'), b.array(exports)) - : b.id('$$props') - ) - ); + declarations.push(b.declarator(id, b.id('$$props'))); continue; } diff --git a/packages/svelte/src/internal/client/validate.js b/packages/svelte/src/internal/client/validate.js index add59ebe82..b2dfee7ab1 100644 --- a/packages/svelte/src/internal/client/validate.js +++ b/packages/svelte/src/internal/client/validate.js @@ -85,16 +85,26 @@ export function loop_guard(timeout) { /** * @param {Record} $$props * @param {string[]} bindable + * @param {string[]} exports + * @param {Function & { filename: string }} component */ -export function validate_prop_bindings($$props, bindable) { +export function validate_prop_bindings($$props, bindable, exports, component) { for (const key in $$props) { - if (!bindable.includes(key)) { - var setter = get_descriptor($$props, key)?.set; + var setter = get_descriptor($$props, key)?.set; + var name = component.name; - if (setter) { + if (setter) { + if (exports.includes(key)) { throw new Error( - `Cannot use bind:${key} on this component because the property was not declared as bindable. ` + - `To mark a property as bindable, use the $bindable() rune like this: \`let { ${key} = $bindable() } = $props()\`` + `Component ${component.filename} has an export named ${key} that a consumer component is trying to access using bind:${key}, which is disallowed. ` + + `Instead, use bind:this (e.g. <${name} bind:this={component} />) ` + + `and then access the property on the bound component instance (e.g. component.${key}).` + ); + } + if (!bindable.includes(key)) { + throw new Error( + `A component is binding to property ${key} of ${name}.svelte (i.e. <${name} bind:${key} />). This is disallowed because the property was not declared as bindable inside ${component.filename}. ` + + `To mark a property as bindable, use the $bindable() rune in ${name}.svelte like this: \`let { ${key} = $bindable() } = $props()\`` ); } } diff --git a/packages/svelte/tests/runtime-runes/samples/export-binding/Counter.svelte b/packages/svelte/tests/runtime-runes/samples/export-binding/Counter.svelte deleted file mode 100644 index 94c822f31f..0000000000 --- a/packages/svelte/tests/runtime-runes/samples/export-binding/Counter.svelte +++ /dev/null @@ -1,12 +0,0 @@ - - - -{Object.keys(rest).length} - -{count} diff --git a/packages/svelte/tests/runtime-runes/samples/export-binding/_config.js b/packages/svelte/tests/runtime-runes/samples/export-binding/_config.js index aa41757bd1..0f64998059 100644 --- a/packages/svelte/tests/runtime-runes/samples/export-binding/_config.js +++ b/packages/svelte/tests/runtime-runes/samples/export-binding/_config.js @@ -2,16 +2,9 @@ import { test } from '../../test'; export default test({ compileOptions: { - dev: true // to ensure we don't throw a false-positive "cannot bind to this" error + dev: true // to ensure we we catch the error }, - html: `0 0 `, - - async test({ assert, target }) { - const btn = target.querySelector('button'); - - btn?.click(); - await Promise.resolve(); - - assert.htmlEqual(target.innerHTML, `0 1 `); - } + error: + 'Component .../export-binding/counter/index.svelte has an export named increment that a consumer component is trying to access using bind:increment, which is disallowed. ' + + 'Instead, use bind:this (e.g. ) and then access the property on the bound component instance (e.g. component.increment).' }); diff --git a/packages/svelte/tests/runtime-runes/samples/export-binding/counter/index.svelte b/packages/svelte/tests/runtime-runes/samples/export-binding/counter/index.svelte new file mode 100644 index 0000000000..14e0de961b --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/export-binding/counter/index.svelte @@ -0,0 +1,8 @@ + + +{count} diff --git a/packages/svelte/tests/runtime-runes/samples/export-binding/main.svelte b/packages/svelte/tests/runtime-runes/samples/export-binding/main.svelte index 89f0aff9a0..4ad1684701 100644 --- a/packages/svelte/tests/runtime-runes/samples/export-binding/main.svelte +++ b/packages/svelte/tests/runtime-runes/samples/export-binding/main.svelte @@ -1,7 +1,7 @@ - \ No newline at end of file + diff --git a/packages/svelte/tests/runtime-runes/samples/props-not-bindable-spread/_config.js b/packages/svelte/tests/runtime-runes/samples/props-not-bindable-spread/_config.js index 4c3fdf5bd9..fc6b46d488 100644 --- a/packages/svelte/tests/runtime-runes/samples/props-not-bindable-spread/_config.js +++ b/packages/svelte/tests/runtime-runes/samples/props-not-bindable-spread/_config.js @@ -5,7 +5,8 @@ export default test({ dev: true }, error: - 'Cannot use bind:count on this component because the property was not declared as bindable. ' + - 'To mark a property as bindable, use the $bindable() rune like this: `let { count = $bindable() } = $props()`', + 'A component is binding to property count of Counter.svelte (i.e. ). This is disallowed because the property was ' + + 'not declared as bindable inside .../samples/props-not-bindable-spread/Counter.svelte. To mark a property as bindable, use the $bindable() rune ' + + 'in Counter.svelte like this: `let { count = $bindable() } = $props()`', html: `0` }); diff --git a/packages/svelte/tests/runtime-runes/samples/props-not-bindable/_config.js b/packages/svelte/tests/runtime-runes/samples/props-not-bindable/_config.js index 4c3fdf5bd9..b3dcc7f23e 100644 --- a/packages/svelte/tests/runtime-runes/samples/props-not-bindable/_config.js +++ b/packages/svelte/tests/runtime-runes/samples/props-not-bindable/_config.js @@ -5,7 +5,8 @@ export default test({ dev: true }, error: - 'Cannot use bind:count on this component because the property was not declared as bindable. ' + - 'To mark a property as bindable, use the $bindable() rune like this: `let { count = $bindable() } = $props()`', + 'A component is binding to property count of Counter.svelte (i.e. ). This is disallowed because the property was ' + + 'not declared as bindable inside .../samples/props-not-bindable/Counter.svelte. To mark a property as bindable, use the $bindable() rune ' + + 'in Counter.svelte like this: `let { count = $bindable() } = $props()`', html: `0` }); 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 25547f72ab..43a5c7134c 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 @@ -121,9 +121,9 @@ Content inside component tags becomes a [snippet prop](/docs/snippets) called `c Some breaking changes only apply once your component is in runes mode. -### Bindings to component exports don't show up in rest props +### Bindings to component exports are not allowed -In runes mode, bindings to component exports don't show up in rest props. For example, `rest` in `let { foo, bar, ...rest } = $props();` would not contain `baz` if `baz` was defined as `export const baz = ...;` inside the component. In Svelte 4 syntax, the equivalent to `rest` would be `$$restProps`, which contains these component exports. +Exports from runes mode components cannot be bound to directly. For example, having `export const foo = ...` in component `A` and then doing `` causes an error. Use `bind:this` instead — `` — and access the export as `a.foo`. This change makes things easier to reason about, as it enforces a clear separation between props and exports. ### Bindings need to be explicitly defined using `$bindable()` From 3866887f3787a61c044448a7557a7f89489b293b Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 23 Apr 2024 09:56:58 -0400 Subject: [PATCH 04/27] Version Packages (next) (#11296) Co-authored-by: github-actions[bot] --- .changeset/pre.json | 1 + packages/svelte/CHANGELOG.md | 6 ++++++ packages/svelte/package.json | 2 +- packages/svelte/src/version.js | 2 +- 4 files changed, 9 insertions(+), 2 deletions(-) diff --git a/.changeset/pre.json b/.changeset/pre.json index 91b0d77715..300038c872 100644 --- a/.changeset/pre.json +++ b/.changeset/pre.json @@ -19,6 +19,7 @@ "beige-flies-wash", "beige-mirrors-listen", "beige-rabbits-shave", + "beige-seas-share", "big-cars-serve", "big-eggs-flash", "big-eyes-carry", diff --git a/packages/svelte/CHANGELOG.md b/packages/svelte/CHANGELOG.md index 109238e281..0e57787e43 100644 --- a/packages/svelte/CHANGELOG.md +++ b/packages/svelte/CHANGELOG.md @@ -1,5 +1,11 @@ # svelte +## 5.0.0-next.113 + +### Patch Changes + +- breaking: disallow binding to component exports in runes mode ([#11238](https://github.com/sveltejs/svelte/pull/11238)) + ## 5.0.0-next.112 ### Patch Changes diff --git a/packages/svelte/package.json b/packages/svelte/package.json index f8bee6890b..11b8c7a46c 100644 --- a/packages/svelte/package.json +++ b/packages/svelte/package.json @@ -2,7 +2,7 @@ "name": "svelte", "description": "Cybernetically enhanced web apps", "license": "MIT", - "version": "5.0.0-next.112", + "version": "5.0.0-next.113", "type": "module", "types": "./types/index.d.ts", "engines": { diff --git a/packages/svelte/src/version.js b/packages/svelte/src/version.js index d17a9f204e..81de554c27 100644 --- a/packages/svelte/src/version.js +++ b/packages/svelte/src/version.js @@ -6,5 +6,5 @@ * https://svelte.dev/docs/svelte-compiler#svelte-version * @type {string} */ -export const VERSION = '5.0.0-next.112'; +export const VERSION = '5.0.0-next.113'; export const PUBLIC_VERSION = '5'; From 73490bbb8e3c0640ac72151d7e9aa8b5bf6604d6 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Tue, 23 Apr 2024 10:39:37 -0400 Subject: [PATCH 05/27] chore: markdown errors (#11294) * rename errors.js to errors-tmp.js * start porting errors * generate markdown files for more errors * convert parse errors * convert css errors * convert special_element errors * convert runes errors * convert element errors * more * more * more * more * more * more * fix some tests * fix some tests * more tests * remove unused error * prettierignore * weird * ugh * lint --- .prettierignore | 2 + eslint.config.js | 5 +- .../messages/compile-errors/attributes.md | 59 + .../messages/compile-errors/bindings.md | 35 + .../compile-errors/compiler_options.md | 7 + .../messages/compile-errors/components.md | 3 + .../messages/compile-errors/const_tag.md | 3 + .../svelte/messages/compile-errors/css.md | 51 + .../messages/compile-errors/elements.md | 27 + .../compile-errors/legacy_reactivity.md | 3 + .../svelte/messages/compile-errors/parse.md | 147 ++ .../svelte/messages/compile-errors/runes.md | 95 + .../svelte/messages/compile-errors/slots.md | 31 + .../compile-errors/special_elements.md | 99 + .../messages/compile-errors/variables.md | 19 + .../svelte/scripts/process-messages/index.js | 213 ++ .../templates/compile-errors.js | 74 + packages/svelte/src/compiler/errors.js | 1849 ++++++++++++----- .../src/compiler/phases/1-parse/index.js | 22 +- .../compiler/phases/1-parse/read/context.js | 10 +- .../phases/1-parse/read/expression.js | 4 +- .../compiler/phases/1-parse/read/options.js | 91 +- .../compiler/phases/1-parse/read/script.js | 8 +- .../src/compiler/phases/1-parse/read/style.js | 22 +- .../compiler/phases/1-parse/state/element.js | 74 +- .../src/compiler/phases/1-parse/state/tag.js | 30 +- .../phases/2-analyze/css/css-analyze.js | 27 +- .../src/compiler/phases/2-analyze/index.js | 22 +- .../compiler/phases/2-analyze/validation.js | 267 +-- .../3-transform/client/transform-client.js | 27 +- .../phases/3-transform/client/utils.js | 3 +- .../3-transform/client/visitors/template.js | 7 +- .../3-transform/server/transform-server.js | 18 +- packages/svelte/src/compiler/phases/scope.js | 12 +- packages/svelte/src/compiler/utils/assert.js | 6 +- .../svelte/src/compiler/validate-options.js | 9 +- .../samples/attribute-empty/_config.js | 2 +- .../_config.js | 2 +- .../attribute-sequence-expression/_config.js | 2 +- .../attribute-unique-binding/_config.js | 2 +- .../attribute-unique-shorthand/_config.js | 2 +- .../samples/attribute-unique/_config.js | 2 +- .../samples/catch-before-closing/_config.js | 2 +- .../samples/catch-without-await/_config.js | 2 +- .../class-state-field-static/_config.js | 5 +- .../samples/comment-unclosed/_config.js | 4 +- .../_config.js | 2 +- .../_config.js | 2 +- .../_config.js | 2 +- .../_config.js | 2 +- .../_config.js | 2 +- .../component-slot-duplicate-error/_config.js | 2 +- .../component-slot-nested-error-2/_config.js | 2 +- .../component-slot-nested-error-3/_config.js | 2 +- .../component-slot-nested-error/_config.js | 2 +- .../css-global-block-combinator/_config.js | 2 +- .../css-global-block-declaration/_config.js | 2 +- .../css-global-block-modifier/_config.js | 2 +- .../css-global-block-multiple/_config.js | 2 +- .../compiler-errors/samples/css/_config.js | 2 +- .../dollar-binding-declaration/_config.js | 2 +- .../dollar-binding-global-js/_config.js | 4 +- .../samples/dollar-binding-global/_config.js | 4 +- .../samples/dollar-binding-import/_config.js | 2 +- .../_config.js | 4 +- .../samples/else-before-closing-2/_config.js | 2 +- .../samples/else-before-closing-3/_config.js | 2 +- .../samples/else-before-closing/_config.js | 2 +- .../else-if-before-closing-2/_config.js | 2 +- .../samples/else-if-before-closing/_config.js | 2 +- .../samples/else-if-without-if/_config.js | 2 +- .../empty-attribute-shorthand/_config.js | 2 +- .../empty-classname-binding/_config.js | 2 +- .../samples/empty-directive-name/_config.js | 2 +- .../samples/export-derived-state/_config.js | 2 +- .../samples/export-state-module/_config.js | 2 +- .../samples/export-state/_config.js | 2 +- .../samples/illegal-expression/_config.js | 2 +- .../invalid-snippet-binding/_config.js | 2 +- .../invalid-snippet-mutation/_config.js | 2 +- .../legacy-no-const-assignment/_config.js | 4 +- .../samples/legacy-no-const-update/_config.js | 4 +- .../samples/multiple-styles/_config.js | 4 +- .../samples/options-children/_config.js | 2 +- .../raw-mustaches-whitespace/_config.js | 2 +- .../render-tag-invalid-call/_config.js | 2 +- .../runes-before-after-update/_config.js | 2 +- .../samples/runes-duplicate-props/_config.js | 4 +- .../samples/runes-export-let/_config.js | 2 +- .../runes-export-named-state/_config.js | 2 +- .../runes-invalid-each-binding/_config.js | 4 +- .../runes-invalid-each-mutation/_config.js | 4 +- .../runes-no-const-assignment/_config.js | 4 +- .../samples/runes-no-const-update/_config.js | 4 +- .../runes-no-derived-assignment/_config.js | 4 +- .../runes-no-derived-binding/_config.js | 4 +- .../_config.js | 4 +- .../_config.js | 4 +- .../runes-no-derived-update/_config.js | 4 +- .../samples/runes-no-rune-each/_config.js | 4 +- .../runes-prop-export-conflict/_config.js | 2 +- .../runes-wrong-bindable-args/_config.js | 4 +- .../runes-wrong-bindable-placement/_config.js | 4 +- .../runes-wrong-derived-args/_config.js | 4 +- .../runes-wrong-derived-placement/_config.js | 5 +- .../runes-wrong-effect-args/_config.js | 4 +- .../runes-wrong-effect-placement/_config.js | 4 +- .../runes-wrong-host-placement/_config.js | 4 +- .../samples/runes-wrong-props-args/_config.js | 4 +- .../runes-wrong-props-placement/_config.js | 4 +- .../samples/runes-wrong-state-args/_config.js | 4 +- .../runes-wrong-state-placement/_config.js | 4 +- .../_config.js | 4 +- .../samples/script-unclosed-eof/_config.js | 2 +- .../samples/script-unclosed/_config.js | 4 +- .../samples/self-reference/_config.js | 4 +- .../_config.js | 4 +- .../snippet-children-conflict/_config.js | 2 +- .../store-autosub-context-module/_config.js | 4 +- .../samples/store-contextual/_config.js | 2 +- .../store-global-disallowed/_config.js | 4 +- .../_config.js | 2 +- .../samples/store-shadow-scope-2/_config.js | 2 +- .../samples/store-shadow-scope-3/_config.js | 2 +- .../samples/store-shadow-scope/_config.js | 2 +- .../_config.js | 2 +- .../samples/style-unclosed-eof/_config.js | 2 +- .../samples/style-unclosed/_config.js | 2 +- .../samples/svelte-selfdestructive/_config.js | 4 +- .../samples/then-before-closing/_config.js | 2 +- .../samples/then-without-await/_config.js | 2 +- .../_config.js | 2 +- .../unexpected-end-of-input-b/_config.js | 2 +- .../unexpected-end-of-input-c/_config.js | 2 +- .../unexpected-end-of-input-d/_config.js | 2 +- .../unexpected-end-of-input/_config.js | 4 +- .../_config.js | 2 +- .../_config.js | 2 +- .../samples/unmatched-closing-tag/_config.js | 2 +- .../samples/void-closing/_config.js | 2 +- .../samples/window-children/_config.js | 2 +- .../samples/window-duplicate/_config.js | 2 +- .../samples/window-inside-block/_config.js | 2 +- .../samples/window-inside-element/_config.js | 2 +- .../samples/action-on-component/errors.json | 2 +- .../samples/animation-duplicate/errors.json | 2 +- .../samples/animation-not-in-each/errors.json | 4 +- .../animation-not-in-keyed-each/errors.json | 4 +- .../animation-on-component/errors.json | 2 +- .../samples/animation-siblings/errors.json | 4 +- .../samples/assignment-to-const-2/errors.json | 4 +- .../samples/assignment-to-const-3/errors.json | 4 +- .../samples/assignment-to-const-4/errors.json | 4 +- .../samples/assignment-to-const-5/errors.json | 4 +- .../samples/assignment-to-const-7/errors.json | 4 +- .../samples/assignment-to-const/errors.json | 4 +- .../attribute-expected-equals/errors.json | 2 +- .../attribute-invalid-name-2/errors.json | 2 +- .../attribute-invalid-name-3/errors.json | 2 +- .../attribute-invalid-name-4/errors.json | 2 +- .../attribute-invalid-name-5/errors.json | 2 +- .../attribute-invalid-name/errors.json | 2 +- .../samples/binding-await-catch/errors.json | 14 +- .../samples/binding-await-then-2/errors.json | 14 +- .../samples/binding-await-then/errors.json | 14 +- .../samples/binding-const/errors.json | 4 +- .../binding-dimensions-svg/errors.json | 4 +- .../samples/binding-input-checked/errors.json | 4 +- .../binding-input-type-boolean/errors.json | 2 +- .../errors.json | 4 +- .../binding-invalid-on-element-2/errors.json | 4 +- .../binding-invalid-on-element/errors.json | 4 +- .../binding-invalid-value-global/errors.json | 2 +- .../samples/binding-invalid-value/errors.json | 2 +- .../samples/binding-invalid/errors.json | 4 +- .../validator/samples/binding-let/errors.json | 4 +- .../errors.json | 2 +- .../errors.json | 2 +- .../errors.json | 12 +- .../errors.json | 4 +- .../errors.json | 2 +- .../component-slot-dynamic/errors.json | 2 +- .../errors.json | 12 +- .../component-slotted-each-block/errors.json | 2 +- .../component-slotted-if-block/errors.json | 2 +- .../samples/const-tag-conflict-1/errors.json | 14 +- .../samples/const-tag-conflict-2/errors.json | 14 +- .../samples/const-tag-placement-1/errors.json | 12 +- .../samples/const-tag-placement-2/errors.json | 12 +- .../samples/const-tag-readonly-1/errors.json | 14 +- .../samples/const-tag-readonly-2/errors.json | 14 +- .../contenteditable-dynamic/errors.json | 2 +- .../contenteditable-missing/errors.json | 2 +- .../errors.json | 2 +- .../errors.json | 2 +- .../errors.json | 2 +- .../errors.json | 2 +- .../errors.json | 2 +- .../errors.json | 2 +- .../errors.json | 2 +- .../errors.json | 2 +- .../errors.json | 2 +- .../css-invalid-global-placement/errors.json | 2 +- .../css-invalid-global-selector-2/errors.json | 2 +- .../css-invalid-global-selector-3/errors.json | 2 +- .../css-invalid-global-selector-4/errors.json | 2 +- .../css-invalid-global-selector-5/errors.json | 2 +- .../css-invalid-global-selector-6/errors.json | 2 +- .../errors.json | 2 +- .../css-invalid-global-selector/errors.json | 2 +- .../errors.json | 2 +- .../samples/css-mismatched-quotes/errors.json | 2 +- .../samples/debug-invalid-args/errors.json | 2 +- .../errors.json | 2 +- .../errors.json | 2 +- .../samples/default-export/errors.json | 2 +- .../directive-non-expression/errors.json | 2 +- .../errors.json | 4 +- .../errors.json | 4 +- .../dollar-global-in-markup/errors.json | 4 +- .../dollar-global-in-script/errors.json | 4 +- .../dynamic-element-invalid-tag/errors.json | 2 +- .../dynamic-element-missing-tag/errors.json | 4 +- .../samples/dynamic-element-this/errors.json | 2 +- .../errors.json | 2 +- .../errors.json | 2 +- .../errors.json | 2 +- .../each-block-invalid-context/errors.json | 2 +- .../samples/event-attribute/errors.json | 2 +- .../errors.json | 2 +- .../errors.json | 2 +- .../event-modifiers-invalid/errors.json | 2 +- .../html-block-in-attribute/errors.json | 12 +- .../html-block-in-textarea/errors.json | 12 +- .../illegal-variable-declaration/errors.json | 4 +- .../invalid-empty-css-declaration/errors.json | 2 +- .../invalid-node-placement-2/errors.json | 2 +- .../invalid-node-placement/errors.json | 2 +- .../samples/let-directive/errors.json | 14 +- .../logic-block-in-attribute/errors.json | 12 +- .../logic-block-in-textarea/errors.json | 12 +- .../errors.json | 4 +- .../errors.json | 4 +- .../samples/namespace-invalid/errors.json | 4 +- .../samples/namespace-non-literal/errors.json | 4 +- .../reactive-declaration-cyclical/errors.json | 12 +- .../script-invalid-context/errors.json | 2 +- .../slot-attribute-invalid/errors.json | 2 +- .../errors.json | 2 +- .../svelte-fragment-placement-2/errors.json | 14 +- .../svelte-fragment-placement/errors.json | 14 +- .../svelte-head-attributes/errors.json | 14 +- .../validator/samples/tag-invalid/errors.json | 4 +- .../samples/tag-non-string/errors.json | 2 +- .../textarea-value-children/errors.json | 4 +- .../samples/title-no-attributes/errors.json | 4 +- .../samples/title-no-children/errors.json | 4 +- .../errors.json | 4 +- .../transition-duplicate-in/errors.json | 4 +- .../errors.json | 4 +- .../transition-duplicate-out/errors.json | 4 +- .../errors.json | 4 +- .../errors.json | 4 +- .../errors.json | 4 +- .../transition-on-component/errors.json | 2 +- .../errors.json | 4 +- .../window-binding-invalid/errors.json | 4 +- 267 files changed, 3027 insertions(+), 1198 deletions(-) create mode 100644 packages/svelte/messages/compile-errors/attributes.md create mode 100644 packages/svelte/messages/compile-errors/bindings.md create mode 100644 packages/svelte/messages/compile-errors/compiler_options.md create mode 100644 packages/svelte/messages/compile-errors/components.md create mode 100644 packages/svelte/messages/compile-errors/const_tag.md create mode 100644 packages/svelte/messages/compile-errors/css.md create mode 100644 packages/svelte/messages/compile-errors/elements.md create mode 100644 packages/svelte/messages/compile-errors/legacy_reactivity.md create mode 100644 packages/svelte/messages/compile-errors/parse.md create mode 100644 packages/svelte/messages/compile-errors/runes.md create mode 100644 packages/svelte/messages/compile-errors/slots.md create mode 100644 packages/svelte/messages/compile-errors/special_elements.md create mode 100644 packages/svelte/messages/compile-errors/variables.md create mode 100644 packages/svelte/scripts/process-messages/index.js create mode 100644 packages/svelte/scripts/process-messages/templates/compile-errors.js diff --git a/.prettierignore b/.prettierignore index 48d37dc02f..77bdd29cf3 100644 --- a/.prettierignore +++ b/.prettierignore @@ -2,6 +2,8 @@ packages/**/dist/*.js packages/**/build/*.js packages/**/npm/**/* packages/**/config/*.js +packages/svelte/messages/**/*.md +packages/svelte/src/compiler/errors.js packages/svelte/tests/**/*.svelte packages/svelte/tests/**/_expected* packages/svelte/tests/**/_actual* diff --git a/eslint.config.js b/eslint.config.js index 75fcc7fa06..a86f724d34 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -33,13 +33,16 @@ export default [ ignores: [ '**/*.d.ts', '**/tests', + 'packages/svelte/scripts/process-messages/templates/*.js', + 'packages/svelte/src/compiler/errors.js', 'packages/svelte/compiler/index.js', // documentation can contain invalid examples 'documentation', // contains a fork of the REPL which doesn't adhere to eslint rules 'sites/svelte-5-preview/**', // wasn't checked previously, reenable at some point - 'sites/svelte.dev/**' + 'sites/svelte.dev/**', + 'tmp/**' ] } ]; diff --git a/packages/svelte/messages/compile-errors/attributes.md b/packages/svelte/messages/compile-errors/attributes.md new file mode 100644 index 0000000000..9501080ee0 --- /dev/null +++ b/packages/svelte/messages/compile-errors/attributes.md @@ -0,0 +1,59 @@ +## empty_attribute_shorthand + +Attribute shorthand cannot be empty + +## duplicate_attribute + +Attributes need to be unique + +## invalid_event_attribute_value + +Event attribute must be a JavaScript expression, not a string + +## invalid_attribute_name + +'%name%' is not a valid attribute name + +## animation_invalid_placement + +An element that uses the `animate:` directive must be the only child of a keyed `{#each ...}` block + +## animation_missing_key + +An element that uses the `animate:` directive must be the only child of a keyed `{#each ...}` block. Did you forget to add a key to your each block? + +## animation_duplicate + +An element can only have one 'animate' directive + +## invalid_event_modifier + +Valid event modifiers are %list% + +## invalid_component_event_modifier + +Event modifiers other than 'once' can only be used on DOM elements + +## invalid_event_modifier_combination + +The '%modifier1%' and '%modifier2%' modifiers cannot be used together + +## transition_duplicate + +Cannot use multiple `%type%:` directives on a single element + +## transition_conflict + +Cannot use `%type%:` alongside existing `%existing%:` directive + +## invalid_let_directive_placement + +`let:` directive at invalid position + +## invalid_style_directive_modifier + +Invalid 'style:' modifier. Valid modifiers are: 'important' + +## invalid_sequence_expression + +Sequence expressions are not allowed as attribute/directive values in runes mode, unless wrapped in parentheses diff --git a/packages/svelte/messages/compile-errors/bindings.md b/packages/svelte/messages/compile-errors/bindings.md new file mode 100644 index 0000000000..5e88c5890a --- /dev/null +++ b/packages/svelte/messages/compile-errors/bindings.md @@ -0,0 +1,35 @@ +## invalid_binding_expression + +Can only bind to an Identifier or MemberExpression + +## invalid_binding_value + +Can only bind to state or props + +## bind_invalid_target + +`bind:%name%` can only be used with %elements% + +## bind_invalid + +`bind:%name%` is not a valid binding + +## bind_invalid_detailed + +`bind:%name%` is not a valid binding. %explanation% + +## invalid_type_attribute + +'type' attribute must be a static text value if input uses two-way binding + +## invalid_multiple_attribute + +'multiple' attribute must be static if select uses two-way binding + +## missing_contenteditable_attribute + +'contenteditable' attribute is required for textContent, innerHTML and innerText two-way bindings + +## dynamic_contenteditable_attribute + +'contenteditable' attribute cannot be dynamic if element uses two-way binding diff --git a/packages/svelte/messages/compile-errors/compiler_options.md b/packages/svelte/messages/compile-errors/compiler_options.md new file mode 100644 index 0000000000..761d74c72a --- /dev/null +++ b/packages/svelte/messages/compile-errors/compiler_options.md @@ -0,0 +1,7 @@ +## invalid_compiler_option + +Invalid compiler option: %msg% + +## removed_compiler_option + +Invalid compiler option: %msg% \ No newline at end of file diff --git a/packages/svelte/messages/compile-errors/components.md b/packages/svelte/messages/compile-errors/components.md new file mode 100644 index 0000000000..88a31a81c6 --- /dev/null +++ b/packages/svelte/messages/compile-errors/components.md @@ -0,0 +1,3 @@ +## invalid_component_directive + +This type of directive is not valid on components \ No newline at end of file diff --git a/packages/svelte/messages/compile-errors/const_tag.md b/packages/svelte/messages/compile-errors/const_tag.md new file mode 100644 index 0000000000..de8a8ca909 --- /dev/null +++ b/packages/svelte/messages/compile-errors/const_tag.md @@ -0,0 +1,3 @@ +## invalid_const_placement + +{@const} must be the immediate child of {#snippet}, {#if}, {:else if}, {:else}, {#each}, {:then}, {:catch}, or \ No newline at end of file diff --git a/packages/svelte/messages/compile-errors/css.md b/packages/svelte/messages/compile-errors/css.md new file mode 100644 index 0000000000..33331cfac6 --- /dev/null +++ b/packages/svelte/messages/compile-errors/css.md @@ -0,0 +1,51 @@ +## invalid_css_empty_declaration + +Declaration cannot be empty + +## invalid_css_global_block_list + +A :global {...} block cannot be part of a selector list with more than one item + +## invalid_css_global_block_modifier + +A :global {...} block cannot modify an existing selector + +## invalid_css_global_block_combinator + +A :global {...} block cannot follow a %name% combinator + +## invalid_css_global_block_declaration + +A :global {...} block can only contain rules, not declarations + +## invalid_css_global_placement + +:global(...) can be at the start or end of a selector sequence, but not in the middle + +## invalid_css_global_selector + +:global(...) must contain exactly one selector + +## invalid_css_global_selector_list + +:global(...) must not contain type or universal selectors when used in a compound selector + +## invalid_css_type_selector_placement + +:global(...) must not be followed with a type selector + +## invalid_css_selector + +Invalid selector + +## invalid_css_identifier + +Expected a valid CSS identifier + +## invalid_nesting_selector + +Nesting selectors can only be used inside a rule + +## invalid_css_declaration + +Declaration cannot be empty diff --git a/packages/svelte/messages/compile-errors/elements.md b/packages/svelte/messages/compile-errors/elements.md new file mode 100644 index 0000000000..466a26a7ac --- /dev/null +++ b/packages/svelte/messages/compile-errors/elements.md @@ -0,0 +1,27 @@ +## invalid_textarea_content + +A ` - + - -
    + +
      -
      -
      +
      +
      -
      +
      -
      -
      -
      +
      +
      +
      -
      -
      -
      -
      -
      -
      +
      +
      +
      +
      +
      +
      -
      +
      -
      +
      -
      -
      -
      +
      +
      +
      -
      +
      -
      -
      +
      +
      -
      -
      +
      +
      -
      -
      -
      -
      -
      +
      +
      +
      +
      +
      -
      -
      -
      +
      +
      +
      -
      -
      +
      +
      -
      +
      -
      -
      -
      +
      +
      +
      -
      +
      -
      +
      -
      -
      -
      +
      +
      +
      -
      +
      -
      +
      -
      -
      +
      +
      -
      -
      +
      +
      -
      +
      -
      +
      -
      +
      -
      -
      +
      +
      -
      +
      -
      +
      -
      +
      -
      +
      @@ -164,208 +164,208 @@ - + - + - + - + - + -
      Link - +Link +
      - + - +
      - -
      -
      + +
      +
      -
      -

      H1

      +
      +

      H1

      H2

      H3

      H4

      -
      H5
      -
      H6
      +
      H5
      +
      H6

      Valid aria role -
    • - - +
    • + + -
        - - +
          + + - +
          - +
          -
          -
          -
          -
          -
          -
          -
          +
          +
          +
          +
          +
          +
          +
          -
          -
          +
          +
          -
          -
          +
          +
          -
          -
          +
          +
          -
          -
          -
          -
          -
          -
          -
          +
          +
          +
          +
          +
          +
          +
          -
          +
          -
          -
          +
          +
          -
          +
          -
          +
          -
          -
          -
          -
          -
          -
          +
          +
          +
          +
          +
          +
          -
          -
          -
          + +
          +
          -
          -
          -
          +
          +
          +
          -
          -
          +
          +
          -
          +
          -
          +
          -
          -
          -
          -
          +
          +
          +
          +
          -
          +
          -
          +
          -
          -
          -
          -
          + +
          + +
          -
          +
          -
          +
          -
          +
          -
          +
          -
          +
          -
          +
          -
          -
          +
          +
          -
          -
          -
          +
          +
          +
          -
          +
          -
          -
          +
          +
          -
          +
          - + - + - - - + + + - - + + - + diff --git a/packages/svelte/tests/validator/samples/a11y-role-supports-aria-props/warnings.json b/packages/svelte/tests/validator/samples/a11y-role-supports-aria-props/warnings.json index 15b0cf1ac0..5d8d2931b3 100644 --- a/packages/svelte/tests/validator/samples/a11y-role-supports-aria-props/warnings.json +++ b/packages/svelte/tests/validator/samples/a11y-role-supports-aria-props/warnings.json @@ -14,7 +14,7 @@ { "code": "a11y_role_supports_aria_props_implicit", "end": { - "column": 18, + "column": 25, "line": 3 }, "message": "The attribute 'aria-pressed' is not supported by the role 'link'. This role is implicit on the element ``", @@ -38,7 +38,7 @@ { "code": "a11y_role_supports_aria_props_implicit", "end": { - "column": 17, + "column": 24, "line": 5 }, "message": "The attribute 'aria-modal' is not supported by the role 'complementary'. This role is implicit on the element `