import MagicString from 'magic-string' ; import svgAttributes from '../svgattributes' ; import { buildTemplateString } from '../utils/node-utils' ; import { Attribute , BaseNode } from '../../interfaces' ; /**
 * List taken from `svelte-jsx.d.ts` by searching for all attributes of type number
 */ const numberOnlyAttributes = new Set ( [ 'cols' , 'colspan' , 'currenttime' , 'defaultplaybackrate' , 'high' , 'low' , 'marginheight' , 'marginwidth' , 'minlength' , 'maxlength' , 'optimum' , 'rows' , 'rowspan' , 'size' , 'span' , 'start' , 'tabindex' , 'results' , 'volume' ] ) ; /**
 * Handle various kinds of attributes and make them conform to JSX.
 * - {x}   --->    x={x}
 * - x="{..}"   --->    x={..}
 * - lowercase DOM attributes
 * - multi-value handling
 */ export function handleAttribute ( htmlx , str , attr , parent , preserveCase ) { const shouldApplySlotCheck = parent . type === 'Slot' && attr . name !== 'name' ; const slotName = shouldApplySlotCheck ? parent . attributes ?. find ( ( a ) => a . name === 'name' ) ?. value [ 0 ] ?. data || 'default' : undefined ; const ensureSlotStr = `__sveltets_ensureSlot("${ slotName }","${ attr . name }",` ; let transformedFromDirectiveOrNamespace = false ; const transformAttributeCase = ( name ) => { if ( ! preserveCase && ! svgAttributes . find ( ( x ) => x == name ) ) { return name . toLowerCase ( ) ; } else { return name ; } } ; if ( parent . type == 'Element' ) { const sapperLinkActions = [ 'sapper:prefetch' , 'sapper:noscroll' ] ; const sveltekitLinkActions = [ 'sveltekit:prefetch' , 'sveltekit:noscroll' ] ; if ( ( attr . value !== true && ! ( attr . value . length && attr . value . length == 1 && attr . value [ 0 ] . type == 'AttributeShorthand' ) ) || sapperLinkActions . includes ( attr . name ) || sveltekitLinkActions . includes ( attr . name ) ) { let name = transformAttributeCase ( attr . name ) ; const colonIndex = name . indexOf ( ':' ) ; if ( colonIndex >= 0 ) { const parts = name . split ( ':' ) ; name = parts [ 0 ] + parts [ 1 ] [ 0 ] . toUpperCase ( ) + parts [ 1 ] . substring ( 1 ) ; } str . overwrite ( attr . start , attr . start + attr . name . length , name ) ; transformedFromDirectiveOrNamespace = true ; } } if ( parent . type === 'InlineComponent' && attr . name . startsWith ( '--' ) && attr . value !== true ) { str . prependRight ( attr . start , '{...__sveltets_1_cssProp({"' ) ; buildTemplateString ( attr , str , htmlx , '": `' , '`})}' ) ; return ; } if ( attr . value === true ) { if ( parent . type === 'Element' && ! transformedFromDirectiveOrNamespace && parent . name !== '!DOCTYPE' ) { str . overwrite ( attr . start , attr . end , transformAttributeCase ( attr . name ) ) ; } return ; } if ( attr . value . length == 0 ) { return ; } if ( attr . value . length == 1 ) { const attrVal = attr . value [ 0 ] ; if ( attr . name == 'slot' ) { str . remove ( attr . start , attr . end ) ; return ; } if ( attrVal . type == 'AttributeShorthand' ) { let attrName = attrVal . expression . name ; if ( parent . type == 'Element' ) { attrName = transformAttributeCase ( attrName ) ; } str . appendRight ( attr . start , `${ attrName }=` ) ; if ( shouldApplySlotCheck ) { str . prependRight ( attr . start + 1 , ensureSlotStr ) ; str . prependLeft ( attr . end - 1 , ')' ) ; } return ; } const equals = htmlx . lastIndexOf ( '=' , attrVal . start ) ; const sanitizedName = sanitizeLeadingChars ( attr . name ) ; if ( sanitizedName !== attr . name ) { str . overwrite ( attr . start , equals , sanitizedName ) ; } if ( attrVal . type == 'Text' ) { const endsWithQuote = htmlx . lastIndexOf ( '"' , attrVal . end ) === attrVal . end - 1 || htmlx . lastIndexOf ( "'" , attrVal . end ) === attrVal . end - 1 ; const needsQuotes = attrVal . end == attr . end && ! endsWithQuote ; const hasBrackets = htmlx . lastIndexOf ( '}' , attrVal . end ) === attrVal . end - 1 || htmlx . lastIndexOf ( '}"' , attrVal . end ) === attrVal . end - 1 || htmlx . lastIndexOf ( "}'" , attrVal . end ) === attrVal . end - 1 ; const needsNumberConversion = ! hasBrackets && parent . type === 'Element' && numberOnlyAttributes . has ( attr . name . toLowerCase ( ) ) && ! isNaN ( attrVal . data ) ; if ( needsNumberConversion ) { const begin = '{' + ( shouldApplySlotCheck ? ensureSlotStr : '' ) ; const end = shouldApplySlotCheck ? ')}' : '}' ; if ( needsQuotes ) { str . prependRight ( equals + 1 , begin ) ; str . appendLeft ( attr . end , end ) ; } else { str . overwrite ( equals + 1 , equals + 2 , begin ) ; str . overwrite ( attr . end - 1 , attr . end , end ) ; } } else if ( needsQuotes ) { const begin = shouldApplySlotCheck ? `{${ ensureSlotStr }"` : '"' ; const end = shouldApplySlotCheck ? '")}' : '"' ; str . prependRight ( equals + 1 , begin ) ; str . appendLeft ( attr . end , end ) ; } else if ( shouldApplySlotCheck ) { str . prependRight ( equals + 1 , `{${ ensureSlotStr }` ) ; str . appendLeft ( attr . end , ')}' ) ; } return ; } if ( attrVal . type == 'MustacheTag' ) { const isInQuotes = attrVal . end != attr . end ; if ( isInQuotes ) { str . remove ( attrVal . start - 1 , attrVal . start ) ; str . remove ( attr . end - 1 , attr . end ) ; } if ( shouldApplySlotCheck ) { str . prependRight ( attrVal . start + 1 , ensureSlotStr ) ; str . appendLeft ( attr . end - ( isInQuotes ? 2 : 1 ) , ')' ) ; } return ; } return ; } buildTemplateString ( attr , str , htmlx , shouldApplySlotCheck ? `={${ ensureSlotStr }\`` : '={`' , shouldApplySlotCheck ? '`)}' : '`}' ) ; } function sanitizeLeadingChars ( attrName ) { let sanitizedName = '' ; for ( let i = 0 ; i < attrName . length ; i ++ ) { if ( /[A-Za-z$_]/ . test ( attrName [ i ] ) ) { sanitizedName += attrName . substr ( i ) ; return sanitizedName ; } else { sanitizedName += '_' ; } } return sanitizedName ; }