@ -3,7 +3,7 @@ import {
interactive _elements ,
interactive _elements ,
is _tag _valid _with _parent
is _tag _valid _with _parent
} from '../../../constants.js' ;
} from '../../../constants.js' ;
import { error } from '../../errors.js' ;
import * as e from '../../errors.js' ;
import {
import {
extract _identifiers ,
extract _identifiers ,
get _parent ,
get _parent ,
@ -44,14 +44,14 @@ function validate_component(node, context) {
attribute . type !== 'OnDirective' &&
attribute . type !== 'OnDirective' &&
attribute . type !== 'BindDirective'
attribute . type !== 'BindDirective'
) {
) {
e rror( attribute , 'invalid-component-directive' ) ;
e . invalid _component _directive ( attribute ) ;
}
}
if (
if (
attribute . type === 'OnDirective' &&
attribute . type === 'OnDirective' &&
( attribute . modifiers . length > 1 || attribute . modifiers . some ( ( m ) => m !== 'once' ) )
( attribute . modifiers . length > 1 || attribute . modifiers . some ( ( m ) => m !== 'once' ) )
) {
) {
e rror( attribute , 'invalid-event-modifier' ) ;
e . invalid _component _event _modifier ( attribute ) ;
}
}
if ( attribute . type === 'Attribute' ) {
if ( attribute . type === 'Attribute' ) {
@ -62,7 +62,7 @@ function validate_component(node, context) {
while ( -- i > 0 ) {
while ( -- i > 0 ) {
const char = context . state . analysis . source [ i ] ;
const char = context . state . analysis . source [ i ] ;
if ( char === '(' ) break ; // parenthesized sequence expressions are ok
if ( char === '(' ) break ; // parenthesized sequence expressions are ok
if ( char === '{' ) e rror( expression , 'invalid-sequence-expression' ) ;
if ( char === '{' ) e . invalid _sequence _expression ( expression ) ;
}
}
}
}
}
}
@ -93,8 +93,12 @@ const react_attributes = new Map([
* /
* /
function validate _element ( node , context ) {
function validate _element ( node , context ) {
let has _animate _directive = false ;
let has _animate _directive = false ;
let has _in _transition = false ;
let has _out _transition = false ;
/** @type {import('#compiler').TransitionDirective | null} */
let in _transition = null ;
/** @type {import('#compiler').TransitionDirective | null} */
let out _transition = null ;
for ( const attribute of node . attributes ) {
for ( const attribute of node . attributes ) {
if ( attribute . type === 'Attribute' ) {
if ( attribute . type === 'Attribute' ) {
@ -107,18 +111,18 @@ function validate_element(node, context) {
while ( -- i > 0 ) {
while ( -- i > 0 ) {
const char = context . state . analysis . source [ i ] ;
const char = context . state . analysis . source [ i ] ;
if ( char === '(' ) break ; // parenthesized sequence expressions are ok
if ( char === '(' ) break ; // parenthesized sequence expressions are ok
if ( char === '{' ) e rror( expression , 'invalid-sequence-expression' ) ;
if ( char === '{' ) e . invalid _sequence _expression ( expression ) ;
}
}
}
}
}
}
if ( regex _illegal _attribute _character . test ( attribute . name ) ) {
if ( regex _illegal _attribute _character . test ( attribute . name ) ) {
e rror( attribute , 'invalid-attribute-name' , attribute . name ) ;
e . invalid _attribute _name ( attribute , attribute . name ) ;
}
}
if ( attribute . name . startsWith ( 'on' ) && attribute . name . length > 2 ) {
if ( attribute . name . startsWith ( 'on' ) && attribute . name . length > 2 ) {
if ( ! is _expression ) {
if ( ! is _expression ) {
e rror( attribute , 'invalid-event-attribute-value' ) ;
e . invalid _event _attribute _value ( attribute ) ;
}
}
const value = attribute . value [ 0 ] . expression ;
const value = attribute . value [ 0 ] . expression ;
@ -162,9 +166,9 @@ function validate_element(node, context) {
} else if ( attribute . type === 'AnimateDirective' ) {
} else if ( attribute . type === 'AnimateDirective' ) {
const parent = context . path . at ( - 2 ) ;
const parent = context . path . at ( - 2 ) ;
if ( parent ? . type !== 'EachBlock' ) {
if ( parent ? . type !== 'EachBlock' ) {
e rror( attribute , 'invalid-animation' , 'no-each' ) ;
e . animation _invalid _placement ( attribute ) ;
} else if ( ! parent . key ) {
} else if ( ! parent . key ) {
e rror( attribute , 'invalid-animation' , 'each-key' ) ;
e . animation _missing _key ( attribute ) ;
} else if (
} else if (
parent . body . nodes . filter (
parent . body . nodes . filter (
( n ) =>
( n ) =>
@ -173,34 +177,39 @@ function validate_element(node, context) {
( n . type !== 'Text' || n . data . trim ( ) !== '' )
( n . type !== 'Text' || n . data . trim ( ) !== '' )
) . length > 1
) . length > 1
) {
) {
e rror( attribute , 'invalid-animation' , 'child' ) ;
e . animation _invalid _placement ( attribute ) ;
}
}
if ( has _animate _directive ) {
if ( has _animate _directive ) {
e rror( attribute , 'duplicate-animation' ) ;
e . animation _duplicate ( attribute ) ;
} else {
} else {
has _animate _directive = true ;
has _animate _directive = true ;
}
}
} else if ( attribute . type === 'TransitionDirective' ) {
} else if ( attribute . type === 'TransitionDirective' ) {
if ( ( attribute . outro && has _out _transition ) || ( attribute . intro && has _in _transition ) ) {
const existing = /** @type {import('#compiler').TransitionDirective | null} */ (
/** @param {boolean} _in @param {boolean} _out */
( attribute . intro && in _transition ) || ( attribute . outro && out _transition )
const type = ( _in , _out ) => ( _in && _out ? 'transition' : _in ? 'in' : 'out' ) ;
) ;
error (
attribute ,
if ( existing ) {
'duplicate-transition' ,
const a = existing . intro ? ( existing . outro ? 'transition' : 'in' ) : 'out' ;
type ( has _in _transition , has _out _transition ) ,
const b = attribute . intro ? ( attribute . outro ? 'transition' : 'in' ) : 'out' ;
type ( attribute . intro , attribute . outro )
) ;
if ( a === b ) {
e . transition _duplicate ( attribute , a ) ;
} else {
e . transition _conflict ( attribute , a , b ) ;
}
}
}
has _in _transition = has _in _transition || attribute . intro ;
if ( attribute . intro ) in _transition = attribute ;
has _out _transition = has _out _transition || attribute . outro ;
if ( attribute . outro ) out _transition = attribute ;
} else if ( attribute . type === 'OnDirective' ) {
} else if ( attribute . type === 'OnDirective' ) {
let has _passive _modifier = false ;
let has _passive _modifier = false ;
let conflicting _passive _modifier = '' ;
let conflicting _passive _modifier = '' ;
for ( const modifier of attribute . modifiers ) {
for ( const modifier of attribute . modifiers ) {
if ( ! EventModifiers . includes ( modifier ) ) {
if ( ! EventModifiers . includes ( modifier ) ) {
error ( attribute , 'invalid-event-modifier' , EventModifiers ) ;
const list = ` ${ EventModifiers . slice ( 0 , - 1 ) . join ( ', ' ) } or ${ EventModifiers . at ( - 1 ) } ` ;
e . invalid _event _modifier ( attribute , list ) ;
}
}
if ( modifier === 'passive' ) {
if ( modifier === 'passive' ) {
has _passive _modifier = true ;
has _passive _modifier = true ;
@ -208,12 +217,7 @@ function validate_element(node, context) {
conflicting _passive _modifier = modifier ;
conflicting _passive _modifier = modifier ;
}
}
if ( has _passive _modifier && conflicting _passive _modifier ) {
if ( has _passive _modifier && conflicting _passive _modifier ) {
error (
e . invalid _event _modifier _combination ( attribute , 'passive' , conflicting _passive _modifier ) ;
attribute ,
'invalid-event-modifier-combination' ,
'passive' ,
conflicting _passive _modifier
) ;
}
}
}
}
}
}
@ -259,7 +263,7 @@ function validate_slot_attribute(context, attribute) {
if ( owner ) {
if ( owner ) {
if ( ! is _text _attribute ( attribute ) ) {
if ( ! is _text _attribute ( attribute ) ) {
e rror( attribute , 'invalid-slot-attribute' ) ;
e . invalid _slot _attribute ( attribute ) ;
}
}
if (
if (
@ -268,13 +272,13 @@ function validate_slot_attribute(context, attribute) {
owner . type === 'SvelteSelf'
owner . type === 'SvelteSelf'
) {
) {
if ( owner !== context . path . at ( - 2 ) ) {
if ( owner !== context . path . at ( - 2 ) ) {
e rror( attribute , 'invalid-slot-placement' ) ;
e . invalid _slot _placement ( attribute ) ;
}
}
const name = attribute . value [ 0 ] . data ;
const name = attribute . value [ 0 ] . data ;
if ( context . state . component _slots . has ( name ) ) {
if ( context . state . component _slots . has ( name ) ) {
e rror( attribute , 'duplicate-slot-name' , name , owner . name ) ;
e . duplicate _slot _name ( attribute , name , owner . name ) ;
}
}
context . state . component _slots . add ( name ) ;
context . state . component _slots . add ( name ) ;
@ -291,12 +295,12 @@ function validate_slot_attribute(context, attribute) {
}
}
}
}
e rror( node , 'invalid-default-slot-content' ) ;
e . invalid _default _slot _content ( node ) ;
}
}
}
}
}
}
} else {
} else {
e rror( attribute , 'invalid-slot-placement' ) ;
e . invalid _slot _placement ( attribute ) ;
}
}
}
}
@ -327,7 +331,7 @@ const validation = {
const left = object ( assignee ) ;
const left = object ( assignee ) ;
if ( left === null ) {
if ( left === null ) {
e rror( node , 'invalid-binding-expression' ) ;
e . invalid _binding _expression ( node ) ;
}
}
const binding = context . state . scope . get ( left . name ) ;
const binding = context . state . scope . get ( left . name ) ;
@ -347,25 +351,25 @@ const validation = {
binding . kind !== 'store_sub' &&
binding . kind !== 'store_sub' &&
! binding . mutated )
! binding . mutated )
) {
) {
e rror( node . expression , 'invalid-binding-value' ) ;
e . invalid _binding _value ( node . expression ) ;
}
}
if ( binding . kind === 'derived' ) {
if ( binding . kind === 'derived' ) {
e rror( node . expression , 'invalid-derived-binding ') ;
e . invalid _binding ( node . expression , 'derived state ') ;
}
}
if ( context . state . analysis . runes && binding . kind === 'each' ) {
if ( context . state . analysis . runes && binding . kind === 'each' ) {
e rror( node , 'invalid-each-assignment' ) ;
e . invalid _each _assignment ( node ) ;
}
}
if ( binding . kind === 'snippet' ) {
if ( binding . kind === 'snippet' ) {
e rror( node , 'invalid-snippet-assignment' ) ;
e . invalid _snippet _assignment ( node ) ;
}
}
}
}
if ( node . name === 'group' ) {
if ( node . name === 'group' ) {
if ( ! binding ) {
if ( ! binding ) {
error ( node , 'INTERNAL' , 'Cannot find declaration for bind:group' ) ;
throw new Error ( 'Cannot find declaration for bind:group' ) ;
}
}
}
}
@ -389,21 +393,14 @@ const validation = {
parent ? . type === 'SvelteBody'
parent ? . type === 'SvelteBody'
) {
) {
if ( context . state . options . namespace === 'foreign' && node . name !== 'this' ) {
if ( context . state . options . namespace === 'foreign' && node . name !== 'this' ) {
error (
e . bind _invalid _detailed ( node , node . name , 'Foreign elements only support `bind:this`' ) ;
node ,
'invalid-binding' ,
node . name ,
undefined ,
'. Foreign elements only support bind:this'
) ;
}
}
if ( node . name in binding _properties ) {
if ( node . name in binding _properties ) {
const property = binding _properties [ node . name ] ;
const property = binding _properties [ node . name ] ;
if ( property . valid _elements && ! property . valid _elements . includes ( parent . name ) ) {
if ( property . valid _elements && ! property . valid _elements . includes ( parent . name ) ) {
e rror (
e . bind _invalid _target (
node ,
node ,
'invalid-binding' ,
node . name ,
node . name ,
property . valid _elements . map ( ( valid _element ) => ` < ${ valid _element } > ` ) . join ( ', ' )
property . valid _elements . map ( ( valid _element ) => ` < ${ valid _element } > ` ) . join ( ', ' )
) ;
) ;
@ -415,17 +412,17 @@ const validation = {
) ;
) ;
if ( type && ! is _text _attribute ( type ) ) {
if ( type && ! is _text _attribute ( type ) ) {
if ( node . name !== 'value' || type . value === true ) {
if ( node . name !== 'value' || type . value === true ) {
e rror( type , 'invalid-type-attribute' ) ;
e . invalid _type _attribute ( type ) ;
}
}
return ; // bind:value can handle dynamic `type` attributes
return ; // bind:value can handle dynamic `type` attributes
}
}
if ( node . name === 'checked' && type ? . value [ 0 ] . data !== 'checkbox' ) {
if ( node . name === 'checked' && type ? . value [ 0 ] . data !== 'checkbox' ) {
e rror( node , 'invalid-binding' , node . name , '<input type="checkbox">' ) ;
e . bind _invalid _target ( node , node . name , '<input type="checkbox">' ) ;
}
}
if ( node . name === 'files' && type ? . value [ 0 ] . data !== 'file' ) {
if ( node . name === 'files' && type ? . value [ 0 ] . data !== 'file' ) {
e rror( node , 'invalid-binding' , node . name , '<input type="file">' ) ;
e . bind _invalid _target ( node , node . name , '<input type="file">' ) ;
}
}
}
}
@ -438,14 +435,13 @@ const validation = {
a . value !== true
a . value !== true
) ;
) ;
if ( multiple ) {
if ( multiple ) {
e rror( multiple , 'invalid-multiple-attribute' ) ;
e . invalid _multiple _attribute ( multiple ) ;
}
}
}
}
if ( node . name === 'offsetWidth' && SVGElements . includes ( parent . name ) ) {
if ( node . name === 'offsetWidth' && SVGElements . includes ( parent . name ) ) {
e rror (
e . bind _invalid _target (
node ,
node ,
'invalid-binding' ,
node . name ,
node . name ,
` non-<svg> elements. Use 'clientWidth' for <svg> instead `
` non-<svg> elements. Use 'clientWidth' for <svg> instead `
) ;
) ;
@ -456,9 +452,9 @@ const validation = {
parent . attributes . find ( ( a ) => a . type === 'Attribute' && a . name === 'contenteditable' )
parent . attributes . find ( ( a ) => a . type === 'Attribute' && a . name === 'contenteditable' )
) ;
) ;
if ( ! contenteditable ) {
if ( ! contenteditable ) {
e rror( node , 'missing-contenteditable-attribute' ) ;
e . missing _contenteditable _attribute ( node ) ;
} else if ( ! is _text _attribute ( contenteditable ) && contenteditable . value !== true ) {
} else if ( ! is _text _attribute ( contenteditable ) && contenteditable . value !== true ) {
e rror( contenteditable , 'dynamic-contenteditable-attribute' ) ;
e . dynamic _contenteditable _attribute ( contenteditable ) ;
}
}
}
}
} else {
} else {
@ -466,15 +462,15 @@ const validation = {
if ( match ) {
if ( match ) {
const property = binding _properties [ match ] ;
const property = binding _properties [ match ] ;
if ( ! property . valid _elements || property . valid _elements . includes ( parent . name ) ) {
if ( ! property . valid _elements || property . valid _elements . includes ( parent . name ) ) {
e rror( node , 'invalid-binding' , node . name , undefined , ` (d id you mean '${ match } '? ) ` ) ;
e . bind _invalid _detailed ( node , node . name , ` D id you mean '${ match } '? ` ) ;
}
}
}
}
e rror( node , 'invalid-binding' , node . name ) ;
e . bind _invalid ( node , node . name ) ;
}
}
}
}
} ,
} ,
ExportDefaultDeclaration ( node ) {
ExportDefaultDeclaration ( node ) {
e rror( node , 'default-export' ) ;
e . default _export ( node ) ;
} ,
} ,
ConstTag ( node , context ) {
ConstTag ( node , context ) {
const parent = context . path . at ( - 1 ) ;
const parent = context . path . at ( - 1 ) ;
@ -491,7 +487,7 @@ const validation = {
( ( grand _parent ? . type !== 'RegularElement' && grand _parent ? . type !== 'SvelteElement' ) ||
( ( grand _parent ? . type !== 'RegularElement' && grand _parent ? . type !== 'SvelteElement' ) ||
! grand _parent . attributes . some ( ( a ) => a . type === 'Attribute' && a . name === 'slot' ) ) )
! grand _parent . attributes . some ( ( a ) => a . type === 'Attribute' && a . name === 'slot' ) ) )
) {
) {
e rror( node , 'invalid-const-placement' ) ;
e . invalid _const _placement ( node ) ;
}
}
} ,
} ,
ImportDeclaration ( node , context ) {
ImportDeclaration ( node , context ) {
@ -502,7 +498,7 @@ const validation = {
specifier . imported . name === 'beforeUpdate' ||
specifier . imported . name === 'beforeUpdate' ||
specifier . imported . name === 'afterUpdate'
specifier . imported . name === 'afterUpdate'
) {
) {
e rror( specifier , 'invalid-runes-mode-import' , specifier . imported . name ) ;
e . invalid _runes _mode _import ( specifier , specifier . imported . name ) ;
}
}
}
}
}
}
@ -520,14 +516,14 @@ const validation = {
parent . type !== 'SvelteSelf' &&
parent . type !== 'SvelteSelf' &&
parent . type !== 'SvelteFragment' )
parent . type !== 'SvelteFragment' )
) {
) {
e rror( node , 'invalid-let-directive-placement' ) ;
e . invalid _let _directive _placement ( node ) ;
}
}
} ,
} ,
RegularElement ( node , context ) {
RegularElement ( node , context ) {
if ( node . name === 'textarea' && node . fragment . nodes . length > 0 ) {
if ( node . name === 'textarea' && node . fragment . nodes . length > 0 ) {
for ( const attribute of node . attributes ) {
for ( const attribute of node . attributes ) {
if ( attribute . type === 'Attribute' && attribute . name === 'value' ) {
if ( attribute . type === 'Attribute' && attribute . name === 'value' ) {
e rror( node , 'invalid-textarea-content' ) ;
e . invalid _textarea _content ( node ) ;
}
}
}
}
}
}
@ -551,7 +547,7 @@ const validation = {
if ( context . state . parent _element ) {
if ( context . state . parent _element ) {
if ( ! is _tag _valid _with _parent ( node . name , context . state . parent _element ) ) {
if ( ! is _tag _valid _with _parent ( node . name , context . state . parent _element ) ) {
e rror( node , 'invalid-node-placement' , ` < ${ node . name } > ` , context . state . parent _element ) ;
e . invalid _node _placement ( node , ` < ${ node . name } > ` , context . state . parent _element ) ;
}
}
}
}
@ -563,7 +559,7 @@ const validation = {
parent . name === node . name &&
parent . name === node . name &&
interactive _elements . has ( parent . name )
interactive _elements . has ( parent . name )
) {
) {
e rror( node , 'invalid-node-placement' , ` < ${ node . name } > ` , parent . name ) ;
e . invalid _node _placement ( node , ` < ${ node . name } > ` , parent . name ) ;
}
}
}
}
}
}
@ -572,7 +568,7 @@ const validation = {
const path = context . path ;
const path = context . path ;
for ( let parent of path ) {
for ( let parent of path ) {
if ( parent . type === 'RegularElement' && parent . name === 'p' ) {
if ( parent . type === 'RegularElement' && parent . name === 'p' ) {
e rror( node , 'invalid-node-placement' , ` < ${ node . name } > ` , parent . name ) ;
e . invalid _node _placement ( node , ` < ${ node . name } > ` , parent . name ) ;
}
}
}
}
}
}
@ -603,7 +599,7 @@ const validation = {
const raw _args = unwrap _optional ( node . expression ) . arguments ;
const raw _args = unwrap _optional ( node . expression ) . arguments ;
for ( const arg of raw _args ) {
for ( const arg of raw _args ) {
if ( arg . type === 'SpreadElement' ) {
if ( arg . type === 'SpreadElement' ) {
e rror( arg , 'invalid-render-spread-argument' ) ;
e . invalid _render _spread _argument ( arg ) ;
}
}
}
}
@ -613,7 +609,7 @@ const validation = {
callee . property . type === 'Identifier' &&
callee . property . type === 'Identifier' &&
[ 'bind' , 'apply' , 'call' ] . includes ( callee . property . name )
[ 'bind' , 'apply' , 'call' ] . includes ( callee . property . name )
) {
) {
e rror( node , 'invalid-render-call' ) ;
e . invalid _render _call ( node ) ;
}
}
const is _inside _textarea = context . path . find ( ( n ) => {
const is _inside _textarea = context . path . find ( ( n ) => {
@ -625,9 +621,8 @@ const validation = {
) ;
) ;
} ) ;
} ) ;
if ( is _inside _textarea ) {
if ( is _inside _textarea ) {
e rror (
e . invalid _tag _placement (
node ,
node ,
'invalid-tag-placement' ,
'inside <textarea> or <svelte:element this="textarea">' ,
'inside <textarea> or <svelte:element this="textarea">' ,
'render'
'render'
) ;
) ;
@ -667,19 +662,19 @@ const validation = {
( node ) => node . type !== 'SnippetBlock' && ( node . type !== 'Text' || node . data . trim ( ) )
( node ) => node . type !== 'SnippetBlock' && ( node . type !== 'Text' || node . data . trim ( ) )
)
)
) {
) {
e rror( node , 'conflicting-children-snippet' ) ;
e . conflicting _children _snippet ( node ) ;
}
}
}
}
} ,
} ,
StyleDirective ( node ) {
StyleDirective ( node ) {
if ( node . modifiers . length > 1 || ( node . modifiers . length && node . modifiers [ 0 ] !== 'important' ) ) {
if ( node . modifiers . length > 1 || ( node . modifiers . length && node . modifiers [ 0 ] !== 'important' ) ) {
e rror( node , 'invalid-style-directive-modifier' ) ;
e . invalid _style _directive _modifier ( node ) ;
}
}
} ,
} ,
SvelteHead ( node ) {
SvelteHead ( node ) {
const attribute = node . attributes [ 0 ] ;
const attribute = node . attributes [ 0 ] ;
if ( attribute ) {
if ( attribute ) {
e rror( attribute , 'illegal-svelte-head-attribute' ) ;
e . illegal _svelte _head _attribute ( attribute ) ;
}
}
} ,
} ,
SvelteElement ( node , context ) {
SvelteElement ( node , context ) {
@ -692,7 +687,7 @@ const validation = {
SvelteFragment ( node , context ) {
SvelteFragment ( node , context ) {
const parent = context . path . at ( - 2 ) ;
const parent = context . path . at ( - 2 ) ;
if ( parent ? . type !== 'Component' && parent ? . type !== 'SvelteComponent' ) {
if ( parent ? . type !== 'Component' && parent ? . type !== 'SvelteComponent' ) {
e rror( node , 'invalid-svelte-fragment-placement' ) ;
e . invalid _svelte _fragment _placement ( node ) ;
}
}
for ( const attribute of node . attributes ) {
for ( const attribute of node . attributes ) {
@ -701,7 +696,7 @@ const validation = {
validate _slot _attribute ( context , attribute ) ;
validate _slot _attribute ( context , attribute ) ;
}
}
} else if ( attribute . type !== 'LetDirective' ) {
} else if ( attribute . type !== 'LetDirective' ) {
e rror( attribute , 'invalid-svelte-fragment-attribute' ) ;
e . invalid _svelte _fragment _attribute ( attribute ) ;
}
}
}
}
} ,
} ,
@ -710,15 +705,15 @@ const validation = {
if ( attribute . type === 'Attribute' ) {
if ( attribute . type === 'Attribute' ) {
if ( attribute . name === 'name' ) {
if ( attribute . name === 'name' ) {
if ( ! is _text _attribute ( attribute ) ) {
if ( ! is _text _attribute ( attribute ) ) {
e rror( attribute , 'invalid-slot-name' , fals e) ;
e . invalid _slot _name ( attribut e) ;
}
}
const slot _name = attribute . value [ 0 ] . data ;
const slot _name = attribute . value [ 0 ] . data ;
if ( slot _name === 'default' ) {
if ( slot _name === 'default' ) {
e rror( attribute , 'invalid-slot-name' , tru e) ;
e . invalid _slot _name _default ( attribut e) ;
}
}
}
}
} else if ( attribute . type !== 'SpreadAttribute' && attribute . type !== 'LetDirective' ) {
} else if ( attribute . type !== 'SpreadAttribute' && attribute . type !== 'LetDirective' ) {
e rror( attribute , 'invalid-slot-element-attribute' ) ;
e . invalid _slot _element _attribute ( attribute ) ;
}
}
}
}
} ,
} ,
@ -729,19 +724,19 @@ const validation = {
if ( ! node . parent ) return ;
if ( ! node . parent ) return ;
if ( context . state . parent _element && regex _not _whitespace . test ( node . data ) ) {
if ( context . state . parent _element && regex _not _whitespace . test ( node . data ) ) {
if ( ! is _tag _valid _with _parent ( '#text' , context . state . parent _element ) ) {
if ( ! is _tag _valid _with _parent ( '#text' , context . state . parent _element ) ) {
e rror( node , 'invalid-node-placement' , 'Text node' , context . state . parent _element ) ;
e . invalid _node _placement ( node , 'Text node' , context . state . parent _element ) ;
}
}
}
}
} ,
} ,
TitleElement ( node ) {
TitleElement ( node ) {
const attribute = node . attributes [ 0 ] ;
const attribute = node . attributes [ 0 ] ;
if ( attribute ) {
if ( attribute ) {
e rror( attribute , 'illegal-title-attribute' ) ;
e . illegal _title _attribute ( attribute ) ;
}
}
const child = node . fragment . nodes . find ( ( n ) => n . type !== 'Text' && n . type !== 'ExpressionTag' ) ;
const child = node . fragment . nodes . find ( ( n ) => n . type !== 'Text' && n . type !== 'ExpressionTag' ) ;
if ( child ) {
if ( child ) {
e rror( child , 'invalid-title-content' ) ;
e . invalid _title _content ( child ) ;
}
}
} ,
} ,
UpdateExpression ( node , context ) {
UpdateExpression ( node , context ) {
@ -751,7 +746,7 @@ const validation = {
if ( ! node . parent ) return ;
if ( ! node . parent ) return ;
if ( context . state . parent _element ) {
if ( context . state . parent _element ) {
if ( ! is _tag _valid _with _parent ( '#text' , context . state . parent _element ) ) {
if ( ! is _tag _valid _with _parent ( '#text' , context . state . parent _element ) ) {
e rror( node , 'invalid-node-placement' , '{expression}' , context . state . parent _element ) ;
e . invalid _node _placement ( node , '{expression}' , context . state . parent _element ) ;
}
}
}
}
}
}
@ -772,7 +767,7 @@ export const validation_legacy = merge(validation, a11y_validators, {
}
}
if ( state . scope . get ( callee . name ) ? . kind !== 'store_sub' ) {
if ( state . scope . get ( callee . name ) ? . kind !== 'store_sub' ) {
e rror( node . init , 'invalid-rune-usage' , callee . name ) ;
e . invalid _rune _usage ( node . init , callee . name ) ;
}
}
} ,
} ,
AssignmentExpression ( node , { state , path } ) {
AssignmentExpression ( node , { state , path } ) {
@ -805,11 +800,11 @@ function validate_export(node, scope, name) {
if ( ! binding ) return ;
if ( ! binding ) return ;
if ( binding . kind === 'derived' ) {
if ( binding . kind === 'derived' ) {
e rror( node , 'invalid-derived-export' ) ;
e . invalid _derived _export ( node ) ;
}
}
if ( ( binding . kind === 'state' || binding . kind === 'frozen_state' ) && binding . reassigned ) {
if ( ( binding . kind === 'state' || binding . kind === 'frozen_state' ) && binding . reassigned ) {
e rror( node , 'invalid-state-export' ) ;
e . invalid _state _export ( node ) ;
}
}
}
}
@ -827,7 +822,7 @@ function validate_call_expression(node, scope, path) {
if ( rune === '$props' ) {
if ( rune === '$props' ) {
if ( parent . type === 'VariableDeclarator' ) return ;
if ( parent . type === 'VariableDeclarator' ) return ;
e rror( node , 'invalid-props-location' ) ;
e . invalid _props _location ( node ) ;
}
}
if ( rune === '$bindable' ) {
if ( rune === '$bindable' ) {
@ -840,7 +835,7 @@ function validate_call_expression(node, scope, path) {
return ;
return ;
}
}
}
}
e rror( node , 'invalid-bindable-location' ) ;
e . invalid _bindable _location ( node ) ;
}
}
if (
if (
@ -851,46 +846,46 @@ function validate_call_expression(node, scope, path) {
) {
) {
if ( parent . type === 'VariableDeclarator' ) return ;
if ( parent . type === 'VariableDeclarator' ) return ;
if ( parent . type === 'PropertyDefinition' && ! parent . static && ! parent . computed ) return ;
if ( parent . type === 'PropertyDefinition' && ! parent . static && ! parent . computed ) return ;
e rror( node , 'invalid-state-location' , rune ) ;
e . invalid _state _location ( node , rune ) ;
}
}
if ( rune === '$effect' || rune === '$effect.pre' ) {
if ( rune === '$effect' || rune === '$effect.pre' ) {
if ( parent . type !== 'ExpressionStatement' ) {
if ( parent . type !== 'ExpressionStatement' ) {
e rror( node , 'invalid-effect-location' ) ;
e . invalid _effect _location ( node ) ;
}
}
if ( node . arguments . length !== 1 ) {
if ( node . arguments . length !== 1 ) {
e rror( node , 'invalid-rune-args-length' , rune , [ 1 ] ) ;
e . invalid _rune _args _length ( node , rune , 'exactly one argument' ) ;
}
}
}
}
if ( rune === '$effect.active' ) {
if ( rune === '$effect.active' ) {
if ( node . arguments . length !== 0 ) {
if ( node . arguments . length !== 0 ) {
e rror( node , 'invalid-rune-args-length' , rune , [ 0 ] ) ;
e . invalid _rune _args ( node , rune ) ;
}
}
}
}
if ( rune === '$effect.root' ) {
if ( rune === '$effect.root' ) {
if ( node . arguments . length !== 1 ) {
if ( node . arguments . length !== 1 ) {
e rror( node , 'invalid-rune-args-length' , rune , [ 1 ] ) ;
e . invalid _rune _args _length ( node , rune , 'exactly one argument' ) ;
}
}
}
}
if ( rune === '$inspect' ) {
if ( rune === '$inspect' ) {
if ( node . arguments . length < 1 ) {
if ( node . arguments . length < 1 ) {
e rror( node , 'invalid-rune-args-length' , rune , [ 1 , 'more' ] ) ;
e . invalid _rune _args _length ( node , rune , 'one or more arguments' ) ;
}
}
}
}
if ( rune === '$inspect().with' ) {
if ( rune === '$inspect().with' ) {
if ( node . arguments . length !== 1 ) {
if ( node . arguments . length !== 1 ) {
e rror( node , 'invalid-rune-args-length' , rune , [ 1 ] ) ;
e . invalid _rune _args _length ( node , rune , 'exactly one argument' ) ;
}
}
}
}
if ( rune === '$state.snapshot' ) {
if ( rune === '$state.snapshot' ) {
if ( node . arguments . length !== 1 ) {
if ( node . arguments . length !== 1 ) {
e rror( node , 'invalid-rune-args-length' , rune , [ 1 ] ) ;
e . invalid _rune _args _length ( node , rune , 'exactly one argument' ) ;
}
}
}
}
}
}
@ -906,7 +901,7 @@ function ensure_no_module_import_conflict(node, state) {
state . scope === state . analysis . instance . scope &&
state . scope === state . analysis . instance . scope &&
state . analysis . module . scope . get ( id . name ) ? . declaration _kind === 'import'
state . analysis . module . scope . get ( id . name ) ? . declaration _kind === 'import'
) {
) {
e rror( node . id , 'illegal-variable-declaration' ) ;
e . illegal _variable _declaration ( node . id ) ;
}
}
}
}
}
}
@ -932,7 +927,7 @@ export const validation_runes_js = {
} ,
} ,
CallExpression ( node , { state , path } ) {
CallExpression ( node , { state , path } ) {
if ( get _rune ( node , state . scope ) === '$host' ) {
if ( get _rune ( node , state . scope ) === '$host' ) {
e rror( node , 'invalid-host-location' ) ;
e . invalid _host _location ( node ) ;
}
}
validate _call _expression ( node , state . scope , path ) ;
validate _call _expression ( node , state . scope , path ) ;
} ,
} ,
@ -945,13 +940,13 @@ export const validation_runes_js = {
const args = /** @type {import('estree').CallExpression} */ ( init ) . arguments ;
const args = /** @type {import('estree').CallExpression} */ ( init ) . arguments ;
if ( ( rune === '$derived' || rune === '$derived.by' ) && args . length !== 1 ) {
if ( ( rune === '$derived' || rune === '$derived.by' ) && args . length !== 1 ) {
e rror( node , 'invalid-rune-args-length' , rune , [ 1 ] ) ;
e . invalid _rune _args _length ( node , rune , 'exactly one argument' ) ;
} else if ( rune === '$state' && args . length > 1 ) {
} else if ( rune === '$state' && args . length > 1 ) {
e rror( node , 'invalid-rune-args-length' , rune , [ 0 , 1 ] ) ;
e . invalid _rune _args _length ( node , rune , 'zero or one arguments' ) ;
} else if ( rune === '$props' ) {
} else if ( rune === '$props' ) {
e rror( node , 'invalid-props-location' ) ;
e . invalid _props _location ( node ) ;
} else if ( rune === '$bindable' ) {
} else if ( rune === '$bindable' ) {
e rror( node , 'invalid-bindable-location' ) ;
e . invalid _bindable _location ( node ) ;
}
}
} ,
} ,
AssignmentExpression ( node , { state } ) {
AssignmentExpression ( node , { state } ) {
@ -1021,16 +1016,24 @@ function validate_no_const_assignment(node, argument, scope, is_binding) {
} else if ( argument . type === 'Identifier' ) {
} else if ( argument . type === 'Identifier' ) {
const binding = scope . get ( argument . name ) ;
const binding = scope . get ( argument . name ) ;
if ( binding ? . declaration _kind === 'const' && binding . kind !== 'each' ) {
if ( binding ? . declaration _kind === 'const' && binding . kind !== 'each' ) {
error (
// e.invalid_const_assignment(
node ,
// node,
'invalid-const-assignment' ,
// is_binding,
is _binding ,
// // This takes advantage of the fact that we don't assign initial for let directives and then/catch variables.
// This takes advantage of the fact that we don't assign initial for let directives and then/catch variables.
// // If we start doing that, we need another property on the binding to differentiate, or give up on the more precise error message.
// If we start doing that, we need another property on the binding to differentiate, or give up on the more precise error message.
// binding.kind !== 'state' &&
binding . kind !== 'state' &&
// binding.kind !== 'frozen_state' &&
binding . kind !== 'frozen_state' &&
// (binding.kind !== 'normal' || !binding.initial)
( binding . kind !== 'normal' || ! binding . initial )
// );
) ;
// TODO have a more specific error message for assignments to things like `{:then foo}`
const thing = 'constant' ;
if ( is _binding ) {
e . invalid _binding ( node , thing ) ;
} else {
e . invalid _assignment ( node , thing ) ;
}
}
}
}
}
}
}
@ -1048,16 +1051,16 @@ function validate_assignment(node, argument, state) {
if ( state . analysis . runes ) {
if ( state . analysis . runes ) {
if ( binding ? . kind === 'derived' ) {
if ( binding ? . kind === 'derived' ) {
e rror( node , 'invalid-derived-assignment ') ;
e . invalid _assignment ( node , 'derived state ') ;
}
}
if ( binding ? . kind === 'each' ) {
if ( binding ? . kind === 'each' ) {
e rror( node , 'invalid-each-assignment' ) ;
e . invalid _each _assignment ( node ) ;
}
}
}
}
if ( binding ? . kind === 'snippet' ) {
if ( binding ? . kind === 'snippet' ) {
e rror( node , 'invalid-snippet-assignment' ) ;
e . invalid _snippet _assignment ( node ) ;
}
}
}
}
@ -1073,7 +1076,7 @@ function validate_assignment(node, argument, state) {
if ( object . type === 'ThisExpression' && property ? . type === 'PrivateIdentifier' ) {
if ( object . type === 'ThisExpression' && property ? . type === 'PrivateIdentifier' ) {
if ( state . private _derived _state . includes ( property . name ) ) {
if ( state . private _derived _state . includes ( property . name ) ) {
e rror( node , 'invalid-derived-assignment ') ;
e . invalid _assignment ( node , 'derived state ') ;
}
}
}
}
}
}
@ -1081,7 +1084,7 @@ function validate_assignment(node, argument, state) {
export const validation _runes = merge ( validation , a11y _validators , {
export const validation _runes = merge ( validation , a11y _validators , {
LabeledStatement ( node , { path } ) {
LabeledStatement ( node , { path } ) {
if ( node . label . name !== '$' || path . at ( - 1 ) ? . type !== 'Program' ) return ;
if ( node . label . name !== '$' || path . at ( - 1 ) ? . type !== 'Program' ) return ;
e rror( node , 'invalid-legacy-reactive-statement' ) ;
e . invalid _legacy _reactive _statement ( node ) ;
} ,
} ,
ExportNamedDeclaration ( node , { state , next } ) {
ExportNamedDeclaration ( node , { state , next } ) {
if ( state . ast _type === 'module' ) {
if ( state . ast _type === 'module' ) {
@ -1099,7 +1102,7 @@ export const validation_runes = merge(validation, a11y_validators, {
if ( node . declaration ? . type !== 'VariableDeclaration' ) return ;
if ( node . declaration ? . type !== 'VariableDeclaration' ) return ;
if ( node . declaration . kind !== 'let' ) return ;
if ( node . declaration . kind !== 'let' ) return ;
if ( state . analysis . instance . scope !== state . scope ) return ;
if ( state . analysis . instance . scope !== state . scope ) return ;
e rror( node , 'invalid-legacy-export' ) ;
e . invalid _legacy _export ( node ) ;
}
}
} ,
} ,
ExportSpecifier ( node , { state } ) {
ExportSpecifier ( node , { state } ) {
@ -1110,12 +1113,12 @@ export const validation_runes = merge(validation, a11y_validators, {
CallExpression ( node , { state , path } ) {
CallExpression ( node , { state , path } ) {
const rune = get _rune ( node , state . scope ) ;
const rune = get _rune ( node , state . scope ) ;
if ( rune === '$bindable' && node . arguments . length > 1 ) {
if ( rune === '$bindable' && node . arguments . length > 1 ) {
e rror( node , 'invalid-rune-args-length' , '$bindable' , [ 0 , 1 ] ) ;
e . invalid _rune _args _length ( node , '$bindable' , 'zero or one arguments' ) ;
} else if ( rune === '$host' ) {
} else if ( rune === '$host' ) {
if ( node . arguments . length > 0 ) {
if ( node . arguments . length > 0 ) {
e rror( node , 'invalid-rune-args-length' , '$host' , [ 0 ] ) ;
e . invalid _rune _args ( node , '$host' ) ;
} else if ( state . ast _type === 'module' || ! state . analysis . custom _element ) {
} else if ( state . ast _type === 'module' || ! state . analysis . custom _element ) {
e rror( node , 'invalid-host-location' ) ;
e . invalid _host _location ( node ) ;
}
}
}
}
@ -1127,7 +1130,7 @@ export const validation_runes = merge(validation, a11y_validators, {
context . type === 'Identifier' &&
context . type === 'Identifier' &&
( context . name === '$state' || context . name === '$derived' )
( context . name === '$state' || context . name === '$derived' )
) {
) {
e rror( node , 'invalid-state-location' , context . name ) ;
e . invalid _state _location ( node , context . name ) ;
}
}
next ( { ... state } ) ;
next ( { ... state } ) ;
} ,
} ,
@ -1148,39 +1151,39 @@ export const validation_runes = merge(validation, a11y_validators, {
// TODO some of this is duplicated with above, seems off
// TODO some of this is duplicated with above, seems off
if ( ( rune === '$derived' || rune === '$derived.by' ) && args . length !== 1 ) {
if ( ( rune === '$derived' || rune === '$derived.by' ) && args . length !== 1 ) {
e rror( node , 'invalid-rune-args-length' , rune , [ 1 ] ) ;
e . invalid _rune _args _length ( node , rune , 'exactly one argument' ) ;
} else if ( rune === '$state' && args . length > 1 ) {
} else if ( rune === '$state' && args . length > 1 ) {
e rror( node , 'invalid-rune-args-length' , rune , [ 0 , 1 ] ) ;
e . invalid _rune _args _length ( node , rune , 'zero or one arguments' ) ;
} else if ( rune === '$props' ) {
} else if ( rune === '$props' ) {
if ( state . has _props _rune ) {
if ( state . has _props _rune ) {
e rror( node , 'duplicate-props-rune' ) ;
e . duplicate _props _rune ( node ) ;
}
}
state . has _props _rune = true ;
state . has _props _rune = true ;
if ( args . length > 0 ) {
if ( args . length > 0 ) {
e rror( node , 'invalid-rune-args-length' , rune , [ 0 ] ) ;
e . invalid _rune _args ( node , rune ) ;
}
}
if ( node . id . type !== 'ObjectPattern' ) {
if ( node . id . type !== 'ObjectPattern' ) {
e rror( node , 'invalid-props-id' ) ;
e . invalid _props _id ( node ) ;
}
}
if ( state . scope !== state . analysis . instance . scope ) {
if ( state . scope !== state . analysis . instance . scope ) {
e rror( node , 'invalid-props-location' ) ;
e . invalid _props _location ( node ) ;
}
}
for ( const property of node . id . properties ) {
for ( const property of node . id . properties ) {
if ( property . type === 'Property' ) {
if ( property . type === 'Property' ) {
if ( property . computed ) {
if ( property . computed ) {
e rror( property , 'invalid-props-pattern' ) ;
e . invalid _props _pattern ( property ) ;
}
}
const value =
const value =
property . value . type === 'AssignmentPattern' ? property . value . left : property . value ;
property . value . type === 'AssignmentPattern' ? property . value . left : property . value ;
if ( value . type !== 'Identifier' ) {
if ( value . type !== 'Identifier' ) {
e rror( property , 'invalid-props-pattern' ) ;
e . invalid _props _pattern ( property ) ;
}
}
}
}
}
}