chore: tidy up messages (#11327)

* start reorganising messages

* tidy up

* more

* more

* more

* alphabetise

* consolidate

* more

* more

* more

* more

* more

* more

* alphabetise

* more

* this is no longer needed

* no longer necessary

* more

* more

* fix

* regenerate messages

* more

* more

* tighten up rune validation

* more

* fix

* more

* tweak a11y messages

* add server errors

* overhaul runtime errors

* regenerate messages

* unused

* lint

* more

* more

* Update packages/svelte/messages/compile-errors/script.md

Co-authored-by: Simon H <5968653+dummdidumm@users.noreply.github.com>

* Update packages/svelte/messages/client-warnings/warnings.md

Co-authored-by: Simon H <5968653+dummdidumm@users.noreply.github.com>

* fix

---------

Co-authored-by: Simon H <5968653+dummdidumm@users.noreply.github.com>
pull/11333/head
Rich Harris 3 months ago committed by GitHub
parent 6ad5cd4461
commit 8e17428316
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -7,7 +7,9 @@ packages/svelte/src/compiler/errors.js
packages/svelte/src/compiler/warnings.js packages/svelte/src/compiler/warnings.js
packages/svelte/src/internal/client/errors.js packages/svelte/src/internal/client/errors.js
packages/svelte/src/internal/client/warnings.js packages/svelte/src/internal/client/warnings.js
packages/svelte/src/internal/shared/errors.js
packages/svelte/src/internal/shared/warnings.js packages/svelte/src/internal/shared/warnings.js
packages/svelte/src/internal/server/errors.js
packages/svelte/tests/**/*.svelte packages/svelte/tests/**/*.svelte
packages/svelte/tests/**/_expected* packages/svelte/tests/**/_expected*
packages/svelte/tests/**/_actual* packages/svelte/tests/**/_actual*

@ -1,3 +0,0 @@
## effect_update_depth_exceeded
> Maximum update depth exceeded. This can happen when a reactive block or effect repeatedly sets a new value. Svelte limits the number of nested updates to prevent infinite loops

@ -0,0 +1,67 @@
## bind_invalid_checkbox_value
> Using `bind:value` together with a checkbox input is not allowed. Use `bind:checked` instead
## bind_invalid_export
> Component %component% 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%`)
## bind_not_bindable
> A component is attempting to bind to a non-bindable property `%key%` belonging to %component% (i.e. `<%name% bind:%key%={...}>`). To mark a property as bindable: `let { %key% = $bindable() } = $props()`
## each_key_duplicate
> Keyed each block has duplicate key at indexes %a% and %b%
> Keyed each block has duplicate key `%value%` at indexes %a% and %b%
## effect_in_teardown
> `%rune%` cannot be used inside an effect cleanup function
## effect_orphan
> `%rune%` can only be used inside an effect (e.g. during component initialisation)
## effect_update_depth_exceeded
> Maximum update depth exceeded. This can happen when a reactive block or effect repeatedly sets a new value. Svelte limits the number of nested updates to prevent infinite loops
## hydration_missing_marker_close
> Missing hydration closing marker
## hydration_missing_marker_open
> Missing hydration opening marker
## lifecycle_legacy_only
> `%name%(...)` cannot be used in runes mode
## props_invalid_value
> Cannot do `bind:%key%={undefined}` when `%key%` has a fallback value
## props_rest_readonly
> Rest element properties of `$props()` such as `%property%` are readonly
## rune_outside_svelte
> The `%rune%` rune is only available inside `.svelte` and `.svelte.js/ts` files
## state_prototype_fixed
> Cannot set prototype of `$state` object
## state_unsafe_mutation
> Unsafe mutations during Svelte's render or derived phase are not permitted in runes mode. This can lead to unexpected errors and possibly cause infinite loops.
>
> If the object is not meant to be reactive, declare it without `$state`
## svelte_component_invalid_this_value
> The `this={...}` property of a `<svelte:component>` must be a Svelte component, if defined

@ -1,7 +0,0 @@
## lifecycle_outside_component
> `%name%(...)` can only be used during component initialisation
## lifecycle_legacy_only
> `%name%(...)` cannot be used in runes mode

@ -1,3 +1,11 @@
## hydration_attribute_changed
> The `%attribute%` attribute on `%html%` changed its value between server and client renders. The client value, `%value%`, will be ignored in favour of the server value
## hydration_mismatch
> Hydration failed because the initial UI does not match what was rendered on the server
## lifecycle_double_unmount ## lifecycle_double_unmount
> Tried to unmount a component that was not mounted > Tried to unmount a component that was not mounted

@ -1,59 +0,0 @@
## 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

@ -1,33 +0,0 @@
## 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:%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

@ -1,7 +0,0 @@
## invalid_compiler_option
> Invalid compiler option: %msg%
## removed_compiler_option
> Invalid compiler option: %msg%

@ -1,3 +0,0 @@
## invalid_component_directive
> This type of directive is not valid on components

@ -1,3 +0,0 @@
## invalid_const_placement
> {@const} must be the immediate child of {#snippet}, {#if}, {:else if}, {:else}, {#each}, {:then}, {:catch}, <svelte:fragment> or <Component>

@ -1,27 +0,0 @@
## invalid_textarea_content
> A `<textarea>` can have either a value attribute or (equivalently) child content, but not both
## invalid_void_content
> Void elements cannot have children or closing tags
## invalid_element_content
> <%name%> cannot have children
## invalid_tag_name
> Expected valid tag name
## invalid_node_placement
> %thing% is invalid inside <%parent%>
## illegal_title_attribute
> `<title>` cannot have attributes nor directives
## invalid_title_content
> `<title>` can only contain text and {tags}

@ -1,3 +0,0 @@
## cyclical_reactive_declaration
> Cyclical dependency detected: %cycle%

@ -0,0 +1,11 @@
## options_invalid_value
> Invalid compiler option: %details%
## options_removed
> Invalid compiler option: %details%
## options_unrecognised
> Unrecognised compiler option %keypath%

@ -1,147 +0,0 @@
## unclosed_element
> `<%name%>` was left open
## unclosed_block
> Block was left open
## unexpected_block_close
> Unexpected block closing tag
## unexpected_eof
> Unexpected end of input
## js_parse_error
> %message%
## expected_token
> Expected token %token%
## unexpected_reserved_word
> '%word%' is a reserved word in JavaScript and cannot be used here
## missing_whitespace
> Expected whitespace
## expected_pattern
> Expected identifier or destructure pattern
## invalid_script_context
> If the context attribute is supplied, its value must be "module"
## invalid_elseif
> 'elseif' should be 'else if'
## invalid_continuing_block_placement
> {:...} block is invalid at this position (did you forget to close the preceeding element or block?)
## invalid_block_missing_parent
> %child% block must be a child of %parent%
## duplicate_block_part
> %name% cannot appear more than once within a block
## expected_block_type
> Expected 'if', 'each', 'await', 'key' or 'snippet'
## expected_identifier
> Expected an identifier
## invalid_debug
> {@debug ...} arguments must be identifiers, not arbitrary expressions
## invalid_const
> {@const ...} must be an assignment
## invalid_block_placement
> {#%name% ...} block cannot be %location%
## invalid_tag_placement
> {@%name% ...} tag cannot be %location%
## missing_attribute_value
> Expected attribute value
## unclosed_attribute_value
> Expected closing %delimiter% character
## invalid_directive_value
> Directive value must be a JavaScript expression enclosed in curly braces
## empty_directive_name
> %type% name cannot be empty
## invalid_closing_tag
> </%name%> attempted to close an element that was not open
## invalid_closing_tag_after_autoclose
> </%name%> attempted to close element that was already automatically closed by <%reason%> (cannot nest <%reason%> inside <%name%>)
## invalid_dollar_binding
> The $ name is reserved, and cannot be used for variables and imports
## invalid_dollar_prefix
> The $ prefix is reserved, and cannot be used for variables and imports
## invalid_dollar_global
> The $ name is reserved. To reference a global variable called $, use globalThis.$
## illegal_subscription
> Cannot reference store value inside `<script context="module">`
## duplicate_style_element
> A component can have a single top-level `<style>` element
## duplicate_script_element
> A component can have a single top-level `<script>` element and/or a single top-level `<script context="module">` element
## invalid_render_expression
> {@render ...} tags can only contain call expressions
## invalid_render_arguments
> expected at most one argument
## invalid_render_call
> Calling a snippet function using apply, bind or call is not allowed
## invalid_render_spread_argument
> cannot use spread arguments in {@render ...} tags
## invalid_snippet_rest_parameter
> snippets do not support rest parameters; use an array instead

@ -1,95 +0,0 @@
## invalid_legacy_props
> Cannot use `$$props` in runes mode
## invalid_legacy_rest_props
> Cannot use `$$restProps` in runes mode
## invalid_legacy_reactive_statement
> `$:` is not allowed in runes mode, use `$derived` or `$effect` instead
## invalid_legacy_export
> Cannot use `export let` in runes mode — use $props instead
## invalid_rune_usage
> Cannot use %rune% rune in non-runes mode
## invalid_state_export
> Cannot export state from a module if it is reassigned. Either export a function returning the state value or only mutate the state value's properties
## invalid_derived_export
> Cannot export derived state from a module. To expose the current derived value, export a function returning its value
## invalid_props_id
> `$props()` can only be used with an object destructuring pattern
## invalid_props_pattern
> `$props()` assignment must not contain nested properties or computed keys
## invalid_props_location
> `$props()` can only be used at the top level of components as a variable declaration initializer
## invalid_bindable_location
> `$bindable()` can only be used inside a `$props()` declaration
## invalid_state_location
> `%rune%(...)` can only be used as a variable declaration initializer or a class field
## invalid_effect_location
> `$effect()` can only be used as an expression statement
## invalid_host_location
> `$host()` can only be used inside custom element component instances
## invalid_assignment
> Cannot assign to %thing%
## invalid_binding
> Cannot bind to %thing%
## invalid_rune_args
> `%rune%` cannot be called with arguments
## invalid_rune_args_length
> `%rune%` must be called with %args%
## invalid_runes_mode_import
> %name% cannot be used in runes mode
## duplicate_props_rune
> Cannot use `$props()` more than once
## invalid_each_assignment
> Cannot reassign or bind to each block argument in runes mode. Use the array and index variables instead (e.g. `array[i] = value` instead of `entry = value`)
## invalid_snippet_assignment
> Cannot reassign or bind to snippet parameter
## invalid_derived_call
> `$derived.call(...)` has been replaced with `$derived.by(...)`
## conflicting_property_name
> Cannot have a property and a component export with the same name

@ -0,0 +1,135 @@
## bindable_invalid_location
> `$bindable()` can only be used inside a `$props()` declaration
## constant_assignment
> Cannot assign to %thing%
## constant_binding
> Cannot bind to %thing%
## declaration_duplicate
> `%name%` has already been declared
## declaration_duplicate_module_import
> Cannot declare same variable name which is imported inside `<script context="module">`
## derived_invalid_export
> Cannot export derived state from a module. To expose the current derived value, export a function returning its value
## dollar_binding_invalid
> The $ name is reserved, and cannot be used for variables and imports
## dollar_prefix_invalid
> The $ prefix is reserved, and cannot be used for variables and imports
## each_item_invalid_assignment
> Cannot reassign or bind to each block argument in runes mode. Use the array and index variables instead (e.g. `array[i] = value` instead of `entry = value`)
## effect_invalid_placement
> `$effect()` can only be used as an expression statement
## global_reference_invalid
> `%name%` is an illegal variable name. To reference a global variable called `%name%`, use `globalThis.%name%`
## host_invalid_placement
> `$host()` can only be used inside custom element component instances
## legacy_export_invalid
> Cannot use `export let` in runes mode — use `$props()` instead
## legacy_props_invalid
> Cannot use `$$props` in runes mode
## legacy_reactive_statement_invalid
> `$:` is not allowed in runes mode, use `$derived` or `$effect` instead
## legacy_rest_props_invalid
> Cannot use `$$restProps` in runes mode
## module_illegal_default_export
> A component cannot have a default export
## props_duplicate
> Cannot use `$props()` more than once
## props_invalid_identifier
> `$props()` can only be used with an object destructuring pattern
## props_invalid_pattern
> `$props()` assignment must not contain nested properties or computed keys
## props_invalid_placement
> `$props()` can only be used at the top level of components as a variable declaration initializer
## reactive_declaration_cycle
> Cyclical dependency detected: %cycle%
## rune_invalid_arguments
> `%rune%` cannot be called with arguments
## rune_invalid_arguments_length
> `%rune%` must be called with %args%
## rune_invalid_computed_property
> Cannot access a computed property of a rune
## rune_invalid_name
> `%name%` is not a valid rune
## rune_invalid_usage
> Cannot use `%rune%` rune in non-runes mode
## rune_missing_parentheses
> Cannot use rune without parentheses
## runes_mode_invalid_import
> %name% cannot be used in runes mode
## snippet_parameter_assignment
> Cannot reassign or bind to snippet parameter
## state_invalid_export
> Cannot export state from a module if it is reassigned. Either export a function returning the state value or only mutate the state value's properties
## state_invalid_placement
> `%rune%(...)` can only be used as a variable declaration initializer or a class field
## store_invalid_scoped_subscription
> Cannot subscribe to stores that are not declared at the top level of the component
## store_invalid_subscription
> Cannot reference store value inside `<script context="module">`

@ -1,31 +0,0 @@
## invalid_slot_element_attribute
> `<slot>` can only receive attributes and (optionally) let directives
## invalid_slot_attribute
> slot attribute must be a static value
## invalid_slot_name_default
> `default` is a reserved word — it cannot be used as a slot name
## invalid_slot_name
> slot attribute must be a static value
## invalid_slot_placement
> Element with a slot='...' attribute must be a child of a component or a descendant of a custom element
## duplicate_slot_name
> Duplicate slot name '%name%' in <%component%>
## invalid_default_slot_content
> Found default slot content alongside an explicit slot="default"
## conflicting_children_snippet
> Cannot use explicit children snippet at the same time as implicit children content. Remove either the non-whitespace content or the children snippet block

@ -1,99 +0,0 @@
## invalid_svelte_option_attribute
> `<svelte:options>` can only receive static attributes
## invalid_svelte_option_namespace
> Unsupported `<svelte:option>` value for "namespace". Valid values are "html", "svg" or "foreign"
## tag_option_deprecated
> "tag" option is deprecated — use "customElement" instead
## invalid_svelte_option_runes
> Unsupported `<svelte:option>` value for "runes". Valid values are true or false
## invalid_svelte_option_accessors
> Unsupported `<svelte:option>` value for "accessors". Valid values are true or false
## invalid_svelte_option_preserveWhitespace
> Unsupported `<svelte:option>` value for "preserveWhitespace". Valid values are true or false
## invalid_svelte_option_immutable
> Unsupported `<svelte:option>` value for "immutable". Valid values are true or false
## invalid_tag_property
> Tag name must be two or more words joined by the "-" character
## invalid_svelte_option_customElement
> "customElement" must be a string literal defining a valid custom element name or an object of the form { tag: string; shadow?: "open" | "none"; props?: { [key: string]: { attribute?: string; reflect?: boolean; type: .. } } }
## invalid_customElement_props_attribute
> "props" must be a statically analyzable object literal of the form "{ [key: string]: { attribute?: string; reflect?: boolean; type?: "String" | "Boolean" | "Number" | "Array" | "Object" }"
## invalid_customElement_shadow_attribute
> "shadow" must be either "open" or "none"
## unknown_svelte_option_attribute
> `<svelte:options>` unknown attribute '%name%'
## illegal_svelte_head_attribute
> `<svelte:head>` cannot have attributes nor directives
## invalid_svelte_fragment_attribute
> `<svelte:fragment>` can only have a slot attribute and (optionally) a let: directive
## invalid_svelte_fragment_slot
> `<svelte:fragment>` slot attribute must have a static value
## invalid_svelte_fragment_placement
> `<svelte:fragment>` must be the direct child of a component
## invalid_svelte_element_placement
> <%name%> tags cannot be inside elements or blocks
## duplicate_svelte_element
> A component can only have one <%name%> element
## invalid_self_placement
> `<svelte:self>` components can only exist inside {#if} blocks, {#each} blocks, {#snippet} blocks or slots passed to components
## missing_svelte_element_definition
> `<svelte:element>` must have a 'this' attribute
## missing_svelte_component_definition
> `<svelte:component>` must have a 'this' attribute
## invalid_svelte_element_definition
> Invalid element definition — must be an {expression}
## invalid_svelte_component_definition
> Invalid component definition — must be an {expression}
## invalid_svelte_tag
> Valid `<svelte:...>` tag names are %list%
## conflicting_slot_usage
> Cannot use `<slot>` syntax and `{@render ...}` tags in the same component. Migrate towards `{@render ...}` tags completely.

@ -1,51 +1,47 @@
## invalid_css_empty_declaration ## css_empty_declaration
> Declaration cannot be empty > Declaration cannot be empty
## invalid_css_global_block_list ## css_expected_identifier
> A :global {...} block cannot be part of a selector list with more than one item > Expected a valid CSS identifier
## invalid_css_global_block_modifier
> A :global {...} block cannot modify an existing selector
## invalid_css_global_block_combinator ## css_global_block_invalid_combinator
> A :global {...} block cannot follow a %name% combinator > A :global {...} block cannot follow a %name% combinator
## invalid_css_global_block_declaration ## css_global_block_invalid_declaration
> A :global {...} block can only contain rules, not declarations > A :global {...} block can only contain rules, not declarations
## invalid_css_global_placement ## css_global_block_invalid_list
> :global(...) can be at the start or end of a selector sequence, but not in the middle > A :global {...} block cannot be part of a selector list with more than one item
## invalid_css_global_selector ## css_global_block_invalid_modifier
> :global(...) must contain exactly one selector > A :global {...} block cannot modify an existing selector
## invalid_css_global_selector_list ## css_global_invalid_placement
> :global(...) must not contain type or universal selectors when used in a compound selector > :global(...) can be at the start or end of a selector sequence, but not in the middle
## invalid_css_type_selector_placement ## css_global_invalid_selector
> :global(...) must not be followed with a type selector > :global(...) must contain exactly one selector
## invalid_css_selector ## css_global_invalid_selector_list
> Invalid selector > :global(...) must not contain type or universal selectors when used in a compound selector
## invalid_css_identifier ## css_nesting_selector_invalid_placement
> Expected a valid CSS identifier > Nesting selectors can only be used inside a rule
## invalid_nesting_selector ## css_selector_invalid
> Nesting selectors can only be used inside a rule > Invalid selector
## invalid_css_declaration ## css_type_selector_invalid_placement
> Declaration cannot be empty > :global(...) must not be followed with a type selector

@ -0,0 +1,365 @@
## animation_duplicate
> An element can only have one 'animate' directive
## 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?
## attribute_contenteditable_dynamic
> 'contenteditable' attribute cannot be dynamic if element uses two-way binding
## attribute_contenteditable_missing
> 'contenteditable' attribute is required for textContent, innerHTML and innerText two-way bindings
## attribute_duplicate
> Attributes need to be unique
## attribute_empty_shorthand
> Attribute shorthand cannot be empty
## attribute_invalid_event_handler
> Event attribute must be a JavaScript expression, not a string
## attribute_invalid_multiple
> 'multiple' attribute must be static if select uses two-way binding
## attribute_invalid_name
> '%name%' is not a valid attribute name
## attribute_invalid_sequence_expression
> Sequence expressions are not allowed as attribute/directive values in runes mode, unless wrapped in parentheses
## attribute_invalid_type
> 'type' attribute must be a static text value if input uses two-way binding
## bind_invalid_expression
> Can only bind to an Identifier or MemberExpression
## bind_invalid_name
> `bind:%name%` is not a valid binding
> `bind:%name%` is not a valid binding. %explanation%
## bind_invalid_target
> `bind:%name%` can only be used with %elements%
## bind_invalid_value
> Can only bind to state or props
## block_duplicate_clause
> %name% cannot appear more than once within a block
## block_invalid_continuation_placement
> {:...} block is invalid at this position (did you forget to close the preceeding element or block?)
## block_invalid_elseif
> 'elseif' should be 'else if'
## block_invalid_placement
> {#%name% ...} block cannot be %location%
## block_unclosed
> Block was left open
## block_unexpected_close
> Unexpected block closing tag
## component_invalid_directive
> This type of directive is not valid on components
## const_tag_invalid_expression
> {@const ...} must be an assignment
## const_tag_invalid_placement
> `{@const}` must be the immediate child of `{#snippet}`, `{#if}`, `{:else if}`, `{:else}`, `{#each}`, `{:then}`, `{:catch}`, `<svelte:fragment>` or `<Component>`
## debug_tag_invalid_arguments
> {@debug ...} arguments must be identifiers, not arbitrary expressions
## directive_invalid_value
> Directive value must be a JavaScript expression enclosed in curly braces
## directive_missing_name
> `%type%` name cannot be empty
## element_invalid_closing_tag
> `</%name%>` attempted to close an element that was not open
## element_invalid_closing_tag_autoclosed
> `</%name%>` attempted to close element that was already automatically closed by `<%reason%>` (cannot nest `<%reason%>` inside `<%name%>`)
## element_invalid_tag_name
> Expected valid tag name
## element_unclosed
> `<%name%>` was left open
## event_handler_invalid_component_modifier
> Event modifiers other than 'once' can only be used on DOM elements
## event_handler_invalid_modifier
> Valid event modifiers are %list%
## event_handler_invalid_modifier_combination
> The '%modifier1%' and '%modifier2%' modifiers cannot be used together
## expected_attribute_value
> Expected attribute value
## expected_block_type
> Expected 'if', 'each', 'await', 'key' or 'snippet'
## expected_identifier
> Expected an identifier
## expected_pattern
> Expected identifier or destructure pattern
## expected_token
> Expected token %token%
## expected_whitespace
> Expected whitespace
## js_parse_error
> %message%
## let_directive_invalid_placement
> `let:` directive at invalid position
## node_invalid_placement
> %thing% is invalid inside <%parent%>
## render_tag_invalid_call_expression
> Calling a snippet function using apply, bind or call is not allowed
## render_tag_invalid_expression
> `{@render ...}` tags can only contain call expressions
## render_tag_invalid_spread_argument
> cannot use spread arguments in `{@render ...}` tags
## script_duplicate
> A component can have a single top-level `<script>` element and/or a single top-level `<script context="module">` element
## script_invalid_context
> If the context attribute is supplied, its value must be "module"
## slot_attribute_duplicate
> Duplicate slot name '%name%' in <%component%>
## slot_attribute_invalid
> slot attribute must be a static value
## slot_attribute_invalid_placement
> Element with a slot='...' attribute must be a child of a component or a descendant of a custom element
## slot_default_duplicate
> Found default slot content alongside an explicit slot="default"
## slot_element_invalid_attribute
> `<slot>` can only receive attributes and (optionally) let directives
## slot_element_invalid_name
> slot attribute must be a static value
## slot_element_invalid_name_default
> `default` is a reserved word — it cannot be used as a slot name
## slot_snippet_conflict
> Cannot use `<slot>` syntax and `{@render ...}` tags in the same component. Migrate towards `{@render ...}` tags completely.
## snippet_conflict
> Cannot use explicit children snippet at the same time as implicit children content. Remove either the non-whitespace content or the children snippet block
## snippet_invalid_rest_parameter
> snippets do not support rest parameters; use an array instead
## style_directive_invalid_modifier
> `style:` directive can only use the `important` modifier
## style_duplicate
> A component can have a single top-level `<style>` element
## svelte_component_invalid_this
> Invalid component definition — must be an `{expression}`
## svelte_component_missing_this
> `<svelte:component>` must have a 'this' attribute
## svelte_element_invalid_this
> Invalid element definition — must be an `{expression}`
## svelte_element_missing_this
> `<svelte:element>` must have a 'this' attribute
## svelte_fragment_invalid_attribute
> `<svelte:fragment>` can only have a slot attribute and (optionally) a let: directive
## svelte_fragment_invalid_placement
> `<svelte:fragment>` must be the direct child of a component
## svelte_fragment_invalid_slot
> `<svelte:fragment>` slot attribute must have a static value
## svelte_head_illegal_attribute
> `<svelte:head>` cannot have attributes nor directives
## svelte_meta_duplicate
> A component can only have one `<%name%>` element
## svelte_meta_invalid_content
> <%name%> cannot have children
## svelte_meta_invalid_placement
> `<%name%>` tags cannot be inside elements or blocks
## svelte_meta_invalid_tag
> Valid `<svelte:...>` tag names are %list%
## svelte_options_deprecated_tag
> "tag" option is deprecated — use "customElement" instead
## svelte_options_invalid_attribute
> `<svelte:options>` can only receive static attributes
## svelte_options_invalid_attribute_value
> Valid values are %list%
## svelte_options_invalid_customelement
> "customElement" must be a string literal defining a valid custom element name or an object of the form { tag: string; shadow?: "open" | "none"; props?: { [key: string]: { attribute?: string; reflect?: boolean; type: .. } } }
## svelte_options_invalid_customelement_props
> "props" must be a statically analyzable object literal of the form "{ [key: string]: { attribute?: string; reflect?: boolean; type?: "String" | "Boolean" | "Number" | "Array" | "Object" }"
## svelte_options_invalid_customelement_shadow
> "shadow" must be either "open" or "none"
## svelte_options_invalid_tagname
> Tag name must be two or more words joined by the "-" character
## svelte_options_unknown_attribute
> `<svelte:options>` unknown attribute '%name%'
## svelte_self_invalid_placement
> `<svelte:self>` components can only exist inside `{#if}` blocks, `{#each}` blocks, `{#snippet}` blocks or slots passed to components
## tag_invalid_placement
> {@%name% ...} tag cannot be %location%
## textarea_invalid_content
> A `<textarea>` can have either a value attribute or (equivalently) child content, but not both
## title_illegal_attribute
> `<title>` cannot have attributes nor directives
## title_invalid_content
> `<title>` can only contain text and {tags}
## transition_conflict
> Cannot use `%type%:` alongside existing `%existing%:` directive
## transition_duplicate
> Cannot use multiple `%type%:` directives on a single element
## unexpected_eof
> Unexpected end of input
## unexpected_reserved_word
> '%word%' is a reserved word in JavaScript and cannot be used here
## void_element_invalid_content
> Void elements cannot have children or closing tags

@ -1,19 +0,0 @@
## illegal_global
> `%name%` is an illegal variable name. To reference a global variable called `%name%`, use `globalThis.%name%`
## duplicate_declaration
> `%name%` has already been declared
## default_export
> A component cannot have a default export
## illegal_variable_declaration
> Cannot declare same variable name which is imported inside `<script context="module">`
## illegal_store_subscription
> Cannot subscribe to stores that are not declared at the top level of the component

@ -1,167 +1,167 @@
## a11y_aria_attributes ## a11y_accesskey
> <%name%> should not have aria-* attributes > Avoid using accesskey
## a11y_unknown_aria_attribute ## a11y_aria_activedescendant_has_tabindex
> Unknown aria attribute 'aria-%attribute%' > An element with an aria-activedescendant attribute should have a tabindex value
> Unknown aria attribute 'aria-%attribute%'. Did you mean '%suggestion%'? ## a11y_aria_attributes
## a11y_hidden > `<%name%>` should not have aria-* attributes
> <%name%> element should not be hidden ## a11y_autocomplete_valid
## a11y_incorrect_aria_attribute_type_boolean > '%value%' is an invalid value for 'autocomplete' on `<input type="%type%">`
> The value of '%attribute%' must be either 'true' or 'false' ## a11y_autofocus
## a11y_incorrect_aria_attribute_type_integer > Avoid using autofocus
> The value of '%attribute%' must be an integer ## a11y_click_events_have_key_events
## a11y_incorrect_aria_attribute_type_id > Visible, non-interactive elements with a click event must be accompanied by a keyboard event handler. Consider whether an interactive element such as `<button type="button">` or `<a>` might be more appropriate. See https://svelte.dev/docs/accessibility-warnings#a11y-click-events-have-key-events for more details.
> The value of '%attribute%' must be a string that represents a DOM element ID ## a11y_distracting_elements
## a11y_incorrect_aria_attribute_type_idlist > Avoid `<%name%>` elements
> The value of '%attribute%' must be a space-separated list of strings that represent DOM element IDs ## a11y_figcaption_index
## a11y_incorrect_aria_attribute_type_tristate > `<figcaption>` must be first or last child of `<figure>`
> The value of '%attribute%' must be exactly one of true, false, or mixed ## a11y_figcaption_parent
## a11y_incorrect_aria_attribute_type_token > `<figcaption>` must be an immediate child of `<figure>`
> The value of '%attribute%' must be exactly one of %values% ## a11y_hidden
## a11y_incorrect_aria_attribute_type_tokenlist > `<%name%>` element should not be hidden
> The value of '%attribute%' must be a space-separated list of one or more of %values% ## a11y_img_redundant_alt
> Screenreaders already announce `<img>` elements as an image.
## a11y_incorrect_aria_attribute_type ## a11y_incorrect_aria_attribute_type
> The value of '%attribute%' must be of type %type% > The value of '%attribute%' must be of type %type%
## a11y_aria_activedescendant_has_tabindex ## a11y_incorrect_aria_attribute_type_boolean
> Elements with attribute aria-activedescendant should have tabindex value > The value of '%attribute%' must be either 'true' or 'false'
## a11y_misplaced_role ## a11y_incorrect_aria_attribute_type_id
> <%name%> should not have role attribute > The value of '%attribute%' must be a string that represents a DOM element ID
## a11y_no_abstract_role ## a11y_incorrect_aria_attribute_type_idlist
> Abstract role '%role%' is forbidden > The value of '%attribute%' must be a space-separated list of strings that represent DOM element IDs
## a11y_unknown_role ## a11y_incorrect_aria_attribute_type_integer
> Unknown role '%role%' > The value of '%attribute%' must be an integer
> Unknown role '%role%'. Did you mean '%suggestion%'? ## a11y_incorrect_aria_attribute_type_token
## a11y_no_redundant_roles > The value of '%attribute%' must be exactly one of %values%
> Redundant role '%role%' ## a11y_incorrect_aria_attribute_type_tokenlist
## a11y_role_has_required_aria_props > The value of '%attribute%' must be a space-separated list of one or more of %values%
> Elements with the ARIA role "%role%" must have the following attributes defined: %props% ## a11y_incorrect_aria_attribute_type_tristate
> The value of '%attribute%' must be exactly one of true, false, or mixed
## a11y_interactive_supports_focus ## a11y_interactive_supports_focus
> Elements with the '%role%' interactive role must have a tabindex value. > Elements with the '%role%' interactive role must have a tabindex value.
## a11y_no_interactive_element_to_noninteractive_role ## a11y_invalid_attribute
> <%element%> cannot have role '%role%' > '%href_value%' is not a valid %href_attribute% attribute
## a11y_no_noninteractive_element_to_interactive_role ## a11y_label_has_associated_control
> Non-interactive element <%element%> cannot have interactive role '%role%' > A form label must be associated with a control.
## a11y_accesskey ## a11y_media_has_caption
> Avoid using accesskey > `<video>` elements must have a `<track kind="captions">`
## a11y_autofocus ## a11y_misplaced_role
> Avoid using autofocus > `<%name%>` should not have role attribute
## a11y_misplaced_scope ## a11y_misplaced_scope
> The scope attribute should only be used with <th> elements > The scope attribute should only be used with `<th>` elements
## a11y_positive_tabindex ## a11y_missing_attribute
> Avoid tabindex values above zero > `<%name%>` element should have %article% %sequence% attribute
## a11y_click_events_have_key_events ## a11y_missing_content
> Visible, non-interactive elements with a click event must be accompanied by a keyboard event handler. Consider whether an interactive element such as <button type="button"> or <a> might be more appropriate. See https://svelte.dev/docs/accessibility-warnings#a11y-click-events-have-key-events for more details. > `<%name%>` element should have child content
## a11y_no_noninteractive_tabindex ## a11y_mouse_events_have_key_events
> noninteractive element cannot have nonnegative tabIndex value > '%event%' event must be accompanied by '%accompanied_by%' event
## a11y_role_supports_aria_props ## a11y_no_abstract_role
> The attribute '%attribute%' is not supported by the role '%role%' > Abstract role '%role%' is forbidden
## a11y_role_supports_aria_props_implicit ## a11y_no_interactive_element_to_noninteractive_role
> The attribute '%attribute%' is not supported by the role '%role%'. This role is implicit on the element <%name%> > `<%element%>` cannot have role '%role%'
## a11y_no_noninteractive_element_interactions ## a11y_no_noninteractive_element_interactions
> Non-interactive element <%element%> should not be assigned mouse or keyboard event listeners. > Non-interactive element `<%element%>` should not be assigned mouse or keyboard event listeners.
## a11y_no_static_element_interactions
> <%element%> with a %handler% handler must have an ARIA role
## a11y_invalid_attribute ## a11y_no_noninteractive_element_to_interactive_role
> '%href_value%' is not a valid %href_attribute% attribute > Non-interactive element `<%element%>` cannot have interactive role '%role%'
## a11y_missing_attribute ## a11y_no_noninteractive_tabindex
> <%name%> element should have %article% %sequence% attribute > noninteractive element cannot have nonnegative tabIndex value
## a11y_autocomplete_valid ## a11y_no_redundant_roles
> The value '%value%' is not supported by the attribute 'autocomplete' on element <input type="%type%"> > Redundant role '%role%'
## a11y_img_redundant_alt ## a11y_no_static_element_interactions
> Screenreaders already announce <img> elements as an image. > `<%element%>` with a %handler% handler must have an ARIA role
## a11y_label_has_associated_control ## a11y_positive_tabindex
> A form label must be associated with a control. > Avoid tabindex values above zero
## a11y_media_has_caption ## a11y_role_has_required_aria_props
> <video> elements must have a <track kind="captions"> > Elements with the ARIA role "%role%" must have the following attributes defined: %props%
## a11y_distracting_elements ## a11y_role_supports_aria_props
> Avoid <%name%> elements > The attribute '%attribute%' is not supported by the role '%role%'
## a11y_figcaption_parent ## a11y_role_supports_aria_props_implicit
> `<figcaption>` must be an immediate child of `<figure>` > The attribute '%attribute%' is not supported by the role '%role%'. This role is implicit on the element `<%name%>`
## a11y_figcaption_index ## a11y_unknown_aria_attribute
> `<figcaption>` must be first or last child of `<figure>` > Unknown aria attribute 'aria-%attribute%'
## a11y_mouse_events_have_key_events > Unknown aria attribute 'aria-%attribute%'. Did you mean '%suggestion%'?
> '%event%' event must be accompanied by '%accompanied_by%' event ## a11y_unknown_role
## a11y_missing_content > Unknown role '%role%'
> <%name%> element should have child content > Unknown role '%role%'. Did you mean '%suggestion%'?

@ -1,15 +0,0 @@
## avoid_is
> The "is" attribute is not supported cross-browser and should be avoided
## global_event_reference
> You are referencing globalThis.%name%. Did you forget to declare a variable with that name?
## illegal_attribute_character
> Attributes should not contain ':' characters to prevent ambiguity with Svelte directives
## invalid_html_attribute
> '%wrong%' is not a valid HTML attribute. Did you mean '%right%'?

@ -1,3 +0,0 @@
## empty_block
> Empty block

@ -1,3 +0,0 @@
## component_name_lowercase
> <%name%> will be treated as an HTML element unless it begins with a capital letter

@ -1,3 +0,0 @@
## css_unused_selector
> Unused CSS selector "%name%"

@ -1,19 +0,0 @@
## no_reactive_declaration
> Reactive declarations only exist at the top level of the instance script
## module_script_reactive_declaration
> All dependencies of the reactive declaration are declared in a module script and will not be reactive
## unused_export_let
> Component has unused export property '%name%'. If it is for external reference only, please consider using `export const %name%`
## deprecated_slot_element
> Using <slot> to render parent content is deprecated. Use {@render ...} tags instead.
## deprecated_event_handler
> Using on:%name% to listen to the %name% event is is deprecated. Use the event attribute on%name% instead.

@ -1,3 +0,0 @@
## invalid_self_closing_tag
> Self-closing HTML tags for non-void elements are ambiguous — use <%name% ...></%name%> rather than <%name% ... />

@ -10,10 +10,6 @@
> The `customElement` option is used when generating a custom element. Did you forget the `customElement: true` compile option? > The `customElement` option is used when generating a custom element. Did you forget the `customElement: true` compile option?
## options_renamed_ssr_dom
> `generate: "dom"` and `generate: "ssr"` options have been renamed to "client" and "server" respectively
## options_removed_enable_sourcemap ## options_removed_enable_sourcemap
> The `enableSourcemap` option has been removed. Source maps are always generated now, and tooling can choose to ignore them > The `enableSourcemap` option has been removed. Source maps are always generated now, and tooling can choose to ignore them
@ -25,3 +21,7 @@
## options_removed_loop_guard_timeout ## options_removed_loop_guard_timeout
> The `loopGuardTimeout` option has been removed > The `loopGuardTimeout` option has been removed
## options_renamed_ssr_dom
> `generate: "dom"` and `generate: "ssr"` options have been renamed to "client" and "server" respectively

@ -1,7 +0,0 @@
## avoid_inline_class
> Avoid 'new class' — instead, declare the class at the top level scope
## avoid_nested_class
> Avoid declaring classes below the top level scope

@ -1,19 +0,0 @@
## store_with_rune_name
> It looks like you're using the `$%name%` rune, but there is a local binding called `%name%`. Referencing a local variable with a `$` prefix will create a store subscription. Please rename `%name%` to avoid the ambiguity
## non_state_reference
> `%name%` is updated, but is not declared with `$state(...)`. Changing its value will not correctly trigger updates
## derived_iife
> Use `$derived.by(() => {...})` instead of `$derived((() => {...})())`
## invalid_props_declaration
> Component properties are declared using `$props()` in runes mode. Did you forget to call the function?
## invalid_bindable_declaration
> Bindable component properties are declared using `$bindable()` in runes mode. Did you forget to call the function?

@ -0,0 +1,35 @@
## derived_iife
> Use `$derived.by(() => {...})` instead of `$derived((() => {...})())`
## export_let_unused
> Component has unused export property '%name%'. If it is for external reference only, please consider using `export const %name%`
## non_reactive_update
> `%name%` is updated, but is not declared with `$state(...)`. Changing its value will not correctly trigger updates
## perf_avoid_inline_class
> Avoid 'new class' — instead, declare the class at the top level scope
## perf_avoid_nested_class
> Avoid declaring classes below the top level scope
## reactive_declaration_invalid_placement
> Reactive declarations only exist at the top level of the instance script
## reactive_declaration_module_script
> All dependencies of the reactive declaration are declared in a module script and will not be reactive
## state_referenced_locally
> State referenced in its own scope will never update. Did you mean to reference it inside a closure?
## store_rune_conflict
> It looks like you're using the `$%name%` rune, but there is a local binding called `%name%`. Referencing a local variable with a `$` prefix will create a store subscription. Please rename `%name%` to avoid the ambiguity

@ -1,7 +0,0 @@
## static_state_reference
> State referenced in its own scope will never update. Did you mean to reference it inside a closure?
## invalid_rest_eachblock_binding
> The rest operator (...) will create a new object and binding '%name%' with the original object will not work

@ -0,0 +1,3 @@
## css_unused_selector
> Unused CSS selector "%name%"

@ -0,0 +1,39 @@
## attribute_avoid_is
> The "is" attribute is not supported cross-browser and should be avoided
## attribute_global_event_reference
> You are referencing `globalThis.%name%`. Did you forget to declare a variable with that name?
## attribute_illegal_colon
> Attributes should not contain ':' characters to prevent ambiguity with Svelte directives
## attribute_invalid_property_name
> '%wrong%' is not a valid HTML attribute. Did you mean '%right%'?
## bind_invalid_each_rest
> The rest operator (...) will create a new object and binding '%name%' with the original object will not work
## block_empty
> Empty block
## component_name_lowercase
> `<%name%>` will be treated as an HTML element unless it begins with a capital letter
## element_invalid_self_closing_tag
> Self-closing HTML tags for non-void elements are ambiguous — use `<%name% ...></%name%>` rather than `<%name% ... />`
## event_directive_deprecated
> Using `on:%name%` to listen to the %name% event is deprecated. Use the event attribute `on%name%` instead.
## slot_element_deprecated
> Using `<slot>` to render parent content is deprecated. Use `{@render ...}` tags instead.

@ -0,0 +1,3 @@
## lifecycle_function_unavailable
> `%name%(...)` is not available on the server

@ -0,0 +1,19 @@
## lifecycle_outside_component
> `%name%(...)` can only be used during component initialisation
## render_tag_invalid_argument
> The argument to `{@render ...}` must be a snippet function, not a component or some other kind of function. If you want to dynamically render one snippet or another, use `$derived` and pass its result to `{@render ...}`
## snippet_used_as_component
> A snippet must be rendered with `{@render ...}`
## store_invalid_shape
> `%name%` is not a store with a `subscribe` method
## svelte_element_invalid_this_value
> The `this` prop on `<svelte:element>` must be a string, if defined

@ -1,3 +1,3 @@
## dynamic_void_element_content ## dynamic_void_element_content
> `<svelte:element this="%tag%">` is a void element — it cannot have content > `<svelte:element this="%tag%">` is a void element — it cannot have content

@ -18,6 +18,8 @@ for (const category of fs.readdirSync('messages')) {
.readFileSync(`messages/${category}/${file}`, 'utf-8') .readFileSync(`messages/${category}/${file}`, 'utf-8')
.replace(/\r\n/g, '\n'); .replace(/\r\n/g, '\n');
const sorted = [];
for (const match of markdown.matchAll(/## ([\w]+)\n\n([^]+?)(?=$|\n\n## )/g)) { for (const match of markdown.matchAll(/## ([\w]+)\n\n([^]+?)(?=$|\n\n## )/g)) {
const [_, code, text] = match; const [_, code, text] = match;
@ -25,6 +27,8 @@ for (const category of fs.readdirSync('messages')) {
throw new Error(`Duplicate message code ${category}/${code}`); throw new Error(`Duplicate message code ${category}/${code}`);
} }
sorted.push({ code, _ });
const sections = text.trim().split('\n\n'); const sections = text.trim().split('\n\n');
let details = null; let details = null;
if (!sections[sections.length - 1].startsWith('> ')) { if (!sections[sections.length - 1].startsWith('> ')) {
@ -41,6 +45,12 @@ for (const category of fs.readdirSync('messages')) {
details details
}; };
} }
sorted.sort((a, b) => (a.code < b.code ? -1 : 1));
fs.writeFileSync(
`messages/${category}/${file}`,
sorted.map((x) => x._.trim()).join('\n\n') + '\n'
);
} }
} }
@ -289,4 +299,6 @@ transform('compile-warnings', 'src/compiler/warnings.js');
transform('client-warnings', 'src/internal/client/warnings.js'); transform('client-warnings', 'src/internal/client/warnings.js');
transform('client-errors', 'src/internal/client/errors.js'); transform('client-errors', 'src/internal/client/errors.js');
transform('server-errors', 'src/internal/server/errors.js');
transform('shared-errors', 'src/internal/shared/errors.js');
transform('shared-warnings', 'src/internal/shared/warnings.js'); transform('shared-warnings', 'src/internal/shared/warnings.js');

