@@ -131,7 +131,7 @@ export default class Element extends Node {
131131
132132 this . namespace = get_namespace ( parent , this , component . namespace ) ;
133133
134- if ( this . name === 'textarea' ) {
134+ if ( this . name === 'textarea' && ! this . is_native ( ) ) {
135135 if ( info . children . length > 0 ) {
136136 const value_attribute = info . attributes . find ( node => node . name === 'value' ) ;
137137 if ( value_attribute ) {
@@ -153,7 +153,7 @@ export default class Element extends Node {
153153 }
154154 }
155155
156- if ( this . name === 'option' ) {
156+ if ( this . name === 'option' && ! this . is_native ( ) ) {
157157 // Special case — treat these the same way:
158158 // <option>{foo}</option>
159159 // <option value={foo}>{foo}</option>
@@ -248,51 +248,53 @@ export default class Element extends Node {
248248 } ) ;
249249 }
250250
251- if ( a11y_distracting_elements . has ( this . name ) ) {
252- // no-distracting-elements
253- this . component . warn ( this , {
254- code : 'a11y-distracting-elements' ,
255- message : `A11y: Avoid <${ this . name } > elements`
256- } ) ;
257- }
251+ if ( ! this . is_native ( ) ) {
252+ if ( a11y_distracting_elements . has ( this . name ) ) {
253+ // no-distracting-elements
254+ this . component . warn ( this , {
255+ code : 'a11y-distracting-elements' ,
256+ message : `A11y: Avoid <${ this . name } > elements`
257+ } ) ;
258+ }
258259
259- if ( this . name === 'figcaption' ) {
260- let { parent } = this ;
261- let is_figure_parent = false ;
260+ if ( this . name === 'figcaption' ) {
261+ let { parent } = this ;
262+ let is_figure_parent = false ;
262263
263- while ( parent ) {
264- if ( ( parent as Element ) . name === 'figure' ) {
265- is_figure_parent = true ;
266- break ;
264+ while ( parent ) {
265+ if ( ( parent as Element ) . name === 'figure' ) {
266+ is_figure_parent = true ;
267+ break ;
268+ }
269+ if ( parent . type === 'Element' ) {
270+ break ;
271+ }
272+ parent = parent . parent ;
267273 }
268- if ( parent . type === 'Element' ) {
269- break ;
274+
275+ if ( ! is_figure_parent ) {
276+ this . component . warn ( this , {
277+ code : 'a11y-structure' ,
278+ message : 'A11y: <figcaption> must be an immediate child of <figure>'
279+ } ) ;
270280 }
271- parent = parent . parent ;
272281 }
273282
274- if ( ! is_figure_parent ) {
275- this . component . warn ( this , {
276- code : 'a11y-structure' ,
277- message : 'A11y: <figcaption> must be an immediate child of <figure>'
283+ if ( this . name === 'figure' ) {
284+ const children = this . children . filter ( node => {
285+ if ( node . type === 'Comment' ) return false ;
286+ if ( node . type === 'Text' ) return / \S / . test ( node . data ) ;
287+ return true ;
278288 } ) ;
279- }
280- }
281-
282- if ( this . name === 'figure' ) {
283- const children = this . children . filter ( node => {
284- if ( node . type === 'Comment' ) return false ;
285- if ( node . type === 'Text' ) return / \S / . test ( node . data ) ;
286- return true ;
287- } ) ;
288289
289- const index = children . findIndex ( child => ( child as Element ) . name === 'figcaption' ) ;
290+ const index = children . findIndex ( child => ( child as Element ) . name === 'figcaption' ) ;
290291
291- if ( index !== - 1 && ( index !== 0 && index !== children . length - 1 ) ) {
292- this . component . warn ( children [ index ] , {
293- code : 'a11y-structure' ,
294- message : 'A11y: <figcaption> must be first or last child of <figure>'
295- } ) ;
292+ if ( index !== - 1 && ( index !== 0 && index !== children . length - 1 ) ) {
293+ this . component . warn ( children [ index ] , {
294+ code : 'a11y-structure' ,
295+ message : 'A11y: <figcaption> must be first or last child of <figure>'
296+ } ) ;
297+ }
296298 }
297299 }
298300
@@ -306,13 +308,50 @@ export default class Element extends Node {
306308 validate_attributes ( ) {
307309 const { component, parent } = this ;
308310
309- const attribute_map = new Map ( ) ;
310-
311311 this . attributes . forEach ( attribute => {
312312 if ( attribute . is_spread ) return ;
313313
314314 const name = attribute . name . toLowerCase ( ) ;
315315
316+ // Errors
317+
318+ if ( / ( ^ [ 0 - 9 - .] ) | [ \^ $ @ % & # ? ! | ( ) [ \] { } ^ * + ~ ; ] / . test ( name ) ) {
319+ component . error ( attribute , {
320+ code : 'illegal-attribute' ,
321+ message : `'${ name } ' is not a valid attribute name`
322+ } ) ;
323+ }
324+
325+ if ( name === 'slot' ) {
326+ if ( ! attribute . is_static ) {
327+ component . error ( attribute , {
328+ code : 'invalid-slot-attribute' ,
329+ message : 'slot attribute cannot have a dynamic value'
330+ } ) ;
331+ }
332+
333+ if ( component . slot_outlets . has ( name ) ) {
334+ component . error ( attribute , {
335+ code : 'duplicate-slot-attribute' ,
336+ message : `Duplicate '${ name } ' slot`
337+ } ) ;
338+
339+ component . slot_outlets . add ( name ) ;
340+ }
341+
342+ if ( ! ( parent . type === 'InlineComponent' || within_custom_element ( parent ) ) ) {
343+ component . error ( attribute , {
344+ code : 'invalid-slotted-content' ,
345+ message : 'Element with a slot=\'...\' attribute must be a child of a component or a descendant of a custom element'
346+ } ) ;
347+ }
348+ }
349+
350+ // The rest of the warnings don't apply to native elements
351+ if ( this . is_native ( ) ) return ;
352+
353+ // Warnings
354+
316355 // aria-props
317356 if ( name . startsWith ( 'aria-' ) ) {
318357 if ( invisible_elements . has ( this . name ) ) {
@@ -404,52 +443,20 @@ export default class Element extends Node {
404443 }
405444 }
406445
407-
408- if ( / ( ^ [ 0 - 9 - .] ) | [ \^ $ @ % & # ? ! | ( ) [ \] { } ^ * + ~ ; ] / . test ( name ) ) {
409- component . error ( attribute , {
410- code : 'illegal-attribute' ,
411- message : `'${ name } ' is not a valid attribute name`
412- } ) ;
413- }
414-
415- if ( name === 'slot' ) {
416- if ( ! attribute . is_static ) {
417- component . error ( attribute , {
418- code : 'invalid-slot-attribute' ,
419- message : 'slot attribute cannot have a dynamic value'
420- } ) ;
421- }
422-
423- if ( component . slot_outlets . has ( name ) ) {
424- component . error ( attribute , {
425- code : 'duplicate-slot-attribute' ,
426- message : `Duplicate '${ name } ' slot`
427- } ) ;
428-
429- component . slot_outlets . add ( name ) ;
430- }
431-
432- if ( ! ( parent . type === 'InlineComponent' || within_custom_element ( parent ) ) ) {
433- component . error ( attribute , {
434- code : 'invalid-slotted-content' ,
435- message : 'Element with a slot=\'...\' attribute must be a child of a component or a descendant of a custom element'
436- } ) ;
437- }
438- }
439-
440446 if ( name === 'is' ) {
441447 component . warn ( attribute , {
442448 code : 'avoid-is' ,
443449 message : 'The \'is\' attribute is not supported cross-browser and should be avoided'
444450 } ) ;
445451 }
446-
447- attribute_map . set ( attribute . name , attribute ) ;
448452 } ) ;
449453 }
450454
451455 validate_special_cases ( ) {
452456 const { component, attributes, handlers } = this ;
457+
458+ if ( this . is_native ( ) ) return ;
459+
453460 const attribute_map = new Map ( ) ;
454461 const handlers_map = new Map ( ) ;
455462
@@ -595,6 +602,16 @@ export default class Element extends Node {
595602 return value ;
596603 } ;
597604
605+ if ( this . is_native ( ) ) {
606+ this . bindings . forEach ( binding => {
607+ component . error ( binding , {
608+ code : 'invalid-binding' ,
609+ message : `'${ binding . name } ' is not a valid binding. Native elements only support bind:this`
610+ } ) ;
611+ } ) ;
612+ return ;
613+ }
614+
598615 this . bindings . forEach ( binding => {
599616 const { name } = binding ;
600617
@@ -754,7 +771,7 @@ export default class Element extends Node {
754771 }
755772
756773 validate_content ( ) {
757- if ( ! a11y_required_content . has ( this . name ) ) return ;
774+ if ( ! a11y_required_content . has ( this . name ) || this . is_native ( ) ) return ;
758775 if (
759776 this . bindings
760777 . some ( ( binding ) => [ 'textContent' , 'innerHTML' ] . includes ( binding . name ) )
@@ -831,6 +848,10 @@ export default class Element extends Node {
831848 return this . name === 'audio' || this . name === 'video' ;
832849 }
833850
851+ is_native ( ) {
852+ return this . namespace == namespaces [ 'native' ] ;
853+ }
854+
834855 add_css_class ( ) {
835856 if ( this . attributes . some ( attr => attr . is_spread ) ) {
836857 this . needs_manual_style_scoping = true ;
0 commit comments