@ -191,11 +191,24 @@ export default function tag(parser) {
} ;
}
/** @type {Set<string>} */
const unique _names = new Set ( ) ;
/** @type {string[]} */
const unique _names = [ ] ;
const current = parser . current ( ) ;
const is _top _level _script _or _style =
( name === 'script' || name === 'style' ) && current . type === 'Root' ;
const read = is _top _level _script _or _style ? read _static _attribute : read _attribute ;
let attribute ;
while ( ( attribute = read _attribute ( parser , unique _names ) ) ) {
while ( ( attribute = read ( parser ) ) ) {
if (
( attribute . type === 'Attribute' || attribute . type === 'BindDirective' ) &&
unique _names . includes ( attribute . name )
) {
error ( attribute . start , 'duplicate-attribute' ) ;
}
element . attributes . push ( attribute ) ;
parser . allow _whitespace ( ) ;
}
@ -245,10 +258,7 @@ export default function tag(parser) {
: chunk . expression ;
}
const current = parser . current ( ) ;
// special cases – top-level <script> and <style>
if ( ( name === 'script' || name === 'style' ) && current . type === 'Root' ) {
if ( is _top _level _script _or _style ) {
parser . eat ( '>' , true ) ;
if ( name === 'script' ) {
const content = read _script ( parser , start , element . attributes ) ;
@ -372,23 +382,61 @@ function read_tag_name(parser) {
// eslint-disable-next-line no-useless-escape
const regex _token _ending _character = /[\s=\/>"']/ ;
const regex _starts _with _quote _characters = /^["']/ ;
const regex _attribute _value = /^(?:"([^"]*)"|'([^'])*'|([^>\s]))/ ;
/ * *
* @ param { import ( '../index.js' ) . Parser } parser
* @ param { Set < string > } unique _names
* @ returns { any }
* @ returns { import ( '#compiler' ) . Attribute | null }
* /
function read _ attribute( parser , unique _names ) {
function read _ static_ attribute( parser ) {
const start = parser . index ;
/** @param {string} name */
function check _unique ( name ) {
if ( unique _names . has ( name ) ) {
error ( start , 'duplicate-attribute' ) ;
const name = parser . read _until ( regex _token _ending _character ) ;
if ( ! name ) return null ;
/** @type {true | Array<import('#compiler').Text | import('#compiler').ExpressionTag>} */
let value = true ;
if ( parser . eat ( '=' ) ) {
parser . allow _whitespace ( ) ;
let raw = parser . match _regex ( regex _attribute _value ) ;
if ( ! raw ) {
error ( parser . index , 'missing-attribute-value' ) ;
}
parser . index += raw . length ;
const quoted = raw [ 0 ] === '"' || raw [ 0 ] === "'" ;
if ( quoted ) {
raw = raw . slice ( 1 , - 1 ) ;
}
unique _names . add ( name ) ;
value = [
{
start : parser . index - raw . length - ( quoted ? 1 : 0 ) ,
end : quoted ? parser . index - 1 : parser . index ,
type : 'Text' ,
raw : raw ,
data : decode _character _references ( raw , true ) ,
parent : null
}
] ;
}
if ( parser . match _regex ( regex _starts _with _quote _characters ) ) {
error ( parser . index , 'expected-token' , '=' ) ;
}
return create _attribute ( name , start , parser . index , value ) ;
}
/ * *
* @ param { import ( '../index.js' ) . Parser } parser
* @ returns { import ( '#compiler' ) . Attribute | import ( '#compiler' ) . SpreadAttribute | import ( '#compiler' ) . Directive | null }
* /
function read _attribute ( parser ) {
const start = parser . index ;
if ( parser . eat ( '{' ) ) {
parser . allow _whitespace ( ) ;
@ -419,8 +467,6 @@ function read_attribute(parser, unique_names) {
error ( start , 'empty-attribute-shorthand' ) ;
}
check _unique ( name ) ;
parser . allow _whitespace ( ) ;
parser . eat ( '}' , true ) ;
@ -473,12 +519,6 @@ function read_attribute(parser, unique_names) {
error ( start + colon _index + 1 , 'empty-directive-name' , type ) ;
}
if ( type === 'BindDirective' && directive _name !== 'this' ) {
check _unique ( directive _name ) ;
} else if ( type !== 'OnDirective' && type !== 'UseDirective' ) {
check _unique ( name ) ;
}
if ( type === 'StyleDirective' ) {
return {
start ,
@ -546,8 +586,6 @@ function read_attribute(parser, unique_names) {
return directive ;
}
check _unique ( name ) ;
return create _attribute ( name , start , end , value ) ;
}
@ -569,7 +607,6 @@ function get_directive_type(name) {
/ * *
* @ param { import ( '../index.js' ) . Parser } parser
* @ returns { any [ ] }
* /
function read _attribute _value ( parser ) {
const quote _mark = parser . eat ( "'" ) ? "'" : parser . eat ( '"' ) ? '"' : null ;