@@ -33,6 +33,7 @@ import {
3333import { ParseError } from "../../errors" ;
3434import type { ScriptLetCallback } from "../../context/script-let" ;
3535import type { AttributeToken } from "../html" ;
36+ import { svelteVersion } from "../svelte-version" ;
3637
3738/** Convert for Attributes */
3839export function * convertAttributes (
@@ -200,6 +201,7 @@ function convertAttribute(
200201 processAttributeValue (
201202 node . value as ( SvAST . Text | SvAST . MustacheTag ) [ ] ,
202203 attribute ,
204+ parent ,
203205 ctx ,
204206 ) ;
205207
@@ -213,27 +215,51 @@ function convertAttribute(
213215function processAttributeValue (
214216 nodeValue : ( SvAST . Text | SvAST . MustacheTag ) [ ] ,
215217 attribute : SvelteAttribute | SvelteStyleDirectiveLongform ,
218+ attributeParent : ( SvelteAttribute | SvelteStyleDirectiveLongform ) [ "parent" ] ,
216219 ctx : Context ,
217220) {
218- for ( let index = 0 ; index < nodeValue . length ; index ++ ) {
219- const v = nodeValue [ index ] ;
220- if ( v . type === "Text" ) {
221- if ( v . start === v . end ) {
222- // Empty
221+ const nodes = nodeValue
222+ . filter (
223+ ( v ) =>
224+ v . type !== "Text" ||
225+ // ignore empty
223226 // https://github.com/sveltejs/svelte/pull/6539
224- continue ;
225- }
226- const next = nodeValue [ index + 1 ] ;
227- if ( next && next . start < v . end ) {
228- // Maybe bug in Svelte can cause the completion index to shift.
229- // console.log(ctx.getText(v), v.data)
230- v . end = next . start ;
227+ v . start < v . end ,
228+ )
229+ . map ( ( v , index , array ) => {
230+ if ( v . type === "Text" ) {
231+ const next = array [ index + 1 ] ;
232+ if ( next && next . start < v . end ) {
233+ // Maybe bug in Svelte can cause the completion index to shift.
234+ return {
235+ ...v ,
236+ end : next . start ,
237+ } ;
238+ }
231239 }
240+ return v ;
241+ } ) ;
242+ if (
243+ nodes . length === 1 &&
244+ nodes [ 0 ] . type === "MustacheTag" &&
245+ attribute . type === "SvelteAttribute"
246+ ) {
247+ const typing = buildAttributeType (
248+ attributeParent . parent ,
249+ attribute . key . name ,
250+ ctx ,
251+ ) ;
252+ const mustache = convertMustacheTag ( nodes [ 0 ] , attribute , typing , ctx ) ;
253+ attribute . value . push ( mustache ) ;
254+ return ;
255+ }
256+ for ( const v of nodes ) {
257+ if ( v . type === "Text" ) {
232258 attribute . value . push ( convertTextToLiteral ( v , attribute , ctx ) ) ;
233259 continue ;
234260 }
235261 if ( v . type === "MustacheTag" ) {
236- const mustache = convertMustacheTag ( v , attribute , ctx ) ;
262+ const mustache = convertMustacheTag ( v , attribute , null , ctx ) ;
237263 attribute . value . push ( mustache ) ;
238264 continue ;
239265 }
@@ -246,6 +272,47 @@ function processAttributeValue(
246272 }
247273}
248274
275+ /** Build attribute type */
276+ function buildAttributeType (
277+ element : SvelteElement | SvelteScriptElement | SvelteStyleElement ,
278+ attrName : string ,
279+ ctx : Context ,
280+ ) {
281+ if (
282+ svelteVersion . gte ( 5 ) &&
283+ attrName . startsWith ( "on" ) &&
284+ ( element . type !== "SvelteElement" || element . kind === "html" )
285+ ) {
286+ return buildEventHandlerType ( element , attrName . slice ( 2 ) , ctx ) ;
287+ }
288+ if ( element . type !== "SvelteElement" || element . kind !== "component" ) {
289+ return null ;
290+ }
291+ const elementName = ctx . elements . get ( element ) ! . name ;
292+ const componentPropsType = `import('svelte').ComponentProps<${ elementName } >` ;
293+ return conditional ( {
294+ check : `'${ attrName } '` ,
295+ extends : `infer PROP` ,
296+ true : conditional ( {
297+ check : `PROP` ,
298+ extends : `keyof ${ componentPropsType } ` ,
299+ true : `${ componentPropsType } [PROP]` ,
300+ false : `never` ,
301+ } ) ,
302+ false : `never` ,
303+ } ) ;
304+
305+ /** Generate `C extends E ? T : F` type. */
306+ function conditional ( types : {
307+ check : string ;
308+ extends : string ;
309+ true : string ;
310+ false : string ;
311+ } ) {
312+ return `${ types . check } extends ${ types . extends } ?(${ types . true } ):(${ types . false } )` ;
313+ }
314+ }
315+
249316/** Convert for Spread */
250317function convertSpreadAttribute (
251318 node : SvAST . Spread ,
@@ -491,7 +558,7 @@ function convertStyleDirective(
491558 end : keyName . range [ 1 ] ,
492559 } ) ;
493560
494- processAttributeValue ( node . value , directive , ctx ) ;
561+ processAttributeValue ( node . value , directive , parent , ctx ) ;
495562
496563 return directive ;
497564}
0 commit comments