@@ -15,6 +15,11 @@ import { parseScriptWithoutAnalyzeScope } from "../../script";
1515import { VirtualTypeScriptContext } from "../context" ;
1616import type { TSESParseForESLintResult } from "../types" ;
1717import type ESTree from "estree" ;
18+ import type { SvelteAttribute , SvelteHTMLElement } from "../../../ast" ;
19+
20+ export type AnalyzeTypeScriptContext = {
21+ slots : Set < SvelteHTMLElement > ;
22+ } ;
1823
1924const RESERVED_NAMES = new Set < string > ( [ "$$props" , "$$restProps" , "$$slots" ] ) ;
2025/**
@@ -25,7 +30,8 @@ const RESERVED_NAMES = new Set<string>(["$$props", "$$restProps", "$$slots"]);
2530export function analyzeTypeScript (
2631 code : { script : string ; render : string } ,
2732 attrs : Record < string , string | undefined > ,
28- parserOptions : any
33+ parserOptions : any ,
34+ context : AnalyzeTypeScriptContext
2935) : VirtualTypeScriptContext {
3036 const ctx = new VirtualTypeScriptContext ( code . script + code . render ) ;
3137 ctx . appendOriginal ( / ^ \s * / u. exec ( code . script ) ! [ 0 ] . length ) ;
@@ -44,6 +50,8 @@ export function analyzeTypeScript(
4450
4551 analyzeStoreReferenceNames ( result , ctx ) ;
4652
53+ analyzeDollarDollarVariables ( result , ctx , context . slots ) ;
54+
4755 analyzeReactiveScopes ( result , ctx ) ;
4856
4957 analyzeRenderScopes ( code , ctx ) ;
@@ -138,6 +146,104 @@ function analyzeStoreReferenceNames(
138146 }
139147}
140148
149+ /**
150+ * Analyze `$$slots`, `$$props`, and `$$restProps` .
151+ * Insert type definitions code to provide correct type information for `$$slots`, `$$props`, and `$$restProps`.
152+ */
153+ function analyzeDollarDollarVariables (
154+ result : TSESParseForESLintResult ,
155+ ctx : VirtualTypeScriptContext ,
156+ slots : Set < SvelteHTMLElement >
157+ ) {
158+ const scopeManager = result . scopeManager ;
159+
160+ if (
161+ scopeManager . globalScope ! . through . some (
162+ ( reference ) => reference . identifier . name === "$$props"
163+ )
164+ ) {
165+ appendDeclareVirtualScript ( "$$props" , `{ [index: string]: any }` ) ;
166+ }
167+ if (
168+ scopeManager . globalScope ! . through . some (
169+ ( reference ) => reference . identifier . name === "$$restProps"
170+ )
171+ ) {
172+ appendDeclareVirtualScript ( "$$restProps" , `{ [index: string]: any }` ) ;
173+ }
174+ if (
175+ scopeManager . globalScope ! . through . some (
176+ ( reference ) => reference . identifier . name === "$$slots"
177+ )
178+ ) {
179+ const nameTypes = new Set < string > ( ) ;
180+ for ( const slot of slots ) {
181+ const nameAttr = slot . startTag . attributes . find (
182+ ( attr ) : attr is SvelteAttribute =>
183+ attr . type === "SvelteAttribute" && attr . key . name === "name"
184+ ) ;
185+ if ( ! nameAttr || nameAttr . value . length === 0 ) {
186+ nameTypes . add ( '"default"' ) ;
187+ continue ;
188+ }
189+
190+ if ( nameAttr . value . length === 1 ) {
191+ const value = nameAttr . value [ 0 ] ;
192+ if ( value . type === "SvelteLiteral" ) {
193+ nameTypes . add ( JSON . stringify ( value . value ) ) ;
194+ } else {
195+ nameTypes . add ( "string" ) ;
196+ }
197+ continue ;
198+ }
199+ nameTypes . add (
200+ `\`${ nameAttr . value
201+ . map ( ( value ) =>
202+ value . type === "SvelteLiteral"
203+ ? value . value . replace ( / ( [ $ ` ] ) / gu, "\\$1" )
204+ : "${string}"
205+ )
206+ . join ( "" ) } \``
207+ ) ;
208+ }
209+
210+ appendDeclareVirtualScript (
211+ "$$slots" ,
212+ `Record<${
213+ nameTypes . size > 0 ? [ ...nameTypes ] . join ( " | " ) : "any"
214+ } , boolean>`
215+ ) ;
216+ }
217+
218+ /** Append declare virtual script */
219+ function appendDeclareVirtualScript ( name : string , type : string ) {
220+ ctx . appendVirtualScript ( `declare let ${ name } : ${ type } ;` ) ;
221+ ctx . restoreContext . addRestoreStatementProcess ( ( node , result ) => {
222+ if (
223+ node . type !== "VariableDeclaration" ||
224+ ! node . declare ||
225+ node . declarations . length !== 1 ||
226+ node . declarations [ 0 ] . id . type !== "Identifier" ||
227+ node . declarations [ 0 ] . id . name !== name
228+ ) {
229+ return false ;
230+ }
231+ const program = result . ast ;
232+ program . body . splice ( program . body . indexOf ( node ) , 1 ) ;
233+
234+ const scopeManager = result . scopeManager as ScopeManager ;
235+
236+ // Remove `declare` variable
237+ removeAllScopeAndVariableAndReference ( node , {
238+ visitorKeys : result . visitorKeys ,
239+ scopeManager,
240+ } ) ;
241+
242+ return true ;
243+ } ) ;
244+ }
245+ }
246+
141247/**
142248 * Analyze the reactive scopes.
143249 * Transform source code to provide the correct type information in the `$:` statements.
0 commit comments