@@ -33,6 +33,7 @@ import {
33
33
import { ParseError } from "../../errors" ;
34
34
import type { ScriptLetCallback } from "../../context/script-let" ;
35
35
import type { AttributeToken } from "../html" ;
36
+ import { svelteVersion } from "../svelte-version" ;
36
37
37
38
/** Convert for Attributes */
38
39
export function * convertAttributes (
@@ -200,6 +201,7 @@ function convertAttribute(
200
201
processAttributeValue (
201
202
node . value as ( SvAST . Text | SvAST . MustacheTag ) [ ] ,
202
203
attribute ,
204
+ parent ,
203
205
ctx ,
204
206
) ;
205
207
@@ -213,27 +215,51 @@ function convertAttribute(
213
215
function processAttributeValue (
214
216
nodeValue : ( SvAST . Text | SvAST . MustacheTag ) [ ] ,
215
217
attribute : SvelteAttribute | SvelteStyleDirectiveLongform ,
218
+ attributeParent : ( SvelteAttribute | SvelteStyleDirectiveLongform ) [ "parent" ] ,
216
219
ctx : Context ,
217
220
) {
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
223
226
// 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
+ }
231
239
}
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" ) {
232
258
attribute . value . push ( convertTextToLiteral ( v , attribute , ctx ) ) ;
233
259
continue ;
234
260
}
235
261
if ( v . type === "MustacheTag" ) {
236
- const mustache = convertMustacheTag ( v , attribute , ctx ) ;
262
+ const mustache = convertMustacheTag ( v , attribute , null , ctx ) ;
237
263
attribute . value . push ( mustache ) ;
238
264
continue ;
239
265
}
@@ -246,6 +272,47 @@ function processAttributeValue(
246
272
}
247
273
}
248
274
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
+
249
316
/** Convert for Spread */
250
317
function convertSpreadAttribute (
251
318
node : SvAST . Spread ,
@@ -491,7 +558,7 @@ function convertStyleDirective(
491
558
end : keyName . range [ 1 ] ,
492
559
} ) ;
493
560
494
- processAttributeValue ( node . value , directive , ctx ) ;
561
+ processAttributeValue ( node . value , directive , parent , ctx ) ;
495
562
496
563
return directive ;
497
564
}
0 commit comments