@ -0,0 +1,10 @@
/**
* MESSAGE
* @param {string} PARAMETER
* @returns {never}
*/
export function CODE(PARAMETER) {
const error = new Error(`${'CODE'}\n${MESSAGE}`);
error.name = 'Svelte error';
throw error;
}

@ -0,0 +1,17 @@
import { DEV } from 'esm-env';
/**
* MESSAGE
* @param {string} PARAMETER
* @returns {never}
*/
export function CODE(PARAMETER) {
if (DEV) {
const error = new Error(`${'CODE'}\n${MESSAGE}`);
error.name = 'Svelte error';
throw error;
} else {
// TODO print a link to the documentation
throw new Error('CODE');
}
}

File diff suppressed because it is too large Load Diff

@ -92,10 +92,10 @@ export class Parser {
if (current.type === 'RegularElement') { if (current.type === 'RegularElement') {
current.end = current.start + 1; current.end = current.start + 1;
e.unclosed_element(current, current.name); e.element_unclosed(current, current.name);
} else { } else {
current.end = current.start + 1; current.end = current.start + 1;
e.unclosed_block(current); e.block_unclosed(current);
} }
} }
@ -263,7 +263,7 @@ export class Parser {
require_whitespace() { require_whitespace() {
if (!regex_whitespace.test(this.template[this.index])) { if (!regex_whitespace.test(this.template[this.index])) {
e.missing_whitespace(this.index); e.expected_whitespace(this.index);
} }
this.allow_whitespace(); this.allow_whitespace();

@ -22,18 +22,18 @@ export default function read_options(node) {
for (const attribute of node.attributes) { for (const attribute of node.attributes) {
if (attribute.type !== 'Attribute') { if (attribute.type !== 'Attribute') {
e.invalid_svelte_option_attribute(attribute); e.svelte_options_invalid_attribute(attribute);
} }
const { name } = attribute; const { name } = attribute;
switch (name) { switch (name) {
case 'runes': { case 'runes': {
component_options.runes = get_boolean_value(attribute, e.invalid_svelte_option_runes); component_options.runes = get_boolean_value(attribute);
break; break;
} }
case 'tag': { case 'tag': {
e.tag_option_deprecated(attribute); e.svelte_options_deprecated_tag(attribute);
break; // eslint doesn't know this is unnecessary break; // eslint doesn't know this is unnecessary
} }
case 'customElement': { case 'customElement': {
@ -42,9 +42,9 @@ export default function read_options(node) {
const { value } = attribute; const { value } = attribute;
if (value === true) { if (value === true) {
e.invalid_svelte_option_customElement(attribute); e.svelte_options_invalid_customelement(attribute);
} else if (value[0].type === 'Text') { } else if (value[0].type === 'Text') {
const tag = get_static_value(attribute, () => e.invalid_tag_property(attribute)); const tag = get_static_value(attribute);
validate_tag(attribute, tag); validate_tag(attribute, tag);
ce.tag = tag; ce.tag = tag;
component_options.customElement = ce; component_options.customElement = ce;
@ -55,7 +55,7 @@ export default function read_options(node) {
if (value[0].expression.type === 'Literal' && value[0].expression.value === null) { if (value[0].expression.type === 'Literal' && value[0].expression.value === null) {
break; break;
} }
e.invalid_svelte_option_customElement(attribute); e.svelte_options_invalid_customelement(attribute);
} }
/** @type {Array<[string, any]>} */ /** @type {Array<[string, any]>} */
@ -66,7 +66,7 @@ export default function read_options(node) {
property.computed || property.computed ||
property.key.type !== 'Identifier' property.key.type !== 'Identifier'
) { ) {
e.invalid_svelte_option_customElement(attribute); e.svelte_options_invalid_customelement(attribute);
} }
properties.push([property.key.name, property.value]); properties.push([property.key.name, property.value]);
} }
@ -77,13 +77,13 @@ export default function read_options(node) {
validate_tag(tag, tag_value); validate_tag(tag, tag_value);
ce.tag = tag_value; ce.tag = tag_value;
} else { } else {
e.invalid_svelte_option_customElement(attribute); e.svelte_options_invalid_customelement(attribute);
} }
const props = properties.find(([name]) => name === 'props')?.[1]; const props = properties.find(([name]) => name === 'props')?.[1];
if (props) { if (props) {
if (props.type !== 'ObjectExpression') { if (props.type !== 'ObjectExpression') {
e.invalid_customElement_props_attribute(attribute); e.svelte_options_invalid_customelement_props(attribute);
} }
ce.props = {}; ce.props = {};
for (const property of /** @type {import('estree').ObjectExpression} */ (props) for (const property of /** @type {import('estree').ObjectExpression} */ (props)
@ -94,7 +94,7 @@ export default function read_options(node) {
property.key.type !== 'Identifier' || property.key.type !== 'Identifier' ||
property.value.type !== 'ObjectExpression' property.value.type !== 'ObjectExpression'
) { ) {
e.invalid_customElement_props_attribute(attribute); e.svelte_options_invalid_customelement_props(attribute);
} }
ce.props[property.key.name] = {}; ce.props[property.key.name] = {};
for (const prop of property.value.properties) { for (const prop of property.value.properties) {
@ -104,7 +104,7 @@ export default function read_options(node) {
prop.key.type !== 'Identifier' || prop.key.type !== 'Identifier' ||
prop.value.type !== 'Literal' prop.value.type !== 'Literal'
) { ) {
e.invalid_customElement_props_attribute(attribute); e.svelte_options_invalid_customelement_props(attribute);
} }
if (prop.key.name === 'type') { if (prop.key.name === 'type') {
@ -113,21 +113,21 @@ export default function read_options(node) {
/** @type {string} */ (prop.value.value) /** @type {string} */ (prop.value.value)
) === -1 ) === -1
) { ) {
e.invalid_customElement_props_attribute(attribute); e.svelte_options_invalid_customelement_props(attribute);
} }
ce.props[property.key.name].type = /** @type {any} */ (prop.value.value); ce.props[property.key.name].type = /** @type {any} */ (prop.value.value);
} else if (prop.key.name === 'reflect') { } else if (prop.key.name === 'reflect') {
if (typeof prop.value.value !== 'boolean') { if (typeof prop.value.value !== 'boolean') {
e.invalid_customElement_props_attribute(attribute); e.svelte_options_invalid_customelement_props(attribute);
} }
ce.props[property.key.name].reflect = prop.value.value; ce.props[property.key.name].reflect = prop.value.value;
} else if (prop.key.name === 'attribute') { } else if (prop.key.name === 'attribute') {
if (typeof prop.value.value !== 'string') { if (typeof prop.value.value !== 'string') {
e.invalid_customElement_props_attribute(attribute); e.svelte_options_invalid_customelement_props(attribute);
} }
ce.props[property.key.name].attribute = prop.value.value; ce.props[property.key.name].attribute = prop.value.value;
} else { } else {
e.invalid_customElement_props_attribute(attribute); e.svelte_options_invalid_customelement_props(attribute);
} }
} }
} }
@ -137,7 +137,7 @@ export default function read_options(node) {
if (shadow) { if (shadow) {
const shadowdom = shadow?.value; const shadowdom = shadow?.value;
if (shadowdom !== 'open' && shadowdom !== 'none') { if (shadowdom !== 'open' && shadowdom !== 'none') {
e.invalid_customElement_shadow_attribute(shadow); e.svelte_options_invalid_customelement_shadow(shadow);
} }
ce.shadow = shadowdom; ce.shadow = shadowdom;
} }
@ -151,46 +151,32 @@ export default function read_options(node) {
break; break;
} }
case 'namespace': { case 'namespace': {
const value = get_static_value(attribute, () => const value = get_static_value(attribute);
e.invalid_svelte_option_namespace(attribute)
);
if (typeof value !== 'string') {
e.invalid_svelte_option_namespace(attribute);
}
if (value === namespace_svg) { if (value === namespace_svg) {
component_options.namespace = 'svg'; component_options.namespace = 'svg';
} else if (value === 'html' || value === 'svg' || value === 'foreign') { } else if (value === 'html' || value === 'svg' || value === 'foreign') {
component_options.namespace = value; component_options.namespace = value;
} else { } else {
e.invalid_svelte_option_namespace(attribute); e.svelte_options_invalid_attribute_value(attribute, `"html", "svg" or "foreign"`);
} }
break; break;
} }
case 'immutable': { case 'immutable': {
component_options.immutable = get_boolean_value( component_options.immutable = get_boolean_value(attribute);
attribute,
e.invalid_svelte_option_immutable
);
break; break;
} }
case 'preserveWhitespace': { case 'preserveWhitespace': {
component_options.preserveWhitespace = get_boolean_value( component_options.preserveWhitespace = get_boolean_value(attribute);
attribute,
e.invalid_svelte_option_preserveWhitespace
);
break; break;
} }
case 'accessors': { case 'accessors': {
component_options.accessors = get_boolean_value( component_options.accessors = get_boolean_value(attribute);
attribute,
e.invalid_svelte_option_accessors
);
break; break;
} }
default: default:
e.unknown_svelte_option_attribute(attribute, name); e.svelte_options_unknown_attribute(attribute, name);
} }
} }
@ -199,45 +185,43 @@ export default function read_options(node) {
/** /**
* @param {any} attribute * @param {any} attribute
* @param {(attribute: any) => never} error
*/ */
function get_static_value(attribute, error) { function get_static_value(attribute) {
const { value } = attribute; const { value } = attribute;
const chunk = value[0]; const chunk = value[0];
if (!chunk) return true; if (!chunk) return true;
if (value.length > 1) { if (value.length > 1) {
error(attribute); return null;
} }
if (chunk.type === 'Text') return chunk.data; if (chunk.type === 'Text') return chunk.data;
if (chunk.expression.type !== 'Literal') { if (chunk.expression.type !== 'Literal') {
error(attribute); return null;
} }
return chunk.expression.value; return chunk.expression.value;
} }
/** /**
* @param {any} attribute * @param {any} attribute
* @param {(attribute: any) => never} error
*/ */
function get_boolean_value(attribute, error) { function get_boolean_value(attribute) {
const value = get_static_value(attribute, () => error(attribute)); const value = get_static_value(attribute);
if (typeof value !== 'boolean') { if (typeof value !== 'boolean') {
error(attribute); e.svelte_options_invalid_attribute_value(attribute, 'true or false');
} }
return value; return value;
} }
/** /**
* @param {any} attribute * @param {any} attribute
* @param {string} tag * @param {string | null} tag
* @returns {asserts tag is string} * @returns {asserts tag is string}
*/ */
function validate_tag(attribute, tag) { function validate_tag(attribute, tag) {
if (typeof tag !== 'string' && tag !== null) { if (typeof tag !== 'string') {
e.invalid_tag_property(attribute); e.svelte_options_invalid_tagname(attribute);
} }
if (tag && !regex_valid_tag_name.test(tag)) { if (tag && !regex_valid_tag_name.test(tag)) {
e.invalid_tag_property(attribute); e.svelte_options_invalid_tagname(attribute);
} }
// TODO do we still need this? // TODO do we still need this?
// if (tag && !component.compile_options.customElement) { // if (tag && !component.compile_options.customElement) {

@ -16,13 +16,13 @@ function get_context(attributes) {
if (!context) return 'default'; if (!context) return 'default';
if (context.value.length !== 1 || context.value[0].type !== 'Text') { if (context.value.length !== 1 || context.value[0].type !== 'Text') {
e.invalid_script_context(context.start); e.script_invalid_context(context.start);
} }
const value = context.value[0].data; const value = context.value[0].data;
if (value !== 'module') { if (value !== 'module') {
e.invalid_script_context(context.start); e.script_invalid_context(context.start);
} }
return value; return value;
@ -38,7 +38,7 @@ export function read_script(parser, start, attributes) {
const script_start = parser.index; const script_start = parser.index;
const data = parser.read_until(regex_closing_script_tag); const data = parser.read_until(regex_closing_script_tag);
if (parser.index >= parser.template.length) { if (parser.index >= parser.template.length) {
e.unclosed_element(parser.template.length, 'script'); e.element_unclosed(parser.template.length, 'script');
} }
const source = const source =

@ -352,7 +352,7 @@ function read_selector(parser, inside_pseudo_class = false) {
if (combinator) { if (combinator) {
if (relative_selector.selectors.length === 0) { if (relative_selector.selectors.length === 0) {
if (!inside_pseudo_class) { if (!inside_pseudo_class) {
e.invalid_css_selector(start); e.css_selector_invalid(start);
} }
} else { } else {
relative_selector.end = index; relative_selector.end = index;
@ -365,7 +365,7 @@ function read_selector(parser, inside_pseudo_class = false) {
parser.allow_whitespace(); parser.allow_whitespace();
if (parser.match(',') || (inside_pseudo_class ? parser.match(')') : parser.match('{'))) { if (parser.match(',') || (inside_pseudo_class ? parser.match(')') : parser.match('{'))) {
e.invalid_css_selector(parser.index); e.css_selector_invalid(parser.index);
} }
} }
} }
@ -471,12 +471,13 @@ function read_declaration(parser) {
const property = parser.read_until(REGEX_WHITESPACE_OR_COLON); const property = parser.read_until(REGEX_WHITESPACE_OR_COLON);
parser.allow_whitespace(); parser.allow_whitespace();
parser.eat(':'); parser.eat(':');
let index = parser.index;
parser.allow_whitespace(); parser.allow_whitespace();
const value = read_value(parser); const value = read_value(parser);
if (!value && !property.startsWith('--')) { if (!value && !property.startsWith('--')) {
e.invalid_css_declaration(parser.index); e.css_empty_declaration({ start, end: index });
} }
const end = parser.index; const end = parser.index;
@ -577,7 +578,7 @@ function read_identifier(parser) {
let identifier = ''; let identifier = '';
if (parser.match('--') || parser.match_regex(REGEX_LEADING_HYPHEN_OR_DIGIT)) { if (parser.match('--') || parser.match_regex(REGEX_LEADING_HYPHEN_OR_DIGIT)) {
e.invalid_css_identifier(start); e.css_expected_identifier(start);
} }
let escaped = false; let escaped = false;
@ -602,7 +603,7 @@ function read_identifier(parser) {
} }
if (identifier === '') { if (identifier === '') {
e.invalid_css_identifier(start); e.css_expected_identifier(start);
} }
return identifier; return identifier;

@ -101,18 +101,18 @@ export default function tag(parser) {
['svelte:options', 'svelte:window', 'svelte:body', 'svelte:document'].includes(name) && ['svelte:options', 'svelte:window', 'svelte:body', 'svelte:document'].includes(name) &&
/** @type {import('#compiler').ElementLike} */ (parent).fragment.nodes.length /** @type {import('#compiler').ElementLike} */ (parent).fragment.nodes.length
) { ) {
e.invalid_element_content( e.svelte_meta_invalid_content(
/** @type {import('#compiler').ElementLike} */ (parent).fragment.nodes[0].start, /** @type {import('#compiler').ElementLike} */ (parent).fragment.nodes[0].start,
name name
); );
} }
} else { } else {
if (name in parser.meta_tags) { if (name in parser.meta_tags) {
e.duplicate_svelte_element(start, name); e.svelte_meta_duplicate(start, name);
} }
if (parent.type !== 'Root') { if (parent.type !== 'Root') {
e.invalid_svelte_element_placement(start, name); e.svelte_meta_invalid_placement(start, name);
} }
parser.meta_tags[name] = true; parser.meta_tags[name] = true;
@ -164,7 +164,7 @@ export default function tag(parser) {
if (is_closing_tag) { if (is_closing_tag) {
if (is_void(name)) { if (is_void(name)) {
e.invalid_void_content(start); e.void_element_invalid_content(start);
} }
parser.eat('>', true); parser.eat('>', true);
@ -173,9 +173,9 @@ export default function tag(parser) {
while (/** @type {import('#compiler').RegularElement} */ (parent).name !== name) { while (/** @type {import('#compiler').RegularElement} */ (parent).name !== name) {
if (parent.type !== 'RegularElement') { if (parent.type !== 'RegularElement') {
if (parser.last_auto_closed_tag && parser.last_auto_closed_tag.tag === name) { if (parser.last_auto_closed_tag && parser.last_auto_closed_tag.tag === name) {
e.invalid_closing_tag_after_autoclose(start, name, parser.last_auto_closed_tag.reason); e.element_invalid_closing_tag_autoclosed(start, name, parser.last_auto_closed_tag.reason);
} else { } else {
e.invalid_closing_tag(start, name); e.element_invalid_closing_tag(start, name);
} }
} }
@ -216,7 +216,7 @@ export default function tag(parser) {
while ((attribute = read(parser))) { while ((attribute = read(parser))) {
if (attribute.type === 'Attribute' || attribute.type === 'BindDirective') { if (attribute.type === 'Attribute' || attribute.type === 'BindDirective') {
if (unique_names.includes(attribute.name)) { if (unique_names.includes(attribute.name)) {
e.duplicate_attribute(attribute.start); e.attribute_duplicate(attribute.start);
// <svelte:element bind:this this=..> is allowed // <svelte:element bind:this this=..> is allowed
} else if (attribute.name !== 'this') { } else if (attribute.name !== 'this') {
unique_names.push(attribute.name); unique_names.push(attribute.name);
@ -233,7 +233,7 @@ export default function tag(parser) {
(attr) => attr.type === 'Attribute' && attr.name === 'this' (attr) => attr.type === 'Attribute' && attr.name === 'this'
); );
if (index === -1) { if (index === -1) {
e.missing_svelte_component_definition(start); e.svelte_component_missing_this(start);
} }
const definition = /** @type {import('#compiler').Attribute} */ ( const definition = /** @type {import('#compiler').Attribute} */ (
@ -244,7 +244,7 @@ export default function tag(parser) {
definition.value.length !== 1 || definition.value.length !== 1 ||
definition.value[0].type === 'Text' definition.value[0].type === 'Text'
) { ) {
e.invalid_svelte_component_definition(definition.start); e.svelte_component_invalid_this(definition.start);
} }
element.expression = definition.value[0].expression; element.expression = definition.value[0].expression;
@ -256,14 +256,14 @@ export default function tag(parser) {
(attr) => attr.type === 'Attribute' && attr.name === 'this' (attr) => attr.type === 'Attribute' && attr.name === 'this'
); );
if (index === -1) { if (index === -1) {
e.missing_svelte_element_definition(start); e.svelte_element_missing_this(start);
} }
const definition = /** @type {import('#compiler').Attribute} */ ( const definition = /** @type {import('#compiler').Attribute} */ (
element.attributes.splice(index, 1)[0] element.attributes.splice(index, 1)[0]
); );
if (definition.value === true || definition.value.length !== 1) { if (definition.value === true || definition.value.length !== 1) {
e.invalid_svelte_element_definition(definition.start); e.svelte_element_invalid_this(definition.start);
} }
const chunk = definition.value[0]; const chunk = definition.value[0];
element.tag = element.tag =
@ -302,17 +302,17 @@ export default function tag(parser) {
} }
if (content.context === 'module') { if (content.context === 'module') {
if (current.module) e.duplicate_script_element(start); if (current.module) e.script_duplicate(start);
current.module = content; current.module = content;
} else { } else {
if (current.instance) e.duplicate_script_element(start); if (current.instance) e.script_duplicate(start);
current.instance = content; current.instance = content;
} }
} else { } else {
const content = read_style(parser, start, element.attributes); const content = read_style(parser, start, element.attributes);
content.content.comment = prev_comment; content.content.comment = prev_comment;
if (current.css) e.duplicate_style_element(start); if (current.css) e.style_duplicate(start);
current.css = content; current.css = content;
} }
return; return;
@ -387,7 +387,7 @@ function read_tag_name(parser) {
} }
if (!legal) { if (!legal) {
e.invalid_self_placement(start); e.svelte_self_invalid_placement(start);
} }
return 'svelte:self'; return 'svelte:self';
@ -404,11 +404,11 @@ function read_tag_name(parser) {
if (name.startsWith('svelte:')) { if (name.startsWith('svelte:')) {
const list = `${valid_meta_tags.slice(0, -1).join(', ')} or ${valid_meta_tags[valid_meta_tags.length - 1]}`; const list = `${valid_meta_tags.slice(0, -1).join(', ')} or ${valid_meta_tags[valid_meta_tags.length - 1]}`;
e.invalid_svelte_tag(start, list); e.svelte_meta_invalid_tag(start, list);
} }
if (!valid_tag_name.test(name)) { if (!valid_tag_name.test(name)) {
e.invalid_tag_name(start); e.element_invalid_tag_name(start);
} }
return name; return name;
@ -436,7 +436,7 @@ function read_static_attribute(parser) {
parser.allow_whitespace(); parser.allow_whitespace();
let raw = parser.match_regex(regex_attribute_value); let raw = parser.match_regex(regex_attribute_value);
if (!raw) { if (!raw) {
e.missing_attribute_value(parser.index); e.expected_attribute_value(parser.index);
} }
parser.index += raw.length; parser.index += raw.length;
@ -500,7 +500,7 @@ function read_attribute(parser) {
const name = parser.read_identifier(); const name = parser.read_identifier();
if (name === null) { if (name === null) {
e.empty_attribute_shorthand(start); e.attribute_empty_shorthand(start);
} }
parser.allow_whitespace(); parser.allow_whitespace();
@ -552,7 +552,7 @@ function read_attribute(parser) {
const [directive_name, ...modifiers] = name.slice(colon_index + 1).split('|'); const [directive_name, ...modifiers] = name.slice(colon_index + 1).split('|');
if (directive_name === '') { if (directive_name === '') {
e.empty_directive_name(start + colon_index + 1, type); e.directive_missing_name({ start, end: start + colon_index + 1 }, name);
} }
if (type === 'StyleDirective') { if (type === 'StyleDirective') {
@ -577,7 +577,7 @@ function read_attribute(parser) {
const attribute_contains_text = const attribute_contains_text =
/** @type {any[]} */ (value).length > 1 || first_value.type === 'Text'; /** @type {any[]} */ (value).length > 1 || first_value.type === 'Text';
if (attribute_contains_text) { if (attribute_contains_text) {
e.invalid_directive_value(/** @type {number} */ (first_value.start)); e.directive_invalid_value(/** @type {number} */ (first_value.start));
} else { } else {
expression = first_value.expression; expression = first_value.expression;
} }
@ -678,14 +678,14 @@ function read_attribute_value(parser) {
const pos = error.position?.[0]; const pos = error.position?.[0];
if (pos !== undefined && parser.template.slice(pos - 1, pos + 1) === '/>') { if (pos !== undefined && parser.template.slice(pos - 1, pos + 1) === '/>') {
parser.index = pos; parser.index = pos;
e.unclosed_attribute_value(pos, quote_mark || '}'); e.expected_token(pos, quote_mark || '}');
} }
} }
throw error; throw error;
} }
if (value.length === 0 && !quote_mark) { if (value.length === 0 && !quote_mark) {
e.missing_attribute_value(parser.index); e.expected_attribute_value(parser.index);
} }
if (quote_mark) parser.index += 1; if (quote_mark) parser.index += 1;
@ -732,12 +732,12 @@ function read_sequence(parser, done, location) {
const index = parser.index - 1; const index = parser.index - 1;
parser.eat('#'); parser.eat('#');
const name = parser.read_until(/[^a-z]/); const name = parser.read_until(/[^a-z]/);
e.invalid_block_placement(index, name, location); e.block_invalid_placement(index, name, location);
} else if (parser.match('@')) { } else if (parser.match('@')) {
const index = parser.index - 1; const index = parser.index - 1;
parser.eat('@'); parser.eat('@');
const name = parser.read_until(/[^a-z]/); const name = parser.read_until(/[^a-z]/);
e.invalid_tag_placement(index, name, location); e.tag_invalid_placement(index, name, location);
} }
flush(parser.index - 1); flush(parser.index - 1);

@ -331,7 +331,7 @@ function next(parser) {
if (block.type === 'IfBlock') { if (block.type === 'IfBlock') {
if (!parser.eat('else')) e.expected_token(start, '{:else} or {:else if}'); if (!parser.eat('else')) e.expected_token(start, '{:else} or {:else if}');
if (parser.eat('if')) e.invalid_elseif(start); if (parser.eat('if')) e.block_invalid_elseif(start);
parser.allow_whitespace(); parser.allow_whitespace();
@ -389,7 +389,7 @@ function next(parser) {
if (block.type === 'AwaitBlock') { if (block.type === 'AwaitBlock') {
if (parser.eat('then')) { if (parser.eat('then')) {
if (block.then) { if (block.then) {
e.duplicate_block_part(start, '{:then}'); e.block_duplicate_clause(start, '{:then}');
} }
if (!parser.eat('}')) { if (!parser.eat('}')) {
@ -408,7 +408,7 @@ function next(parser) {
if (parser.eat('catch')) { if (parser.eat('catch')) {
if (block.catch) { if (block.catch) {
e.duplicate_block_part(start, '{:catch}'); e.block_duplicate_clause(start, '{:catch}');
} }
if (!parser.eat('}')) { if (!parser.eat('}')) {
@ -428,7 +428,7 @@ function next(parser) {
e.expected_token(start, '{:then ...} or {:catch ...}'); e.expected_token(start, '{:then ...} or {:catch ...}');
} }
e.invalid_continuing_block_placement(start); e.block_invalid_continuation_placement(start);
} }
/** @param {import('../index.js').Parser} parser */ /** @param {import('../index.js').Parser} parser */
@ -466,11 +466,11 @@ function close(parser) {
case 'RegularElement': case 'RegularElement':
// TODO handle implicitly closed elements // TODO handle implicitly closed elements
e.unexpected_block_close(start); e.block_unexpected_close(start);
break; break;
default: default:
e.unexpected_block_close(start); e.block_unexpected_close(start);
} }
parser.allow_whitespace(); parser.allow_whitespace();
@ -522,7 +522,7 @@ function special(parser) {
identifiers.forEach( identifiers.forEach(
/** @param {any} node */ (node) => { /** @param {any} node */ (node) => {
if (node.type !== 'Identifier') { if (node.type !== 'Identifier') {
e.invalid_debug(/** @type {number} */ (node.start)); e.debug_tag_invalid_arguments(/** @type {number} */ (node.start));
} }
} }
); );
@ -583,7 +583,7 @@ function special(parser) {
expression.expression.type !== 'CallExpression' || expression.expression.type !== 'CallExpression' ||
!expression.expression.optional) !expression.expression.optional)
) { ) {
e.invalid_render_expression(expression); e.render_tag_invalid_expression(expression);
} }
parser.allow_whitespace(); parser.allow_whitespace();

@ -98,26 +98,26 @@ const validation_visitors = {
Rule(node, context) { Rule(node, context) {
if (node.metadata.is_global_block) { if (node.metadata.is_global_block) {
if (node.prelude.children.length > 1) { if (node.prelude.children.length > 1) {
e.invalid_css_global_block_list(node.prelude); e.css_global_block_invalid_list(node.prelude);
} }
const complex_selector = node.prelude.children[0]; const complex_selector = node.prelude.children[0];
const relative_selector = complex_selector.children[complex_selector.children.length - 1]; const relative_selector = complex_selector.children[complex_selector.children.length - 1];
if (relative_selector.selectors.length > 1) { if (relative_selector.selectors.length > 1) {
e.invalid_css_global_block_modifier( e.css_global_block_invalid_modifier(
relative_selector.selectors[relative_selector.selectors.length - 1] relative_selector.selectors[relative_selector.selectors.length - 1]
); );
} }
if (relative_selector.combinator && relative_selector.combinator.name !== ' ') { if (relative_selector.combinator && relative_selector.combinator.name !== ' ') {
e.invalid_css_global_block_combinator(relative_selector, relative_selector.combinator.name); e.css_global_block_invalid_combinator(relative_selector, relative_selector.combinator.name);
} }
const declaration = node.block.children.find((child) => child.type === 'Declaration'); const declaration = node.block.children.find((child) => child.type === 'Declaration');
if (declaration) { if (declaration) {
e.invalid_css_global_block_declaration(declaration); e.css_global_block_invalid_declaration(declaration);
} }
} }
@ -132,7 +132,7 @@ const validation_visitors = {
if (a !== b) { if (a !== b) {
for (let i = a; i <= b; i += 1) { for (let i = a; i <= b; i += 1) {
if (is_global(node.children[i])) { if (is_global(node.children[i])) {
e.invalid_css_global_placement(node.children[i].selectors[0]); e.css_global_invalid_placement(node.children[i].selectors[0]);
} }
} }
} }
@ -147,12 +147,12 @@ const validation_visitors = {
const child = selector.args?.children[0].children[0]; const child = selector.args?.children[0].children[0];
// ensure `:global(element)` to be at the first position in a compound selector // ensure `:global(element)` to be at the first position in a compound selector
if (child?.selectors[0].type === 'TypeSelector' && i !== 0) { if (child?.selectors[0].type === 'TypeSelector' && i !== 0) {
e.invalid_css_global_selector_list(selector); e.css_global_invalid_selector_list(selector);
} }
// ensure `:global(.class)` is not followed by a type selector, eg: `:global(.class)element` // ensure `:global(.class)` is not followed by a type selector, eg: `:global(.class)element`
if (relative_selector.selectors[i + 1]?.type === 'TypeSelector') { if (relative_selector.selectors[i + 1]?.type === 'TypeSelector') {
e.invalid_css_type_selector_placement(relative_selector.selectors[i + 1]); e.css_type_selector_invalid_placement(relative_selector.selectors[i + 1]);
} }
// ensure `:global(...)`contains a single selector // ensure `:global(...)`contains a single selector
@ -162,7 +162,7 @@ const validation_visitors = {
selector.args.children.length > 1 && selector.args.children.length > 1 &&
(node.children.length > 1 || relative_selector.selectors.length > 1) (node.children.length > 1 || relative_selector.selectors.length > 1)
) { ) {
e.invalid_css_global_selector(selector); e.css_global_invalid_selector(selector);
} }
} }
} }
@ -171,7 +171,7 @@ const validation_visitors = {
NestingSelector(node, context) { NestingSelector(node, context) {
const rule = /** @type {import('#compiler').Css.Rule} */ (context.state.rule); const rule = /** @type {import('#compiler').Css.Rule} */ (context.state.rule);
if (!rule.metadata.parent_rule) { if (!rule.metadata.parent_rule) {
e.invalid_nesting_selector(node); e.css_nesting_selector_invalid_placement(node);
} }
} }
}; };

@ -230,7 +230,7 @@ export function analyze_module(ast, options) {
for (const [name, references] of scope.references) { for (const [name, references] of scope.references) {
if (name[0] !== '$' || ReservedKeywords.includes(name)) continue; if (name[0] !== '$' || ReservedKeywords.includes(name)) continue;
if (name === '$' || name[1] === '$') { if (name === '$' || name[1] === '$') {
e.illegal_global(references[0].node, name); e.global_reference_invalid(references[0].node, name);
} }
} }
@ -271,7 +271,7 @@ export function analyze_component(root, source, options) {
for (const [name, references] of module.scope.references) { for (const [name, references] of module.scope.references) {
if (name[0] !== '$' || ReservedKeywords.includes(name)) continue; if (name[0] !== '$' || ReservedKeywords.includes(name)) continue;
if (name === '$' || name[1] === '$') { if (name === '$' || name[1] === '$') {
e.illegal_global(references[0].node, name); e.global_reference_invalid(references[0].node, name);
} }
const store_name = name.slice(1); const store_name = name.slice(1);
@ -311,16 +311,16 @@ export function analyze_component(root, source, options) {
} }
if (is_nested_store_subscription_node) { if (is_nested_store_subscription_node) {
e.illegal_store_subscription(is_nested_store_subscription_node); e.store_invalid_scoped_subscription(is_nested_store_subscription_node);
} }
if (options.runes !== false) { if (options.runes !== false) {
if (declaration === null && /[a-z]/.test(store_name[0])) { if (declaration === null && /[a-z]/.test(store_name[0])) {
e.illegal_global(references[0].node, name); e.global_reference_invalid(references[0].node, name);
} else if (declaration !== null && Runes.includes(/** @type {any} */ (name))) { } else if (declaration !== null && Runes.includes(/** @type {any} */ (name))) {
for (const { node, path } of references) { for (const { node, path } of references) {
if (path.at(-1)?.type === 'CallExpression') { if (path.at(-1)?.type === 'CallExpression') {
w.store_with_rune_name(node, store_name); w.store_rune_conflict(node, store_name);
} }
} }
} }
@ -335,7 +335,7 @@ export function analyze_component(root, source, options) {
// const state = $state(0) is valid // const state = $state(0) is valid
get_rune(/** @type {import('estree').Node} */ (path.at(-1)), module.scope) === null get_rune(/** @type {import('estree').Node} */ (path.at(-1)), module.scope) === null
) { ) {
e.illegal_subscription(node); e.store_invalid_subscription(node);
} }
} }
} }
@ -414,12 +414,12 @@ export function analyze_component(root, source, options) {
if (analysis.runes) { if (analysis.runes) {
const props_refs = module.scope.references.get('$$props'); const props_refs = module.scope.references.get('$$props');
if (props_refs) { if (props_refs) {
e.invalid_legacy_props(props_refs[0].node); e.legacy_props_invalid(props_refs[0].node);
} }
const rest_props_refs = module.scope.references.get('$$restProps'); const rest_props_refs = module.scope.references.get('$$restProps');
if (rest_props_refs) { if (rest_props_refs) {
e.invalid_legacy_rest_props(rest_props_refs[0].node); e.legacy_rest_props_invalid(rest_props_refs[0].node);
} }
for (const { ast, scope, scopes } of [module, instance, template]) { for (const { ast, scope, scopes } of [module, instance, template]) {
@ -444,20 +444,6 @@ export function analyze_component(root, source, options) {
merge(set_scope(scopes), validation_runes, runes_scope_tweaker, common_visitors) merge(set_scope(scopes), validation_runes, runes_scope_tweaker, common_visitors)
); );
} }
if (analysis.exports.length > 0) {
for (const [_, binding] of instance.scope.declarations) {
if (binding.kind === 'prop' || binding.kind === 'bindable_prop') {
if (
analysis.exports.some(
({ alias, name }) => (binding.prop_alias ?? binding.node.name) === (alias ?? name)
)
) {
e.conflicting_property_name(binding.node);
}
}
}
}
} else { } else {
instance.scope.declare(b.id('$$props'), 'bindable_prop', 'synthetic'); instance.scope.declare(b.id('$$props'), 'bindable_prop', 'synthetic');
instance.scope.declare(b.id('$$restProps'), 'rest_prop', 'synthetic'); instance.scope.declare(b.id('$$restProps'), 'rest_prop', 'synthetic');
@ -498,7 +484,7 @@ export function analyze_component(root, source, options) {
(r) => r.node !== binding.node && r.path.at(-1)?.type !== 'ExportSpecifier' (r) => r.node !== binding.node && r.path.at(-1)?.type !== 'ExportSpecifier'
); );
if (!references.length && !instance.scope.declarations.has(`$${name}`)) { if (!references.length && !instance.scope.declarations.has(`$${name}`)) {
w.unused_export_let(binding.node, name); w.export_let_unused(binding.node, name);
} }
} }
} }
@ -507,7 +493,7 @@ export function analyze_component(root, source, options) {
} }
if (analysis.uses_render_tags && (analysis.uses_slots || analysis.slot_names.size > 0)) { if (analysis.uses_render_tags && (analysis.uses_slots || analysis.slot_names.size > 0)) {
e.conflicting_slot_usage(analysis.slot_names.values().next().value); e.slot_snippet_conflict(analysis.slot_names.values().next().value);
} }
// warn on any nonstate declarations that are a) reassigned and b) referenced in the template // warn on any nonstate declarations that are a) reassigned and b) referenced in the template
@ -538,7 +524,7 @@ export function analyze_component(root, source, options) {
type === 'AwaitBlock' || type === 'AwaitBlock' ||
type === 'KeyBlock' type === 'KeyBlock'
) { ) {
w.non_state_reference(binding.node, name); w.non_reactive_update(binding.node, name);
continue outer; continue outer;
} }
} }
@ -546,7 +532,7 @@ export function analyze_component(root, source, options) {
} }
} }
w.non_state_reference(binding.node, name); w.non_reactive_update(binding.node, name);
continue outer; continue outer;
} }
} }
@ -688,7 +674,7 @@ const legacy_scope_tweaker = {
(d) => d.scope === state.analysis.module.scope && d.declaration_kind !== 'const' (d) => d.scope === state.analysis.module.scope && d.declaration_kind !== 'const'
) )
) { ) {
w.module_script_reactive_declaration(node); w.reactive_declaration_module_script(node);
} }
if ( if (
@ -1210,7 +1196,7 @@ const common_visitors = {
binding.kind === 'derived') && binding.kind === 'derived') &&
context.state.function_depth === binding.scope.function_depth context.state.function_depth === binding.scope.function_depth
) { ) {
w.static_state_reference(node); w.state_referenced_locally(node);
} }
} }
}, },
@ -1493,7 +1479,7 @@ function order_reactive_statements(unsorted_reactive_declarations) {
const cycle = check_graph_for_cycles(edges); const cycle = check_graph_for_cycles(edges);
if (cycle?.length) { if (cycle?.length) {
const declaration = /** @type {Tuple[]} */ (lookup.get(cycle[0]))[0]; const declaration = /** @type {Tuple[]} */ (lookup.get(cycle[0]))[0];
e.cyclical_reactive_declaration(declaration[0], cycle.join(' → ')); e.reactive_declaration_cycle(declaration[0], cycle.join(' → '));
} }
// We use a map and take advantage of the fact that the spec says insertion order is preserved when iterating // We use a map and take advantage of the fact that the spec says insertion order is preserved when iterating

@ -1,3 +1,4 @@
import is_reference from 'is-reference';
import { import {
disallowed_paragraph_contents, disallowed_paragraph_contents,
interactive_elements, interactive_elements,
@ -18,6 +19,7 @@ import { binding_properties } from '../bindings.js';
import { import {
ContentEditableBindings, ContentEditableBindings,
EventModifiers, EventModifiers,
Runes,
SVGElements, SVGElements,
VoidElements VoidElements
} from '../constants.js'; } from '../constants.js';
@ -44,14 +46,14 @@ function validate_component(node, context) {
attribute.type !== 'OnDirective' && attribute.type !== 'OnDirective' &&
attribute.type !== 'BindDirective' attribute.type !== 'BindDirective'
) { ) {
e.invalid_component_directive(attribute); e.component_invalid_directive(attribute);
} }
if ( if (
attribute.type === 'OnDirective' && attribute.type === 'OnDirective' &&
(attribute.modifiers.length > 1 || attribute.modifiers.some((m) => m !== 'once')) (attribute.modifiers.length > 1 || attribute.modifiers.some((m) => m !== 'once'))
) { ) {
e.invalid_component_event_modifier(attribute); e.event_handler_invalid_component_modifier(attribute);
} }
if (attribute.type === 'Attribute') { if (attribute.type === 'Attribute') {
@ -62,7 +64,7 @@ function validate_component(node, context) {
while (--i > 0) { while (--i > 0) {
const char = context.state.analysis.source[i]; const char = context.state.analysis.source[i];
if (char === '(') break; // parenthesized sequence expressions are ok if (char === '(') break; // parenthesized sequence expressions are ok
if (char === '{') e.invalid_sequence_expression(expression); if (char === '{') e.attribute_invalid_sequence_expression(expression);
} }
} }
} }
@ -111,18 +113,18 @@ function validate_element(node, context) {
while (--i > 0) { while (--i > 0) {
const char = context.state.analysis.source[i]; const char = context.state.analysis.source[i];
if (char === '(') break; // parenthesized sequence expressions are ok if (char === '(') break; // parenthesized sequence expressions are ok
if (char === '{') e.invalid_sequence_expression(expression); if (char === '{') e.attribute_invalid_sequence_expression(expression);
} }
} }
} }
if (regex_illegal_attribute_character.test(attribute.name)) { if (regex_illegal_attribute_character.test(attribute.name)) {
e.invalid_attribute_name(attribute, attribute.name); e.attribute_invalid_name(attribute, attribute.name);
} }
if (attribute.name.startsWith('on') && attribute.name.length > 2) { if (attribute.name.startsWith('on') && attribute.name.length > 2) {
if (!is_expression) { if (!is_expression) {
e.invalid_event_attribute_value(attribute); e.attribute_invalid_event_handler(attribute);
} }
const value = attribute.value[0].expression; const value = attribute.value[0].expression;
@ -131,7 +133,7 @@ function validate_element(node, context) {
value.name === attribute.name && value.name === attribute.name &&
!context.state.scope.get(value.name) !context.state.scope.get(value.name)
) { ) {
w.global_event_reference(attribute, attribute.name); w.attribute_global_event_reference(attribute, attribute.name);
} }
} }
@ -141,12 +143,12 @@ function validate_element(node, context) {
} }
if (attribute.name === 'is' && context.state.options.namespace !== 'foreign') { if (attribute.name === 'is' && context.state.options.namespace !== 'foreign') {
w.avoid_is(attribute); w.attribute_avoid_is(attribute);
} }
const correct_name = react_attributes.get(attribute.name); const correct_name = react_attributes.get(attribute.name);
if (correct_name) { if (correct_name) {
w.invalid_html_attribute(attribute, attribute.name, correct_name); w.attribute_invalid_property_name(attribute, attribute.name, correct_name);
} }
validate_attribute_name(attribute); validate_attribute_name(attribute);
@ -196,7 +198,7 @@ function validate_element(node, context) {
for (const modifier of attribute.modifiers) { for (const modifier of attribute.modifiers) {
if (!EventModifiers.includes(modifier)) { if (!EventModifiers.includes(modifier)) {
const list = `${EventModifiers.slice(0, -1).join(', ')} or ${EventModifiers.at(-1)}`; const list = `${EventModifiers.slice(0, -1).join(', ')} or ${EventModifiers.at(-1)}`;
e.invalid_event_modifier(attribute, list); e.event_handler_invalid_modifier(attribute, list);
} }
if (modifier === 'passive') { if (modifier === 'passive') {
has_passive_modifier = true; has_passive_modifier = true;
@ -204,7 +206,11 @@ function validate_element(node, context) {
conflicting_passive_modifier = modifier; conflicting_passive_modifier = modifier;
} }
if (has_passive_modifier && conflicting_passive_modifier) { if (has_passive_modifier && conflicting_passive_modifier) {
e.invalid_event_modifier_combination(attribute, 'passive', conflicting_passive_modifier); e.event_handler_invalid_modifier_combination(
attribute,
'passive',
conflicting_passive_modifier
);
} }
} }
} }
@ -221,7 +227,7 @@ function validate_attribute_name(attribute) {
!attribute.name.startsWith('xlink:') && !attribute.name.startsWith('xlink:') &&
!attribute.name.startsWith('xml:') !attribute.name.startsWith('xml:')
) { ) {
w.illegal_attribute_character(attribute); w.attribute_illegal_colon(attribute);
} }
} }
@ -249,7 +255,7 @@ function validate_slot_attribute(context, attribute) {
if (owner) { if (owner) {
if (!is_text_attribute(attribute)) { if (!is_text_attribute(attribute)) {
e.invalid_slot_attribute(attribute); e.slot_attribute_invalid(attribute);
} }
if ( if (
@ -258,13 +264,13 @@ function validate_slot_attribute(context, attribute) {
owner.type === 'SvelteSelf' owner.type === 'SvelteSelf'
) { ) {
if (owner !== context.path.at(-2)) { if (owner !== context.path.at(-2)) {
e.invalid_slot_placement(attribute); e.slot_attribute_invalid_placement(attribute);
} }
const name = attribute.value[0].data; const name = attribute.value[0].data;
if (context.state.component_slots.has(name)) { if (context.state.component_slots.has(name)) {
e.duplicate_slot_name(attribute, name, owner.name); e.slot_attribute_duplicate(attribute, name, owner.name);
} }
context.state.component_slots.add(name); context.state.component_slots.add(name);
@ -281,12 +287,12 @@ function validate_slot_attribute(context, attribute) {
} }
} }
e.invalid_default_slot_content(node); e.slot_default_duplicate(node);
} }
} }
} }
} else { } else {
e.invalid_slot_placement(attribute); e.slot_attribute_invalid_placement(attribute);
} }
} }
@ -299,7 +305,7 @@ function validate_block_not_empty(node, context) {
// Assumption: If the block has zero elements, someone's in the middle of typing it out, // Assumption: If the block has zero elements, someone's in the middle of typing it out,
// so don't warn in that case because it would be distracting. // so don't warn in that case because it would be distracting.
if (node.nodes.length === 1 && node.nodes[0].type === 'Text' && !node.nodes[0].raw.trim()) { if (node.nodes.length === 1 && node.nodes[0].type === 'Text' && !node.nodes[0].raw.trim()) {
w.empty_block(node.nodes[0]); w.block_empty(node.nodes[0]);
} }
} }
@ -317,7 +323,7 @@ const validation = {
const left = object(assignee); const left = object(assignee);
if (left === null) { if (left === null) {
e.invalid_binding_expression(node); e.bind_invalid_expression(node);
} }
const binding = context.state.scope.get(left.name); const binding = context.state.scope.get(left.name);
@ -337,19 +343,19 @@ const validation = {
binding.kind !== 'store_sub' && binding.kind !== 'store_sub' &&
!binding.mutated) !binding.mutated)
) { ) {
e.invalid_binding_value(node.expression); e.bind_invalid_value(node.expression);
} }
if (binding.kind === 'derived') { if (binding.kind === 'derived') {
e.invalid_binding(node.expression, 'derived state'); e.constant_binding(node.expression, 'derived state');
} }
if (context.state.analysis.runes && binding.kind === 'each') { if (context.state.analysis.runes && binding.kind === 'each') {
e.invalid_each_assignment(node); e.each_item_invalid_assignment(node);
} }
if (binding.kind === 'snippet') { if (binding.kind === 'snippet') {
e.invalid_snippet_assignment(node); e.snippet_parameter_assignment(node);
} }
} }
@ -360,7 +366,7 @@ const validation = {
} }
if (binding?.kind === 'each' && binding.metadata?.inside_rest) { if (binding?.kind === 'each' && binding.metadata?.inside_rest) {
w.invalid_rest_eachblock_binding(binding.node, binding.node.name); w.bind_invalid_each_rest(binding.node, binding.node.name);
} }
const parent = context.path.at(-1); const parent = context.path.at(-1);
@ -373,7 +379,7 @@ const validation = {
parent?.type === 'SvelteBody' parent?.type === 'SvelteBody'
) { ) {
if (context.state.options.namespace === 'foreign' && node.name !== 'this') { if (context.state.options.namespace === 'foreign' && node.name !== 'this') {
e.bind_invalid(node, node.name, 'Foreign elements only support `bind:this`'); e.bind_invalid_name(node, node.name, 'Foreign elements only support `bind:this`');
} }
if (node.name in binding_properties) { if (node.name in binding_properties) {
@ -392,7 +398,7 @@ const validation = {
); );
if (type && !is_text_attribute(type)) { if (type && !is_text_attribute(type)) {
if (node.name !== 'value' || type.value === true) { if (node.name !== 'value' || type.value === true) {
e.invalid_type_attribute(type); e.attribute_invalid_type(type);
} }
return; // bind:value can handle dynamic `type` attributes return; // bind:value can handle dynamic `type` attributes
} }
@ -415,7 +421,7 @@ const validation = {
a.value !== true a.value !== true
); );
if (multiple) { if (multiple) {
e.invalid_multiple_attribute(multiple); e.attribute_invalid_multiple(multiple);
} }
} }
@ -432,9 +438,9 @@ const validation = {
parent.attributes.find((a) => a.type === 'Attribute' && a.name === 'contenteditable') parent.attributes.find((a) => a.type === 'Attribute' && a.name === 'contenteditable')
); );
if (!contenteditable) { if (!contenteditable) {
e.missing_contenteditable_attribute(node); e.attribute_contenteditable_missing(node);
} else if (!is_text_attribute(contenteditable) && contenteditable.value !== true) { } else if (!is_text_attribute(contenteditable) && contenteditable.value !== true) {
e.dynamic_contenteditable_attribute(contenteditable); e.attribute_contenteditable_dynamic(contenteditable);
} }
} }
} else { } else {
@ -442,15 +448,15 @@ const validation = {
if (match) { if (match) {
const property = binding_properties[match]; const property = binding_properties[match];
if (!property.valid_elements || property.valid_elements.includes(parent.name)) { if (!property.valid_elements || property.valid_elements.includes(parent.name)) {
e.bind_invalid(node, node.name, `Did you mean '${match}'?`); e.bind_invalid_name(node, node.name, `Did you mean '${match}'?`);
} }
} }
e.bind_invalid(node, node.name); e.bind_invalid_name(node, node.name);
} }
} }
}, },
ExportDefaultDeclaration(node) { ExportDefaultDeclaration(node) {
e.default_export(node); e.module_illegal_default_export(node);
}, },
ConstTag(node, context) { ConstTag(node, context) {
const parent = context.path.at(-1); const parent = context.path.at(-1);
@ -467,7 +473,7 @@ const validation = {
((grand_parent?.type !== 'RegularElement' && grand_parent?.type !== 'SvelteElement') || ((grand_parent?.type !== 'RegularElement' && grand_parent?.type !== 'SvelteElement') ||
!grand_parent.attributes.some((a) => a.type === 'Attribute' && a.name === 'slot'))) !grand_parent.attributes.some((a) => a.type === 'Attribute' && a.name === 'slot')))
) { ) {
e.invalid_const_placement(node); e.const_tag_invalid_placement(node);
} }
}, },
ImportDeclaration(node, context) { ImportDeclaration(node, context) {
@ -478,7 +484,7 @@ const validation = {
specifier.imported.name === 'beforeUpdate' || specifier.imported.name === 'beforeUpdate' ||
specifier.imported.name === 'afterUpdate' specifier.imported.name === 'afterUpdate'
) { ) {
e.invalid_runes_mode_import(specifier, specifier.imported.name); e.runes_mode_invalid_import(specifier, specifier.imported.name);
} }
} }
} }
@ -496,14 +502,14 @@ const validation = {
parent.type !== 'SvelteSelf' && parent.type !== 'SvelteSelf' &&
parent.type !== 'SvelteFragment') parent.type !== 'SvelteFragment')
) { ) {
e.invalid_let_directive_placement(node); e.let_directive_invalid_placement(node);
} }
}, },
RegularElement(node, context) { RegularElement(node, context) {
if (node.name === 'textarea' && node.fragment.nodes.length > 0) { if (node.name === 'textarea' && node.fragment.nodes.length > 0) {
for (const attribute of node.attributes) { for (const attribute of node.attributes) {
if (attribute.type === 'Attribute' && attribute.name === 'value') { if (attribute.type === 'Attribute' && attribute.name === 'value') {
e.invalid_textarea_content(node); e.textarea_invalid_content(node);
} }
} }
} }
@ -521,7 +527,7 @@ const validation = {
if (context.state.parent_element) { if (context.state.parent_element) {
if (!is_tag_valid_with_parent(node.name, context.state.parent_element)) { if (!is_tag_valid_with_parent(node.name, context.state.parent_element)) {
e.invalid_node_placement(node, `<${node.name}>`, context.state.parent_element); e.node_invalid_placement(node, `<${node.name}>`, context.state.parent_element);
} }
} }
@ -533,7 +539,7 @@ const validation = {
parent.name === node.name && parent.name === node.name &&
interactive_elements.has(parent.name) interactive_elements.has(parent.name)
) { ) {
e.invalid_node_placement(node, `<${node.name}>`, parent.name); e.node_invalid_placement(node, `<${node.name}>`, parent.name);
} }
} }
} }
@ -542,7 +548,7 @@ const validation = {
const path = context.path; const path = context.path;
for (let parent of path) { for (let parent of path) {
if (parent.type === 'RegularElement' && parent.name === 'p') { if (parent.type === 'RegularElement' && parent.name === 'p') {
e.invalid_node_placement(node, `<${node.name}>`, parent.name); e.node_invalid_placement(node, `<${node.name}>`, parent.name);
} }
} }
} }
@ -553,7 +559,7 @@ const validation = {
!VoidElements.includes(node.name) && !VoidElements.includes(node.name) &&
!SVGElements.includes(node.name) !SVGElements.includes(node.name)
) { ) {
w.invalid_self_closing_tag(node, node.name); w.element_invalid_self_closing_tag(node, node.name);
} }
context.next({ context.next({
@ -567,7 +573,7 @@ const validation = {
const raw_args = unwrap_optional(node.expression).arguments; const raw_args = unwrap_optional(node.expression).arguments;
for (const arg of raw_args) { for (const arg of raw_args) {
if (arg.type === 'SpreadElement') { if (arg.type === 'SpreadElement') {
e.invalid_render_spread_argument(arg); e.render_tag_invalid_spread_argument(arg);
} }
} }
@ -577,7 +583,7 @@ const validation = {
callee.property.type === 'Identifier' && callee.property.type === 'Identifier' &&
['bind', 'apply', 'call'].includes(callee.property.name) ['bind', 'apply', 'call'].includes(callee.property.name)
) { ) {
e.invalid_render_call(node); e.render_tag_invalid_call_expression(node);
} }
const is_inside_textarea = context.path.find((n) => { const is_inside_textarea = context.path.find((n) => {
@ -589,7 +595,7 @@ const validation = {
); );
}); });
if (is_inside_textarea) { if (is_inside_textarea) {
e.invalid_tag_placement( e.tag_invalid_placement(
node, node,
'inside <textarea> or <svelte:element this="textarea">', 'inside <textarea> or <svelte:element this="textarea">',
'render' 'render'
@ -630,19 +636,19 @@ const validation = {
(node) => node.type !== 'SnippetBlock' && (node.type !== 'Text' || node.data.trim()) (node) => node.type !== 'SnippetBlock' && (node.type !== 'Text' || node.data.trim())
) )
) { ) {
e.conflicting_children_snippet(node); e.snippet_conflict(node);
} }
} }
}, },
StyleDirective(node) { StyleDirective(node) {
if (node.modifiers.length > 1 || (node.modifiers.length && node.modifiers[0] !== 'important')) { if (node.modifiers.length > 1 || (node.modifiers.length && node.modifiers[0] !== 'important')) {
e.invalid_style_directive_modifier(node); e.style_directive_invalid_modifier(node);
} }
}, },
SvelteHead(node) { SvelteHead(node) {
const attribute = node.attributes[0]; const attribute = node.attributes[0];
if (attribute) { if (attribute) {
e.illegal_svelte_head_attribute(attribute); e.svelte_head_illegal_attribute(attribute);
} }
}, },
SvelteElement(node, context) { SvelteElement(node, context) {
@ -655,7 +661,7 @@ const validation = {
SvelteFragment(node, context) { SvelteFragment(node, context) {
const parent = context.path.at(-2); const parent = context.path.at(-2);
if (parent?.type !== 'Component' && parent?.type !== 'SvelteComponent') { if (parent?.type !== 'Component' && parent?.type !== 'SvelteComponent') {
e.invalid_svelte_fragment_placement(node); e.svelte_fragment_invalid_placement(node);
} }
for (const attribute of node.attributes) { for (const attribute of node.attributes) {
@ -664,7 +670,7 @@ const validation = {
validate_slot_attribute(context, attribute); validate_slot_attribute(context, attribute);
} }
} else if (attribute.type !== 'LetDirective') { } else if (attribute.type !== 'LetDirective') {
e.invalid_svelte_fragment_attribute(attribute); e.svelte_fragment_invalid_attribute(attribute);
} }
} }
}, },
@ -673,15 +679,15 @@ const validation = {
if (attribute.type === 'Attribute') { if (attribute.type === 'Attribute') {
if (attribute.name === 'name') { if (attribute.name === 'name') {
if (!is_text_attribute(attribute)) { if (!is_text_attribute(attribute)) {
e.invalid_slot_name(attribute); e.slot_element_invalid_name(attribute);
} }
const slot_name = attribute.value[0].data; const slot_name = attribute.value[0].data;
if (slot_name === 'default') { if (slot_name === 'default') {
e.invalid_slot_name_default(attribute); e.slot_element_invalid_name_default(attribute);
} }
} }
} else if (attribute.type !== 'SpreadAttribute' && attribute.type !== 'LetDirective') { } else if (attribute.type !== 'SpreadAttribute' && attribute.type !== 'LetDirective') {
e.invalid_slot_element_attribute(attribute); e.slot_element_invalid_attribute(attribute);
} }
} }
}, },
@ -692,19 +698,19 @@ const validation = {
if (!node.parent) return; if (!node.parent) return;
if (context.state.parent_element && regex_not_whitespace.test(node.data)) { if (context.state.parent_element && regex_not_whitespace.test(node.data)) {
if (!is_tag_valid_with_parent('#text', context.state.parent_element)) { if (!is_tag_valid_with_parent('#text', context.state.parent_element)) {
e.invalid_node_placement(node, 'Text node', context.state.parent_element); e.node_invalid_placement(node, 'Text node', context.state.parent_element);
} }
} }
}, },
TitleElement(node) { TitleElement(node) {
const attribute = node.attributes[0]; const attribute = node.attributes[0];
if (attribute) { if (attribute) {
e.illegal_title_attribute(attribute); e.title_illegal_attribute(attribute);
} }
const child = node.fragment.nodes.find((n) => n.type !== 'Text' && n.type !== 'ExpressionTag'); const child = node.fragment.nodes.find((n) => n.type !== 'Text' && n.type !== 'ExpressionTag');
if (child) { if (child) {
e.invalid_title_content(child); e.title_invalid_content(child);
} }
}, },
UpdateExpression(node, context) { UpdateExpression(node, context) {
@ -714,7 +720,7 @@ const validation = {
if (!node.parent) return; if (!node.parent) return;
if (context.state.parent_element) { if (context.state.parent_element) {
if (!is_tag_valid_with_parent('#text', context.state.parent_element)) { if (!is_tag_valid_with_parent('#text', context.state.parent_element)) {
e.invalid_node_placement(node, '{expression}', context.state.parent_element); e.node_invalid_placement(node, '{expression}', context.state.parent_element);
} }
} }
} }
@ -735,7 +741,7 @@ export const validation_legacy = merge(validation, a11y_validators, {
} }
if (state.scope.get(callee.name)?.kind !== 'store_sub') { if (state.scope.get(callee.name)?.kind !== 'store_sub') {
e.invalid_rune_usage(node.init, callee.name); e.rune_invalid_usage(node.init, callee.name);
} }
}, },
AssignmentExpression(node, { state, path }) { AssignmentExpression(node, { state, path }) {
@ -749,7 +755,7 @@ export const validation_legacy = merge(validation, a11y_validators, {
(state.ast_type !== 'instance' || (state.ast_type !== 'instance' ||
/** @type {import('#compiler').SvelteNode} */ (path.at(-1)).type !== 'Program') /** @type {import('#compiler').SvelteNode} */ (path.at(-1)).type !== 'Program')
) { ) {
w.no_reactive_declaration(node); w.reactive_declaration_invalid_placement(node);
} }
}, },
UpdateExpression(node, { state }) { UpdateExpression(node, { state }) {
@ -768,11 +774,11 @@ function validate_export(node, scope, name) {
if (!binding) return; if (!binding) return;
if (binding.kind === 'derived') { if (binding.kind === 'derived') {
e.invalid_derived_export(node); e.derived_invalid_export(node);
} }
if ((binding.kind === 'state' || binding.kind === 'frozen_state') && binding.reassigned) { if ((binding.kind === 'state' || binding.kind === 'frozen_state') && binding.reassigned) {
e.invalid_state_export(node); e.state_invalid_export(node);
} }
} }
@ -790,7 +796,7 @@ function validate_call_expression(node, scope, path) {
if (rune === '$props') { if (rune === '$props') {
if (parent.type === 'VariableDeclarator') return; if (parent.type === 'VariableDeclarator') return;
e.invalid_props_location(node); e.props_invalid_placement(node);
} }
if (rune === '$bindable') { if (rune === '$bindable') {
@ -803,7 +809,7 @@ function validate_call_expression(node, scope, path) {
return; return;
} }
} }
e.invalid_bindable_location(node); e.bindable_invalid_location(node);
} }
if ( if (
@ -814,46 +820,46 @@ function validate_call_expression(node, scope, path) {
) { ) {
if (parent.type === 'VariableDeclarator') return; if (parent.type === 'VariableDeclarator') return;
if (parent.type === 'PropertyDefinition' && !parent.static && !parent.computed) return; if (parent.type === 'PropertyDefinition' && !parent.static && !parent.computed) return;
e.invalid_state_location(node, rune); e.state_invalid_placement(node, rune);
} }
if (rune === '$effect' || rune === '$effect.pre') { if (rune === '$effect' || rune === '$effect.pre') {
if (parent.type !== 'ExpressionStatement') { if (parent.type !== 'ExpressionStatement') {
e.invalid_effect_location(node); e.effect_invalid_placement(node);
} }
if (node.arguments.length !== 1) { if (node.arguments.length !== 1) {
e.invalid_rune_args_length(node, rune, 'exactly one argument'); e.rune_invalid_arguments_length(node, rune, 'exactly one argument');
} }
} }
if (rune === '$effect.active') { if (rune === '$effect.active') {
if (node.arguments.length !== 0) { if (node.arguments.length !== 0) {
e.invalid_rune_args(node, rune); e.rune_invalid_arguments(node, rune);
} }
} }
if (rune === '$effect.root') { if (rune === '$effect.root') {
if (node.arguments.length !== 1) { if (node.arguments.length !== 1) {
e.invalid_rune_args_length(node, rune, 'exactly one argument'); e.rune_invalid_arguments_length(node, rune, 'exactly one argument');
} }
} }
if (rune === '$inspect') { if (rune === '$inspect') {
if (node.arguments.length < 1) { if (node.arguments.length < 1) {
e.invalid_rune_args_length(node, rune, 'one or more arguments'); e.rune_invalid_arguments_length(node, rune, 'one or more arguments');
} }
} }
if (rune === '$inspect().with') { if (rune === '$inspect().with') {
if (node.arguments.length !== 1) { if (node.arguments.length !== 1) {
e.invalid_rune_args_length(node, rune, 'exactly one argument'); e.rune_invalid_arguments_length(node, rune, 'exactly one argument');
} }
} }
if (rune === '$state.snapshot') { if (rune === '$state.snapshot') {
if (node.arguments.length !== 1) { if (node.arguments.length !== 1) {
e.invalid_rune_args_length(node, rune, 'exactly one argument'); e.rune_invalid_arguments_length(node, rune, 'exactly one argument');
} }
} }
} }
@ -869,7 +875,7 @@ function ensure_no_module_import_conflict(node, state) {
state.scope === state.analysis.instance.scope && state.scope === state.analysis.instance.scope &&
state.analysis.module.scope.get(id.name)?.declaration_kind === 'import' state.analysis.module.scope.get(id.name)?.declaration_kind === 'import'
) { ) {
e.illegal_variable_declaration(node.id); e.declaration_duplicate_module_import(node.id);
} }
} }
} }
@ -895,7 +901,7 @@ export const validation_runes_js = {
}, },
CallExpression(node, { state, path }) { CallExpression(node, { state, path }) {
if (get_rune(node, state.scope) === '$host') { if (get_rune(node, state.scope) === '$host') {
e.invalid_host_location(node); e.host_invalid_placement(node);
} }
validate_call_expression(node, state.scope, path); validate_call_expression(node, state.scope, path);
}, },
@ -908,13 +914,13 @@ export const validation_runes_js = {
const args = /** @type {import('estree').CallExpression} */ (init).arguments; const args = /** @type {import('estree').CallExpression} */ (init).arguments;
if ((rune === '$derived' || rune === '$derived.by') && args.length !== 1) { if ((rune === '$derived' || rune === '$derived.by') && args.length !== 1) {
e.invalid_rune_args_length(node, rune, 'exactly one argument'); e.rune_invalid_arguments_length(node, rune, 'exactly one argument');
} else if (rune === '$state' && args.length > 1) { } else if (rune === '$state' && args.length > 1) {
e.invalid_rune_args_length(node, rune, 'zero or one arguments'); e.rune_invalid_arguments_length(node, rune, 'zero or one arguments');
} else if (rune === '$props') { } else if (rune === '$props') {
e.invalid_props_location(node); e.props_invalid_placement(node);
} else if (rune === '$bindable') { } else if (rune === '$bindable') {
e.invalid_bindable_location(node); e.bindable_invalid_location(node);
} }
}, },
AssignmentExpression(node, { state }) { AssignmentExpression(node, { state }) {
@ -952,12 +958,12 @@ export const validation_runes_js = {
const allowed_depth = context.state.ast_type === 'module' ? 0 : 1; const allowed_depth = context.state.ast_type === 'module' ? 0 : 1;
if (context.state.scope.function_depth > allowed_depth) { if (context.state.scope.function_depth > allowed_depth) {
w.avoid_nested_class(node); w.perf_avoid_nested_class(node);
} }
}, },
NewExpression(node, context) { NewExpression(node, context) {
if (node.callee.type === 'ClassExpression' && context.state.scope.function_depth > 0) { if (node.callee.type === 'ClassExpression' && context.state.scope.function_depth > 0) {
w.avoid_inline_class(node); w.perf_avoid_inline_class(node);
} }
} }
}; };
@ -998,9 +1004,9 @@ function validate_no_const_assignment(node, argument, scope, is_binding) {
const thing = 'constant'; const thing = 'constant';
if (is_binding) { if (is_binding) {
e.invalid_binding(node, thing); e.constant_binding(node, thing);
} else { } else {
e.invalid_assignment(node, thing); e.constant_assignment(node, thing);
} }
} }
} }
@ -1019,16 +1025,16 @@ function validate_assignment(node, argument, state) {
if (state.analysis.runes) { if (state.analysis.runes) {
if (binding?.kind === 'derived') { if (binding?.kind === 'derived') {
e.invalid_assignment(node, 'derived state'); e.constant_assignment(node, 'derived state');
} }
if (binding?.kind === 'each') { if (binding?.kind === 'each') {
e.invalid_each_assignment(node); e.each_item_invalid_assignment(node);
} }
} }
if (binding?.kind === 'snippet') { if (binding?.kind === 'snippet') {
e.invalid_snippet_assignment(node); e.snippet_parameter_assignment(node);
} }
} }
@ -1044,15 +1050,46 @@ function validate_assignment(node, argument, state) {
if (object.type === 'ThisExpression' && property?.type === 'PrivateIdentifier') { if (object.type === 'ThisExpression' && property?.type === 'PrivateIdentifier') {
if (state.private_derived_state.includes(property.name)) { if (state.private_derived_state.includes(property.name)) {
e.invalid_assignment(node, 'derived state'); e.constant_assignment(node, 'derived state');
} }
} }
} }
export const validation_runes = merge(validation, a11y_validators, { export const validation_runes = merge(validation, a11y_validators, {
Identifier(node, { path, state }) {
let i = path.length;
let parent = /** @type {import('estree').Expression} */ (path[--i]);
if (
Runes.includes(/** @type {Runes[number]} */ (node.name)) &&
is_reference(node, parent) &&
state.scope.get(node.name) === null &&
state.scope.get(node.name.slice(1)) === null
) {
/** @type {import('estree').Expression} */
let current = node;
let name = node.name;
while (parent.type === 'MemberExpression') {
if (parent.computed) e.rune_invalid_computed_property(parent);
name += `.${/** @type {import('estree').Identifier} */ (parent.property).name}`;
current = parent;
parent = /** @type {import('estree').Expression} */ (path[--i]);
if (!Runes.includes(/** @type {Runes[number]} */ (name))) {
e.rune_invalid_name(parent, name);
}
}
if (parent.type !== 'CallExpression') {
e.rune_missing_parentheses(current);
}
}
},
LabeledStatement(node, { path }) { LabeledStatement(node, { path }) {
if (node.label.name !== '$' || path.at(-1)?.type !== 'Program') return; if (node.label.name !== '$' || path.at(-1)?.type !== 'Program') return;
e.invalid_legacy_reactive_statement(node); e.legacy_reactive_statement_invalid(node);
}, },
ExportNamedDeclaration(node, { state, next }) { ExportNamedDeclaration(node, { state, next }) {
if (state.ast_type === 'module') { if (state.ast_type === 'module') {
@ -1070,7 +1107,7 @@ export const validation_runes = merge(validation, a11y_validators, {
if (node.declaration?.type !== 'VariableDeclaration') return; if (node.declaration?.type !== 'VariableDeclaration') return;
if (node.declaration.kind !== 'let') return; if (node.declaration.kind !== 'let') return;
if (state.analysis.instance.scope !== state.scope) return; if (state.analysis.instance.scope !== state.scope) return;
e.invalid_legacy_export(node); e.legacy_export_invalid(node);
} }
}, },
ExportSpecifier(node, { state }) { ExportSpecifier(node, { state }) {
@ -1081,12 +1118,12 @@ export const validation_runes = merge(validation, a11y_validators, {
CallExpression(node, { state, path }) { CallExpression(node, { state, path }) {
const rune = get_rune(node, state.scope); const rune = get_rune(node, state.scope);
if (rune === '$bindable' && node.arguments.length > 1) { if (rune === '$bindable' && node.arguments.length > 1) {
e.invalid_rune_args_length(node, '$bindable', 'zero or one arguments'); e.rune_invalid_arguments_length(node, '$bindable', 'zero or one arguments');
} else if (rune === '$host') { } else if (rune === '$host') {
if (node.arguments.length > 0) { if (node.arguments.length > 0) {
e.invalid_rune_args(node, '$host'); e.rune_invalid_arguments(node, '$host');
} else if (state.ast_type === 'module' || !state.analysis.custom_element) { } else if (state.ast_type === 'module' || !state.analysis.custom_element) {
e.invalid_host_location(node); e.host_invalid_placement(node);
} }
} }
@ -1098,7 +1135,7 @@ export const validation_runes = merge(validation, a11y_validators, {
context.type === 'Identifier' && context.type === 'Identifier' &&
(context.name === '$state' || context.name === '$derived') (context.name === '$state' || context.name === '$derived')
) { ) {
e.invalid_state_location(node, context.name); e.state_invalid_placement(node, context.name);
} }
next({ ...state }); next({ ...state });
}, },
@ -1108,50 +1145,45 @@ export const validation_runes = merge(validation, a11y_validators, {
const init = node.init; const init = node.init;
const rune = get_rune(init, state.scope); const rune = get_rune(init, state.scope);
if (rune === null) { if (rune === null) return;
if (init?.type === 'Identifier' && init.name === '$props' && !state.scope.get('props')) {
w.invalid_props_declaration(node);
}
return;
}
const args = /** @type {import('estree').CallExpression} */ (init).arguments; const args = /** @type {import('estree').CallExpression} */ (init).arguments;
// TODO some of this is duplicated with above, seems off // TODO some of this is duplicated with above, seems off
if ((rune === '$derived' || rune === '$derived.by') && args.length !== 1) { if ((rune === '$derived' || rune === '$derived.by') && args.length !== 1) {
e.invalid_rune_args_length(node, rune, 'exactly one argument'); e.rune_invalid_arguments_length(node, rune, 'exactly one argument');
} else if (rune === '$state' && args.length > 1) { } else if (rune === '$state' && args.length > 1) {
e.invalid_rune_args_length(node, rune, 'zero or one arguments'); e.rune_invalid_arguments_length(node, rune, 'zero or one arguments');
} else if (rune === '$props') { } else if (rune === '$props') {
if (state.has_props_rune) { if (state.has_props_rune) {
e.duplicate_props_rune(node); e.props_duplicate(node);
} }
state.has_props_rune = true; state.has_props_rune = true;
if (args.length > 0) { if (args.length > 0) {
e.invalid_rune_args(node, rune); e.rune_invalid_arguments(node, rune);
} }
if (node.id.type !== 'ObjectPattern') { if (node.id.type !== 'ObjectPattern') {
e.invalid_props_id(node); e.props_invalid_identifier(node);
} }
if (state.scope !== state.analysis.instance.scope) { if (state.scope !== state.analysis.instance.scope) {
e.invalid_props_location(node); e.props_invalid_placement(node);
} }
for (const property of node.id.properties) { for (const property of node.id.properties) {
if (property.type === 'Property') { if (property.type === 'Property') {
if (property.computed) { if (property.computed) {
e.invalid_props_pattern(property); e.props_invalid_pattern(property);
} }
const value = const value =
property.value.type === 'AssignmentPattern' ? property.value.left : property.value; property.value.type === 'AssignmentPattern' ? property.value.left : property.value;
if (value.type !== 'Identifier') { if (value.type !== 'Identifier') {
e.invalid_props_pattern(property); e.props_invalid_pattern(property);
} }
} }
} }
@ -1167,25 +1199,16 @@ export const validation_runes = merge(validation, a11y_validators, {
} }
} }
}, },
AssignmentPattern(node, { state }) {
if (
node.right.type === 'Identifier' &&
node.right.name === '$bindable' &&
!state.scope.get('bindable')
) {
w.invalid_bindable_declaration(node);
}
},
SlotElement(node, { state }) { SlotElement(node, { state }) {
if (!state.analysis.custom_element) { if (!state.analysis.custom_element) {
w.deprecated_slot_element(node); w.slot_element_deprecated(node);
} }
}, },
OnDirective(node, { path }) { OnDirective(node, { path }) {
const parent_type = path.at(-1)?.type; const parent_type = path.at(-1)?.type;
// Don't warn on component events; these might not be under the author's control so the warning would be unactionable // Don't warn on component events; these might not be under the author's control so the warning would be unactionable
if (parent_type === 'RegularElement' || parent_type === 'SvelteElement') { if (parent_type === 'RegularElement' || parent_type === 'SvelteElement') {
w.deprecated_event_handler(node, node.name); w.event_directive_deprecated(node, node.name);
} }
}, },
// TODO this is a code smell. need to refactor this stuff // TODO this is a code smell. need to refactor this stuff

@ -70,7 +70,7 @@ export class Scope {
*/ */
declare(node, kind, declaration_kind, initial = null) { declare(node, kind, declaration_kind, initial = null) {
if (node.name === '$') { if (node.name === '$') {
e.invalid_dollar_binding(node); e.dollar_binding_invalid(node);
} }
if ( if (
@ -80,7 +80,7 @@ export class Scope {
declaration_kind !== 'rest_param' && declaration_kind !== 'rest_param' &&
this.function_depth <= 1 this.function_depth <= 1
) { ) {
e.invalid_dollar_prefix(node); e.dollar_prefix_invalid(node);
} }
if (this.parent) { if (this.parent) {
@ -95,7 +95,7 @@ export class Scope {
if (this.declarations.has(node.name)) { if (this.declarations.has(node.name)) {
// This also errors on var/function types, but that's arguably a good thing // This also errors on var/function types, but that's arguably a good thing
e.duplicate_declaration(node, node.name); e.declaration_duplicate(node, node.name);
} }
/** @type {import('#compiler').Binding} */ /** @type {import('#compiler').Binding} */
@ -767,7 +767,6 @@ export function get_rune(node, scope) {
joined = n.name + joined; joined = n.name + joined;
if (joined === '$derived.call') e.invalid_derived_call(node);
if (!Runes.includes(/** @type {any} */ (joined))) return null; if (!Runes.includes(/** @type {any} */ (joined))) return null;
const binding = scope.get(n.name); const binding = scope.get(n.name);

@ -133,7 +133,7 @@ export const validate_component_options =
function removed(msg) { function removed(msg) {
return (input) => { return (input) => {
if (input !== undefined) { if (input !== undefined) {
e.removed_compiler_option(null, msg); e.options_removed(null, msg);
} }
return /** @type {any} */ (undefined); return /** @type {any} */ (undefined);
}; };
@ -191,10 +191,7 @@ function object(children, allow_unknown = false) {
if (allow_unknown) { if (allow_unknown) {
output[key] = input[key]; output[key] = input[key];
} else { } else {
e.invalid_compiler_option( e.options_unrecognised(null, `${keypath ? `${keypath}.${key}` : key}`);
null,
`Unexpected option ${keypath ? `${keypath}.${key}` : key}`
);
} }
} }
} }
@ -297,5 +294,5 @@ function fun(fallback) {
/** @param {string} msg */ /** @param {string} msg */
function throw_error(msg) { function throw_error(msg) {
e.invalid_compiler_option(null, msg); e.options_invalid_value(null, msg);
} }

@ -41,96 +41,96 @@ function w(node, code, message) {
} }
/** /**
* <%name%> should not have aria-* attributes * Avoid using accesskey
* @param {null | NodeLike} node * @param {null | NodeLike} node
* @param {string} name
*/ */
export function a11y_aria_attributes(node, name) { export function a11y_accesskey(node) {
w(node, "a11y_aria_attributes", `<${name}> should not have aria-* attributes`); w(node, "a11y_accesskey", "Avoid using accesskey");
} }
/** /**
* Unknown aria attribute 'aria-%attribute%'. Did you mean '%suggestion%'? * An element with an aria-activedescendant attribute should have a tabindex value
* @param {null | NodeLike} node * @param {null | NodeLike} node
* @param {string} attribute
* @param {string | undefined | null} [suggestion]
*/ */
export function a11y_unknown_aria_attribute(node, attribute, suggestion) { export function a11y_aria_activedescendant_has_tabindex(node) {
w(node, "a11y_unknown_aria_attribute", suggestion ? `Unknown aria attribute 'aria-${attribute}'. Did you mean '${suggestion}'?` : `Unknown aria attribute 'aria-${attribute}'`); w(node, "a11y_aria_activedescendant_has_tabindex", "An element with an aria-activedescendant attribute should have a tabindex value");
} }
/** /**
* <%name%> element should not be hidden * `<%name%>` should not have aria-* attributes
* @param {null | NodeLike} node * @param {null | NodeLike} node
* @param {string} name * @param {string} name
*/ */
export function a11y_hidden(node, name) { export function a11y_aria_attributes(node, name) {
w(node, "a11y_hidden", `<${name}> element should not be hidden`); w(node, "a11y_aria_attributes", `\`<${name}>\` should not have aria-* attributes`);
} }
/** /**
* The value of '%attribute%' must be either 'true' or 'false' * '%value%' is an invalid value for 'autocomplete' on `<input type="%type%">`
* @param {null | NodeLike} node * @param {null | NodeLike} node
* @param {string} attribute * @param {string} value
* @param {string} type
*/ */
export function a11y_incorrect_aria_attribute_type_boolean(node, attribute) { export function a11y_autocomplete_valid(node, value, type) {
w(node, "a11y_incorrect_aria_attribute_type_boolean", `The value of '${attribute}' must be either 'true' or 'false'`); w(node, "a11y_autocomplete_valid", `'${value}' is an invalid value for 'autocomplete' on \`<input type="${type}">\``);
} }
/** /**
* The value of '%attribute%' must be an integer * Avoid using autofocus
* @param {null | NodeLike} node * @param {null | NodeLike} node
* @param {string} attribute
*/ */
export function a11y_incorrect_aria_attribute_type_integer(node, attribute) { export function a11y_autofocus(node) {
w(node, "a11y_incorrect_aria_attribute_type_integer", `The value of '${attribute}' must be an integer`); w(node, "a11y_autofocus", "Avoid using autofocus");
} }
/** /**
* The value of '%attribute%' must be a string that represents a DOM element ID * Visible, non-interactive elements with a click event must be accompanied by a keyboard event handler. Consider whether an interactive element such as `<button type="button">` or `<a>` might be more appropriate. See https://svelte.dev/docs/accessibility-warnings#a11y-click-events-have-key-events for more details.
* @param {null | NodeLike} node * @param {null | NodeLike} node
* @param {string} attribute
*/ */
export function a11y_incorrect_aria_attribute_type_id(node, attribute) { export function a11y_click_events_have_key_events(node) {
w(node, "a11y_incorrect_aria_attribute_type_id", `The value of '${attribute}' must be a string that represents a DOM element ID`); w(node, "a11y_click_events_have_key_events", "Visible, non-interactive elements with a click event must be accompanied by a keyboard event handler. Consider whether an interactive element such as `<button type=\"button\">` or `<a>` might be more appropriate. See https://svelte.dev/docs/accessibility-warnings#a11y-click-events-have-key-events for more details.");
} }
/** /**
* The value of '%attribute%' must be a space-separated list of strings that represent DOM element IDs * Avoid `<%name%>` elements
* @param {null | NodeLike} node * @param {null | NodeLike} node
* @param {string} attribute * @param {string} name
*/ */
export function a11y_incorrect_aria_attribute_type_idlist(node, attribute) { export function a11y_distracting_elements(node, name) {
w(node, "a11y_incorrect_aria_attribute_type_idlist", `The value of '${attribute}' must be a space-separated list of strings that represent DOM element IDs`); w(node, "a11y_distracting_elements", `Avoid \`<${name}>\` elements`);
} }
/** /**
* The value of '%attribute%' must be exactly one of true, false, or mixed * `<figcaption>` must be first or last child of `<figure>`
* @param {null | NodeLike} node * @param {null | NodeLike} node
* @param {string} attribute
*/ */
export function a11y_incorrect_aria_attribute_type_tristate(node, attribute) { export function a11y_figcaption_index(node) {
w(node, "a11y_incorrect_aria_attribute_type_tristate", `The value of '${attribute}' must be exactly one of true, false, or mixed`); w(node, "a11y_figcaption_index", "`<figcaption>` must be first or last child of `<figure>`");
} }
/** /**
* The value of '%attribute%' must be exactly one of %values% * `<figcaption>` must be an immediate child of `<figure>`
* @param {null | NodeLike} node * @param {null | NodeLike} node
* @param {string} attribute
* @param {string} values
*/ */
export function a11y_incorrect_aria_attribute_type_token(node, attribute, values) { export function a11y_figcaption_parent(node) {
w(node, "a11y_incorrect_aria_attribute_type_token", `The value of '${attribute}' must be exactly one of ${values}`); w(node, "a11y_figcaption_parent", "`<figcaption>` must be an immediate child of `<figure>`");
} }
/** /**
* The value of '%attribute%' must be a space-separated list of one or more of %values% * `<%name%>` element should not be hidden
* @param {null | NodeLike} node * @param {null | NodeLike} node
* @param {string} attribute * @param {string} name
* @param {string} values
*/ */
export function a11y_incorrect_aria_attribute_type_tokenlist(node, attribute, values) { export function a11y_hidden(node, name) {
w(node, "a11y_incorrect_aria_attribute_type_tokenlist", `The value of '${attribute}' must be a space-separated list of one or more of ${values}`); w(node, "a11y_hidden", `\`<${name}>\` element should not be hidden`);
}
/**
* Screenreaders already announce `<img>` elements as an image.
* @param {null | NodeLike} node
*/
export function a11y_img_redundant_alt(node) {
w(node, "a11y_img_redundant_alt", "Screenreaders already announce `<img>` elements as an image.");
} }
/** /**
@ -144,58 +144,68 @@ export function a11y_incorrect_aria_attribute_type(node, attribute, type) {
} }
/** /**
* Elements with attribute aria-activedescendant should have tabindex value * The value of '%attribute%' must be either 'true' or 'false'
* @param {null | NodeLike} node * @param {null | NodeLike} node
* @param {string} attribute
*/ */
export function a11y_aria_activedescendant_has_tabindex(node) { export function a11y_incorrect_aria_attribute_type_boolean(node, attribute) {
w(node, "a11y_aria_activedescendant_has_tabindex", "Elements with attribute aria-activedescendant should have tabindex value"); w(node, "a11y_incorrect_aria_attribute_type_boolean", `The value of '${attribute}' must be either 'true' or 'false'`);
} }
/** /**
* <%name%> should not have role attribute * The value of '%attribute%' must be a string that represents a DOM element ID
* @param {null | NodeLike} node * @param {null | NodeLike} node
* @param {string} name * @param {string} attribute
*/ */
export function a11y_misplaced_role(node, name) { export function a11y_incorrect_aria_attribute_type_id(node, attribute) {
w(node, "a11y_misplaced_role", `<${name}> should not have role attribute`); w(node, "a11y_incorrect_aria_attribute_type_id", `The value of '${attribute}' must be a string that represents a DOM element ID`);
} }
/** /**
* Abstract role '%role%' is forbidden * The value of '%attribute%' must be a space-separated list of strings that represent DOM element IDs
* @param {null | NodeLike} node * @param {null | NodeLike} node
* @param {string} role * @param {string} attribute
*/ */
export function a11y_no_abstract_role(node, role) { export function a11y_incorrect_aria_attribute_type_idlist(node, attribute) {
w(node, "a11y_no_abstract_role", `Abstract role '${role}' is forbidden`); w(node, "a11y_incorrect_aria_attribute_type_idlist", `The value of '${attribute}' must be a space-separated list of strings that represent DOM element IDs`);
} }
/** /**
* Unknown role '%role%'. Did you mean '%suggestion%'? * The value of '%attribute%' must be an integer
* @param {null | NodeLike} node * @param {null | NodeLike} node
* @param {string} role * @param {string} attribute
* @param {string | undefined | null} [suggestion]
*/ */
export function a11y_unknown_role(node, role, suggestion) { export function a11y_incorrect_aria_attribute_type_integer(node, attribute) {
w(node, "a11y_unknown_role", suggestion ? `Unknown role '${role}'. Did you mean '${suggestion}'?` : `Unknown role '${role}'`); w(node, "a11y_incorrect_aria_attribute_type_integer", `The value of '${attribute}' must be an integer`);
} }
/** /**
* Redundant role '%role%' * The value of '%attribute%' must be exactly one of %values%
* @param {null | NodeLike} node * @param {null | NodeLike} node
* @param {string} role * @param {string} attribute
* @param {string} values
*/ */
export function a11y_no_redundant_roles(node, role) { export function a11y_incorrect_aria_attribute_type_token(node, attribute, values) {
w(node, "a11y_no_redundant_roles", `Redundant role '${role}'`); w(node, "a11y_incorrect_aria_attribute_type_token", `The value of '${attribute}' must be exactly one of ${values}`);
} }
/** /**
* Elements with the ARIA role "%role%" must have the following attributes defined: %props% * The value of '%attribute%' must be a space-separated list of one or more of %values%
* @param {null | NodeLike} node * @param {null | NodeLike} node
* @param {string} role * @param {string} attribute
* @param {string} props * @param {string} values
*/ */
export function a11y_role_has_required_aria_props(node, role, props) { export function a11y_incorrect_aria_attribute_type_tokenlist(node, attribute, values) {
w(node, "a11y_role_has_required_aria_props", `Elements with the ARIA role "${role}" must have the following attributes defined: ${props}`); w(node, "a11y_incorrect_aria_attribute_type_tokenlist", `The value of '${attribute}' must be a space-separated list of one or more of ${values}`);
}
/**
* The value of '%attribute%' must be exactly one of true, false, or mixed
* @param {null | NodeLike} node
* @param {string} attribute
*/
export function a11y_incorrect_aria_attribute_type_tristate(node, attribute) {
w(node, "a11y_incorrect_aria_attribute_type_tristate", `The value of '${attribute}' must be exactly one of true, false, or mixed`);
} }
/** /**
@ -208,287 +218,264 @@ export function a11y_interactive_supports_focus(node, role) {
} }
/** /**
* <%element%> cannot have role '%role%' * '%href_value%' is not a valid %href_attribute% attribute
* @param {null | NodeLike} node * @param {null | NodeLike} node
* @param {string} element * @param {string} href_value
* @param {string} role * @param {string} href_attribute
*/ */
export function a11y_no_interactive_element_to_noninteractive_role(node, element, role) { export function a11y_invalid_attribute(node, href_value, href_attribute) {
w(node, "a11y_no_interactive_element_to_noninteractive_role", `<${element}> cannot have role '${role}'`); w(node, "a11y_invalid_attribute", `'${href_value}' is not a valid ${href_attribute} attribute`);
} }
/** /**
* Non-interactive element <%element%> cannot have interactive role '%role%' * A form label must be associated with a control.
* @param {null | NodeLike} node * @param {null | NodeLike} node
* @param {string} element
* @param {string} role
*/ */
export function a11y_no_noninteractive_element_to_interactive_role(node, element, role) { export function a11y_label_has_associated_control(node) {
w(node, "a11y_no_noninteractive_element_to_interactive_role", `Non-interactive element <${element}> cannot have interactive role '${role}'`); w(node, "a11y_label_has_associated_control", "A form label must be associated with a control.");
} }
/** /**
* Avoid using accesskey * `<video>` elements must have a `<track kind="captions">`
* @param {null | NodeLike} node * @param {null | NodeLike} node
*/ */
export function a11y_accesskey(node) { export function a11y_media_has_caption(node) {
w(node, "a11y_accesskey", "Avoid using accesskey"); w(node, "a11y_media_has_caption", "`<video>` elements must have a `<track kind=\"captions\">`");
} }
/** /**
* Avoid using autofocus * `<%name%>` should not have role attribute
* @param {null | NodeLike} node * @param {null | NodeLike} node
* @param {string} name
*/ */
export function a11y_autofocus(node) { export function a11y_misplaced_role(node, name) {
w(node, "a11y_autofocus", "Avoid using autofocus"); w(node, "a11y_misplaced_role", `\`<${name}>\` should not have role attribute`);
} }
/** /**
* The scope attribute should only be used with <th> elements * The scope attribute should only be used with `<th>` elements
* @param {null | NodeLike} node * @param {null | NodeLike} node
*/ */
export function a11y_misplaced_scope(node) { export function a11y_misplaced_scope(node) {
w(node, "a11y_misplaced_scope", "The scope attribute should only be used with <th> elements"); w(node, "a11y_misplaced_scope", "The scope attribute should only be used with `<th>` elements");
} }
/** /**
* Avoid tabindex values above zero * `<%name%>` element should have %article% %sequence% attribute
* @param {null | NodeLike} node * @param {null | NodeLike} node
* @param {string} name
* @param {string} article
* @param {string} sequence
*/ */
export function a11y_positive_tabindex(node) { export function a11y_missing_attribute(node, name, article, sequence) {
w(node, "a11y_positive_tabindex", "Avoid tabindex values above zero"); w(node, "a11y_missing_attribute", `\`<${name}>\` element should have ${article} ${sequence} attribute`);
} }
/** /**
* Visible, non-interactive elements with a click event must be accompanied by a keyboard event handler. Consider whether an interactive element such as <button type="button"> or <a> might be more appropriate. See https://svelte.dev/docs/accessibility-warnings#a11y-click-events-have-key-events for more details. * `<%name%>` element should have child content
* @param {null | NodeLike} node * @param {null | NodeLike} node
* @param {string} name
*/ */
export function a11y_click_events_have_key_events(node) { export function a11y_missing_content(node, name) {
w(node, "a11y_click_events_have_key_events", "Visible, non-interactive elements with a click event must be accompanied by a keyboard event handler. Consider whether an interactive element such as <button type=\"button\"> or <a> might be more appropriate. See https://svelte.dev/docs/accessibility-warnings#a11y-click-events-have-key-events for more details."); w(node, "a11y_missing_content", `\`<${name}>\` element should have child content`);
} }
/** /**
* noninteractive element cannot have nonnegative tabIndex value * '%event%' event must be accompanied by '%accompanied_by%' event
* @param {null | NodeLike} node * @param {null | NodeLike} node
* @param {string} event
* @param {string} accompanied_by
*/ */
export function a11y_no_noninteractive_tabindex(node) { export function a11y_mouse_events_have_key_events(node, event, accompanied_by) {
w(node, "a11y_no_noninteractive_tabindex", "noninteractive element cannot have nonnegative tabIndex value"); w(node, "a11y_mouse_events_have_key_events", `'${event}' event must be accompanied by '${accompanied_by}' event`);
} }
/** /**
* The attribute '%attribute%' is not supported by the role '%role%' * Abstract role '%role%' is forbidden
* @param {null | NodeLike} node * @param {null | NodeLike} node
* @param {string} attribute
* @param {string} role * @param {string} role
*/ */
export function a11y_role_supports_aria_props(node, attribute, role) { export function a11y_no_abstract_role(node, role) {
w(node, "a11y_role_supports_aria_props", `The attribute '${attribute}' is not supported by the role '${role}'`); w(node, "a11y_no_abstract_role", `Abstract role '${role}' is forbidden`);
} }
/** /**
* The attribute '%attribute%' is not supported by the role '%role%'. This role is implicit on the element <%name%> * `<%element%>` cannot have role '%role%'
* @param {null | NodeLike} node * @param {null | NodeLike} node
* @param {string} attribute * @param {string} element
* @param {string} role * @param {string} role
* @param {string} name
*/ */
export function a11y_role_supports_aria_props_implicit(node, attribute, role, name) { export function a11y_no_interactive_element_to_noninteractive_role(node, element, role) {
w(node, "a11y_role_supports_aria_props_implicit", `The attribute '${attribute}' is not supported by the role '${role}'. This role is implicit on the element <${name}>`); w(node, "a11y_no_interactive_element_to_noninteractive_role", `\`<${element}>\` cannot have role '${role}'`);
} }
/** /**
* Non-interactive element <%element%> should not be assigned mouse or keyboard event listeners. * Non-interactive element `<%element%>` should not be assigned mouse or keyboard event listeners.
* @param {null | NodeLike} node * @param {null | NodeLike} node
* @param {string} element * @param {string} element
*/ */
export function a11y_no_noninteractive_element_interactions(node, element) { export function a11y_no_noninteractive_element_interactions(node, element) {
w(node, "a11y_no_noninteractive_element_interactions", `Non-interactive element <${element}> should not be assigned mouse or keyboard event listeners.`); w(node, "a11y_no_noninteractive_element_interactions", `Non-interactive element \`<${element}>\` should not be assigned mouse or keyboard event listeners.`);
} }
/** /**
* <%element%> with a %handler% handler must have an ARIA role * Non-interactive element `<%element%>` cannot have interactive role '%role%'
* @param {null | NodeLike} node * @param {null | NodeLike} node
* @param {string} element * @param {string} element
* @param {string} handler * @param {string} role
*/ */
export function a11y_no_static_element_interactions(node, element, handler) { export function a11y_no_noninteractive_element_to_interactive_role(node, element, role) {
w(node, "a11y_no_static_element_interactions", `<${element}> with a ${handler} handler must have an ARIA role`); w(node, "a11y_no_noninteractive_element_to_interactive_role", `Non-interactive element \`<${element}>\` cannot have interactive role '${role}'`);
} }
/** /**
* '%href_value%' is not a valid %href_attribute% attribute * noninteractive element cannot have nonnegative tabIndex value
* @param {null | NodeLike} node * @param {null | NodeLike} node
* @param {string} href_value
* @param {string} href_attribute
*/ */
export function a11y_invalid_attribute(node, href_value, href_attribute) { export function a11y_no_noninteractive_tabindex(node) {
w(node, "a11y_invalid_attribute", `'${href_value}' is not a valid ${href_attribute} attribute`); w(node, "a11y_no_noninteractive_tabindex", "noninteractive element cannot have nonnegative tabIndex value");
} }
/** /**
* <%name%> element should have %article% %sequence% attribute * Redundant role '%role%'
* @param {null | NodeLike} node * @param {null | NodeLike} node
* @param {string} name * @param {string} role
* @param {string} article
* @param {string} sequence
*/ */
export function a11y_missing_attribute(node, name, article, sequence) { export function a11y_no_redundant_roles(node, role) {
w(node, "a11y_missing_attribute", `<${name}> element should have ${article} ${sequence} attribute`); w(node, "a11y_no_redundant_roles", `Redundant role '${role}'`);
} }
/** /**
* The value '%value%' is not supported by the attribute 'autocomplete' on element <input type="%type%"> * `<%element%>` with a %handler% handler must have an ARIA role
* @param {null | NodeLike} node * @param {null | NodeLike} node
* @param {string} value * @param {string} element
* @param {string} type * @param {string} handler
*/ */
export function a11y_autocomplete_valid(node, value, type) { export function a11y_no_static_element_interactions(node, element, handler) {
w(node, "a11y_autocomplete_valid", `The value '${value}' is not supported by the attribute 'autocomplete' on element <input type="${type}">`); w(node, "a11y_no_static_element_interactions", `\`<${element}>\` with a ${handler} handler must have an ARIA role`);
} }
/** /**
* Screenreaders already announce <img> elements as an image. * Avoid tabindex values above zero
* @param {null | NodeLike} node * @param {null | NodeLike} node
*/ */
export function a11y_img_redundant_alt(node) { export function a11y_positive_tabindex(node) {
w(node, "a11y_img_redundant_alt", "Screenreaders already announce <img> elements as an image."); w(node, "a11y_positive_tabindex", "Avoid tabindex values above zero");
} }
/** /**
* A form label must be associated with a control. * Elements with the ARIA role "%role%" must have the following attributes defined: %props%
* @param {null | NodeLike} node * @param {null | NodeLike} node
* @param {string} role
* @param {string} props
*/ */
export function a11y_label_has_associated_control(node) { export function a11y_role_has_required_aria_props(node, role, props) {
w(node, "a11y_label_has_associated_control", "A form label must be associated with a control."); w(node, "a11y_role_has_required_aria_props", `Elements with the ARIA role "${role}" must have the following attributes defined: ${props}`);
} }
/** /**
* <video> elements must have a <track kind="captions"> * The attribute '%attribute%' is not supported by the role '%role%'
* @param {null | NodeLike} node * @param {null | NodeLike} node
* @param {string} attribute
* @param {string} role
*/ */
export function a11y_media_has_caption(node) { export function a11y_role_supports_aria_props(node, attribute, role) {
w(node, "a11y_media_has_caption", "<video> elements must have a <track kind=\"captions\">"); w(node, "a11y_role_supports_aria_props", `The attribute '${attribute}' is not supported by the role '${role}'`);
} }
/** /**
* Avoid <%name%> elements * The attribute '%attribute%' is not supported by the role '%role%'. This role is implicit on the element `<%name%>`
* @param {null | NodeLike} node * @param {null | NodeLike} node
* @param {string} attribute
* @param {string} role
* @param {string} name * @param {string} name
*/ */
export function a11y_distracting_elements(node, name) { export function a11y_role_supports_aria_props_implicit(node, attribute, role, name) {
w(node, "a11y_distracting_elements", `Avoid <${name}> elements`); w(node, "a11y_role_supports_aria_props_implicit", `The attribute '${attribute}' is not supported by the role '${role}'. This role is implicit on the element \`<${name}>\``);
}
/**
* `<figcaption>` must be an immediate child of `<figure>`
* @param {null | NodeLike} node
*/
export function a11y_figcaption_parent(node) {
w(node, "a11y_figcaption_parent", "`<figcaption>` must be an immediate child of `<figure>`");
}
/**
* `<figcaption>` must be first or last child of `<figure>`
* @param {null | NodeLike} node
*/
export function a11y_figcaption_index(node) {
w(node, "a11y_figcaption_index", "`<figcaption>` must be first or last child of `<figure>`");
} }
/** /**
* '%event%' event must be accompanied by '%accompanied_by%' event * Unknown aria attribute 'aria-%attribute%'. Did you mean '%suggestion%'?
* @param {null | NodeLike} node * @param {null | NodeLike} node
* @param {string} event * @param {string} attribute
* @param {string} accompanied_by * @param {string | undefined | null} [suggestion]
*/ */
export function a11y_mouse_events_have_key_events(node, event, accompanied_by) { export function a11y_unknown_aria_attribute(node, attribute, suggestion) {
w(node, "a11y_mouse_events_have_key_events", `'${event}' event must be accompanied by '${accompanied_by}' event`); w(node, "a11y_unknown_aria_attribute", suggestion ? `Unknown aria attribute 'aria-${attribute}'. Did you mean '${suggestion}'?` : `Unknown aria attribute 'aria-${attribute}'`);
} }
/** /**
* <%name%> element should have child content * Unknown role '%role%'. Did you mean '%suggestion%'?
* @param {null | NodeLike} node * @param {null | NodeLike} node
* @param {string} name * @param {string} role
* @param {string | undefined | null} [suggestion]
*/ */
export function a11y_missing_content(node, name) { export function a11y_unknown_role(node, role, suggestion) {
w(node, "a11y_missing_content", `<${name}> element should have child content`); w(node, "a11y_unknown_role", suggestion ? `Unknown role '${role}'. Did you mean '${suggestion}'?` : `Unknown role '${role}'`);
} }
/** /**
* The "is" attribute is not supported cross-browser and should be avoided * The `accessors` option has been deprecated. It will have no effect in runes mode
* @param {null | NodeLike} node * @param {null | NodeLike} node
*/ */
export function avoid_is(node) { export function options_deprecated_accessors(node) {
w(node, "avoid_is", "The \"is\" attribute is not supported cross-browser and should be avoided"); w(node, "options_deprecated_accessors", "The `accessors` option has been deprecated. It will have no effect in runes mode");
} }
/** /**
* You are referencing globalThis.%name%. Did you forget to declare a variable with that name? * The `immutable` option has been deprecated. It will have no effect in runes mode
* @param {null | NodeLike} node * @param {null | NodeLike} node
* @param {string} name
*/ */
export function global_event_reference(node, name) { export function options_deprecated_immutable(node) {
w(node, "global_event_reference", `You are referencing globalThis.${name}. Did you forget to declare a variable with that name?`); w(node, "options_deprecated_immutable", "The `immutable` option has been deprecated. It will have no effect in runes mode");
} }
/** /**
* Attributes should not contain ':' characters to prevent ambiguity with Svelte directives * The `customElement` option is used when generating a custom element. Did you forget the `customElement: true` compile option?
* @param {null | NodeLike} node * @param {null | NodeLike} node
*/ */
export function illegal_attribute_character(node) { export function options_missing_custom_element(node) {
w(node, "illegal_attribute_character", "Attributes should not contain ':' characters to prevent ambiguity with Svelte directives"); w(node, "options_missing_custom_element", "The `customElement` option is used when generating a custom element. Did you forget the `customElement: true` compile option?");
}
/**
* '%wrong%' is not a valid HTML attribute. Did you mean '%right%'?
* @param {null | NodeLike} node
* @param {string} wrong
* @param {string} right
*/
export function invalid_html_attribute(node, wrong, right) {
w(node, "invalid_html_attribute", `'${wrong}' is not a valid HTML attribute. Did you mean '${right}'?`);
} }
/** /**
* Empty block * The `enableSourcemap` option has been removed. Source maps are always generated now, and tooling can choose to ignore them
* @param {null | NodeLike} node * @param {null | NodeLike} node
*/ */
export function empty_block(node) { export function options_removed_enable_sourcemap(node) {
w(node, "empty_block", "Empty block"); w(node, "options_removed_enable_sourcemap", "The `enableSourcemap` option has been removed. Source maps are always generated now, and tooling can choose to ignore them");
} }
/** /**
* <%name%> will be treated as an HTML element unless it begins with a capital letter * The `hydratable` option has been removed. Svelte components are always hydratable now
* @param {null | NodeLike} node * @param {null | NodeLike} node
* @param {string} name
*/ */
export function component_name_lowercase(node, name) { export function options_removed_hydratable(node) {
w(node, "component_name_lowercase", `<${name}> will be treated as an HTML element unless it begins with a capital letter`); w(node, "options_removed_hydratable", "The `hydratable` option has been removed. Svelte components are always hydratable now");
} }
/** /**
* Unused CSS selector "%name%" * The `loopGuardTimeout` option has been removed
* @param {null | NodeLike} node * @param {null | NodeLike} node
* @param {string} name
*/ */
export function css_unused_selector(node, name) { export function options_removed_loop_guard_timeout(node) {
w(node, "css_unused_selector", `Unused CSS selector "${name}"`); w(node, "options_removed_loop_guard_timeout", "The `loopGuardTimeout` option has been removed");
} }
/** /**
* Reactive declarations only exist at the top level of the instance script * `generate: "dom"` and `generate: "ssr"` options have been renamed to "client" and "server" respectively
* @param {null | NodeLike} node * @param {null | NodeLike} node
*/ */
export function no_reactive_declaration(node) { export function options_renamed_ssr_dom(node) {
w(node, "no_reactive_declaration", "Reactive declarations only exist at the top level of the instance script"); w(node, "options_renamed_ssr_dom", "`generate: \"dom\"` and `generate: \"ssr\"` options have been renamed to \"client\" and \"server\" respectively");
} }
/** /**
* All dependencies of the reactive declaration are declared in a module script and will not be reactive * Use `$derived.by(() => {...})` instead of `$derived((() => {...})())`
* @param {null | NodeLike} node * @param {null | NodeLike} node
*/ */
export function module_script_reactive_declaration(node) { export function derived_iife(node) {
w(node, "module_script_reactive_declaration", "All dependencies of the reactive declaration are declared in a module script and will not be reactive"); w(node, "derived_iife", "Use `$derived.by(() => {...})` instead of `$derived((() => {...})())`");
} }
/** /**
@ -496,163 +483,160 @@ export function module_script_reactive_declaration(node) {
* @param {null | NodeLike} node * @param {null | NodeLike} node
* @param {string} name * @param {string} name
*/ */
export function unused_export_let(node, name) { export function export_let_unused(node, name) {
w(node, "unused_export_let", `Component has unused export property '${name}'. If it is for external reference only, please consider using \`export const ${name}\``); w(node, "export_let_unused", `Component has unused export property '${name}'. If it is for external reference only, please consider using \`export const ${name}\``);
} }
/** /**
* Using <slot> to render parent content is deprecated. Use {@render ...} tags instead. * `%name%` is updated, but is not declared with `$state(...)`. Changing its value will not correctly trigger updates
* @param {null | NodeLike} node
*/
export function deprecated_slot_element(node) {
w(node, "deprecated_slot_element", "Using <slot> to render parent content is deprecated. Use {@render ...} tags instead.");
}
/**
* Using on:%name% to listen to the %name% event is is deprecated. Use the event attribute on%name% instead.
* @param {null | NodeLike} node * @param {null | NodeLike} node
* @param {string} name * @param {string} name
*/ */
export function deprecated_event_handler(node, name) { export function non_reactive_update(node, name) {
w(node, "deprecated_event_handler", `Using on:${name} to listen to the ${name} event is is deprecated. Use the event attribute on${name} instead.`); w(node, "non_reactive_update", `\`${name}\` is updated, but is not declared with \`$state(...)\`. Changing its value will not correctly trigger updates`);
} }
/** /**
* Self-closing HTML tags for non-void elements are ambiguous use <%name% ...></%name%> rather than <%name% ... /> * Avoid 'new class' instead, declare the class at the top level scope
* @param {null | NodeLike} node * @param {null | NodeLike} node
* @param {string} name
*/ */
export function invalid_self_closing_tag(node, name) { export function perf_avoid_inline_class(node) {
w(node, "invalid_self_closing_tag", `Self-closing HTML tags for non-void elements are ambiguous — use <${name} ...></${name}> rather than <${name} ... />`); w(node, "perf_avoid_inline_class", "Avoid 'new class' — instead, declare the class at the top level scope");
} }
/** /**
* The `accessors` option has been deprecated. It will have no effect in runes mode * Avoid declaring classes below the top level scope
* @param {null | NodeLike} node * @param {null | NodeLike} node
*/ */
export function options_deprecated_accessors(node) { export function perf_avoid_nested_class(node) {
w(node, "options_deprecated_accessors", "The `accessors` option has been deprecated. It will have no effect in runes mode"); w(node, "perf_avoid_nested_class", "Avoid declaring classes below the top level scope");
} }
/** /**
* The `immutable` option has been deprecated. It will have no effect in runes mode * Reactive declarations only exist at the top level of the instance script
* @param {null | NodeLike} node * @param {null | NodeLike} node
*/ */
export function options_deprecated_immutable(node) { export function reactive_declaration_invalid_placement(node) {
w(node, "options_deprecated_immutable", "The `immutable` option has been deprecated. It will have no effect in runes mode"); w(node, "reactive_declaration_invalid_placement", "Reactive declarations only exist at the top level of the instance script");
} }
/** /**
* The `customElement` option is used when generating a custom element. Did you forget the `customElement: true` compile option? * All dependencies of the reactive declaration are declared in a module script and will not be reactive
* @param {null | NodeLike} node * @param {null | NodeLike} node
*/ */
export function options_missing_custom_element(node) { export function reactive_declaration_module_script(node) {
w(node, "options_missing_custom_element", "The `customElement` option is used when generating a custom element. Did you forget the `customElement: true` compile option?"); w(node, "reactive_declaration_module_script", "All dependencies of the reactive declaration are declared in a module script and will not be reactive");
} }
/** /**
* `generate: "dom"` and `generate: "ssr"` options have been renamed to "client" and "server" respectively * State referenced in its own scope will never update. Did you mean to reference it inside a closure?
* @param {null | NodeLike} node * @param {null | NodeLike} node
*/ */
export function options_renamed_ssr_dom(node) { export function state_referenced_locally(node) {
w(node, "options_renamed_ssr_dom", "`generate: \"dom\"` and `generate: \"ssr\"` options have been renamed to \"client\" and \"server\" respectively"); w(node, "state_referenced_locally", "State referenced in its own scope will never update. Did you mean to reference it inside a closure?");
} }
/** /**
* The `enableSourcemap` option has been removed. Source maps are always generated now, and tooling can choose to ignore them * It looks like you're using the `$%name%` rune, but there is a local binding called `%name%`. Referencing a local variable with a `$` prefix will create a store subscription. Please rename `%name%` to avoid the ambiguity
* @param {null | NodeLike} node * @param {null | NodeLike} node
* @param {string} name
*/ */
export function options_removed_enable_sourcemap(node) { export function store_rune_conflict(node, name) {
w(node, "options_removed_enable_sourcemap", "The `enableSourcemap` option has been removed. Source maps are always generated now, and tooling can choose to ignore them"); w(node, "store_rune_conflict", `It looks like you're using the \`$${name}\` rune, but there is a local binding called \`${name}\`. Referencing a local variable with a \`$\` prefix will create a store subscription. Please rename \`${name}\` to avoid the ambiguity`);
} }
/** /**
* The `hydratable` option has been removed. Svelte components are always hydratable now * Unused CSS selector "%name%"
* @param {null | NodeLike} node * @param {null | NodeLike} node
* @param {string} name
*/ */
export function options_removed_hydratable(node) { export function css_unused_selector(node, name) {
w(node, "options_removed_hydratable", "The `hydratable` option has been removed. Svelte components are always hydratable now"); w(node, "css_unused_selector", `Unused CSS selector "${name}"`);
} }
/** /**
* The `loopGuardTimeout` option has been removed * The "is" attribute is not supported cross-browser and should be avoided
* @param {null | NodeLike} node * @param {null | NodeLike} node
*/ */
export function options_removed_loop_guard_timeout(node) { export function attribute_avoid_is(node) {
w(node, "options_removed_loop_guard_timeout", "The `loopGuardTimeout` option has been removed"); w(node, "attribute_avoid_is", "The \"is\" attribute is not supported cross-browser and should be avoided");
} }
/** /**
* Avoid 'new class' instead, declare the class at the top level scope * You are referencing `globalThis.%name%`. Did you forget to declare a variable with that name?
* @param {null | NodeLike} node * @param {null | NodeLike} node
* @param {string} name
*/ */
export function avoid_inline_class(node) { export function attribute_global_event_reference(node, name) {
w(node, "avoid_inline_class", "Avoid 'new class' — instead, declare the class at the top level scope"); w(node, "attribute_global_event_reference", `You are referencing \`globalThis.${name}\`. Did you forget to declare a variable with that name?`);
} }
/** /**
* Avoid declaring classes below the top level scope * Attributes should not contain ':' characters to prevent ambiguity with Svelte directives
* @param {null | NodeLike} node * @param {null | NodeLike} node
*/ */
export function avoid_nested_class(node) { export function attribute_illegal_colon(node) {
w(node, "avoid_nested_class", "Avoid declaring classes below the top level scope"); w(node, "attribute_illegal_colon", "Attributes should not contain ':' characters to prevent ambiguity with Svelte directives");
} }
/** /**
* It looks like you're using the `$%name%` rune, but there is a local binding called `%name%`. Referencing a local variable with a `$` prefix will create a store subscription. Please rename `%name%` to avoid the ambiguity * '%wrong%' is not a valid HTML attribute. Did you mean '%right%'?
* @param {null | NodeLike} node * @param {null | NodeLike} node
* @param {string} name * @param {string} wrong
* @param {string} right
*/ */
export function store_with_rune_name(node, name) { export function attribute_invalid_property_name(node, wrong, right) {
w(node, "store_with_rune_name", `It looks like you're using the \`$${name}\` rune, but there is a local binding called \`${name}\`. Referencing a local variable with a \`$\` prefix will create a store subscription. Please rename \`${name}\` to avoid the ambiguity`); w(node, "attribute_invalid_property_name", `'${wrong}' is not a valid HTML attribute. Did you mean '${right}'?`);
} }
/** /**
* `%name%` is updated, but is not declared with `$state(...)`. Changing its value will not correctly trigger updates * The rest operator (...) will create a new object and binding '%name%' with the original object will not work
* @param {null | NodeLike} node * @param {null | NodeLike} node
* @param {string} name * @param {string} name
*/ */
export function non_state_reference(node, name) { export function bind_invalid_each_rest(node, name) {
w(node, "non_state_reference", `\`${name}\` is updated, but is not declared with \`$state(...)\`. Changing its value will not correctly trigger updates`); w(node, "bind_invalid_each_rest", `The rest operator (...) will create a new object and binding '${name}' with the original object will not work`);
} }
/** /**
* Use `$derived.by(() => {...})` instead of `$derived((() => {...})())` * Empty block
* @param {null | NodeLike} node * @param {null | NodeLike} node
*/ */
export function derived_iife(node) { export function block_empty(node) {
w(node, "derived_iife", "Use `$derived.by(() => {...})` instead of `$derived((() => {...})())`"); w(node, "block_empty", "Empty block");
} }
/** /**
* Component properties are declared using `$props()` in runes mode. Did you forget to call the function? * `<%name%>` will be treated as an HTML element unless it begins with a capital letter
* @param {null | NodeLike} node * @param {null | NodeLike} node
* @param {string} name
*/ */
export function invalid_props_declaration(node) { export function component_name_lowercase(node, name) {
w(node, "invalid_props_declaration", "Component properties are declared using `$props()` in runes mode. Did you forget to call the function?"); w(node, "component_name_lowercase", `\`<${name}>\` will be treated as an HTML element unless it begins with a capital letter`);
} }
/** /**
* Bindable component properties are declared using `$bindable()` in runes mode. Did you forget to call the function? * Self-closing HTML tags for non-void elements are ambiguous use `<%name% ...></%name%>` rather than `<%name% ... />`
* @param {null | NodeLike} node * @param {null | NodeLike} node
* @param {string} name
*/ */
export function invalid_bindable_declaration(node) { export function element_invalid_self_closing_tag(node, name) {
w(node, "invalid_bindable_declaration", "Bindable component properties are declared using `$bindable()` in runes mode. Did you forget to call the function?"); w(node, "element_invalid_self_closing_tag", `Self-closing HTML tags for non-void elements are ambiguous — use \`<${name} ...></${name}>\` rather than \`<${name} ... />\``);
} }
/** /**
* State referenced in its own scope will never update. Did you mean to reference it inside a closure? * Using `on:%name%` to listen to the %name% event is deprecated. Use the event attribute `on%name%` instead.
* @param {null | NodeLike} node * @param {null | NodeLike} node
* @param {string} name
*/ */
export function static_state_reference(node) { export function event_directive_deprecated(node, name) {
w(node, "static_state_reference", "State referenced in its own scope will never update. Did you mean to reference it inside a closure?"); w(node, "event_directive_deprecated", `Using \`on:${name}\` to listen to the ${name} event is deprecated. Use the event attribute \`on${name}\` instead.`);
} }
/** /**
* The rest operator (...) will create a new object and binding '%name%' with the original object will not work * Using `<slot>` to render parent content is deprecated. Use `{@render ...}` tags instead.
* @param {null | NodeLike} node * @param {null | NodeLike} node
* @param {string} name
*/ */
export function invalid_rest_eachblock_binding(node, name) { export function slot_element_deprecated(node) {
w(node, "invalid_rest_eachblock_binding", `The rest operator (...) will create a new object and binding '${name}' with the original object will not work`); w(node, "slot_element_deprecated", "Using `<slot>` to render parent content is deprecated. Use `{@render ...}` tags instead.");
} }

@ -2,6 +2,7 @@ import { current_component_context, flush_sync, untrack } from './internal/clien
import { is_array } from './internal/client/utils.js'; import { is_array } from './internal/client/utils.js';
import { user_effect } from './internal/client/index.js'; import { user_effect } from './internal/client/index.js';
import * as e from './internal/client/errors.js'; import * as e from './internal/client/errors.js';
import { lifecycle_outside_component } from './internal/shared/errors.js';
/** /**
* The `onMount` function schedules a callback to run as soon as the component has been mounted to the DOM. * The `onMount` function schedules a callback to run as soon as the component has been mounted to the DOM.
@ -19,7 +20,7 @@ import * as e from './internal/client/errors.js';
*/ */
export function onMount(fn) { export function onMount(fn) {
if (current_component_context === null) { if (current_component_context === null) {
e.lifecycle_outside_component('onMount'); lifecycle_outside_component('onMount');
} }
if (current_component_context.l !== null) { if (current_component_context.l !== null) {
@ -44,7 +45,7 @@ export function onMount(fn) {
*/ */
export function onDestroy(fn) { export function onDestroy(fn) {
if (current_component_context === null) { if (current_component_context === null) {
e.lifecycle_outside_component('onDestroy'); lifecycle_outside_component('onDestroy');
} }
onMount(() => () => untrack(fn)); onMount(() => () => untrack(fn));
@ -88,7 +89,7 @@ function create_custom_event(type, detail, { bubbles = false, cancelable = false
export function createEventDispatcher() { export function createEventDispatcher() {
const component_context = current_component_context; const component_context = current_component_context;
if (component_context === null) { if (component_context === null) {
e.lifecycle_outside_component('createEventDispatcher'); lifecycle_outside_component('createEventDispatcher');
} }
return (type, detail, options) => { return (type, detail, options) => {
@ -127,11 +128,11 @@ export function createEventDispatcher() {
*/ */
export function beforeUpdate(fn) { export function beforeUpdate(fn) {
if (current_component_context === null) { if (current_component_context === null) {
e.lifecycle_outside_component('beforeUpdate'); lifecycle_outside_component('beforeUpdate');
} }
if (current_component_context.l === null) { if (current_component_context.l === null) {
throw new Error('beforeUpdate cannot be used in runes mode'); e.lifecycle_legacy_only('beforeUpdate');
} }
init_update_callbacks(current_component_context).b.push(fn); init_update_callbacks(current_component_context).b.push(fn);
@ -151,7 +152,7 @@ export function beforeUpdate(fn) {
*/ */
export function afterUpdate(fn) { export function afterUpdate(fn) {
if (current_component_context === null) { if (current_component_context === null) {
e.lifecycle_outside_component('afterUpdate'); lifecycle_outside_component('afterUpdate');
} }
if (current_component_context.l === null) { if (current_component_context.l === null) {

@ -1,5 +1,6 @@
import { current_component } from './internal/server/context.js'; import { current_component } from './internal/server/context.js';
import { noop } from './internal/shared/utils.js'; import { noop } from './internal/shared/utils.js';
import * as e from './internal/server/errors.js';
/** @param {() => void} fn */ /** @param {() => void} fn */
export function onDestroy(fn) { export function onDestroy(fn) {
@ -20,15 +21,15 @@ export function createEventDispatcher() {
} }
export function mount() { export function mount() {
throw new Error('mount(...) is not available on the server'); e.lifecycle_function_unavailable('mount');
} }
export function hydrate() { export function hydrate() {
throw new Error('hydrate(...) is not available on the server'); e.lifecycle_function_unavailable('hydrate');
} }
export function unmount() { export function unmount() {
throw new Error('unmount(...) is not available on the server'); e.lifecycle_function_unavailable('unmount');
} }
export async function tick() {} export async function tick() {}

@ -6,6 +6,7 @@ import { delegate } from './events.js';
import { autofocus } from './misc.js'; import { autofocus } from './misc.js';
import { effect } from '../../reactivity/effects.js'; import { effect } from '../../reactivity/effects.js';
import { run } from '../../../shared/utils.js'; import { run } from '../../../shared/utils.js';
import * as w from '../../warnings.js';
/** /**
* The value/checked attribute in the template actually corresponds to the defaultValue property, so we need * The value/checked attribute in the template actually corresponds to the defaultValue property, so we need
@ -263,13 +264,10 @@ function check_src_in_dev_hydration(element, attribute, value) {
if (attribute === 'srcset' && srcset_url_equal(element, value)) return; if (attribute === 'srcset' && srcset_url_equal(element, value)) return;
if (src_url_equal(element.getAttribute(attribute) ?? '', value ?? '')) return; if (src_url_equal(element.getAttribute(attribute) ?? '', value ?? '')) return;
// eslint-disable-next-line no-console w.hydration_attribute_changed(
console.error( attribute,
`Detected a ${attribute} attribute value change during hydration. This will not be repaired during hydration, ` + element.outerHTML.replace(element.innerHTML, '...'),
`the ${attribute} value that came from the server will be used. Related element:`, String(value)
element,
' Differing value:',
value
); );
} }

@ -2,6 +2,7 @@ import { DEV } from 'esm-env';
import { render_effect, effect } from '../../../reactivity/effects.js'; import { render_effect, effect } from '../../../reactivity/effects.js';
import { stringify } from '../../../render.js'; import { stringify } from '../../../render.js';
import { listen_to_event_and_reset_event } from './shared.js'; import { listen_to_event_and_reset_event } from './shared.js';
import * as e from '../../../errors.js';
/** /**
* @param {HTMLInputElement} input * @param {HTMLInputElement} input
@ -12,9 +13,8 @@ import { listen_to_event_and_reset_event } from './shared.js';
export function bind_value(input, get_value, update) { export function bind_value(input, get_value, update) {
listen_to_event_and_reset_event(input, 'input', () => { listen_to_event_and_reset_event(input, 'input', () => {
if (DEV && input.type === 'checkbox') { if (DEV && input.type === 'checkbox') {
throw new Error( // TODO should this happen in prod too?
'Using bind:value together with a checkbox input is not allowed. Use bind:checked instead' e.bind_invalid_checkbox_value();
);
} }
update(is_numberlike_input(input) ? to_number(input.value) : input.value); update(is_numberlike_input(input) ? to_number(input.value) : input.value);
@ -22,9 +22,8 @@ export function bind_value(input, get_value, update) {
render_effect(() => { render_effect(() => {
if (DEV && input.type === 'checkbox') { if (DEV && input.type === 'checkbox') {
throw new Error( // TODO should this happen in prod too?
'Using bind:value together with a checkbox input is not allowed. Use bind:checked instead' e.bind_invalid_checkbox_value();
);
} }
var value = get_value(); var value = get_value();

@ -1,4 +1,5 @@
import { HYDRATION_END, HYDRATION_START } from '../../../constants.js'; import { HYDRATION_END, HYDRATION_START } from '../../../constants.js';
import * as e from '../errors.js';
/** /**
* Use this variable to guard everything related to hydration code so it can be treeshaken out * Use this variable to guard everything related to hydration code so it can be treeshaken out
@ -66,5 +67,5 @@ export function hydrate_anchor(node) {
nodes.push(current); nodes.push(current);
} }
throw new Error('Expected a closing hydration marker'); e.hydration_missing_marker_close();
} }

@ -2,6 +2,113 @@
import { DEV } from 'esm-env'; import { DEV } from 'esm-env';
/**
* Using `bind:value` together with a checkbox input is not allowed. Use `bind:checked` instead
* @returns {never}
*/
export function bind_invalid_checkbox_value() {
if (DEV) {
const error = new Error(`${"bind_invalid_checkbox_value"}\n${"Using `bind:value` together with a checkbox input is not allowed. Use `bind:checked` instead"}`);
error.name = 'Svelte error';
throw error;
} else {
// TODO print a link to the documentation
throw new Error("bind_invalid_checkbox_value");
}
}
/**
* Component %component% 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%`)
* @param {string} component
* @param {string} key
* @param {string} name
* @returns {never}
*/
export function bind_invalid_export(component, key, name) {
if (DEV) {
const error = new Error(`${"bind_invalid_export"}\n${`Component ${component} 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}\`)`}`);
error.name = 'Svelte error';
throw error;
} else {
// TODO print a link to the documentation
throw new Error("bind_invalid_export");
}
}
/**
* A component is attempting to bind to a non-bindable property `%key%` belonging to %component% (i.e. `<%name% bind:%key%={...}>`). To mark a property as bindable: `let { %key% = $bindable() } = $props()`
* @param {string} key
* @param {string} component
* @param {string} name
* @returns {never}
*/
export function bind_not_bindable(key, component, name) {
if (DEV) {
const error = new Error(`${"bind_not_bindable"}\n${`A component is attempting to bind to a non-bindable property \`${key}\` belonging to ${component} (i.e. \`<${name} bind:${key}={...}>\`). To mark a property as bindable: \`let { ${key} = $bindable() } = $props()\``}`);
error.name = 'Svelte error';
throw error;
} else {
// TODO print a link to the documentation
throw new Error("bind_not_bindable");
}
}
/**
* Keyed each block has duplicate key `%value%` at indexes %a% and %b%
* @param {string} a
* @param {string} b
* @param {string | undefined | null} [value]
* @returns {never}
*/
export function each_key_duplicate(a, b, value) {
if (DEV) {
const error = new Error(`${"each_key_duplicate"}\n${value ? `Keyed each block has duplicate key \`${value}\` at indexes ${a} and ${b}` : `Keyed each block has duplicate key at indexes ${a} and ${b}`}`);
error.name = 'Svelte error';
throw error;
} else {
// TODO print a link to the documentation
throw new Error("each_key_duplicate");
}
}
/**
* `%rune%` cannot be used inside an effect cleanup function
* @param {string} rune
* @returns {never}
*/
export function effect_in_teardown(rune) {
if (DEV) {
const error = new Error(`${"effect_in_teardown"}\n${`\`${rune}\` cannot be used inside an effect cleanup function`}`);
error.name = 'Svelte error';
throw error;
} else {
// TODO print a link to the documentation
throw new Error("effect_in_teardown");
}
}
/**
* `%rune%` can only be used inside an effect (e.g. during component initialisation)
* @param {string} rune
* @returns {never}
*/
export function effect_orphan(rune) {
if (DEV) {
const error = new Error(`${"effect_orphan"}\n${`\`${rune}\` can only be used inside an effect (e.g. during component initialisation)`}`);
error.name = 'Svelte error';
throw error;
} else {
// TODO print a link to the documentation
throw new Error("effect_orphan");
}
}
/** /**
* Maximum update depth exceeded. This can happen when a reactive block or effect repeatedly sets a new value. Svelte limits the number of nested updates to prevent infinite loops * Maximum update depth exceeded. This can happen when a reactive block or effect repeatedly sets a new value. Svelte limits the number of nested updates to prevent infinite loops
* @returns {never} * @returns {never}
@ -19,19 +126,34 @@ export function effect_update_depth_exceeded() {
} }
/** /**
* `%name%(...)` can only be used during component initialisation * Missing hydration closing marker
* @param {string} name
* @returns {never} * @returns {never}
*/ */
export function lifecycle_outside_component(name) { export function hydration_missing_marker_close() {
if (DEV) { if (DEV) {
const error = new Error(`${"lifecycle_outside_component"}\n${`\`${name}(...)\` can only be used during component initialisation`}`); const error = new Error(`${"hydration_missing_marker_close"}\n${"Missing hydration closing marker"}`);
error.name = 'Svelte error'; error.name = 'Svelte error';
throw error; throw error;
} else { } else {
// TODO print a link to the documentation // TODO print a link to the documentation
throw new Error("lifecycle_outside_component"); throw new Error("hydration_missing_marker_close");
}
}
/**
* Missing hydration opening marker
* @returns {never}
*/
export function hydration_missing_marker_open() {
if (DEV) {
const error = new Error(`${"hydration_missing_marker_open"}\n${"Missing hydration opening marker"}`);
error.name = 'Svelte error';
throw error;
} else {
// TODO print a link to the documentation
throw new Error("hydration_missing_marker_open");
} }
} }
@ -50,4 +172,105 @@ export function lifecycle_legacy_only(name) {
// TODO print a link to the documentation // TODO print a link to the documentation
throw new Error("lifecycle_legacy_only"); throw new Error("lifecycle_legacy_only");
} }
}
/**
* Cannot do `bind:%key%={undefined}` when `%key%` has a fallback value
* @param {string} key
* @returns {never}
*/
export function props_invalid_value(key) {
if (DEV) {
const error = new Error(`${"props_invalid_value"}\n${`Cannot do \`bind:${key}={undefined}\` when \`${key}\` has a fallback value`}`);
error.name = 'Svelte error';
throw error;
} else {
// TODO print a link to the documentation
throw new Error("props_invalid_value");
}
}
/**
* Rest element properties of `$props()` such as `%property%` are readonly
* @param {string} property
* @returns {never}
*/
export function props_rest_readonly(property) {
if (DEV) {
const error = new Error(`${"props_rest_readonly"}\n${`Rest element properties of \`$props()\` such as \`${property}\` are readonly`}`);
error.name = 'Svelte error';
throw error;
} else {
// TODO print a link to the documentation
throw new Error("props_rest_readonly");
}
}
/**
* The `%rune%` rune is only available inside `.svelte` and `.svelte.js/ts` files
* @param {string} rune
* @returns {never}
*/
export function rune_outside_svelte(rune) {
if (DEV) {
const error = new Error(`${"rune_outside_svelte"}\n${`The \`${rune}\` rune is only available inside \`.svelte\` and \`.svelte.js/ts\` files`}`);
error.name = 'Svelte error';
throw error;
} else {
// TODO print a link to the documentation
throw new Error("rune_outside_svelte");
}
}
/**
* Cannot set prototype of `$state` object
* @returns {never}
*/
export function state_prototype_fixed() {
if (DEV) {
const error = new Error(`${"state_prototype_fixed"}\n${"Cannot set prototype of `$state` object"}`);
error.name = 'Svelte error';
throw error;
} else {
// TODO print a link to the documentation
throw new Error("state_prototype_fixed");
}
}
/**
* Unsafe mutations during Svelte's render or derived phase are not permitted in runes mode. This can lead to unexpected errors and possibly cause infinite loops.
* >
* If the object is not meant to be reactive, declare it without `$state`
* @returns {never}
*/
export function state_unsafe_mutation() {
if (DEV) {
const error = new Error(`${"state_unsafe_mutation"}\n${"Unsafe mutations during Svelte's render or derived phase are not permitted in runes mode. This can lead to unexpected errors and possibly cause infinite loops.\n>\nIf the object is not meant to be reactive, declare it without `$state`"}`);
error.name = 'Svelte error';
throw error;
} else {
// TODO print a link to the documentation
throw new Error("state_unsafe_mutation");
}
}
/**
* The `this={...}` property of a `<svelte:component>` must be a Svelte component, if defined
* @returns {never}
*/
export function svelte_component_invalid_this_value() {
if (DEV) {
const error = new Error(`${"svelte_component_invalid_this_value"}\n${"The `this={...}` property of a `<svelte:component>` must be a Svelte component, if defined"}`);
error.name = 'Svelte error';
throw error;
} else {
// TODO print a link to the documentation
throw new Error("svelte_component_invalid_this_value");
}
} }

@ -128,8 +128,7 @@ export {
export { export {
validate_dynamic_component, validate_dynamic_component,
validate_each_keys, validate_each_keys,
validate_prop_bindings, validate_prop_bindings
validate_store
} from './validate.js'; } from './validate.js';
export { raf } from './timing.js'; export { raf } from './timing.js';
export { proxy, snapshot } from './proxy.js'; export { proxy, snapshot } from './proxy.js';
@ -147,5 +146,6 @@ export {
validate_component, validate_component,
validate_dynamic_element_tag, validate_dynamic_element_tag,
validate_snippet, validate_snippet,
validate_store,
validate_void_dynamic_element validate_void_dynamic_element
} from '../shared/validate.js'; } from '../shared/validate.js';

@ -20,6 +20,7 @@ import { check_ownership, widen_ownership } from './dev/ownership.js';
import { mutable_source, source, set } from './reactivity/sources.js'; import { mutable_source, source, set } from './reactivity/sources.js';
import { STATE_SYMBOL } from './constants.js'; import { STATE_SYMBOL } from './constants.js';
import { UNINITIALIZED } from '../../constants.js'; import { UNINITIALIZED } from '../../constants.js';
import * as e from './errors.js';
/** /**
* @template T * @template T
@ -334,6 +335,6 @@ const state_proxy_handler = {
if (DEV) { if (DEV) {
state_proxy_handler.setPrototypeOf = () => { state_proxy_handler.setPrototypeOf = () => {
throw new Error('Cannot set prototype of $state object'); e.state_prototype_fixed();
}; };
} }

@ -1,4 +1,3 @@
import { DEV } from 'esm-env';
import { import {
check_dirtiness, check_dirtiness,
current_component_context, current_component_context,
@ -30,6 +29,7 @@ import {
} from '../constants.js'; } from '../constants.js';
import { set } from './sources.js'; import { set } from './sources.js';
import { remove } from '../dom/reconciler.js'; import { remove } from '../dom/reconciler.js';
import * as e from '../errors.js';
/** /**
* @param {import('#client').Effect | null} effect * @param {import('#client').Effect | null} effect
@ -38,19 +38,11 @@ import { remove } from '../dom/reconciler.js';
*/ */
export function validate_effect(effect, rune) { export function validate_effect(effect, rune) {
if (effect === null) { if (effect === null) {
throw new Error( e.effect_orphan(rune);
'ERR_SVELTE_ORPHAN_EFFECT' +
(DEV
? `: ${rune} can only be used inside an effect (e.g. during component initialisation)`
: '')
);
} }
if (is_destroying_effect) { if (is_destroying_effect) {
throw new Error( e.effect_in_teardown(rune);
'ERR_SVELTE_EFFECT_IN_TEARDOWN' +
(DEV ? `: ${rune} cannot be used inside an effect cleanup function.` : '')
);
} }
} }

@ -11,6 +11,7 @@ import { derived } from './deriveds.js';
import { get, is_signals_recorded, untrack } from '../runtime.js'; import { get, is_signals_recorded, untrack } from '../runtime.js';
import { safe_equals } from './equality.js'; import { safe_equals } from './equality.js';
import { inspect_fn } from '../dev/inspect.js'; import { inspect_fn } from '../dev/inspect.js';
import * as e from '../errors.js';
/** /**
* @param {((value?: number) => number)} fn * @param {((value?: number) => number)} fn
@ -46,9 +47,8 @@ const rest_props_handler = {
}, },
set(target, key) { set(target, key) {
if (DEV) { if (DEV) {
throw new Error( // TODO should this happen in prod too?
`Rest element properties of $props() such as ${target.name}.${String(key)} are readonly` e.props_rest_readonly(`${target.name}.${String(key)}`);
);
} }
return false; return false;
@ -169,13 +169,7 @@ export function prop(props, key, flags, fallback) {
if (prop_value === undefined && fallback !== undefined) { if (prop_value === undefined && fallback !== undefined) {
if (setter && runes) { if (setter && runes) {
// TODO consolidate all these random runtime errors e.props_invalid_value(key);
throw new Error(
'ERR_SVELTE_BINDING_FALLBACK' +
(DEV
? `: Cannot pass undefined to bind:${key} because the property contains a fallback value. Pass a different value than undefined to ${key}.`
: '')
);
} }
prop_value = get_fallback(); prop_value = get_fallback();

@ -19,6 +19,7 @@ import {
import { equals, safe_equals } from './equality.js'; import { equals, safe_equals } from './equality.js';
import { CLEAN, DERIVED, DIRTY, BRANCH_EFFECT } from '../constants.js'; import { CLEAN, DERIVED, DIRTY, BRANCH_EFFECT } from '../constants.js';
import { UNINITIALIZED } from '../../../constants.js'; import { UNINITIALIZED } from '../../../constants.js';
import * as e from '../errors.js';
/** /**
* @template V * @template V
@ -91,14 +92,7 @@ export function set(signal, value) {
is_runes() && is_runes() &&
(current_reaction.f & DERIVED) !== 0 (current_reaction.f & DERIVED) !== 0
) { ) {
throw new Error( e.state_unsafe_mutation();
'ERR_SVELTE_UNSAFE_MUTATION' +
(DEV
? ": Unsafe mutations during Svelte's render or derived phase are not permitted in runes mode. " +
'This can lead to unexpected errors and possibly cause infinite loops.\n\nIf this mutation is not meant ' +
'to be reactive do not use the "$state" rune for that declaration.'
: '')
);
} }
if (!signal.equals(value)) { if (!signal.equals(value)) {

@ -20,6 +20,7 @@ import { array_from } from './utils.js';
import { handle_event_propagation } from './dom/elements/events.js'; import { handle_event_propagation } from './dom/elements/events.js';
import { reset_head_anchor } from './dom/blocks/svelte-head.js'; import { reset_head_anchor } from './dom/blocks/svelte-head.js';
import * as w from './warnings.js'; import * as w from './warnings.js';
import * as e from './errors.js';
/** @type {Set<string>} */ /** @type {Set<string>} */
export const all_registered_events = new Set(); export const all_registered_events = new Set();
@ -143,7 +144,7 @@ export function hydrate(component, options) {
} }
if (!node) { if (!node) {
throw new Error('Missing hydration marker'); e.hydration_missing_marker_open();
} }
const anchor = hydrate_anchor(node); const anchor = hydrate_anchor(node);
@ -158,14 +159,7 @@ export function hydrate(component, options) {
}, false); }, false);
} catch (error) { } catch (error) {
if (!hydrated && options.recover !== false) { if (!hydrated && options.recover !== false) {
// eslint-disable-next-line no-console w.hydration_mismatch();
console.error(
'ERR_SVELTE_HYDRATION_MISMATCH' +
(DEV
? ': Hydration failed because the initial UI does not match what was rendered on the server.'
: ''),
error
);
clear_text_content(target); clear_text_content(target);

@ -23,6 +23,7 @@ import { mutate, set, source } from './reactivity/sources.js';
import { update_derived } from './reactivity/deriveds.js'; import { update_derived } from './reactivity/deriveds.js';
import { inspect_captured_signals, inspect_fn, set_inspect_fn } from './dev/inspect.js'; import { inspect_captured_signals, inspect_fn, set_inspect_fn } from './dev/inspect.js';
import * as e from './errors.js'; import * as e from './errors.js';
import { lifecycle_outside_component } from '../shared/errors.js';
const FLUSH_MICROTASK = 0; const FLUSH_MICROTASK = 0;
const FLUSH_SYNC = 1; const FLUSH_SYNC = 1;
@ -952,7 +953,7 @@ export function getAllContexts() {
*/ */
function get_or_init_context_map(name) { function get_or_init_context_map(name) {
if (current_component_context === null) { if (current_component_context === null) {
e.lifecycle_outside_component(name); lifecycle_outside_component(name);
} }
return (current_component_context.c ??= new Map( return (current_component_context.c ??= new Map(
@ -1185,13 +1186,13 @@ if (DEV) {
let value; // let's hope noone modifies this global, but belts and braces let value; // let's hope noone modifies this global, but belts and braces
Object.defineProperty(globalThis, rune, { Object.defineProperty(globalThis, rune, {
configurable: true, configurable: true,
// eslint-disable-next-line getter-return
get: () => { get: () => {
if (value !== undefined) { if (value !== undefined) {
return value; return value;
} }
throw new Error(
`The ${rune} rune is only available inside .svelte and .svelte.js/ts files` e.rune_outside_svelte(rune);
);
}, },
set: (v) => { set: (v) => {
value = v; value = v;

@ -1,5 +1,6 @@
import { untrack } from './runtime.js'; import { untrack } from './runtime.js';
import { get_descriptor, is_array } from './utils.js'; import { get_descriptor, is_array } from './utils.js';
import * as e from './errors.js';
/** regex of all html void element names */ /** regex of all html void element names */
const void_element_names = const void_element_names =
@ -10,35 +11,27 @@ function is_void(tag) {
return void_element_names.test(tag) || tag.toLowerCase() === '!doctype'; return void_element_names.test(tag) || tag.toLowerCase() === '!doctype';
} }
/**
* @param {any} store
* @param {string} name
*/
export function validate_store(store, name) {
if (store != null && typeof store.subscribe !== 'function') {
throw new Error(`'${name}' is not a store with a 'subscribe' method`);
}
}
/** /**
* @param {() => any} component_fn * @param {() => any} component_fn
* @returns {any} * @returns {any}
*/ */
export function validate_dynamic_component(component_fn) { export function validate_dynamic_component(component_fn) {
const error_message = 'this={...} of <svelte:component> should specify a Svelte component.';
try { try {
const instance = component_fn(); const instance = component_fn();
if (instance !== undefined && typeof instance !== 'object') { if (instance !== undefined && typeof instance !== 'object') {
throw new Error(error_message); e.svelte_component_invalid_this_value();
} }
return instance; return instance;
} catch (err) { } catch (err) {
const { message } = /** @type {Error} */ (err); const { message } = /** @type {Error} */ (err);
if (typeof message === 'string' && message.indexOf('is not a function') !== -1) { if (typeof message === 'string' && message.indexOf('is not a function') !== -1) {
throw new Error(error_message); e.svelte_component_invalid_this_value();
} else {
throw err;
} }
throw err;
} }
} }
@ -59,29 +52,19 @@ export function validate_each_keys(collection, key_fn) {
for (let i = 0; i < length; i++) { for (let i = 0; i < length; i++) {
const key = key_fn(array[i], i); const key = key_fn(array[i], i);
if (keys.has(key)) { if (keys.has(key)) {
throw new Error( const a = String(keys.get(key));
`Cannot have duplicate keys in a keyed each: Keys at index ${keys.get( const b = String(i);
key
)} and ${i} with value '${array[i]}' are duplicates` /** @type {string | null} */
); let k = String(array[i]);
if (k.startsWith('[object ')) k = null;
e.each_key_duplicate(a, b, k);
} }
keys.set(key, i); keys.set(key, i);
} }
} }
/**
* @param {number} timeout
* @returns {() => void}
* */
export function loop_guard(timeout) {
const start = Date.now();
return () => {
if (Date.now() - start > timeout) {
throw new Error('Infinite loop detected');
}
};
}
/** /**
* @param {Record<string, any>} $$props * @param {Record<string, any>} $$props
* @param {string[]} bindable * @param {string[]} bindable
@ -95,17 +78,11 @@ export function validate_prop_bindings($$props, bindable, exports, component) {
if (setter) { if (setter) {
if (exports.includes(key)) { if (exports.includes(key)) {
throw new Error( e.bind_invalid_export(component.filename, key, name);
`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)) { if (!bindable.includes(key)) {
throw new Error( e.bind_not_bindable(key, component.filename, name);
`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()\``
);
} }
} }
} }

@ -5,6 +5,33 @@ import { DEV } from 'esm-env';
var bold = 'font-weight: bold'; var bold = 'font-weight: bold';
var normal = 'font-weight: normal'; var normal = 'font-weight: normal';
/**
* The `%attribute%` attribute on `%html%` changed its value between server and client renders. The client value, `%value%`, will be ignored in favour of the server value
* @param {string} attribute
* @param {string} html
* @param {string} value
*/
export function hydration_attribute_changed(attribute, html, value) {
if (DEV) {
console.warn(`%c[svelte] ${"hydration_attribute_changed"}\n%c${`The \`${attribute}\` attribute on \`${html}\` changed its value between server and client renders. The client value, \`${value}\`, will be ignored in favour of the server value`}`, bold, normal);
} else {
// TODO print a link to the documentation
console.warn("hydration_attribute_changed");
}
}
/**
* Hydration failed because the initial UI does not match what was rendered on the server
*/
export function hydration_mismatch() {
if (DEV) {
console.warn(`%c[svelte] ${"hydration_mismatch"}\n%c${"Hydration failed because the initial UI does not match what was rendered on the server"}`, bold, normal);
} else {
// TODO print a link to the documentation
console.warn("hydration_mismatch");
}
}
/** /**
* Tried to unmount a component that was not mounted * Tried to unmount a component that was not mounted
*/ */

@ -1,5 +1,6 @@
import { DEV } from 'esm-env'; import { DEV } from 'esm-env';
import { on_destroy } from './index.js'; import { on_destroy } from './index.js';
import * as e from '../shared/errors.js';
/** @type {import('#server').Component | null} */ /** @type {import('#server').Component | null} */
export var current_component = null; export var current_component = null;
@ -10,7 +11,7 @@ export var current_component = null;
* @returns {T} * @returns {T}
*/ */
export function getContext(key) { export function getContext(key) {
const context_map = getAllContexts(); const context_map = get_or_init_context_map('getContext');
const result = /** @type {T} */ (context_map.get(key)); const result = /** @type {T} */ (context_map.get(key));
return result; return result;
@ -23,7 +24,7 @@ export function getContext(key) {
* @returns {T} * @returns {T}
*/ */
export function setContext(key, context) { export function setContext(key, context) {
getAllContexts().set(key, context); get_or_init_context_map('setContext').set(key, context);
return context; return context;
} }
@ -32,21 +33,24 @@ export function setContext(key, context) {
* @returns {boolean} * @returns {boolean}
*/ */
export function hasContext(key) { export function hasContext(key) {
return getAllContexts().has(key); return get_or_init_context_map('hasContext').has(key);
} }
/** @returns {Map<any, any>} */ /** @returns {Map<any, any>} */
export function getAllContexts() { export function getAllContexts() {
const context = current_component; return get_or_init_context_map('getAllContexts');
}
if (context === null) { /**
throw new Error( * @param {string} name
'ERR_SVELTE_ORPHAN_CONTEXT' + * @returns {Map<unknown, unknown>}
(DEV ? 'Context can only be used during component initialisation.' : '') */
); function get_or_init_context_map(name) {
if (current_component === null) {
e.lifecycle_outside_component(name);
} }
return (context.c ??= new Map(get_parent_context(context) || undefined)); return (current_component.c ??= new Map(get_parent_context(current_component) || undefined));
} }
export function push() { export function push() {

@ -0,0 +1,13 @@
/* This file is generated by scripts/process-messages/index.js. Do not edit! */
/**
* `%name%(...)` is not available on the server
* @param {string} name
* @returns {never}
*/
export function lifecycle_function_unavailable(name) {
const error = new Error(`${"lifecycle_function_unavailable"}\n${`\`${name}(...)\` is not available on the server`}`);
error.name = 'Svelte error';
throw error;
}

@ -11,6 +11,7 @@ import {
import { DEV } from 'esm-env'; import { DEV } from 'esm-env';
import { current_component, pop, push } from './context.js'; import { current_component, pop, push } from './context.js';
import { BLOCK_CLOSE, BLOCK_OPEN } from './hydration.js'; import { BLOCK_CLOSE, BLOCK_OPEN } from './hydration.js';
import { validate_store } from '../shared/validate.js';
/** /**
* @typedef {{ * @typedef {{
@ -443,16 +444,6 @@ export function store_get(store_values, store_name, store) {
return store_values[store_name][2]; return store_values[store_name][2];
} }
/**
* @param {any} store
* @param {string} name
*/
export function validate_store(store, name) {
if (store != null && typeof store.subscribe !== 'function') {
throw new Error(`'${name}' is not a store with a 'subscribe' method`);
}
}
/** /**
* Sets the new value of a store and returns that value. * Sets the new value of a store and returns that value.
* @template V * @template V
@ -631,19 +622,6 @@ export function ensure_array_like(array_like_or_iterator) {
: Array.from(array_like_or_iterator); : Array.from(array_like_or_iterator);
} }
/**
* @param {number} timeout
* @returns {() => void}
* */
export function loop_guard(timeout) {
const start = Date.now();
return () => {
if (Date.now() - start > timeout) {
throw new Error('Infinite loop detected');
}
};
}
/** /**
* @param {any[]} args * @param {any[]} args
* @param {Function} [inspect] * @param {Function} [inspect]

@ -0,0 +1,85 @@
/* This file is generated by scripts/process-messages/index.js. Do not edit! */
import { DEV } from 'esm-env';
/**
* `%name%(...)` can only be used during component initialisation
* @param {string} name
* @returns {never}
*/
export function lifecycle_outside_component(name) {
if (DEV) {
const error = new Error(`${"lifecycle_outside_component"}\n${`\`${name}(...)\` can only be used during component initialisation`}`);
error.name = 'Svelte error';
throw error;
} else {
// TODO print a link to the documentation
throw new Error("lifecycle_outside_component");
}
}
/**
* The argument to `{@render ...}` must be a snippet function, not a component or some other kind of function. If you want to dynamically render one snippet or another, use `$derived` and pass its result to `{@render ...}`
* @returns {never}
*/
export function render_tag_invalid_argument() {
if (DEV) {
const error = new Error(`${"render_tag_invalid_argument"}\n${"The argument to `{@render ...}` must be a snippet function, not a component or some other kind of function. If you want to dynamically render one snippet or another, use `$derived` and pass its result to `{@render ...}`"}`);
error.name = 'Svelte error';
throw error;
} else {
// TODO print a link to the documentation
throw new Error("render_tag_invalid_argument");
}
}
/**
* A snippet must be rendered with `{@render ...}`
* @returns {never}
*/
export function snippet_used_as_component() {
if (DEV) {
const error = new Error(`${"snippet_used_as_component"}\n${"A snippet must be rendered with `{@render ...}`"}`);
error.name = 'Svelte error';
throw error;
} else {
// TODO print a link to the documentation
throw new Error("snippet_used_as_component");
}
}
/**
* `%name%` is not a store with a `subscribe` method
* @param {string} name
* @returns {never}
*/
export function store_invalid_shape(name) {
if (DEV) {
const error = new Error(`${"store_invalid_shape"}\n${`\`${name}\` is not a store with a \`subscribe\` method`}`);
error.name = 'Svelte error';
throw error;
} else {
// TODO print a link to the documentation
throw new Error("store_invalid_shape");
}
}
/**
* The `this` prop on `<svelte:element>` must be a string, if defined
* @returns {never}
*/
export function svelte_element_invalid_this_value() {
if (DEV) {
const error = new Error(`${"svelte_element_invalid_this_value"}\n${"The `this` prop on `<svelte:element>` must be a string, if defined"}`);
error.name = 'Svelte error';
throw error;
} else {
// TODO print a link to the documentation
throw new Error("svelte_element_invalid_this_value");
}
}

@ -1,5 +1,6 @@
import { is_void } from '../../compiler/phases/1-parse/utils/names.js'; import { is_void } from '../../compiler/phases/1-parse/utils/names.js';
import * as w from './warnings.js'; import * as w from './warnings.js';
import * as e from './errors.js';
const snippet_symbol = Symbol.for('svelte.snippet'); const snippet_symbol = Symbol.for('svelte.snippet');
@ -17,11 +18,9 @@ export function add_snippet_symbol(fn) {
*/ */
export function validate_snippet(snippet_fn) { export function validate_snippet(snippet_fn) {
if (snippet_fn && snippet_fn[snippet_symbol] !== true) { if (snippet_fn && snippet_fn[snippet_symbol] !== true) {
throw new Error( e.render_tag_invalid_argument();
'The argument to `{@render ...}` must be a snippet function, not a component or some other kind of function. ' +
'If you want to dynamically render one snippet or another, use `$derived` and pass its result to `{@render ...}`.'
);
} }
return snippet_fn; return snippet_fn;
} }
@ -31,8 +30,9 @@ export function validate_snippet(snippet_fn) {
*/ */
export function validate_component(component_fn) { export function validate_component(component_fn) {
if (component_fn?.[snippet_symbol] === true) { if (component_fn?.[snippet_symbol] === true) {
throw new Error('A snippet must be rendered with `{@render ...}`'); e.snippet_used_as_component();
} }
return component_fn; return component_fn;
} }
@ -52,6 +52,16 @@ export function validate_dynamic_element_tag(tag_fn) {
const tag = tag_fn(); const tag = tag_fn();
const is_string = typeof tag === 'string'; const is_string = typeof tag === 'string';
if (tag && !is_string) { if (tag && !is_string) {
throw new Error('<svelte:element> expects "this" attribute to be a string.'); e.svelte_element_invalid_this_value();
}
}
/**
* @param {any} store
* @param {string} name
*/
export function validate_store(store, name) {
if (store != null && typeof store.subscribe !== 'function') {
e.store_invalid_shape(name);
} }
} }

@ -2,7 +2,7 @@ import { test } from '../../test';
export default test({ export default test({
error: { error: {
code: 'missing_attribute_value', code: 'expected_attribute_value',
message: 'Expected attribute value', message: 'Expected attribute value',
position: [12, 12] position: [12, 12]
} }

@ -2,7 +2,7 @@ import { test } from '../../test';
export default test({ export default test({
error: { error: {
code: 'invalid_sequence_expression', code: 'attribute_invalid_sequence_expression',
message: message:
'Sequence expressions are not allowed as attribute/directive values in runes mode, unless wrapped in parentheses', 'Sequence expressions are not allowed as attribute/directive values in runes mode, unless wrapped in parentheses',
position: [124, 131] position: [124, 131]

@ -2,7 +2,7 @@ import { test } from '../../test';
export default test({ export default test({
error: { error: {
code: 'invalid_sequence_expression', code: 'attribute_invalid_sequence_expression',
message: message:
'Sequence expressions are not allowed as attribute/directive values in runes mode, unless wrapped in parentheses', 'Sequence expressions are not allowed as attribute/directive values in runes mode, unless wrapped in parentheses',
position: [163, 170] position: [163, 170]

@ -2,7 +2,7 @@ import { test } from '../../test';
export default test({ export default test({
error: { error: {
code: 'duplicate_attribute', code: 'attribute_duplicate',
message: 'Attributes need to be unique', message: 'Attributes need to be unique',
position: [17, 17] position: [17, 17]
} }

@ -2,7 +2,7 @@ import { test } from '../../test';
export default test({ export default test({
error: { error: {
code: 'duplicate_attribute', code: 'attribute_duplicate',
message: 'Attributes need to be unique', message: 'Attributes need to be unique',
position: [17, 17] position: [17, 17]
} }

@ -2,7 +2,7 @@ import { test } from '../../test';
export default test({ export default test({
error: { error: {
code: 'duplicate_attribute', code: 'attribute_duplicate',
message: 'Attributes need to be unique', message: 'Attributes need to be unique',
position: [17, 17] position: [17, 17]
} }

@ -2,7 +2,7 @@ import { test } from '../../test';
export default test({ export default test({
error: { error: {
code: 'invalid_continuing_block_placement', code: 'block_invalid_continuation_placement',
message: message:
'{:...} block is invalid at this position (did you forget to close the preceeding element or block?)', '{:...} block is invalid at this position (did you forget to close the preceeding element or block?)',
position: [1, 1] position: [1, 1]

@ -2,7 +2,7 @@ import { test } from '../../test';
export default test({ export default test({
error: { error: {
code: 'invalid_state_location', code: 'state_invalid_placement',
message: message:
'`$state(...)` can only be used as a variable declaration initializer or a class field', '`$state(...)` can only be used as a variable declaration initializer or a class field',
position: [33, 41] position: [33, 41]

@ -2,7 +2,7 @@ import { test } from '../../test';
export default test({ export default test({
error: { error: {
code: 'duplicate_slot_name', code: 'slot_attribute_duplicate',
message: "Duplicate slot name 'foo' in <Nested>" message: "Duplicate slot name 'foo' in <Nested>"
} }
}); });

@ -2,7 +2,7 @@ import { test } from '../../test';
export default test({ export default test({
error: { error: {
code: 'duplicate_slot_name', code: 'slot_attribute_duplicate',
message: "Duplicate slot name 'foo' in <Nested>" message: "Duplicate slot name 'foo' in <Nested>"
} }
}); });

@ -2,7 +2,7 @@ import { test } from '../../test';
export default test({ export default test({
error: { error: {
code: 'invalid_default_slot_content', code: 'slot_default_duplicate',
message: 'Found default slot content alongside an explicit slot="default"' message: 'Found default slot content alongside an explicit slot="default"'
} }
}); });

@ -2,7 +2,7 @@ import { test } from '../../test';
export default test({ export default test({
error: { error: {
code: 'duplicate_slot_name', code: 'slot_attribute_duplicate',
message: "Duplicate slot name 'foo' in <Nested>" message: "Duplicate slot name 'foo' in <Nested>"
} }
}); });

@ -2,7 +2,7 @@ import { test } from '../../test';
export default test({ export default test({
error: { error: {
code: 'duplicate_slot_name', code: 'slot_attribute_duplicate',
message: "Duplicate slot name 'foo' in <Nested>" message: "Duplicate slot name 'foo' in <Nested>"
} }
}); });

@ -2,7 +2,7 @@ import { test } from '../../test';
export default test({ export default test({
error: { error: {
code: 'duplicate_slot_name', code: 'slot_attribute_duplicate',
message: "Duplicate slot name 'foo' in <Nested>" message: "Duplicate slot name 'foo' in <Nested>"
} }
}); });

@ -2,7 +2,7 @@ import { test } from '../../test';
export default test({ export default test({
error: { error: {
code: 'invalid_slot_placement', code: 'slot_attribute_invalid_placement',
message: message:
"Element with a slot='...' attribute must be a child of a component or a descendant of a custom element" "Element with a slot='...' attribute must be a child of a component or a descendant of a custom element"
} }

@ -2,7 +2,7 @@ import { test } from '../../test';
export default test({ export default test({
error: { error: {
code: 'invalid_slot_placement', code: 'slot_attribute_invalid_placement',
message: message:
"Element with a slot='...' attribute must be a child of a component or a descendant of a custom element" "Element with a slot='...' attribute must be a child of a component or a descendant of a custom element"
} }

@ -2,7 +2,7 @@ import { test } from '../../test';
export default test({ export default test({
error: { error: {
code: 'invalid_slot_placement', code: 'slot_attribute_invalid_placement',
message: message:
"Element with a slot='...' attribute must be a child of a component or a descendant of a custom element" "Element with a slot='...' attribute must be a child of a component or a descendant of a custom element"
} }

@ -2,7 +2,7 @@ import { test } from '../../test';
export default test({ export default test({
error: { error: {
code: 'invalid_css_global_block_combinator', code: 'css_global_block_invalid_combinator',
message: 'A :global {...} block cannot follow a > combinator', message: 'A :global {...} block cannot follow a > combinator',
position: [12, 21] position: [12, 21]
} }

@ -2,7 +2,7 @@ import { test } from '../../test';
export default test({ export default test({
error: { error: {
code: 'invalid_css_global_block_declaration', code: 'css_global_block_invalid_declaration',
message: 'A :global {...} block can only contain rules, not declarations', message: 'A :global {...} block can only contain rules, not declarations',
position: [24, 34] position: [24, 34]
} }

@ -2,7 +2,7 @@ import { test } from '../../test';
export default test({ export default test({
error: { error: {
code: 'invalid_css_global_block_modifier', code: 'css_global_block_invalid_modifier',
message: 'A :global {...} block cannot modify an existing selector', message: 'A :global {...} block cannot modify an existing selector',
position: [14, 21] position: [14, 21]
} }

@ -2,7 +2,7 @@ import { test } from '../../test';
export default test({ export default test({
error: { error: {
code: 'invalid_css_global_block_list', code: 'css_global_block_invalid_list',
message: 'A :global {...} block cannot be part of a selector list with more than one item', message: 'A :global {...} block cannot be part of a selector list with more than one item',
position: [9, 31] position: [9, 31]
} }

@ -2,7 +2,7 @@ import { test } from '../../test';
export default test({ export default test({
error: { error: {
code: 'invalid_css_identifier', code: 'css_expected_identifier',
message: 'Expected a valid CSS identifier', message: 'Expected a valid CSS identifier',
position: [25, 25] position: [25, 25]
} }

@ -2,7 +2,7 @@ import { test } from '../../test';
export default test({ export default test({
error: { error: {
code: 'invalid_dollar_binding', code: 'dollar_binding_invalid',
message: 'The $ name is reserved, and cannot be used for variables and imports' message: 'The $ name is reserved, and cannot be used for variables and imports'
} }
}); });

@ -2,7 +2,7 @@ import { test } from '../../test';
export default test({ export default test({
error: { error: {
code: 'illegal_global', code: 'global_reference_invalid',
message: message:
'`$` is an illegal variable name. To reference a global variable called `$`, use `globalThis.$`' '`$` is an illegal variable name. To reference a global variable called `$`, use `globalThis.$`'
} }

@ -2,7 +2,7 @@ import { test } from '../../test';
export default test({ export default test({
error: { error: {
code: 'illegal_global', code: 'global_reference_invalid',
message: message:
'`$` is an illegal variable name. To reference a global variable called `$`, use `globalThis.$`' '`$` is an illegal variable name. To reference a global variable called `$`, use `globalThis.$`'
} }

@ -2,7 +2,7 @@ import { test } from '../../test';
export default test({ export default test({
error: { error: {
code: 'invalid_dollar_binding', code: 'dollar_binding_invalid',
message: 'The $ name is reserved, and cannot be used for variables and imports' message: 'The $ name is reserved, and cannot be used for variables and imports'
} }
}); });

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save