@@ -34,6 +34,28 @@ function applyImports(markup: string, imports: string): string {
3434 return `<script>\n${ imports } \n</script>\n` + markup ;
3535}
3636
37+ function getTransitiveDependencies (
38+ entryPoints : Set < string > ,
39+ dependencyGraph : Map < string , Set < string > >
40+ ) : Set < string > {
41+ const resolved = new Set < string > ( ) ;
42+ const queue = [ ...entryPoints ] ;
43+
44+ while ( queue . length > 0 ) {
45+ const current = queue . shift ( ) ! ;
46+ if ( resolved . has ( current ) ) continue ;
47+
48+ resolved . add ( current ) ;
49+ const dependencies = dependencyGraph . get ( current ) ;
50+ if ( dependencies ) {
51+ for ( const dep of dependencies ) {
52+ queue . push ( dep ) ;
53+ }
54+ }
55+ }
56+ return resolved ;
57+ }
58+
3759/* ───────── plugin ───────── */
3860
3961export default function inlineSveltePlugin ( {
@@ -47,16 +69,15 @@ export default function inlineSveltePlugin({
4769 const tplRE = new RegExp ( `(?:${ tagGroup } )\\s*\`([\\s\\S]*?)\`` , "g" ) ;
4870 const fenceRE = new RegExp ( `${ esc ( fenceStart ) } ([\\s\\S]*?)${ esc ( fenceEnd ) } ` , "m" ) ;
4971 const globalsRE = new RegExp ( `${ esc ( globalsStart ) } ([\\s\\S]*?)${ esc ( globalsEnd ) } ` , "m" ) ;
50- // FIX: Simplified the regex to remove a problematic negative lookahead that was preventing multiple matches .
51- const globalDefWithTplRE = new RegExp (
52- `( const\\s+([a-zA-Z0-9_$]+)\\s*=\\s*)(( ?:${ tagGroup } )\\s*\`[\\s\\S]*?\`)` ,
53- "g "
72+ // FIX: New, safer regex anchored to the start of a line .
73+ const globalDefRE = new RegExp (
74+ `^ const\\s+([a-zA-Z0-9_$]+)\\s*=\\s*( ?:${ tagGroup } )\\s*( \`[\\s\\S]*?\`)` ,
75+ "gm "
5476 ) ;
5577
5678 const VIRT = "virtual:inline-svelte/" ;
5779 const RSLV = "\0" + VIRT ;
5880
59- /** virtualId → full markup (with injected imports) */
6081 const cache = new Map < string , string > ( ) ;
6182
6283 return {
@@ -74,8 +95,8 @@ export default function inlineSveltePlugin({
7495 let importsToAdd = "" ;
7596 let globalImportsForTpl = "" ;
7697 let hoistedCode = "" ;
77- const hashToLocal = new Map < string , string > ( ) ;
7898 const globalVarDefs = new Map < string , string > ( ) ;
99+ const hashToLocal = new Map < string , string > ( ) ;
79100 const globalComponentNames = new Set < string > ( ) ;
80101
81102 /* 1. Process globals */
@@ -84,42 +105,86 @@ export default function inlineSveltePlugin({
84105 edited = true ;
85106 const globalsContent = globalsMatch [ 1 ] ?? "" ;
86107 ms . overwrite ( globalsMatch . index , globalsMatch . index + globalsMatch [ 0 ] . length , "" ) ;
108+ hoistedCode = globalsContent ;
87109
88- globalDefWithTplRE . lastIndex = 0 ;
89-
90- hoistedCode = globalsContent . replace (
91- globalDefWithTplRE ,
92- ( match , declaration : string , compName : string , templateLiteral : string ) => {
93- globalComponentNames . add ( compName ) ;
94- const rawMarkup = templateLiteral . match ( / ` ( [ \s \S ] * ? ) ` / ) [ 1 ] ;
95- const markup = applyImports ( rawMarkup , imports ) ;
96- const hash = createHash ( "sha1" ) . update ( markup ) . digest ( "hex" ) . slice ( 0 , 8 ) ;
97-
98- let local = hashToLocal . get ( hash ) ;
99- if ( ! local ) {
100- local = `Inline_ ${ hash } ` ;
101- hashToLocal . set ( hash , local ) ;
102- const virt = `${ VIRT } ${ hash } .js ` ;
103- if ( ! cache . has ( virt ) ) cache . set ( virt , markup ) ;
104- const ns = `__InlineNS_ ${ hash } ` ;
105- importsToAdd += `import * as ${ ns } from ' ${ virt } ';\nconst ${ local } =Object.assign( ${ ns } .default, ${ ns } );\n` ;
106- globalImportsForTpl + = `import ${ compName } from ' ${ virt } ';\n ` ;
107- }
108- return ` ${ declaration } ${ local } `;
110+ // FIX: Refactored component processing to use a more stable while/exec loop.
111+ const replacements = [ ] ;
112+ let match ;
113+ globalDefRE . lastIndex = 0 ;
114+ while ( ( match = globalDefRE . exec ( globalsContent ) ) !== null ) {
115+ const [ fullMatch , compName , templateLiteralWithTicks ] = match ;
116+ globalComponentNames . add ( compName ) ;
117+
118+ const rawMarkup = templateLiteralWithTicks . slice ( 1 , - 1 ) ;
119+ const markup = applyImports ( rawMarkup , imports ) ;
120+ const hash = createHash ( "sha1" ) . update ( markup ) . digest ( "hex" ) . slice ( 0 , 8 ) ;
121+
122+ let local = hashToLocal . get ( hash ) ;
123+ if ( ! local ) {
124+ local = `Inline_ ${ hash } ` ;
125+ hashToLocal . set ( hash , local ) ;
126+ const virt = `${ VIRT } ${ hash } .js ` ;
127+ if ( ! cache . has ( virt ) ) cache . set ( virt , markup ) ;
128+ const ns = `__InlineNS_ ${ hash } ` ;
129+ importsToAdd += `import * as ${ ns } from ' ${ virt } ';\nconst ${ local } =Object.assign( ${ ns } .default, ${ ns } );\n` ;
130+ globalImportsForTpl += `import ${ compName } from ' ${ virt } ';\n `;
109131 }
110- ) ;
111132
112- const varLines = hoistedCode
113- . split ( "\n" )
114- . filter ( line => line . trim ( ) . match ( / ^ ( c o n s t | l e t | v a r ) \s / ) ) ;
115- for ( const line of varLines ) {
116- const nameMatch = line . match ( / (?: c o n s t | l e t | v a r ) \s + ( [ a - z A - Z 0 - 9 _ $ ] + ) / ) ;
117- if ( nameMatch ) {
118- globalVarDefs . set ( nameMatch [ 1 ] , line ) ;
133+ replacements . push ( {
134+ start : match . index ,
135+ end : match . index + fullMatch . length ,
136+ newText : `const ${ compName } = ${ local } ` ,
137+ } ) ;
138+ }
139+
140+ // Apply replacements from end to start to avoid index shifting
141+ for ( const rep of replacements . reverse ( ) ) {
142+ hoistedCode = hoistedCode . slice ( 0 , rep . start ) + rep . newText + hoistedCode . slice ( rep . end ) ;
143+ }
144+
145+ const startOfDeclRegex = / ^ (?: c o n s t | l e t | v a r ) \s + ( [ a - z A - Z 0 - 9 _ $ ] + ) / gm;
146+ let declMatch ;
147+ while ( ( declMatch = startOfDeclRegex . exec ( hoistedCode ) ) !== null ) {
148+ const varName = declMatch [ 1 ] ;
149+ const startIndex = declMatch . index ;
150+ let braceDepth = 0 ,
151+ bracketDepth = 0 ,
152+ parenDepth = 0 ,
153+ endIndex = - 1 ;
154+ for ( let i = startIndex ; i < hoistedCode . length ; i ++ ) {
155+ const char = hoistedCode [ i ] ;
156+ if ( char === "{" ) braceDepth ++ ;
157+ else if ( char === "}" ) braceDepth -- ;
158+ else if ( char === "[" ) bracketDepth ++ ;
159+ else if ( char === "]" ) bracketDepth -- ;
160+ else if ( char === "(" ) parenDepth ++ ;
161+ else if ( char === ")" ) parenDepth -- ;
162+ else if ( char === ";" && braceDepth === 0 && bracketDepth === 0 && parenDepth === 0 ) {
163+ endIndex = i + 1 ;
164+ break ;
165+ }
119166 }
167+ if ( endIndex === - 1 ) {
168+ const nextNewline = hoistedCode . indexOf ( "\n" , startIndex ) ;
169+ endIndex = nextNewline !== - 1 ? nextNewline : hoistedCode . length ;
170+ }
171+ const definition = hoistedCode . substring ( startIndex , endIndex ) . trim ( ) ;
172+ if ( definition ) globalVarDefs . set ( varName , definition ) ;
173+ startOfDeclRegex . lastIndex = endIndex ;
120174 }
121175 }
122176
177+ const depGraph = new Map < string , Set < string > > ( ) ;
178+ const allGlobalNames = [ ...globalVarDefs . keys ( ) ] ;
179+ for ( const [ name , definition ] of globalVarDefs . entries ( ) ) {
180+ const dependencies = new Set < string > ( ) ;
181+ for ( const depName of allGlobalNames ) {
182+ if ( name === depName ) continue ;
183+ if ( new RegExp ( `\\b${ depName } \\b` ) . test ( definition ) ) dependencies . add ( depName ) ;
184+ }
185+ depGraph . set ( name , dependencies ) ;
186+ }
187+
123188 /* 2. Process all regular templates */
124189 let m : RegExpExecArray | null ;
125190 tplRE . lastIndex = 0 ;
@@ -128,22 +193,26 @@ export default function inlineSveltePlugin({
128193 globalsMatch &&
129194 m . index >= globalsMatch . index &&
130195 m . index < globalsMatch . index + globalsMatch [ 0 ] . length
131- ) {
196+ )
132197 continue ;
133- }
134198
135199 const rawMarkup = m [ 1 ] ;
136- const scriptContentRE = / < s c r i p t .* ?> ( [ \s \S ] * ?) < \/ s c r i p t > / ;
137- const existingScriptContent = rawMarkup . match ( scriptContentRE ) ?. [ 1 ] ?? "" ;
200+ const directDeps = new Set < string > ( ) ;
201+ for ( const name of allGlobalNames ) {
202+ if ( new RegExp ( `\\b${ name } \\b` ) . test ( rawMarkup ) ) directDeps . add ( name ) ;
203+ }
204+ const allDeps = getTransitiveDependencies ( directDeps , depGraph ) ;
138205 let scriptToInject = globalImportsForTpl ;
206+ const existingScriptContent = rawMarkup . match ( / < s c r i p t .* ?> ( [ \s \S ] * ?) < \/ s c r i p t > / ) ?. [ 1 ] ?? "" ;
207+ const sortedDeps = [ ...allDeps ] . sort (
208+ ( a , b ) => allGlobalNames . indexOf ( a ) - allGlobalNames . indexOf ( b )
209+ ) ;
139210
140- for ( const [ name , definition ] of globalVarDefs . entries ( ) ) {
211+ for ( const name of sortedDeps ) {
141212 if ( globalComponentNames . has ( name ) ) continue ;
142-
143- const isUsedInTemplate = new RegExp ( `\\b${ name } \\b` ) . test ( rawMarkup ) ;
144- if ( isUsedInTemplate && ! existingScriptContent . includes ( name ) ) {
145- scriptToInject += `\n${ definition } ` ;
146- }
213+ if ( existingScriptContent . includes ( name ) ) continue ;
214+ const definition = globalVarDefs . get ( name ) ;
215+ if ( definition ) scriptToInject += `\n${ definition } ` ;
147216 }
148217
149218 const markupWithGlobals = applyImports ( rawMarkup , scriptToInject ) ;
0 commit comments