@@ -131,7 +131,7 @@ export default class Element extends Node {
131
131
132
132
this . namespace = get_namespace ( parent , this , component . namespace ) ;
133
133
134
- if ( this . name === 'textarea' ) {
134
+ if ( this . name === 'textarea' && ! this . is_native ( ) ) {
135
135
if ( info . children . length > 0 ) {
136
136
const value_attribute = info . attributes . find ( node => node . name === 'value' ) ;
137
137
if ( value_attribute ) {
@@ -153,7 +153,7 @@ export default class Element extends Node {
153
153
}
154
154
}
155
155
156
- if ( this . name === 'option' ) {
156
+ if ( this . name === 'option' && ! this . is_native ( ) ) {
157
157
// Special case — treat these the same way:
158
158
// <option>{foo}</option>
159
159
// <option value={foo}>{foo}</option>
@@ -248,51 +248,53 @@ export default class Element extends Node {
248
248
} ) ;
249
249
}
250
250
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
+ }
258
259
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 ;
262
263
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 ;
267
273
}
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
+ } ) ;
270
280
}
271
- parent = parent . parent ;
272
281
}
273
282
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 ;
278
288
} ) ;
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
- } ) ;
288
289
289
- const index = children . findIndex ( child => ( child as Element ) . name === 'figcaption' ) ;
290
+ const index = children . findIndex ( child => ( child as Element ) . name === 'figcaption' ) ;
290
291
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
+ }
296
298
}
297
299
}
298
300
@@ -306,13 +308,50 @@ export default class Element extends Node {
306
308
validate_attributes ( ) {
307
309
const { component, parent } = this ;
308
310
309
- const attribute_map = new Map ( ) ;
310
-
311
311
this . attributes . forEach ( attribute => {
312
312
if ( attribute . is_spread ) return ;
313
313
314
314
const name = attribute . name . toLowerCase ( ) ;
315
315
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
+
316
355
// aria-props
317
356
if ( name . startsWith ( 'aria-' ) ) {
318
357
if ( invisible_elements . has ( this . name ) ) {
@@ -404,52 +443,20 @@ export default class Element extends Node {
404
443
}
405
444
}
406
445
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
-
440
446
if ( name === 'is' ) {
441
447
component . warn ( attribute , {
442
448
code : 'avoid-is' ,
443
449
message : 'The \'is\' attribute is not supported cross-browser and should be avoided'
444
450
} ) ;
445
451
}
446
-
447
- attribute_map . set ( attribute . name , attribute ) ;
448
452
} ) ;
449
453
}
450
454
451
455
validate_special_cases ( ) {
452
456
const { component, attributes, handlers } = this ;
457
+
458
+ if ( this . is_native ( ) ) return ;
459
+
453
460
const attribute_map = new Map ( ) ;
454
461
const handlers_map = new Map ( ) ;
455
462
@@ -595,6 +602,16 @@ export default class Element extends Node {
595
602
return value ;
596
603
} ;
597
604
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
+
598
615
this . bindings . forEach ( binding => {
599
616
const { name } = binding ;
600
617
@@ -754,7 +771,7 @@ export default class Element extends Node {
754
771
}
755
772
756
773
validate_content ( ) {
757
- if ( ! a11y_required_content . has ( this . name ) ) return ;
774
+ if ( ! a11y_required_content . has ( this . name ) || this . is_native ( ) ) return ;
758
775
if (
759
776
this . bindings
760
777
. some ( ( binding ) => [ 'textContent' , 'innerHTML' ] . includes ( binding . name ) )
@@ -831,6 +848,10 @@ export default class Element extends Node {
831
848
return this . name === 'audio' || this . name === 'video' ;
832
849
}
833
850
851
+ is_native ( ) {
852
+ return this . namespace == namespaces [ 'native' ] ;
853
+ }
854
+
834
855
add_css_class ( ) {
835
856
if ( this . attributes . some ( attr => attr . is_spread ) ) {
836
857
this . needs_manual_style_scoping = true ;
0 commit comments