mirror of https://github.com/sveltejs/svelte
commit
46d8f1a79e
@ -0,0 +1,5 @@
|
||||
---
|
||||
"svelte": patch
|
||||
---
|
||||
|
||||
breaking: disallow binding to component exports in runes mode
|
||||
@ -0,0 +1,5 @@
|
||||
---
|
||||
'svelte': patch
|
||||
---
|
||||
|
||||
fix: correctly interpret empty aria- attribute
|
||||
@ -0,0 +1,5 @@
|
||||
---
|
||||
"svelte": patch
|
||||
---
|
||||
|
||||
fix: disallow mixing on:click and onclick syntax
|
||||
@ -0,0 +1,5 @@
|
||||
---
|
||||
"svelte": patch
|
||||
---
|
||||
|
||||
fix: make hr, script and template valid select children
|
||||
@ -0,0 +1,5 @@
|
||||
---
|
||||
"svelte": patch
|
||||
---
|
||||
|
||||
fix: disallow mounting a snippet
|
||||
@ -0,0 +1,5 @@
|
||||
---
|
||||
'svelte': patch
|
||||
---
|
||||
|
||||
fix: remove `bind_prop` in runes mode
|
||||
@ -0,0 +1,5 @@
|
||||
---
|
||||
'svelte': patch
|
||||
---
|
||||
|
||||
feat: only inject push/init/pop when necessary
|
||||
@ -0,0 +1,5 @@
|
||||
---
|
||||
"svelte": patch
|
||||
---
|
||||
|
||||
feat: provide migration helper
|
||||
@ -0,0 +1,5 @@
|
||||
---
|
||||
"svelte": patch
|
||||
---
|
||||
|
||||
feat: introduce types to express bindability
|
||||
@ -0,0 +1,5 @@
|
||||
---
|
||||
"svelte": patch
|
||||
---
|
||||
|
||||
fix: avoid hoisting error by using 'let' instead of 'var'
|
||||
@ -0,0 +1,5 @@
|
||||
---
|
||||
'svelte': patch
|
||||
---
|
||||
|
||||
fix: mark `accessors` and `immutable` as deprecated
|
||||
@ -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
|
||||
@ -0,0 +1,21 @@
|
||||
## 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
|
||||
|
||||
> Tried to unmount a component that was not mounted
|
||||
|
||||
## ownership_invalid_binding
|
||||
|
||||
> %parent% passed a value to %child% with `bind:`, but the value is owned by %owner%. Consider creating a binding between %owner% and %parent%
|
||||
|
||||
## ownership_invalid_mutation
|
||||
|
||||
> Mutating a value outside the component that created it is strongly discouraged. Consider passing values to child components with `bind:`, or use a callback instead
|
||||
|
||||
> %component% mutated a value owned by %owner%. This is strongly discouraged. Consider passing values to child components with `bind:`, or use a callback instead
|
||||
@ -0,0 +1,11 @@
|
||||
## options_invalid_value
|
||||
|
||||
> Invalid compiler option: %details%
|
||||
|
||||
## options_removed
|
||||
|
||||
> Invalid compiler option: %details%
|
||||
|
||||
## options_unrecognised
|
||||
|
||||
> Unrecognised compiler option %keypath%
|
||||
@ -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">`
|
||||
@ -0,0 +1,47 @@
|
||||
## css_empty_declaration
|
||||
|
||||
> Declaration cannot be empty
|
||||
|
||||
## css_expected_identifier
|
||||
|
||||
> Expected a valid CSS identifier
|
||||
|
||||
## css_global_block_invalid_combinator
|
||||
|
||||
> A :global {...} block cannot follow a %name% combinator
|
||||
|
||||
## css_global_block_invalid_declaration
|
||||
|
||||
> A :global {...} block can only contain rules, not declarations
|
||||
|
||||
## css_global_block_invalid_list
|
||||
|
||||
> A :global {...} block cannot be part of a selector list with more than one item
|
||||
|
||||
## css_global_block_invalid_modifier
|
||||
|
||||
> A :global {...} block cannot modify an existing selector
|
||||
|
||||
## css_global_invalid_placement
|
||||
|
||||
> :global(...) can be at the start or end of a selector sequence, but not in the middle
|
||||
|
||||
## css_global_invalid_selector
|
||||
|
||||
> :global(...) must contain exactly one selector
|
||||
|
||||
## css_global_invalid_selector_list
|
||||
|
||||
> :global(...) must not contain type or universal selectors when used in a compound selector
|
||||
|
||||
## css_nesting_selector_invalid_placement
|
||||
|
||||
> Nesting selectors can only be used inside a rule
|
||||
|
||||
## css_selector_invalid
|
||||
|
||||
> Invalid selector
|
||||
|
||||
## css_type_selector_invalid_placement
|
||||
|
||||
> :global(...) must not be followed with a type selector
|
||||
@ -0,0 +1,369 @@
|
||||
## 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
|
||||
|
||||
## mixed_event_handler_syntaxes
|
||||
|
||||
> Mixing old (on:%name%) and new syntaxes for event handling is not allowed. Use only the on%name% syntax.
|
||||
|
||||
## 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
|
||||
@ -0,0 +1,167 @@
|
||||
## a11y_accesskey
|
||||
|
||||
> Avoid using accesskey
|
||||
|
||||
## a11y_aria_activedescendant_has_tabindex
|
||||
|
||||
> An element with an aria-activedescendant attribute should have a tabindex value
|
||||
|
||||
## a11y_aria_attributes
|
||||
|
||||
> `<%name%>` should not have aria-* attributes
|
||||
|
||||
## a11y_autocomplete_valid
|
||||
|
||||
> '%value%' is an invalid value for 'autocomplete' on `<input type="%type%">`
|
||||
|
||||
## a11y_autofocus
|
||||
|
||||
> Avoid using autofocus
|
||||
|
||||
## 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.
|
||||
|
||||
## a11y_distracting_elements
|
||||
|
||||
> Avoid `<%name%>` elements
|
||||
|
||||
## a11y_figcaption_index
|
||||
|
||||
> `<figcaption>` must be first or last child of `<figure>`
|
||||
|
||||
## a11y_figcaption_parent
|
||||
|
||||
> `<figcaption>` must be an immediate child of `<figure>`
|
||||
|
||||
## a11y_hidden
|
||||
|
||||
> `<%name%>` element should not be hidden
|
||||
|
||||
## a11y_img_redundant_alt
|
||||
|
||||
> Screenreaders already announce `<img>` elements as an image.
|
||||
|
||||
## a11y_incorrect_aria_attribute_type
|
||||
|
||||
> The value of '%attribute%' must be a %type%
|
||||
|
||||
## a11y_incorrect_aria_attribute_type_boolean
|
||||
|
||||
> The value of '%attribute%' must be either 'true' or 'false'. It cannot be empty
|
||||
|
||||
## a11y_incorrect_aria_attribute_type_id
|
||||
|
||||
> The value of '%attribute%' must be a string that represents a DOM element ID
|
||||
|
||||
## a11y_incorrect_aria_attribute_type_idlist
|
||||
|
||||
> The value of '%attribute%' must be a space-separated list of strings that represent DOM element IDs
|
||||
|
||||
## a11y_incorrect_aria_attribute_type_integer
|
||||
|
||||
> The value of '%attribute%' must be an integer
|
||||
|
||||
## a11y_incorrect_aria_attribute_type_token
|
||||
|
||||
> The value of '%attribute%' must be exactly one of %values%
|
||||
|
||||
## a11y_incorrect_aria_attribute_type_tokenlist
|
||||
|
||||
> The value of '%attribute%' must be a space-separated list of one or more of %values%
|
||||
|
||||
## a11y_incorrect_aria_attribute_type_tristate
|
||||
|
||||
> The value of '%attribute%' must be exactly one of true, false, or mixed
|
||||
|
||||
## a11y_interactive_supports_focus
|
||||
|
||||
> Elements with the '%role%' interactive role must have a tabindex value.
|
||||
|
||||
## a11y_invalid_attribute
|
||||
|
||||
> '%href_value%' is not a valid %href_attribute% attribute
|
||||
|
||||
## a11y_label_has_associated_control
|
||||
|
||||
> A form label must be associated with a control.
|
||||
|
||||
## a11y_media_has_caption
|
||||
|
||||
> `<video>` elements must have a `<track kind="captions">`
|
||||
|
||||
## a11y_misplaced_role
|
||||
|
||||
> `<%name%>` should not have role attribute
|
||||
|
||||
## a11y_misplaced_scope
|
||||
|
||||
> The scope attribute should only be used with `<th>` elements
|
||||
|
||||
## a11y_missing_attribute
|
||||
|
||||
> `<%name%>` element should have %article% %sequence% attribute
|
||||
|
||||
## a11y_missing_content
|
||||
|
||||
> `<%name%>` element should have child content
|
||||
|
||||
## a11y_mouse_events_have_key_events
|
||||
|
||||
> '%event%' event must be accompanied by '%accompanied_by%' event
|
||||
|
||||
## a11y_no_abstract_role
|
||||
|
||||
> Abstract role '%role%' is forbidden
|
||||
|
||||
## a11y_no_interactive_element_to_noninteractive_role
|
||||
|
||||
> `<%element%>` cannot have role '%role%'
|
||||
|
||||
## a11y_no_noninteractive_element_interactions
|
||||
|
||||
> Non-interactive element `<%element%>` should not be assigned mouse or keyboard event listeners.
|
||||
|
||||
## a11y_no_noninteractive_element_to_interactive_role
|
||||
|
||||
> Non-interactive element `<%element%>` cannot have interactive role '%role%'
|
||||
|
||||
## a11y_no_noninteractive_tabindex
|
||||
|
||||
> noninteractive element cannot have nonnegative tabIndex value
|
||||
|
||||
## a11y_no_redundant_roles
|
||||
|
||||
> Redundant role '%role%'
|
||||
|
||||
## a11y_no_static_element_interactions
|
||||
|
||||
> `<%element%>` with a %handler% handler must have an ARIA role
|
||||
|
||||
## a11y_positive_tabindex
|
||||
|
||||
> Avoid tabindex values above zero
|
||||
|
||||
## a11y_role_has_required_aria_props
|
||||
|
||||
> Elements with the ARIA role "%role%" must have the following attributes defined: %props%
|
||||
|
||||
## a11y_role_supports_aria_props
|
||||
|
||||
> The attribute '%attribute%' is not supported by the role '%role%'
|
||||
|
||||
## a11y_role_supports_aria_props_implicit
|
||||
|
||||
> The attribute '%attribute%' is not supported by the role '%role%'. This role is implicit on the element `<%name%>`
|
||||
|
||||
## a11y_unknown_aria_attribute
|
||||
|
||||
> Unknown aria attribute 'aria-%attribute%'
|
||||
|
||||
> Unknown aria attribute 'aria-%attribute%'. Did you mean '%suggestion%'?
|
||||
|
||||
## a11y_unknown_role
|
||||
|
||||
> Unknown role '%role%'
|
||||
|
||||
> Unknown role '%role%'. Did you mean '%suggestion%'?
|
||||
@ -0,0 +1,27 @@
|
||||
## options_deprecated_accessors
|
||||
|
||||
> The `accessors` option has been deprecated. It will have no effect in runes mode
|
||||
|
||||
## options_deprecated_immutable
|
||||
|
||||
> The `immutable` option has been deprecated. It will have no effect in runes mode
|
||||
|
||||
## options_missing_custom_element
|
||||
|
||||
> The `customElement` option is used when generating a custom element. Did you forget the `customElement: true` compile option?
|
||||
|
||||
## options_removed_enable_sourcemap
|
||||
|
||||
> The `enableSourcemap` option has been removed. Source maps are always generated now, and tooling can choose to ignore them
|
||||
|
||||
## options_removed_hydratable
|
||||
|
||||
> The `hydratable` option has been removed. Svelte components are always hydratable now
|
||||
|
||||
## options_removed_loop_guard_timeout
|
||||
|
||||
> The `loopGuardTimeout` option has been removed
|
||||
|
||||
## options_renamed_ssr_dom
|
||||
|
||||
> `generate: "dom"` and `generate: "ssr"` options have been renamed to "client" and "server" respectively
|
||||
@ -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
|
||||
@ -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
|
||||
@ -0,0 +1,3 @@
|
||||
## dynamic_void_element_content
|
||||
|
||||
> `<svelte:element this="%tag%">` is a void element — it cannot have content
|
||||
@ -0,0 +1,304 @@
|
||||
// @ts-check
|
||||
import fs from 'node:fs';
|
||||
import * as acorn from 'acorn';
|
||||
import { walk } from 'zimmerframe';
|
||||
import * as esrap from 'esrap';
|
||||
|
||||
/** @type {Record<string, Record<string, { messages: string[], details: string | null }>>} */
|
||||
const messages = {};
|
||||
const seen = new Set();
|
||||
|
||||
for (const category of fs.readdirSync('messages')) {
|
||||
messages[category] = {};
|
||||
|
||||
for (const file of fs.readdirSync(`messages/${category}`)) {
|
||||
if (!file.endsWith('.md')) continue;
|
||||
|
||||
const markdown = fs
|
||||
.readFileSync(`messages/${category}/${file}`, 'utf-8')
|
||||
.replace(/\r\n/g, '\n');
|
||||
|
||||
const sorted = [];
|
||||
|
||||
for (const match of markdown.matchAll(/## ([\w]+)\n\n([^]+?)(?=$|\n\n## )/g)) {
|
||||
const [_, code, text] = match;
|
||||
|
||||
if (seen.has(code)) {
|
||||
throw new Error(`Duplicate message code ${category}/${code}`);
|
||||
}
|
||||
|
||||
sorted.push({ code, _ });
|
||||
|
||||
const sections = text.trim().split('\n\n');
|
||||
let details = null;
|
||||
if (!sections[sections.length - 1].startsWith('> ')) {
|
||||
details = /** @type {string} */ (sections.pop());
|
||||
}
|
||||
|
||||
if (sections.length === 0) {
|
||||
throw new Error('No message text');
|
||||
}
|
||||
|
||||
seen.add(code);
|
||||
messages[category][code] = {
|
||||
messages: sections.map((section) => section.replace(/^> /gm, '')),
|
||||
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'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function transform(name, dest) {
|
||||
const source = fs
|
||||
.readFileSync(new URL(`./templates/${name}.js`, import.meta.url), 'utf-8')
|
||||
.replace(/\r\n/g, '\n');
|
||||
|
||||
const comments = [];
|
||||
|
||||
const ast = acorn.parse(source, {
|
||||
ecmaVersion: 'latest',
|
||||
sourceType: 'module',
|
||||
onComment: (block, value, start, end) => {
|
||||
if (block && /\n/.test(value)) {
|
||||
let a = start;
|
||||
while (a > 0 && source[a - 1] !== '\n') a -= 1;
|
||||
|
||||
let b = a;
|
||||
while (/[ \t]/.test(source[b])) b += 1;
|
||||
|
||||
const indentation = source.slice(a, b);
|
||||
value = value.replace(new RegExp(`^${indentation}`, 'gm'), '');
|
||||
}
|
||||
|
||||
comments.push({ type: block ? 'Block' : 'Line', value, start, end });
|
||||
}
|
||||
});
|
||||
|
||||
walk(ast, null, {
|
||||
_(node, { next }) {
|
||||
let comment;
|
||||
|
||||
while (comments[0] && comments[0].start < node.start) {
|
||||
comment = comments.shift();
|
||||
// @ts-expect-error
|
||||
(node.leadingComments ||= []).push(comment);
|
||||
}
|
||||
|
||||
next();
|
||||
|
||||
if (comments[0]) {
|
||||
const slice = source.slice(node.end, comments[0].start);
|
||||
|
||||
if (/^[,) \t]*$/.test(slice)) {
|
||||
// @ts-expect-error
|
||||
node.trailingComments = [comments.shift()];
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const category = messages[name];
|
||||
|
||||
// find the `export function CODE` node
|
||||
const index = ast.body.findIndex((node) => {
|
||||
if (
|
||||
node.type === 'ExportNamedDeclaration' &&
|
||||
node.declaration &&
|
||||
node.declaration.type === 'FunctionDeclaration'
|
||||
) {
|
||||
return node.declaration.id.name === 'CODE';
|
||||
}
|
||||
});
|
||||
|
||||
if (index === -1) throw new Error(`missing export function CODE in ${name}.js`);
|
||||
|
||||
const template_node = ast.body[index];
|
||||
ast.body.splice(index, 1);
|
||||
|
||||
for (const code in category) {
|
||||
const { messages } = category[code];
|
||||
const vars = [];
|
||||
|
||||
const group = messages.map((text, i) => {
|
||||
for (const match of text.matchAll(/%(\w+)%/g)) {
|
||||
const name = match[1];
|
||||
if (!vars.includes(name)) {
|
||||
vars.push(match[1]);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
text,
|
||||
vars: vars.slice()
|
||||
};
|
||||
});
|
||||
|
||||
/** @type {import('estree').Expression} */
|
||||
let message = { type: 'Literal', value: '' };
|
||||
let prev_vars;
|
||||
|
||||
for (let i = 0; i < group.length; i += 1) {
|
||||
const { text, vars } = group[i];
|
||||
|
||||
if (vars.length === 0) {
|
||||
message = {
|
||||
type: 'Literal',
|
||||
value: text
|
||||
};
|
||||
continue;
|
||||
}
|
||||
|
||||
const parts = text.split(/(%\w+%)/);
|
||||
|
||||
/** @type {import('estree').Expression[]} */
|
||||
const expressions = [];
|
||||
|
||||
/** @type {import('estree').TemplateElement[]} */
|
||||
const quasis = [];
|
||||
|
||||
for (let i = 0; i < parts.length; i += 1) {
|
||||
const part = parts[i];
|
||||
if (i % 2 === 0) {
|
||||
const str = part.replace(/(`|\${)/g, '\\$1');
|
||||
quasis.push({
|
||||
type: 'TemplateElement',
|
||||
value: { raw: str, cooked: str },
|
||||
tail: i === parts.length - 1
|
||||
});
|
||||
} else {
|
||||
expressions.push({
|
||||
type: 'Identifier',
|
||||
name: part.slice(1, -1)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/** @type {import('estree').Expression} */
|
||||
const expression = {
|
||||
type: 'TemplateLiteral',
|
||||
expressions,
|
||||
quasis
|
||||
};
|
||||
|
||||
if (prev_vars) {
|
||||
if (vars.length === prev_vars.length) {
|
||||
throw new Error('Message overloads must have new parameters');
|
||||
}
|
||||
|
||||
message = {
|
||||
type: 'ConditionalExpression',
|
||||
test: {
|
||||
type: 'Identifier',
|
||||
name: vars[prev_vars.length]
|
||||
},
|
||||
consequent: expression,
|
||||
alternate: message
|
||||
};
|
||||
} else {
|
||||
message = expression;
|
||||
}
|
||||
|
||||
prev_vars = vars;
|
||||
}
|
||||
|
||||
const clone = walk(/** @type {import('estree').Node} */ (template_node), null, {
|
||||
// @ts-expect-error Block is a block comment, which is not recognised
|
||||
Block(node, context) {
|
||||
if (!node.value.includes('PARAMETER')) return;
|
||||
|
||||
const value = node.value
|
||||
.split('\n')
|
||||
.map((line) => {
|
||||
if (line === ' * MESSAGE') {
|
||||
return messages[messages.length - 1]
|
||||
.split('\n')
|
||||
.map((line) => ` * ${line}`)
|
||||
.join('\n');
|
||||
}
|
||||
|
||||
if (line.includes('PARAMETER')) {
|
||||
return vars
|
||||
.map((name, i) => {
|
||||
const optional = i >= group[0].vars.length;
|
||||
|
||||
return optional
|
||||
? ` * @param {string | undefined | null} [${name}]`
|
||||
: ` * @param {string} ${name}`;
|
||||
})
|
||||
.join('\n');
|
||||
}
|
||||
|
||||
return line;
|
||||
})
|
||||
.filter((x) => x !== '')
|
||||
.join('\n');
|
||||
|
||||
if (value !== node.value) {
|
||||
return { ...node, value };
|
||||
}
|
||||
},
|
||||
FunctionDeclaration(node, context) {
|
||||
if (node.id.name !== 'CODE') return;
|
||||
|
||||
const params = [];
|
||||
|
||||
for (const param of node.params) {
|
||||
if (param.type === 'Identifier' && param.name === 'PARAMETER') {
|
||||
params.push(...vars.map((name) => ({ type: 'Identifier', name })));
|
||||
} else {
|
||||
params.push(param);
|
||||
}
|
||||
}
|
||||
|
||||
return /** @type {import('estree').FunctionDeclaration} */ ({
|
||||
.../** @type {import('estree').FunctionDeclaration} */ (context.next()),
|
||||
params,
|
||||
id: {
|
||||
...node.id,
|
||||
name: code
|
||||
}
|
||||
});
|
||||
},
|
||||
Literal(node) {
|
||||
if (node.value === 'CODE') {
|
||||
return {
|
||||
type: 'Literal',
|
||||
value: code
|
||||
};
|
||||
}
|
||||
},
|
||||
Identifier(node) {
|
||||
if (node.name !== 'MESSAGE') return;
|
||||
return message;
|
||||
}
|
||||
});
|
||||
|
||||
// @ts-expect-error
|
||||
ast.body.push(clone);
|
||||
}
|
||||
|
||||
// @ts-expect-error
|
||||
const module = esrap.print(ast);
|
||||
|
||||
fs.writeFileSync(
|
||||
dest,
|
||||
`/* This file is generated by scripts/process-messages/index.js. Do not edit! */\n\n` +
|
||||
module.code,
|
||||
'utf-8'
|
||||
);
|
||||
}
|
||||
|
||||
transform('compile-errors', 'src/compiler/errors.js');
|
||||
transform('compile-warnings', 'src/compiler/warnings.js');
|
||||
|
||||
transform('client-warnings', 'src/internal/client/warnings.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');
|
||||
@ -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');
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,17 @@
|
||||
import { DEV } from 'esm-env';
|
||||
|
||||
var bold = 'font-weight: bold';
|
||||
var normal = 'font-weight: normal';
|
||||
|
||||
/**
|
||||
* MESSAGE
|
||||
* @param {string} PARAMETER
|
||||
*/
|
||||
export function CODE(PARAMETER) {
|
||||
if (DEV) {
|
||||
console.warn(`%c[svelte] ${'CODE'}\n%c${MESSAGE}`, bold, normal);
|
||||
} else {
|
||||
// TODO print a link to the documentation
|
||||
console.warn('CODE');
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,74 @@
|
||||
/** @typedef {{ start?: number, end?: number }} NodeLike */
|
||||
|
||||
// interface is duplicated between here (used internally) and ./interfaces.js
|
||||
// (exposed publicly), and I'm not sure how to avoid that
|
||||
export class CompileError extends Error {
|
||||
name = 'CompileError';
|
||||
|
||||
/** @type {import('#compiler').CompileError['filename']} */
|
||||
filename = undefined;
|
||||
|
||||
/** @type {import('#compiler').CompileError['position']} */
|
||||
position = undefined;
|
||||
|
||||
/** @type {import('#compiler').CompileError['start']} */
|
||||
start = undefined;
|
||||
|
||||
/** @type {import('#compiler').CompileError['end']} */
|
||||
end = undefined;
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {string} code
|
||||
* @param {string} message
|
||||
* @param {[number, number] | undefined} position
|
||||
*/
|
||||
constructor(code, message, position) {
|
||||
super(message);
|
||||
this.code = code;
|
||||
this.position = position;
|
||||
}
|
||||
|
||||
toString() {
|
||||
let out = `${this.name}: ${this.message}`;
|
||||
|
||||
out += `\n(${this.code})`;
|
||||
|
||||
if (this.filename) {
|
||||
out += `\n${this.filename}`;
|
||||
|
||||
if (this.start) {
|
||||
out += `${this.start.line}:${this.start.column}`;
|
||||
}
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {null | number | NodeLike} node
|
||||
* @param {string} code
|
||||
* @param {string} message
|
||||
* @returns {never}
|
||||
*/
|
||||
function e(node, code, message) {
|
||||
const start = typeof node === 'number' ? node : node?.start;
|
||||
const end = typeof node === 'number' ? node : node?.end;
|
||||
|
||||
throw new CompileError(
|
||||
code,
|
||||
message,
|
||||
start !== undefined && end !== undefined ? [start, end] : undefined
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* MESSAGE
|
||||
* @param {null | number | NodeLike} node
|
||||
* @param {string} PARAMETER
|
||||
* @returns {never}
|
||||
*/
|
||||
export function CODE(node, PARAMETER) {
|
||||
e(node, 'CODE', MESSAGE);
|
||||
}
|
||||
@ -0,0 +1,52 @@
|
||||
import { getLocator } from 'locate-character';
|
||||
|
||||
/** @typedef {{ start?: number, end?: number }} NodeLike */
|
||||
|
||||
/** @type {import('#compiler').Warning[]} */
|
||||
let warnings = [];
|
||||
|
||||
/** @type {string | undefined} */
|
||||
let filename;
|
||||
|
||||
let locator = getLocator('', { offsetLine: 1 });
|
||||
|
||||
/**
|
||||
* @param {{
|
||||
* source: string;
|
||||
* filename: string | undefined;
|
||||
* }} options
|
||||
* @returns {import('#compiler').Warning[]}
|
||||
*/
|
||||
export function reset_warnings(options) {
|
||||
filename = options.filename;
|
||||
locator = getLocator(options.source, { offsetLine: 1 });
|
||||
|
||||
return (warnings = []);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {null | NodeLike} node
|
||||
* @param {string} code
|
||||
* @param {string} message
|
||||
*/
|
||||
function w(node, code, message) {
|
||||
// @ts-expect-error
|
||||
if (node?.ignores?.has(code)) return;
|
||||
|
||||
warnings.push({
|
||||
code,
|
||||
message,
|
||||
filename,
|
||||
start: node?.start !== undefined ? locator(node.start) : undefined,
|
||||
end: node?.end !== undefined ? locator(node.end) : undefined
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* MESSAGE
|
||||
* @param {null | NodeLike} node
|
||||
* @param {string} PARAMETER
|
||||
*/
|
||||
export function CODE(node, PARAMETER) {
|
||||
w(node, 'CODE', MESSAGE);
|
||||
}
|
||||
@ -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');
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,19 @@
|
||||
import { DEV } from 'esm-env';
|
||||
|
||||
var bold = 'font-weight: bold';
|
||||
var normal = 'font-weight: normal';
|
||||
|
||||
/**
|
||||
* MESSAGE
|
||||
* @param {boolean} trace
|
||||
* @param {string} PARAMETER
|
||||
*/
|
||||
export function CODE(trace, PARAMETER) {
|
||||
if (DEV) {
|
||||
console.warn(`%c[svelte] ${'CODE'}\n%c${MESSAGE}`, bold, normal);
|
||||
if (trace) console.trace('stack trace');
|
||||
} else {
|
||||
// TODO print a link to the documentation
|
||||
console.warn('CODE');
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,770 @@
|
||||
import MagicString from 'magic-string';
|
||||
import { walk } from 'zimmerframe';
|
||||
import { parse } from '../phases/1-parse/index.js';
|
||||
import { analyze_component } from '../phases/2-analyze/index.js';
|
||||
import { validate_component_options } from '../validate-options.js';
|
||||
import { get_rune } from '../phases/scope.js';
|
||||
import { reset_warnings } from '../warnings.js';
|
||||
import { extract_identifiers } from '../utils/ast.js';
|
||||
import { regex_is_valid_identifier } from '../phases/patterns.js';
|
||||
|
||||
/**
|
||||
* Does a best-effort migration of Svelte code towards using runes, event attributes and render tags.
|
||||
* May throw an error if the code is too complex to migrate automatically.
|
||||
*
|
||||
* @param {string} source
|
||||
* @returns {{ code: string; }}
|
||||
*/
|
||||
export function migrate(source) {
|
||||
try {
|
||||
reset_warnings({ source, filename: 'migrate.svelte' });
|
||||
|
||||
let parsed = parse(source);
|
||||
|
||||
const { customElement: customElementOptions, ...parsed_options } = parsed.options || {};
|
||||
|
||||
/** @type {import('#compiler').ValidatedCompileOptions} */
|
||||
const combined_options = {
|
||||
...validate_component_options({}, ''),
|
||||
...parsed_options,
|
||||
customElementOptions
|
||||
};
|
||||
|
||||
const str = new MagicString(source);
|
||||
const analysis = analyze_component(parsed, source, combined_options);
|
||||
const indent = guess_indent(source);
|
||||
|
||||
/** @type {State} */
|
||||
let state = {
|
||||
scope: analysis.instance.scope,
|
||||
analysis,
|
||||
str,
|
||||
indent,
|
||||
props: [],
|
||||
props_insertion_point: parsed.instance?.content.start ?? 0,
|
||||
has_props_rune: false,
|
||||
props_name: analysis.root.unique('props').name,
|
||||
rest_props_name: analysis.root.unique('rest').name,
|
||||
end: source.length,
|
||||
run_name: analysis.root.unique('run').name,
|
||||
needs_run: false
|
||||
};
|
||||
|
||||
if (parsed.instance) {
|
||||
walk(parsed.instance.content, state, instance_script);
|
||||
}
|
||||
|
||||
state = { ...state, scope: analysis.template.scope };
|
||||
walk(parsed.fragment, state, template);
|
||||
|
||||
const run_import = `import { run${state.run_name === 'run' ? '' : `as ${state.run_name}`} } from 'svelte/legacy';`;
|
||||
let added_legacy_import = false;
|
||||
|
||||
if (state.props.length > 0 || analysis.uses_rest_props || analysis.uses_props) {
|
||||
const has_many_props = state.props.length > 3;
|
||||
const props_separator = has_many_props ? `\n${indent}${indent}` : ' ';
|
||||
let props = '';
|
||||
if (analysis.uses_props) {
|
||||
props = `...${state.props_name}`;
|
||||
} else {
|
||||
props = state.props
|
||||
.map((prop) => {
|
||||
let prop_str =
|
||||
prop.local === prop.exported ? prop.local : `${prop.exported}: ${prop.local}`;
|
||||
if (prop.bindable) {
|
||||
prop_str += ` = $bindable(${prop.init})`;
|
||||
} else if (prop.init) {
|
||||
prop_str += ` = ${prop.init}`;
|
||||
}
|
||||
return prop_str;
|
||||
})
|
||||
.join(`,${props_separator}`);
|
||||
|
||||
if (analysis.uses_rest_props) {
|
||||
props += `,${props_separator}...${state.rest_props_name}`;
|
||||
}
|
||||
}
|
||||
|
||||
if (state.has_props_rune) {
|
||||
// some render tags or forwarded event attributes to add
|
||||
str.appendRight(state.props_insertion_point, ` ${props},`);
|
||||
} else {
|
||||
const uses_ts = parsed.instance?.attributes.some(
|
||||
(attr) => attr.name === 'lang' && /** @type {any} */ (attr).value[0].data === 'ts'
|
||||
);
|
||||
const type_name = state.scope.root.unique('Props').name;
|
||||
let type = '';
|
||||
if (uses_ts) {
|
||||
if (analysis.uses_props || analysis.uses_rest_props) {
|
||||
type = `interface ${type_name} { [key: string]: any }`;
|
||||
} else {
|
||||
type = `interface ${type_name} {${props_separator}${state.props
|
||||
.map((prop) => {
|
||||
return `${prop.exported}${prop.optional ? '?' : ''}: ${prop.type}`;
|
||||
})
|
||||
.join(`,${props_separator}`)}${has_many_props ? `\n${indent}` : ' '}}`;
|
||||
}
|
||||
} else {
|
||||
if (analysis.uses_props || analysis.uses_rest_props) {
|
||||
type = `{Record<string, any>}`;
|
||||
} else {
|
||||
type = `{${state.props
|
||||
.map((prop) => {
|
||||
return `${prop.exported}${prop.optional ? '?' : ''}: ${prop.type}`;
|
||||
})
|
||||
.join(`, `)}}`;
|
||||
}
|
||||
}
|
||||
|
||||
let props_declaration = `let {${props_separator}${props}${has_many_props ? `\n${indent}` : ' '}}`;
|
||||
if (uses_ts) {
|
||||
props_declaration = `${type}\n\n${indent}${props_declaration}`;
|
||||
props_declaration = `${props_declaration}${type ? `: ${type_name}` : ''} = $props();`;
|
||||
} else {
|
||||
props_declaration = `/** @type {${type}} */\n${indent}${props_declaration}`;
|
||||
props_declaration = `${props_declaration} = $props();`;
|
||||
}
|
||||
|
||||
if (parsed.instance) {
|
||||
props_declaration = `\n${indent}${props_declaration}`;
|
||||
str.appendRight(state.props_insertion_point, props_declaration);
|
||||
} else {
|
||||
const imports = state.needs_run ? `${indent}${run_import}\n` : '';
|
||||
str.prepend(`<script>\n${imports}${indent}${props_declaration}\n</script>\n\n`);
|
||||
added_legacy_import = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (state.needs_run && !added_legacy_import) {
|
||||
if (parsed.instance) {
|
||||
str.appendRight(
|
||||
/** @type {number} */ (parsed.instance.content.start),
|
||||
`\n${indent}${run_import}\n`
|
||||
);
|
||||
} else {
|
||||
str.prepend(`<script>\n${indent}${run_import}\n</script>\n\n`);
|
||||
}
|
||||
}
|
||||
|
||||
return { code: str.toString() };
|
||||
} catch (e) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error('Error while migrating Svelte code');
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef {{
|
||||
* scope: import('../phases/scope.js').Scope;
|
||||
* str: MagicString;
|
||||
* analysis: import('../phases/types.js').ComponentAnalysis;
|
||||
* indent: string;
|
||||
* props: Array<{ local: string; exported: string; init: string; bindable: boolean; optional: boolean; type: string }>;
|
||||
* props_insertion_point: number;
|
||||
* has_props_rune: boolean;
|
||||
* props_name: string;
|
||||
* rest_props_name: string;
|
||||
* end: number;
|
||||
* run_name: string;
|
||||
* needs_run: boolean;
|
||||
* }} State
|
||||
*/
|
||||
|
||||
/** @type {import('zimmerframe').Visitors<import('../types/template.js').SvelteNode, State>} */
|
||||
const instance_script = {
|
||||
Identifier(node, { state }) {
|
||||
handle_identifier(node, state);
|
||||
},
|
||||
ExportNamedDeclaration(node, { state, next }) {
|
||||
if (node.declaration) {
|
||||
next();
|
||||
return;
|
||||
}
|
||||
|
||||
let count_removed = 0;
|
||||
for (const specifier of node.specifiers) {
|
||||
const binding = state.scope.get(specifier.local.name);
|
||||
if (binding?.kind === 'bindable_prop') {
|
||||
state.str.remove(
|
||||
/** @type {number} */ (specifier.start),
|
||||
/** @type {number} */ (specifier.end)
|
||||
);
|
||||
count_removed++;
|
||||
}
|
||||
}
|
||||
if (count_removed === node.specifiers.length) {
|
||||
state.str.remove(/** @type {number} */ (node.start), /** @type {number} */ (node.end));
|
||||
}
|
||||
},
|
||||
VariableDeclaration(node, { state, path }) {
|
||||
if (state.scope !== state.analysis.instance.scope) {
|
||||
return;
|
||||
}
|
||||
|
||||
let nr_of_props = 0;
|
||||
|
||||
for (const declarator of node.declarations) {
|
||||
if (state.analysis.runes) {
|
||||
if (get_rune(declarator.init, state.scope) === '$props') {
|
||||
state.props_insertion_point = /** @type {number} */ (declarator.id.start) + 1;
|
||||
state.has_props_rune = true;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
let bindings;
|
||||
try {
|
||||
bindings = state.scope.get_bindings(declarator);
|
||||
} catch (e) {
|
||||
// no bindings, so we can skip this
|
||||
continue;
|
||||
}
|
||||
const has_state = bindings.some((binding) => binding.kind === 'state');
|
||||
const has_props = bindings.some((binding) => binding.kind === 'bindable_prop');
|
||||
|
||||
if (!has_state && !has_props) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (has_props) {
|
||||
nr_of_props++;
|
||||
|
||||
if (declarator.id.type !== 'Identifier') {
|
||||
// TODO invest time in this?
|
||||
throw new Error(
|
||||
'Encountered an export declaration pattern that is not supported for automigration.'
|
||||
);
|
||||
// Turn export let into props. It's really really weird because export let { x: foo, z: [bar]} = ..
|
||||
// means that foo and bar are the props (i.e. the leafs are the prop names), not x and z.
|
||||
// const tmp = state.scope.generate('tmp');
|
||||
// const paths = extract_paths(declarator.id);
|
||||
// state.props_pre.push(
|
||||
// b.declaration('const', b.id(tmp), visit(declarator.init!) as Expression)
|
||||
// );
|
||||
// for (const path of paths) {
|
||||
// const name = (path.node as Identifier).name;
|
||||
// const binding = state.scope.get(name)!;
|
||||
// const value = path.expression!(b.id(tmp));
|
||||
// if (binding.kind === 'bindable_prop' || binding.kind === 'rest_prop') {
|
||||
// state.props.push({
|
||||
// local: name,
|
||||
// exported: binding.prop_alias ? binding.prop_alias : name,
|
||||
// init: value
|
||||
// });
|
||||
// state.props_insertion_point = /** @type {number} */(declarator.end);
|
||||
// } else {
|
||||
// declarations.push(b.declarator(path.node, value));
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
const binding = /** @type {import('#compiler').Binding} */ (
|
||||
state.scope.get(declarator.id.name)
|
||||
);
|
||||
|
||||
if (
|
||||
state.analysis.uses_props &&
|
||||
(declarator.init || binding.mutated || binding.reassigned)
|
||||
) {
|
||||
throw new Error(
|
||||
'$$props is used together with named props in a way that cannot be automatically migrated.'
|
||||
);
|
||||
}
|
||||
|
||||
state.props.push({
|
||||
local: declarator.id.name,
|
||||
exported: binding.prop_alias ? binding.prop_alias : declarator.id.name,
|
||||
init: declarator.init
|
||||
? state.str.original.substring(
|
||||
/** @type {number} */ (declarator.init.start),
|
||||
/** @type {number} */ (declarator.init.end)
|
||||
)
|
||||
: '',
|
||||
optional: !!declarator.init,
|
||||
type: extract_type(declarator, state.str, path),
|
||||
bindable: binding.mutated || binding.reassigned
|
||||
});
|
||||
state.props_insertion_point = /** @type {number} */ (declarator.end);
|
||||
state.str.update(
|
||||
/** @type {number} */ (declarator.start),
|
||||
/** @type {number} */ (declarator.end),
|
||||
''
|
||||
);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// state
|
||||
if (declarator.init) {
|
||||
state.str.prependLeft(/** @type {number} */ (declarator.init.start), '$state(');
|
||||
state.str.appendRight(/** @type {number} */ (declarator.init.end), ')');
|
||||
} else {
|
||||
state.str.prependLeft(/** @type {number} */ (declarator.id.end), ' = $state()');
|
||||
}
|
||||
}
|
||||
|
||||
if (nr_of_props === node.declarations.length) {
|
||||
let start = /** @type {number} */ (node.start);
|
||||
let end = /** @type {number} */ (node.end);
|
||||
|
||||
const parent = path.at(-1);
|
||||
if (parent?.type === 'ExportNamedDeclaration') {
|
||||
start = /** @type {number} */ (parent.start);
|
||||
end = /** @type {number} */ (parent.end);
|
||||
}
|
||||
while (state.str.original[start] !== '\n') start--;
|
||||
while (state.str.original[end] !== '\n') end++;
|
||||
state.str.update(start, end, '');
|
||||
}
|
||||
},
|
||||
BreakStatement(node, { state, path }) {
|
||||
if (path[1].type !== 'LabeledStatement') return;
|
||||
if (node.label?.name !== '$') return;
|
||||
state.str.update(
|
||||
/** @type {number} */ (node.start),
|
||||
/** @type {number} */ (node.end),
|
||||
'return;'
|
||||
);
|
||||
},
|
||||
LabeledStatement(node, { path, state, next }) {
|
||||
if (state.analysis.runes) return;
|
||||
if (path.length > 1) return;
|
||||
if (node.label.name !== '$') return;
|
||||
|
||||
next();
|
||||
|
||||
if (
|
||||
node.body.type === 'ExpressionStatement' &&
|
||||
node.body.expression.type === 'AssignmentExpression'
|
||||
) {
|
||||
const ids = extract_identifiers(node.body.expression.left);
|
||||
const bindings = ids.map((id) => state.scope.get(id.name));
|
||||
const reassigned_bindings = bindings.filter((b) => b?.reassigned);
|
||||
if (reassigned_bindings.length === 0 && !bindings.some((b) => b?.kind === 'store_sub')) {
|
||||
// $derived
|
||||
state.str.update(
|
||||
/** @type {number} */ (node.start),
|
||||
/** @type {number} */ (node.body.expression.start),
|
||||
'let '
|
||||
);
|
||||
state.str.prependLeft(
|
||||
/** @type {number} */ (node.body.expression.right.start),
|
||||
'$derived('
|
||||
);
|
||||
state.str.update(
|
||||
/** @type {number} */ (node.body.expression.right.end),
|
||||
/** @type {number} */ (node.end),
|
||||
');'
|
||||
);
|
||||
return;
|
||||
} else {
|
||||
for (const binding of reassigned_bindings) {
|
||||
if (binding && ids.includes(binding.node)) {
|
||||
// implicitly-declared variable which we need to make explicit
|
||||
state.str.prependLeft(
|
||||
/** @type {number} */ (node.start),
|
||||
`let ${binding.node.name}${binding.kind === 'state' ? ' = $state()' : ''};\n${state.indent}`
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
state.needs_run = true;
|
||||
const is_block_stmt = node.body.type === 'BlockStatement';
|
||||
const start_end = /** @type {number} */ (node.body.start);
|
||||
// TODO try to find out if we can use $derived.by instead?
|
||||
if (is_block_stmt) {
|
||||
state.str.update(
|
||||
/** @type {number} */ (node.start),
|
||||
start_end + 1,
|
||||
`${state.run_name}(() => {`
|
||||
);
|
||||
const end = /** @type {number} */ (node.body.end);
|
||||
state.str.update(end - 1, end, '});');
|
||||
} else {
|
||||
state.str.update(
|
||||
/** @type {number} */ (node.start),
|
||||
start_end,
|
||||
`${state.run_name}(() => {\n${state.indent}`
|
||||
);
|
||||
state.str.indent(state.indent, {
|
||||
exclude: [
|
||||
[0, /** @type {number} */ (node.body.start)],
|
||||
[/** @type {number} */ (node.body.end), state.end]
|
||||
]
|
||||
});
|
||||
state.str.appendRight(/** @type {number} */ (node.end), `\n${state.indent}});`);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/** @type {import('zimmerframe').Visitors<import('../types/template.js').SvelteNode, State>} */
|
||||
const template = {
|
||||
Identifier(node, { state }) {
|
||||
handle_identifier(node, state);
|
||||
},
|
||||
RegularElement(node, { state, next }) {
|
||||
handle_events(node, state);
|
||||
next();
|
||||
},
|
||||
SvelteElement(node, { state, next }) {
|
||||
handle_events(node, state);
|
||||
next();
|
||||
},
|
||||
SvelteWindow(node, { state, next }) {
|
||||
handle_events(node, state);
|
||||
next();
|
||||
},
|
||||
SvelteBody(node, { state, next }) {
|
||||
handle_events(node, state);
|
||||
next();
|
||||
},
|
||||
SvelteDocument(node, { state, next }) {
|
||||
handle_events(node, state);
|
||||
next();
|
||||
},
|
||||
SlotElement(node, { state, next }) {
|
||||
let name = 'children';
|
||||
let slot_props = '{ ';
|
||||
|
||||
for (const attr of node.attributes) {
|
||||
if (attr.type === 'SpreadAttribute') {
|
||||
slot_props += `...${state.str.original.substring(/** @type {number} */ (attr.expression.start), attr.expression.end)}, `;
|
||||
} else if (attr.type === 'Attribute') {
|
||||
if (attr.name === 'name') {
|
||||
name = state.scope.generate(/** @type {any} */ (attr.value)[0].data);
|
||||
} else {
|
||||
const value =
|
||||
attr.value !== true
|
||||
? state.str.original.substring(
|
||||
attr.value[0].start,
|
||||
attr.value[attr.value.length - 1].end
|
||||
)
|
||||
: 'true';
|
||||
slot_props += value === attr.name ? `${value}, ` : `${attr.name}: ${value}, `;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
slot_props += '}';
|
||||
if (slot_props === '{ }') {
|
||||
slot_props = '';
|
||||
}
|
||||
|
||||
state.props.push({
|
||||
local: name,
|
||||
exported: name,
|
||||
init: '',
|
||||
bindable: false,
|
||||
optional: true,
|
||||
type: `import('svelte').${slot_props ? 'Snippet<[any]>' : 'Snippet'}`
|
||||
});
|
||||
|
||||
if (node.fragment.nodes.length > 0) {
|
||||
next();
|
||||
state.str.update(
|
||||
node.start,
|
||||
node.fragment.nodes[0].start,
|
||||
`{#if ${name}}{@render ${name}(${slot_props})}{:else}`
|
||||
);
|
||||
state.str.update(node.fragment.nodes[node.fragment.nodes.length - 1].end, node.end, '{/if}');
|
||||
} else {
|
||||
state.str.update(node.start, node.end, `{@render ${name}?.(${slot_props})}`);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {import('estree').VariableDeclarator} declarator
|
||||
* @param {MagicString} str
|
||||
* @param {import('#compiler').SvelteNode[]} path
|
||||
*/
|
||||
function extract_type(declarator, str, path) {
|
||||
if (declarator.id.typeAnnotation) {
|
||||
let start = declarator.id.typeAnnotation.start + 1; // skip the colon
|
||||
while (str.original[start] === ' ') {
|
||||
start++;
|
||||
}
|
||||
return str.original.substring(start, declarator.id.typeAnnotation.end);
|
||||
}
|
||||
|
||||
// try to find a comment with a type annotation, hinting at jsdoc
|
||||
const parent = path.at(-1);
|
||||
if (parent?.type === 'ExportNamedDeclaration' && parent.leadingComments) {
|
||||
const last = parent.leadingComments[parent.leadingComments.length - 1];
|
||||
if (last.type === 'Block') {
|
||||
const match = /@type {([^}]+)}/.exec(last.value);
|
||||
if (match) {
|
||||
str.update(/** @type {any} */ (last).start, /** @type {any} */ (last).end, '');
|
||||
return match[1];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// try to infer it from the init
|
||||
if (declarator.init?.type === 'Literal') {
|
||||
const type = typeof declarator.init.value;
|
||||
if (type === 'string' || type === 'number' || type === 'boolean') {
|
||||
return type;
|
||||
}
|
||||
}
|
||||
|
||||
return 'any';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {import('#compiler').RegularElement | import('#compiler').SvelteElement | import('#compiler').SvelteWindow | import('#compiler').SvelteDocument | import('#compiler').SvelteBody} node
|
||||
* @param {State} state
|
||||
*/
|
||||
function handle_events(node, state) {
|
||||
/** @type {Map<string, import('#compiler').OnDirective[]>} */
|
||||
const handlers = new Map();
|
||||
for (const attribute of node.attributes) {
|
||||
if (attribute.type !== 'OnDirective') continue;
|
||||
|
||||
let name = `on${attribute.name}`;
|
||||
if (attribute.modifiers.includes('capture')) {
|
||||
name += 'capture';
|
||||
}
|
||||
|
||||
const nodes = handlers.get(name) || [];
|
||||
nodes.push(attribute);
|
||||
handlers.set(name, nodes);
|
||||
}
|
||||
|
||||
for (const [name, nodes] of handlers) {
|
||||
// turn on:click into a prop
|
||||
let exported = name;
|
||||
if (!regex_is_valid_identifier.test(name)) {
|
||||
exported = `'${exported}'`;
|
||||
}
|
||||
// Check if prop already set, could happen when on:click on different elements
|
||||
let local = state.props.find((prop) => prop.exported === exported)?.local;
|
||||
|
||||
const last = nodes[nodes.length - 1];
|
||||
const payload_name =
|
||||
last.expression?.type === 'ArrowFunctionExpression' &&
|
||||
last.expression.params[0]?.type === 'Identifier'
|
||||
? last.expression.params[0].name
|
||||
: generate_event_name(last, state);
|
||||
let prepend = '';
|
||||
|
||||
for (let i = 0; i < nodes.length - 1; i += 1) {
|
||||
const node = nodes[i];
|
||||
if (node.expression) {
|
||||
let body = '';
|
||||
if (node.expression.type === 'ArrowFunctionExpression') {
|
||||
body = state.str.original.substring(
|
||||
/** @type {number} */ (node.expression.body.start),
|
||||
/** @type {number} */ (node.expression.body.end)
|
||||
);
|
||||
} else {
|
||||
body = `${state.str.original.substring(
|
||||
/** @type {number} */ (node.expression.start),
|
||||
/** @type {number} */ (node.expression.end)
|
||||
)}();`;
|
||||
}
|
||||
// TODO check how many indents needed
|
||||
for (const modifier of node.modifiers) {
|
||||
if (modifier === 'stopPropagation') {
|
||||
body = `\n${state.indent}${payload_name}.stopPropagation();\n${body}`;
|
||||
} else if (modifier === 'preventDefault') {
|
||||
body = `\n${state.indent}${payload_name}.preventDefault();\n${body}`;
|
||||
} else if (modifier === 'stopImmediatePropagation') {
|
||||
body = `\n${state.indent}${payload_name}.stopImmediatePropagation();\n${body}`;
|
||||
} else {
|
||||
body = `\n${state.indent}// @migration-task: incorporate ${modifier} modifier\n${body}`;
|
||||
}
|
||||
}
|
||||
prepend += `\n${state.indent}${body}\n`;
|
||||
} else {
|
||||
if (!local) {
|
||||
local = state.scope.generate(`on${node.name}`);
|
||||
state.props.push({
|
||||
local,
|
||||
exported,
|
||||
init: '',
|
||||
bindable: false,
|
||||
optional: true,
|
||||
type: '(event: any) => void'
|
||||
});
|
||||
}
|
||||
prepend += `\n${state.indent}${local}?.(${payload_name});\n`;
|
||||
}
|
||||
|
||||
state.str.remove(node.start, node.end);
|
||||
}
|
||||
|
||||
if (last.expression) {
|
||||
// remove : from on:click
|
||||
state.str.remove(last.start + 2, last.start + 3);
|
||||
// remove modifiers
|
||||
if (last.modifiers.length > 0) {
|
||||
state.str.remove(
|
||||
last.start + last.name.length + 3,
|
||||
state.str.original.indexOf('=', last.start)
|
||||
);
|
||||
}
|
||||
if (last.modifiers.includes('capture')) {
|
||||
state.str.appendRight(last.start + last.name.length + 3, 'capture');
|
||||
}
|
||||
|
||||
for (const modifier of last.modifiers) {
|
||||
if (modifier === 'stopPropagation') {
|
||||
prepend += `\n${state.indent}${payload_name}.stopPropagation();\n`;
|
||||
} else if (modifier === 'preventDefault') {
|
||||
prepend += `\n${state.indent}${payload_name}.preventDefault();\n`;
|
||||
} else if (modifier === 'stopImmediatePropagation') {
|
||||
prepend += `\n${state.indent}${payload_name}.stopImmediatePropagation();\n`;
|
||||
} else if (modifier !== 'capture') {
|
||||
prepend += `\n${state.indent}// @migration-task: incorporate ${modifier} modifier\n`;
|
||||
}
|
||||
}
|
||||
|
||||
if (prepend) {
|
||||
let pos = last.expression.start;
|
||||
if (last.expression.type === 'ArrowFunctionExpression') {
|
||||
pos = last.expression.body.start;
|
||||
if (
|
||||
last.expression.params.length > 0 &&
|
||||
last.expression.params[0].type !== 'Identifier'
|
||||
) {
|
||||
const start = /** @type {number} */ (last.expression.params[0].start);
|
||||
const end = /** @type {number} */ (last.expression.params[0].end);
|
||||
// replace event payload with generated one that others use,
|
||||
// then destructure generated payload param into what the user wrote
|
||||
state.str.overwrite(start, end, payload_name);
|
||||
prepend = `let ${state.str.original.substring(
|
||||
start,
|
||||
end
|
||||
)} = ${payload_name};\n${prepend}`;
|
||||
} else if (last.expression.params.length === 0) {
|
||||
// add generated payload param to arrow function
|
||||
const pos = state.str.original.lastIndexOf(')', last.expression.body.start);
|
||||
state.str.prependLeft(pos, payload_name);
|
||||
}
|
||||
|
||||
const needs_curlies = last.expression.body.type !== 'BlockStatement';
|
||||
state.str.prependRight(
|
||||
/** @type {number} */ (pos) + (needs_curlies ? 0 : 1),
|
||||
`${needs_curlies ? '{' : ''}${prepend}${state.indent}`
|
||||
);
|
||||
state.str.appendRight(
|
||||
/** @type {number} */ (last.expression.body.end) - (needs_curlies ? 0 : 1),
|
||||
`\n${needs_curlies ? '}' : ''}`
|
||||
);
|
||||
} else {
|
||||
state.str.update(
|
||||
/** @type {number} */ (last.expression.start),
|
||||
/** @type {number} */ (last.expression.end),
|
||||
`(${payload_name}) => {${prepend}\n${state.indent}${state.str.original.substring(
|
||||
/** @type {number} */ (last.expression.start),
|
||||
/** @type {number} */ (last.expression.end)
|
||||
)}?.(${payload_name});\n}`
|
||||
);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// turn on:click into a prop
|
||||
// Check if prop already set, could happen when on:click on different elements
|
||||
if (!local) {
|
||||
local = state.scope.generate(`on${last.name}`);
|
||||
state.props.push({
|
||||
local,
|
||||
exported,
|
||||
init: '',
|
||||
bindable: false,
|
||||
optional: true,
|
||||
type: '(event: any) => void'
|
||||
});
|
||||
}
|
||||
|
||||
let replacement = '';
|
||||
if (!prepend) {
|
||||
if (exported === local) {
|
||||
replacement = `{${name}}`;
|
||||
} else {
|
||||
replacement = `${name}={${local}}`;
|
||||
}
|
||||
} else {
|
||||
replacement = `${name}={(${payload_name}) => {${prepend}\n${state.indent}${local}?.(${payload_name});\n}}`;
|
||||
}
|
||||
|
||||
state.str.update(last.start, last.end, replacement);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {import('#compiler').OnDirective} last
|
||||
* @param {State} state
|
||||
*/
|
||||
function generate_event_name(last, state) {
|
||||
const scope =
|
||||
(last.expression && state.analysis.template.scopes.get(last.expression)) || state.scope;
|
||||
|
||||
let name = 'event';
|
||||
if (!scope.get(name)) return name;
|
||||
|
||||
let i = 1;
|
||||
while (scope.get(`${name}${i}`)) i += 1;
|
||||
return `${name}${i}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {import('estree').Identifier} node
|
||||
* @param {State} state
|
||||
*/
|
||||
function handle_identifier(node, state) {
|
||||
if (state.analysis.uses_props) {
|
||||
if (node.name === '$$props' || node.name === '$$restProps') {
|
||||
// not 100% correct for $$restProps but it'll do
|
||||
state.str.update(
|
||||
/** @type {number} */ (node.start),
|
||||
/** @type {number} */ (node.end),
|
||||
state.props_name
|
||||
);
|
||||
} else {
|
||||
const binding = state.scope.get(node.name);
|
||||
if (binding?.kind === 'bindable_prop') {
|
||||
state.str.prependLeft(/** @type {number} */ (node.start), `${state.props_name}.`);
|
||||
}
|
||||
}
|
||||
} else if (node.name === '$$restProps' && state.analysis.uses_rest_props) {
|
||||
state.str.update(
|
||||
/** @type {number} */ (node.start),
|
||||
/** @type {number} */ (node.end),
|
||||
state.rest_props_name
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/** @param {string} content */
|
||||
function guess_indent(content) {
|
||||
const lines = content.split('\n');
|
||||
|
||||
const tabbed = lines.filter((line) => /^\t+/.test(line));
|
||||
const spaced = lines.filter((line) => /^ {2,}/.test(line));
|
||||
|
||||
if (tabbed.length === 0 && spaced.length === 0) {
|
||||
return '\t';
|
||||
}
|
||||
|
||||
// More lines tabbed than spaced? Assume tabs, and
|
||||
// default to tabs in the case of a tie (or nothing
|
||||
// to go on)
|
||||
if (tabbed.length >= spaced.length) {
|
||||
return '\t';
|
||||
}
|
||||
|
||||
// Otherwise, we need to guess the multiple
|
||||
const min = spaced.reduce((previous, current) => {
|
||||
const count = /^ +/.exec(current)?.[0].length ?? 0;
|
||||
return Math.min(count, previous);
|
||||
}, Infinity);
|
||||
|
||||
return ' '.repeat(min);
|
||||
}
|
||||
@ -0,0 +1,9 @@
|
||||
/**
|
||||
* @param {string[]} strings
|
||||
* @param {string} conjunction
|
||||
*/
|
||||
export function list(strings, conjunction = 'or') {
|
||||
if (strings.length === 1) return strings[0];
|
||||
if (strings.length === 2) return `${strings[0]} ${conjunction} ${strings[1]}`;
|
||||
return `${strings.slice(0, -1).join(', ')} ${conjunction} ${strings[strings.length - 1]}`;
|
||||
}
|
||||
@ -1,336 +1,642 @@
|
||||
import {
|
||||
extract_ignores_above_position,
|
||||
extract_svelte_ignore_from_comments
|
||||
} from './utils/extract_svelte_ignore.js';
|
||||
|
||||
/** @typedef {Record<string, (...args: any[]) => string>} Warnings */
|
||||
|
||||
/** @satisfies {Warnings} */
|
||||
const css = {
|
||||
/** @param {string} name */
|
||||
'css-unused-selector': (name) => `Unused CSS selector "${name}"`
|
||||
};
|
||||
|
||||
/** @satisfies {Warnings} */
|
||||
const attributes = {
|
||||
'avoid-is': () => 'The "is" attribute is not supported cross-browser and should be avoided',
|
||||
/** @param {string} name */
|
||||
'global-event-reference': (name) =>
|
||||
`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",
|
||||
/**
|
||||
* @param {string} wrong
|
||||
* @param {string} right
|
||||
*/
|
||||
'invalid-html-attribute': (wrong, right) =>
|
||||
`'${wrong}' is not a valid HTML attribute. Did you mean '${right}'?`
|
||||
};
|
||||
|
||||
/** @satisfies {Warnings} */
|
||||
const runes = {
|
||||
/** @param {string} name */
|
||||
'store-with-rune-name': (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.`,
|
||||
/** @param {string} name */
|
||||
'non-state-reference': (name) =>
|
||||
`${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?`
|
||||
};
|
||||
|
||||
/** @satisfies {Warnings} */
|
||||
const a11y = {
|
||||
/** @param {string} name */
|
||||
'a11y-aria-attributes': (name) => `A11y: <${name}> should not have aria-* attributes`,
|
||||
/**
|
||||
* @param {string} attribute
|
||||
* @param {string | null} [suggestion]
|
||||
*/
|
||||
'a11y-unknown-aria-attribute': (attribute, suggestion) =>
|
||||
`A11y: Unknown aria attribute 'aria-${attribute}'` +
|
||||
(suggestion ? ` (did you mean '${suggestion}'?)` : ''),
|
||||
/** @param {string} name */
|
||||
'a11y-hidden': (name) => `A11y: <${name}> element should not be hidden`,
|
||||
/**
|
||||
* @param {import('aria-query').ARIAPropertyDefinition} schema
|
||||
* @param {string} attribute
|
||||
*/
|
||||
'a11y-incorrect-aria-attribute-type': (schema, attribute) => {
|
||||
let message;
|
||||
switch (schema.type) {
|
||||
case 'boolean':
|
||||
message = `The value of '${attribute}' must be exactly one of true or false`;
|
||||
break;
|
||||
case 'id':
|
||||
message = `The value of '${attribute}' must be a string that represents a DOM element ID`;
|
||||
break;
|
||||
case 'idlist':
|
||||
message = `The value of '${attribute}' must be a space-separated list of strings that represent DOM element IDs`;
|
||||
break;
|
||||
case 'tristate':
|
||||
message = `The value of '${attribute}' must be exactly one of true, false, or mixed`;
|
||||
break;
|
||||
case 'token':
|
||||
message = `The value of '${attribute}' must be exactly one of ${(schema.values || []).join(
|
||||
', '
|
||||
)}`;
|
||||
break;
|
||||
case 'tokenlist':
|
||||
message = `The value of '${attribute}' must be a space-separated list of one or more of ${(
|
||||
schema.values || []
|
||||
).join(', ')}`;
|
||||
break;
|
||||
default:
|
||||
message = `The value of '${attribute}' must be of type ${schema.type}`;
|
||||
}
|
||||
return `A11y: ${message}`;
|
||||
},
|
||||
'a11y-aria-activedescendant-has-tabindex': () =>
|
||||
'A11y: Elements with attribute aria-activedescendant should have tabindex value',
|
||||
/** @param {string} name */
|
||||
'a11y-misplaced-role': (name) => `A11y: <${name}> should not have role attribute`,
|
||||
/** @param {string | boolean} role */
|
||||
'a11y-no-abstract-role': (role) => `A11y: Abstract role '${role}' is forbidden`,
|
||||
/**
|
||||
* @param {string | boolean} role
|
||||
* @param {string | null} [suggestion]
|
||||
*/
|
||||
'a11y-unknown-role': (role, suggestion) =>
|
||||
`A11y: Unknown role '${role}'` + (suggestion ? ` (did you mean '${suggestion}'?)` : ''),
|
||||
/** @param {string | boolean} role */
|
||||
'a11y-no-redundant-roles': (role) => `A11y: Redundant role '${role}'`,
|
||||
/**
|
||||
* @param {string} role
|
||||
* @param {string[]} props
|
||||
*/
|
||||
'a11y-role-has-required-aria-props': (role, props) =>
|
||||
`A11y: Elements with the ARIA role "${role}" must have the following attributes defined: ${props
|
||||
.map((name) => `"${name}"`)
|
||||
.join(', ')}`,
|
||||
/** @param {string} role */
|
||||
'a11y-interactive-supports-focus': (role) =>
|
||||
`A11y: Elements with the '${role}' interactive role must have a tabindex value.`,
|
||||
/**
|
||||
* @param {string | boolean} role
|
||||
* @param {string} element
|
||||
*/
|
||||
'a11y-no-interactive-element-to-noninteractive-role': (role, element) =>
|
||||
`A11y: <${element}> cannot have role '${role}'`,
|
||||
/**
|
||||
* @param {string | boolean} role
|
||||
* @param {string} element
|
||||
*/
|
||||
'a11y-no-noninteractive-element-to-interactive-role': (role, element) =>
|
||||
`A11y: Non-interactive element <${element}> cannot have interactive role '${role}'`,
|
||||
'a11y-accesskey': () => 'A11y: Avoid using accesskey',
|
||||
'a11y-autofocus': () => 'A11y: Avoid using autofocus',
|
||||
'a11y-misplaced-scope': () => 'A11y: The scope attribute should only be used with <th> elements',
|
||||
'a11y-positive-tabindex': () => 'A11y: avoid tabindex values above zero',
|
||||
'a11y-click-events-have-key-events': () =>
|
||||
'A11y: 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.',
|
||||
'a11y-no-noninteractive-tabindex': () =>
|
||||
'A11y: noninteractive element cannot have nonnegative tabIndex value',
|
||||
/**
|
||||
* @param {string} attribute
|
||||
* @param {string} role
|
||||
* @param {boolean} is_implicit
|
||||
* @param {string} name
|
||||
*/
|
||||
'a11y-role-supports-aria-props': (attribute, role, is_implicit, name) => {
|
||||
let message = `The attribute '${attribute}' is not supported by the role '${role}'.`;
|
||||
if (is_implicit) {
|
||||
message += ` This role is implicit on the element <${name}>.`;
|
||||
}
|
||||
return `A11y: ${message}`;
|
||||
},
|
||||
/** @param {string} element */
|
||||
'a11y-no-noninteractive-element-interactions': (element) =>
|
||||
`A11y: Non-interactive element <${element}> should not be assigned mouse or keyboard event listeners.`,
|
||||
/**
|
||||
* @param {string} element
|
||||
* @param {string[]} handlers
|
||||
*/
|
||||
'a11y-no-static-element-interactions': (element, handlers) =>
|
||||
`A11y: <${element}> with ${handlers.join(', ')} ${
|
||||
handlers.length === 1 ? 'handler' : 'handlers'
|
||||
} must have an ARIA role`,
|
||||
/**
|
||||
* @param {string} href_attribute
|
||||
* @param {string} href_value
|
||||
*/
|
||||
'a11y-invalid-attribute': (href_attribute, href_value) =>
|
||||
`A11y: '${href_value}' is not a valid ${href_attribute} attribute`,
|
||||
/**
|
||||
* @param {string} name
|
||||
* @param {string} article
|
||||
* @param {string} sequence
|
||||
*/
|
||||
'a11y-missing-attribute': (name, article, sequence) =>
|
||||
`A11y: <${name}> element should have ${article} ${sequence} attribute`,
|
||||
/**
|
||||
* @param {null | true | string} type
|
||||
* @param {null | true | string} value
|
||||
*/
|
||||
'a11y-autocomplete-valid': (type, value) =>
|
||||
`A11y: The value '${value}' is not supported by the attribute 'autocomplete' on element <input type="${
|
||||
type || '...'
|
||||
}">`,
|
||||
'a11y-img-redundant-alt': () =>
|
||||
'A11y: Screenreaders already announce <img> elements as an image.',
|
||||
'a11y-label-has-associated-control': () =>
|
||||
'A11y: A form label must be associated with a control.',
|
||||
'a11y-media-has-caption': () => 'A11y: <video> elements must have a <track kind="captions">',
|
||||
/** @param {string} name */
|
||||
'a11y-distracting-elements': (name) => `A11y: Avoid <${name}> elements`,
|
||||
/** @param {boolean} immediate */
|
||||
'a11y-structure': (immediate) =>
|
||||
immediate
|
||||
? 'A11y: <figcaption> must be an immediate child of <figure>'
|
||||
: 'A11y: <figcaption> must be first or last child of <figure>',
|
||||
/**
|
||||
* @param {string} event
|
||||
* @param {string} accompanied_by
|
||||
*/
|
||||
'a11y-mouse-events-have-key-events': (event, accompanied_by) =>
|
||||
`A11y: '${event}' event must be accompanied by '${accompanied_by}' event`,
|
||||
/** @param {string} name */
|
||||
'a11y-missing-content': (name) => `A11y: <${name}> element should have child content`
|
||||
};
|
||||
|
||||
/** @satisfies {Warnings} */
|
||||
const state = {
|
||||
'static-state-reference': () =>
|
||||
`State referenced in its own scope will never update. Did you mean to reference it inside a closure?`,
|
||||
/** @param {string} name */
|
||||
'invalid-rest-eachblock-binding': (name) =>
|
||||
`The rest operator (...) will create a new object and binding '${name}' with the original object will not work`
|
||||
};
|
||||
|
||||
/** @satisfies {Warnings} */
|
||||
const performance = {
|
||||
'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`
|
||||
};
|
||||
|
||||
/** @satisfies {Warnings} */
|
||||
const components = {
|
||||
/** @param {string} name */
|
||||
'component-name-lowercase': (name) =>
|
||||
`<${name}> will be treated as an HTML element unless it begins with a capital letter`
|
||||
};
|
||||
|
||||
const legacy = {
|
||||
'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',
|
||||
/** @param {string} name */
|
||||
'unused-export-let': (name) =>
|
||||
`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.`,
|
||||
/** @param {string} name */
|
||||
'deprecated-event-handler': (name) =>
|
||||
`Using on:${name} to listen to the ${name} event is is deprecated. Use the event attribute on${name} instead.`
|
||||
};
|
||||
|
||||
const block = {
|
||||
'empty-block': () => 'Empty block'
|
||||
};
|
||||
|
||||
const options = {
|
||||
'missing-custom-element-compile-option': () =>
|
||||
"The 'customElement' option is used when generating a custom element. Did you forget the 'customElement: true' compile option?"
|
||||
};
|
||||
|
||||
const misc = {
|
||||
/** @param {string} name */
|
||||
'invalid-self-closing-tag': (name) =>
|
||||
`Self-closing HTML tags for non-void elements are ambiguous — use <${name} ...></${name}> rather than <${name} ... />`
|
||||
};
|
||||
|
||||
/** @satisfies {Warnings} */
|
||||
const warnings = {
|
||||
...css,
|
||||
...attributes,
|
||||
...runes,
|
||||
...a11y,
|
||||
...performance,
|
||||
...state,
|
||||
...components,
|
||||
...legacy,
|
||||
...block,
|
||||
...options,
|
||||
...misc
|
||||
};
|
||||
|
||||
/** @typedef {typeof warnings} AllWarnings */
|
||||
|
||||
/**
|
||||
* @template {keyof AllWarnings} T
|
||||
* @param {import('./phases/types').RawWarning[]} array the array to push the warning to, if not ignored
|
||||
* @param {{ start?: number, end?: number, type?: string, parent?: import('#compiler').SvelteNode | null, leadingComments?: import('estree').Comment[] } | null} node the node related to the warning
|
||||
* @param {import('#compiler').SvelteNode[]} path the path to the node, so that we can traverse upwards to find svelte-ignore comments
|
||||
* @param {T} code the warning code
|
||||
* @param {Parameters<AllWarnings[T]>} args the arguments to pass to the warning function
|
||||
* @returns {void}
|
||||
*/
|
||||
export function warn(array, node, path, code, ...args) {
|
||||
const fn = warnings[code];
|
||||
|
||||
// Traverse the AST upwards to find any svelte-ignore comments.
|
||||
// This assumes that people don't have their code littered with warnings,
|
||||
// at which point this might become inefficient.
|
||||
/** @type {string[]} */
|
||||
const ignores = [];
|
||||
|
||||
if (node) {
|
||||
// comments inside JavaScript (estree)
|
||||
if ('leadingComments' in node) {
|
||||
ignores.push(...extract_svelte_ignore_from_comments(node));
|
||||
}
|
||||
}
|
||||
|
||||
for (let i = path.length - 1; i >= 0; i--) {
|
||||
const current = path[i];
|
||||
|
||||
// comments inside JavaScript (estree)
|
||||
if ('leadingComments' in current) {
|
||||
ignores.push(...extract_svelte_ignore_from_comments(current));
|
||||
}
|
||||
|
||||
// Svelte nodes
|
||||
if (current.type === 'Fragment') {
|
||||
ignores.push(
|
||||
...extract_ignores_above_position(
|
||||
/** @type {import('#compiler').TemplateNode} */ (path[i + 1] ?? node),
|
||||
current.nodes
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// Style nodes
|
||||
if (current.type === 'StyleSheet' && current.content.comment) {
|
||||
ignores.push(...current.content.comment.ignores);
|
||||
}
|
||||
}
|
||||
|
||||
if (ignores.includes(code)) return;
|
||||
|
||||
const start = node?.start;
|
||||
const end = node?.end;
|
||||
|
||||
array.push({
|
||||
/* This file is generated by scripts/process-messages/index.js. Do not edit! */
|
||||
|
||||
import { getLocator } from 'locate-character';
|
||||
|
||||
/** @typedef {{ start?: number, end?: number }} NodeLike */
|
||||
/** @type {import('#compiler').Warning[]} */
|
||||
let warnings = [];
|
||||
/** @type {string | undefined} */
|
||||
let filename;
|
||||
let locator = getLocator('', { offsetLine: 1 });
|
||||
|
||||
/**
|
||||
* @param {{
|
||||
* source: string;
|
||||
* filename: string | undefined;
|
||||
* }} options
|
||||
* @returns {import('#compiler').Warning[]}
|
||||
*/
|
||||
export function reset_warnings(options) {
|
||||
filename = options.filename;
|
||||
locator = getLocator(options.source, { offsetLine: 1 });
|
||||
return warnings = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {null | NodeLike} node
|
||||
* @param {string} code
|
||||
* @param {string} message
|
||||
*/
|
||||
function w(node, code, message) {
|
||||
// @ts-expect-error
|
||||
if (node?.ignores?.has(code)) return;
|
||||
|
||||
warnings.push({
|
||||
code,
|
||||
// @ts-expect-error
|
||||
message: fn(...args),
|
||||
position: start !== undefined && end !== undefined ? [start, end] : undefined
|
||||
message,
|
||||
filename,
|
||||
start: node?.start !== undefined ? locator(node.start) : undefined,
|
||||
end: node?.end !== undefined ? locator(node.end) : undefined
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Avoid using accesskey
|
||||
* @param {null | NodeLike} node
|
||||
*/
|
||||
export function a11y_accesskey(node) {
|
||||
w(node, "a11y_accesskey", "Avoid using accesskey");
|
||||
}
|
||||
|
||||
/**
|
||||
* An element with an aria-activedescendant attribute should have a tabindex value
|
||||
* @param {null | NodeLike} node
|
||||
*/
|
||||
export function a11y_aria_activedescendant_has_tabindex(node) {
|
||||
w(node, "a11y_aria_activedescendant_has_tabindex", "An element with an aria-activedescendant attribute should have a tabindex value");
|
||||
}
|
||||
|
||||
/**
|
||||
* `<%name%>` should not have aria-* attributes
|
||||
* @param {null | NodeLike} node
|
||||
* @param {string} name
|
||||
*/
|
||||
export function a11y_aria_attributes(node, name) {
|
||||
w(node, "a11y_aria_attributes", `\`<${name}>\` should not have aria-* attributes`);
|
||||
}
|
||||
|
||||
/**
|
||||
* '%value%' is an invalid value for 'autocomplete' on `<input type="%type%">`
|
||||
* @param {null | NodeLike} node
|
||||
* @param {string} value
|
||||
* @param {string} type
|
||||
*/
|
||||
export function a11y_autocomplete_valid(node, value, type) {
|
||||
w(node, "a11y_autocomplete_valid", `'${value}' is an invalid value for 'autocomplete' on \`<input type="${type}">\``);
|
||||
}
|
||||
|
||||
/**
|
||||
* Avoid using autofocus
|
||||
* @param {null | NodeLike} node
|
||||
*/
|
||||
export function a11y_autofocus(node) {
|
||||
w(node, "a11y_autofocus", "Avoid using autofocus");
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
export function a11y_click_events_have_key_events(node) {
|
||||
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.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Avoid `<%name%>` elements
|
||||
* @param {null | NodeLike} node
|
||||
* @param {string} name
|
||||
*/
|
||||
export function a11y_distracting_elements(node, name) {
|
||||
w(node, "a11y_distracting_elements", `Avoid \`<${name}>\` elements`);
|
||||
}
|
||||
|
||||
/**
|
||||
* `<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>`");
|
||||
}
|
||||
|
||||
/**
|
||||
* `<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>`");
|
||||
}
|
||||
|
||||
/**
|
||||
* `<%name%>` element should not be hidden
|
||||
* @param {null | NodeLike} node
|
||||
* @param {string} name
|
||||
*/
|
||||
export function a11y_hidden(node, name) {
|
||||
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.");
|
||||
}
|
||||
|
||||
/**
|
||||
* The value of '%attribute%' must be a %type%
|
||||
* @param {null | NodeLike} node
|
||||
* @param {string} attribute
|
||||
* @param {string} type
|
||||
*/
|
||||
export function a11y_incorrect_aria_attribute_type(node, attribute, type) {
|
||||
w(node, "a11y_incorrect_aria_attribute_type", `The value of '${attribute}' must be a ${type}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* The value of '%attribute%' must be either 'true' or 'false'. It cannot be empty
|
||||
* @param {null | NodeLike} node
|
||||
* @param {string} attribute
|
||||
*/
|
||||
export function a11y_incorrect_aria_attribute_type_boolean(node, attribute) {
|
||||
w(node, "a11y_incorrect_aria_attribute_type_boolean", `The value of '${attribute}' must be either 'true' or 'false'. It cannot be empty`);
|
||||
}
|
||||
|
||||
/**
|
||||
* The value of '%attribute%' must be a string that represents a DOM element ID
|
||||
* @param {null | NodeLike} node
|
||||
* @param {string} attribute
|
||||
*/
|
||||
export function a11y_incorrect_aria_attribute_type_id(node, attribute) {
|
||||
w(node, "a11y_incorrect_aria_attribute_type_id", `The value of '${attribute}' must be a string that represents a DOM element ID`);
|
||||
}
|
||||
|
||||
/**
|
||||
* The value of '%attribute%' must be a space-separated list of strings that represent DOM element IDs
|
||||
* @param {null | NodeLike} node
|
||||
* @param {string} attribute
|
||||
*/
|
||||
export function a11y_incorrect_aria_attribute_type_idlist(node, attribute) {
|
||||
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`);
|
||||
}
|
||||
|
||||
/**
|
||||
* The value of '%attribute%' must be an integer
|
||||
* @param {null | NodeLike} node
|
||||
* @param {string} attribute
|
||||
*/
|
||||
export function a11y_incorrect_aria_attribute_type_integer(node, attribute) {
|
||||
w(node, "a11y_incorrect_aria_attribute_type_integer", `The value of '${attribute}' must be an integer`);
|
||||
}
|
||||
|
||||
/**
|
||||
* The value of '%attribute%' must be exactly one of %values%
|
||||
* @param {null | NodeLike} node
|
||||
* @param {string} attribute
|
||||
* @param {string} values
|
||||
*/
|
||||
export function a11y_incorrect_aria_attribute_type_token(node, attribute, values) {
|
||||
w(node, "a11y_incorrect_aria_attribute_type_token", `The value of '${attribute}' must be exactly one of ${values}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* The value of '%attribute%' must be a space-separated list of one or more of %values%
|
||||
* @param {null | NodeLike} node
|
||||
* @param {string} attribute
|
||||
* @param {string} values
|
||||
*/
|
||||
export function a11y_incorrect_aria_attribute_type_tokenlist(node, attribute, values) {
|
||||
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`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Elements with the '%role%' interactive role must have a tabindex value.
|
||||
* @param {null | NodeLike} node
|
||||
* @param {string} role
|
||||
*/
|
||||
export function a11y_interactive_supports_focus(node, role) {
|
||||
w(node, "a11y_interactive_supports_focus", `Elements with the '${role}' interactive role must have a tabindex value.`);
|
||||
}
|
||||
|
||||
/**
|
||||
* '%href_value%' is not a valid %href_attribute% attribute
|
||||
* @param {null | NodeLike} node
|
||||
* @param {string} href_value
|
||||
* @param {string} href_attribute
|
||||
*/
|
||||
export function a11y_invalid_attribute(node, href_value, href_attribute) {
|
||||
w(node, "a11y_invalid_attribute", `'${href_value}' is not a valid ${href_attribute} attribute`);
|
||||
}
|
||||
|
||||
/**
|
||||
* A form label must be associated with a control.
|
||||
* @param {null | NodeLike} node
|
||||
*/
|
||||
export function a11y_label_has_associated_control(node) {
|
||||
w(node, "a11y_label_has_associated_control", "A form label must be associated with a control.");
|
||||
}
|
||||
|
||||
/**
|
||||
* `<video>` elements must have a `<track kind="captions">`
|
||||
* @param {null | NodeLike} node
|
||||
*/
|
||||
export function a11y_media_has_caption(node) {
|
||||
w(node, "a11y_media_has_caption", "`<video>` elements must have a `<track kind=\"captions\">`");
|
||||
}
|
||||
|
||||
/**
|
||||
* `<%name%>` should not have role attribute
|
||||
* @param {null | NodeLike} node
|
||||
* @param {string} name
|
||||
*/
|
||||
export function a11y_misplaced_role(node, name) {
|
||||
w(node, "a11y_misplaced_role", `\`<${name}>\` should not have role attribute`);
|
||||
}
|
||||
|
||||
/**
|
||||
* The scope attribute should only be used with `<th>` elements
|
||||
* @param {null | NodeLike} node
|
||||
*/
|
||||
export function a11y_misplaced_scope(node) {
|
||||
w(node, "a11y_misplaced_scope", "The scope attribute should only be used with `<th>` elements");
|
||||
}
|
||||
|
||||
/**
|
||||
* `<%name%>` element should have %article% %sequence% attribute
|
||||
* @param {null | NodeLike} node
|
||||
* @param {string} name
|
||||
* @param {string} article
|
||||
* @param {string} sequence
|
||||
*/
|
||||
export function a11y_missing_attribute(node, name, article, sequence) {
|
||||
w(node, "a11y_missing_attribute", `\`<${name}>\` element should have ${article} ${sequence} attribute`);
|
||||
}
|
||||
|
||||
/**
|
||||
* `<%name%>` element should have child content
|
||||
* @param {null | NodeLike} node
|
||||
* @param {string} name
|
||||
*/
|
||||
export function a11y_missing_content(node, name) {
|
||||
w(node, "a11y_missing_content", `\`<${name}>\` element should have child content`);
|
||||
}
|
||||
|
||||
/**
|
||||
* '%event%' event must be accompanied by '%accompanied_by%' event
|
||||
* @param {null | NodeLike} node
|
||||
* @param {string} event
|
||||
* @param {string} accompanied_by
|
||||
*/
|
||||
export function a11y_mouse_events_have_key_events(node, event, accompanied_by) {
|
||||
w(node, "a11y_mouse_events_have_key_events", `'${event}' event must be accompanied by '${accompanied_by}' event`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Abstract role '%role%' is forbidden
|
||||
* @param {null | NodeLike} node
|
||||
* @param {string} role
|
||||
*/
|
||||
export function a11y_no_abstract_role(node, role) {
|
||||
w(node, "a11y_no_abstract_role", `Abstract role '${role}' is forbidden`);
|
||||
}
|
||||
|
||||
/**
|
||||
* `<%element%>` cannot have role '%role%'
|
||||
* @param {null | NodeLike} node
|
||||
* @param {string} element
|
||||
* @param {string} role
|
||||
*/
|
||||
export function a11y_no_interactive_element_to_noninteractive_role(node, element, role) {
|
||||
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.
|
||||
* @param {null | NodeLike} node
|
||||
* @param {string} 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.`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Non-interactive element `<%element%>` cannot have interactive role '%role%'
|
||||
* @param {null | NodeLike} node
|
||||
* @param {string} element
|
||||
* @param {string} role
|
||||
*/
|
||||
export function a11y_no_noninteractive_element_to_interactive_role(node, element, role) {
|
||||
w(node, "a11y_no_noninteractive_element_to_interactive_role", `Non-interactive element \`<${element}>\` cannot have interactive role '${role}'`);
|
||||
}
|
||||
|
||||
/**
|
||||
* noninteractive element cannot have nonnegative tabIndex value
|
||||
* @param {null | NodeLike} node
|
||||
*/
|
||||
export function a11y_no_noninteractive_tabindex(node) {
|
||||
w(node, "a11y_no_noninteractive_tabindex", "noninteractive element cannot have nonnegative tabIndex value");
|
||||
}
|
||||
|
||||
/**
|
||||
* Redundant role '%role%'
|
||||
* @param {null | NodeLike} node
|
||||
* @param {string} role
|
||||
*/
|
||||
export function a11y_no_redundant_roles(node, role) {
|
||||
w(node, "a11y_no_redundant_roles", `Redundant role '${role}'`);
|
||||
}
|
||||
|
||||
/**
|
||||
* `<%element%>` with a %handler% handler must have an ARIA role
|
||||
* @param {null | NodeLike} node
|
||||
* @param {string} element
|
||||
* @param {string} handler
|
||||
*/
|
||||
export function a11y_no_static_element_interactions(node, element, handler) {
|
||||
w(node, "a11y_no_static_element_interactions", `\`<${element}>\` with a ${handler} handler must have an ARIA role`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Avoid tabindex values above zero
|
||||
* @param {null | NodeLike} node
|
||||
*/
|
||||
export function a11y_positive_tabindex(node) {
|
||||
w(node, "a11y_positive_tabindex", "Avoid tabindex values above zero");
|
||||
}
|
||||
|
||||
/**
|
||||
* Elements with the ARIA role "%role%" must have the following attributes defined: %props%
|
||||
* @param {null | NodeLike} node
|
||||
* @param {string} role
|
||||
* @param {string} props
|
||||
*/
|
||||
export function a11y_role_has_required_aria_props(node, role, props) {
|
||||
w(node, "a11y_role_has_required_aria_props", `Elements with the ARIA role "${role}" must have the following attributes defined: ${props}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* The attribute '%attribute%' is not supported by the role '%role%'
|
||||
* @param {null | NodeLike} node
|
||||
* @param {string} attribute
|
||||
* @param {string} role
|
||||
*/
|
||||
export function a11y_role_supports_aria_props(node, attribute, role) {
|
||||
w(node, "a11y_role_supports_aria_props", `The attribute '${attribute}' is not supported by the role '${role}'`);
|
||||
}
|
||||
|
||||
/**
|
||||
* The attribute '%attribute%' is not supported by the role '%role%'. This role is implicit on the element `<%name%>`
|
||||
* @param {null | NodeLike} node
|
||||
* @param {string} attribute
|
||||
* @param {string} role
|
||||
* @param {string} name
|
||||
*/
|
||||
export function a11y_role_supports_aria_props_implicit(node, attribute, role, name) {
|
||||
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}>\``);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unknown aria attribute 'aria-%attribute%'. Did you mean '%suggestion%'?
|
||||
* @param {null | NodeLike} node
|
||||
* @param {string} attribute
|
||||
* @param {string | undefined | null} [suggestion]
|
||||
*/
|
||||
export function a11y_unknown_aria_attribute(node, attribute, suggestion) {
|
||||
w(node, "a11y_unknown_aria_attribute", suggestion ? `Unknown aria attribute 'aria-${attribute}'. Did you mean '${suggestion}'?` : `Unknown aria attribute 'aria-${attribute}'`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unknown role '%role%'. Did you mean '%suggestion%'?
|
||||
* @param {null | NodeLike} node
|
||||
* @param {string} role
|
||||
* @param {string | undefined | null} [suggestion]
|
||||
*/
|
||||
export function a11y_unknown_role(node, role, suggestion) {
|
||||
w(node, "a11y_unknown_role", suggestion ? `Unknown role '${role}'. Did you mean '${suggestion}'?` : `Unknown role '${role}'`);
|
||||
}
|
||||
|
||||
/**
|
||||
* The `accessors` option has been deprecated. It will have no effect in runes mode
|
||||
* @param {null | NodeLike} node
|
||||
*/
|
||||
export function options_deprecated_accessors(node) {
|
||||
w(node, "options_deprecated_accessors", "The `accessors` option has been deprecated. It will have no effect in runes mode");
|
||||
}
|
||||
|
||||
/**
|
||||
* The `immutable` option has been deprecated. It will have no effect in runes mode
|
||||
* @param {null | NodeLike} node
|
||||
*/
|
||||
export function options_deprecated_immutable(node) {
|
||||
w(node, "options_deprecated_immutable", "The `immutable` option has been deprecated. It will have no effect in runes mode");
|
||||
}
|
||||
|
||||
/**
|
||||
* The `customElement` option is used when generating a custom element. Did you forget the `customElement: true` compile option?
|
||||
* @param {null | NodeLike} node
|
||||
*/
|
||||
export function options_missing_custom_element(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?");
|
||||
}
|
||||
|
||||
/**
|
||||
* The `enableSourcemap` option has been removed. Source maps are always generated now, and tooling can choose to ignore them
|
||||
* @param {null | NodeLike} node
|
||||
*/
|
||||
export function options_removed_enable_sourcemap(node) {
|
||||
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");
|
||||
}
|
||||
|
||||
/**
|
||||
* The `hydratable` option has been removed. Svelte components are always hydratable now
|
||||
* @param {null | NodeLike} node
|
||||
*/
|
||||
export function options_removed_hydratable(node) {
|
||||
w(node, "options_removed_hydratable", "The `hydratable` option has been removed. Svelte components are always hydratable now");
|
||||
}
|
||||
|
||||
/**
|
||||
* The `loopGuardTimeout` option has been removed
|
||||
* @param {null | NodeLike} node
|
||||
*/
|
||||
export function options_removed_loop_guard_timeout(node) {
|
||||
w(node, "options_removed_loop_guard_timeout", "The `loopGuardTimeout` option has been removed");
|
||||
}
|
||||
|
||||
/**
|
||||
* `generate: "dom"` and `generate: "ssr"` options have been renamed to "client" and "server" respectively
|
||||
* @param {null | NodeLike} node
|
||||
*/
|
||||
export function options_renamed_ssr_dom(node) {
|
||||
w(node, "options_renamed_ssr_dom", "`generate: \"dom\"` and `generate: \"ssr\"` options have been renamed to \"client\" and \"server\" respectively");
|
||||
}
|
||||
|
||||
/**
|
||||
* Use `$derived.by(() => {...})` instead of `$derived((() => {...})())`
|
||||
* @param {null | NodeLike} node
|
||||
*/
|
||||
export function derived_iife(node) {
|
||||
w(node, "derived_iife", "Use `$derived.by(() => {...})` instead of `$derived((() => {...})())`");
|
||||
}
|
||||
|
||||
/**
|
||||
* Component has unused export property '%name%'. If it is for external reference only, please consider using `export const %name%`
|
||||
* @param {null | NodeLike} node
|
||||
* @param {string} name
|
||||
*/
|
||||
export function export_let_unused(node, 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}\``);
|
||||
}
|
||||
|
||||
/**
|
||||
* `%name%` is updated, but is not declared with `$state(...)`. Changing its value will not correctly trigger updates
|
||||
* @param {null | NodeLike} node
|
||||
* @param {string} name
|
||||
*/
|
||||
export function non_reactive_update(node, name) {
|
||||
w(node, "non_reactive_update", `\`${name}\` is updated, but is not declared with \`$state(...)\`. Changing its value will not correctly trigger updates`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Avoid 'new class' — instead, declare the class at the top level scope
|
||||
* @param {null | NodeLike} node
|
||||
*/
|
||||
export function perf_avoid_inline_class(node) {
|
||||
w(node, "perf_avoid_inline_class", "Avoid 'new class' — instead, declare the class at the top level scope");
|
||||
}
|
||||
|
||||
/**
|
||||
* Avoid declaring classes below the top level scope
|
||||
* @param {null | NodeLike} node
|
||||
*/
|
||||
export function perf_avoid_nested_class(node) {
|
||||
w(node, "perf_avoid_nested_class", "Avoid declaring classes below the top level scope");
|
||||
}
|
||||
|
||||
/**
|
||||
* Reactive declarations only exist at the top level of the instance script
|
||||
* @param {null | NodeLike} node
|
||||
*/
|
||||
export function reactive_declaration_invalid_placement(node) {
|
||||
w(node, "reactive_declaration_invalid_placement", "Reactive declarations only exist at the top level of the instance script");
|
||||
}
|
||||
|
||||
/**
|
||||
* All dependencies of the reactive declaration are declared in a module script and will not be reactive
|
||||
* @param {null | NodeLike} node
|
||||
*/
|
||||
export function reactive_declaration_module_script(node) {
|
||||
w(node, "reactive_declaration_module_script", "All dependencies of the reactive declaration are declared in a module script and will not be reactive");
|
||||
}
|
||||
|
||||
/**
|
||||
* State referenced in its own scope will never update. Did you mean to reference it inside a closure?
|
||||
* @param {null | NodeLike} node
|
||||
*/
|
||||
export function state_referenced_locally(node) {
|
||||
w(node, "state_referenced_locally", "State referenced in its own scope will never update. Did you mean to reference it inside a closure?");
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 {string} name
|
||||
*/
|
||||
export function store_rune_conflict(node, name) {
|
||||
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`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unused CSS selector "%name%"
|
||||
* @param {null | NodeLike} node
|
||||
* @param {string} name
|
||||
*/
|
||||
export function css_unused_selector(node, name) {
|
||||
w(node, "css_unused_selector", `Unused CSS selector "${name}"`);
|
||||
}
|
||||
|
||||
/**
|
||||
* The "is" attribute is not supported cross-browser and should be avoided
|
||||
* @param {null | NodeLike} node
|
||||
*/
|
||||
export function attribute_avoid_is(node) {
|
||||
w(node, "attribute_avoid_is", "The \"is\" attribute is not supported cross-browser and should be avoided");
|
||||
}
|
||||
|
||||
/**
|
||||
* You are referencing `globalThis.%name%`. Did you forget to declare a variable with that name?
|
||||
* @param {null | NodeLike} node
|
||||
* @param {string} name
|
||||
*/
|
||||
export function attribute_global_event_reference(node, name) {
|
||||
w(node, "attribute_global_event_reference", `You are referencing \`globalThis.${name}\`. Did you forget to declare a variable with that name?`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Attributes should not contain ':' characters to prevent ambiguity with Svelte directives
|
||||
* @param {null | NodeLike} node
|
||||
*/
|
||||
export function attribute_illegal_colon(node) {
|
||||
w(node, "attribute_illegal_colon", "Attributes should not contain ':' characters to prevent ambiguity with Svelte directives");
|
||||
}
|
||||
|
||||
/**
|
||||
* '%wrong%' is not a valid HTML attribute. Did you mean '%right%'?
|
||||
* @param {null | NodeLike} node
|
||||
* @param {string} wrong
|
||||
* @param {string} right
|
||||
*/
|
||||
export function attribute_invalid_property_name(node, wrong, right) {
|
||||
w(node, "attribute_invalid_property_name", `'${wrong}' is not a valid HTML attribute. Did you mean '${right}'?`);
|
||||
}
|
||||
|
||||
/**
|
||||
* The rest operator (...) will create a new object and binding '%name%' with the original object will not work
|
||||
* @param {null | NodeLike} node
|
||||
* @param {string} name
|
||||
*/
|
||||
export function bind_invalid_each_rest(node, name) {
|
||||
w(node, "bind_invalid_each_rest", `The rest operator (...) will create a new object and binding '${name}' with the original object will not work`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Empty block
|
||||
* @param {null | NodeLike} node
|
||||
*/
|
||||
export function block_empty(node) {
|
||||
w(node, "block_empty", "Empty block");
|
||||
}
|
||||
|
||||
/**
|
||||
* `<%name%>` will be treated as an HTML element unless it begins with a capital letter
|
||||
* @param {null | NodeLike} node
|
||||
* @param {string} name
|
||||
*/
|
||||
export function component_name_lowercase(node, name) {
|
||||
w(node, "component_name_lowercase", `\`<${name}>\` will be treated as an HTML element unless it begins with a capital letter`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Self-closing HTML tags for non-void elements are ambiguous — use `<%name% ...></%name%>` rather than `<%name% ... />`
|
||||
* @param {null | NodeLike} node
|
||||
* @param {string} name
|
||||
*/
|
||||
export function element_invalid_self_closing_tag(node, name) {
|
||||
w(node, "element_invalid_self_closing_tag", `Self-closing HTML tags for non-void elements are ambiguous — use \`<${name} ...></${name}>\` rather than \`<${name} ... />\``);
|
||||
}
|
||||
|
||||
/**
|
||||
* Using `on:%name%` to listen to the %name% event is deprecated. Use the event attribute `on%name%` instead.
|
||||
* @param {null | NodeLike} node
|
||||
* @param {string} name
|
||||
*/
|
||||
export function event_directive_deprecated(node, name) {
|
||||
w(node, "event_directive_deprecated", `Using \`on:${name}\` to listen to the ${name} event is deprecated. Use the event attribute \`on${name}\` instead.`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Using `<slot>` to render parent content is deprecated. Use `{@render ...}` tags instead.
|
||||
* @param {null | NodeLike} node
|
||||
*/
|
||||
export function slot_element_deprecated(node) {
|
||||
w(node, "slot_element_deprecated", "Using `<slot>` to render parent content is deprecated. Use `{@render ...}` tags instead.");
|
||||
}
|
||||
@ -0,0 +1,276 @@
|
||||
/* This file is generated by scripts/process-messages/index.js. Do not edit! */
|
||||
|
||||
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
|
||||
* @returns {never}
|
||||
*/
|
||||
export function effect_update_depth_exceeded() {
|
||||
if (DEV) {
|
||||
const error = new Error(`${"effect_update_depth_exceeded"}\n${"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"}`);
|
||||
|
||||
error.name = 'Svelte error';
|
||||
throw error;
|
||||
} else {
|
||||
// TODO print a link to the documentation
|
||||
throw new Error("effect_update_depth_exceeded");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Missing hydration closing marker
|
||||
* @returns {never}
|
||||
*/
|
||||
export function hydration_missing_marker_close() {
|
||||
if (DEV) {
|
||||
const error = new Error(`${"hydration_missing_marker_close"}\n${"Missing hydration closing marker"}`);
|
||||
|
||||
error.name = 'Svelte error';
|
||||
throw error;
|
||||
} else {
|
||||
// TODO print a link to the documentation
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* `%name%(...)` cannot be used in runes mode
|
||||
* @param {string} name
|
||||
* @returns {never}
|
||||
*/
|
||||
export function lifecycle_legacy_only(name) {
|
||||
if (DEV) {
|
||||
const error = new Error(`${"lifecycle_legacy_only"}\n${`\`${name}(...)\` cannot be used in runes mode`}`);
|
||||
|
||||
error.name = 'Svelte error';
|
||||
throw error;
|
||||
} else {
|
||||
// TODO print a link to the documentation
|
||||
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");
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,74 @@
|
||||
/* This file is generated by scripts/process-messages/index.js. Do not edit! */
|
||||
|
||||
import { DEV } from 'esm-env';
|
||||
|
||||
var bold = 'font-weight: bold';
|
||||
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
|
||||
*/
|
||||
export function lifecycle_double_unmount() {
|
||||
if (DEV) {
|
||||
console.warn(`%c[svelte] ${"lifecycle_double_unmount"}\n%c${"Tried to unmount a component that was not mounted"}`, bold, normal);
|
||||
} else {
|
||||
// TODO print a link to the documentation
|
||||
console.warn("lifecycle_double_unmount");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* %parent% passed a value to %child% with `bind:`, but the value is owned by %owner%. Consider creating a binding between %owner% and %parent%
|
||||
* @param {string} parent
|
||||
* @param {string} child
|
||||
* @param {string} owner
|
||||
*/
|
||||
export function ownership_invalid_binding(parent, child, owner) {
|
||||
if (DEV) {
|
||||
console.warn(`%c[svelte] ${"ownership_invalid_binding"}\n%c${`${parent} passed a value to ${child} with \`bind:\`, but the value is owned by ${owner}. Consider creating a binding between ${owner} and ${parent}`}`, bold, normal);
|
||||
} else {
|
||||
// TODO print a link to the documentation
|
||||
console.warn("ownership_invalid_binding");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* %component% mutated a value owned by %owner%. This is strongly discouraged. Consider passing values to child components with `bind:`, or use a callback instead
|
||||
* @param {string | undefined | null} [component]
|
||||
* @param {string | undefined | null} [owner]
|
||||
*/
|
||||
export function ownership_invalid_mutation(component, owner) {
|
||||
if (DEV) {
|
||||
console.warn(`%c[svelte] ${"ownership_invalid_mutation"}\n%c${`${component} mutated a value owned by ${owner}. This is strongly discouraged. Consider passing values to child components with \`bind:\`, or use a callback instead`}`, bold, normal);
|
||||
} else {
|
||||
// TODO print a link to the documentation
|
||||
console.warn("ownership_invalid_mutation");
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
@ -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");
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,21 @@
|
||||
/* This file is generated by scripts/process-messages/index.js. Do not edit! */
|
||||
|
||||
import { DEV } from 'esm-env';
|
||||
|
||||
var bold = 'font-weight: bold';
|
||||
var normal = 'font-weight: normal';
|
||||
|
||||
/**
|
||||
* `<svelte:element this="%tag%">` is a void element — it cannot have content
|
||||
* @param {boolean} trace
|
||||
* @param {string} tag
|
||||
*/
|
||||
export function dynamic_void_element_content(trace, tag) {
|
||||
if (DEV) {
|
||||
console.warn(`%c[svelte] ${"dynamic_void_element_content"}\n%c${`\`<svelte:element this="${tag}">\` is a void element — it cannot have content`}`, bold, normal);
|
||||
if (trace) console.trace('stack trace');
|
||||
} else {
|
||||
// TODO print a link to the documentation
|
||||
console.warn("dynamic_void_element_content");
|
||||
}
|
||||
}
|
||||
@ -1,2 +1,8 @@
|
||||
import type { Bindable } from '../index.js';
|
||||
|
||||
/** Anything except a function */
|
||||
export type NotFunction<T> = T extends Function ? never : T;
|
||||
|
||||
export type RemoveBindable<Props extends Record<string, any>> = {
|
||||
[Key in keyof Props]: Props[Key] extends Bindable<infer Value> ? Value : Props[Key];
|
||||
};
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue