@@ -81,42 +81,78 @@ const withStallionBundleProvider = (
8181 } ) ;
8282} ;
8383
84+ /**
85+ * Finds a safe position to insert a new top-level import in a Swift file.
86+ *
87+ * Returns the offset immediately after the last top-level import that is
88+ * NOT inside an `#if`/`#endif` conditional block and NOT inside an
89+ * `// @generated begin ... // @generated end` block. Scanning stops at the
90+ * first top-level `@main` / class / struct / enum / protocol / extension
91+ * declaration so we never insert below it (which would break `@main`).
92+ *
93+ * Falls back to position 0 if no safe top-level import is found.
94+ */
95+ function findSwiftImportInsertPosition ( contents : string ) : number {
96+ const lines = contents . split ( "\n" ) ;
97+ const importLine =
98+ / ^ ( ( @ \w + ( \( [ ^ ) ] * \) ) ? | p u b l i c | p r i v a t e | i n t e r n a l | f i l e p r i v a t e | o p e n ) \s + ) * i m p o r t \s + \S / ;
99+ const stopLine =
100+ / ^ ( @ m a i n \b | c l a s s \s | s t r u c t \s | e n u m \s | p r o t o c o l \s | e x t e n s i o n \s | a c t o r \s ) / ;
101+
102+ let conditionalDepth = 0 ;
103+ let inGenerated = false ;
104+ let lastSafeInsertPos = - 1 ;
105+ let pos = 0 ;
106+
107+ for ( let i = 0 ; i < lines . length ; i ++ ) {
108+ const line = lines [ i ] ;
109+ const trimmed = line . trim ( ) ;
110+ const isLast = i === lines . length - 1 ;
111+ const afterLinePos = pos + line . length + ( isLast ? 0 : 1 ) ;
112+
113+ if ( trimmed . startsWith ( "// @generated begin" ) ) {
114+ inGenerated = true ;
115+ } else if ( trimmed . startsWith ( "// @generated end" ) ) {
116+ inGenerated = false ;
117+ } else if ( / ^ # i f \b / . test ( trimmed ) ) {
118+ conditionalDepth ++ ;
119+ } else if ( / ^ # e n d i f \b / . test ( trimmed ) ) {
120+ conditionalDepth = Math . max ( 0 , conditionalDepth - 1 ) ;
121+ } else {
122+ const insideBlock = inGenerated || conditionalDepth > 0 ;
123+ if ( ! insideBlock && stopLine . test ( trimmed ) ) {
124+ break ;
125+ }
126+ if ( ! insideBlock && importLine . test ( trimmed ) ) {
127+ lastSafeInsertPos = afterLinePos ;
128+ }
129+ }
130+
131+ pos = afterLinePos ;
132+ }
133+
134+ return lastSafeInsertPos >= 0 ? lastSafeInsertPos : 0 ;
135+ }
136+
84137/**
85138 * Adds required imports to Swift AppDelegate
86139 */
87140function addStallionSwiftImports ( contents : string ) : string {
88- // Add react_native_stallion import if missing
89141 if ( ! contents . includes ( "import react_native_stallion" ) ) {
90- const importPattern = / ( i m p o r t \s + [ ^ \n ] + \n ) / g;
91- const imports = contents . match ( importPattern ) ;
92- if ( imports && imports . length > 0 ) {
93- const lastImport = imports [ imports . length - 1 ] ;
94- const lastImportIndex = contents . lastIndexOf ( lastImport ) ;
95- const insertPos = lastImportIndex + lastImport . length ;
96- contents =
97- contents . substring ( 0 , insertPos ) +
98- "import react_native_stallion\n" +
99- contents . substring ( insertPos ) ;
100- } else {
101- contents = "import react_native_stallion\n" + contents ;
102- }
142+ const insertPos = findSwiftImportInsertPosition ( contents ) ;
143+ contents =
144+ contents . substring ( 0 , insertPos ) +
145+ "import react_native_stallion\n" +
146+ contents . substring ( insertPos ) ;
103147 }
104148
105- // Add React import if missing ( needed for RCTBundleURLProvider)
149+ // React is needed for RCTBundleURLProvider in the bundleURL override
106150 if ( ! contents . includes ( "import React" ) ) {
107- const importPattern = / ( i m p o r t \s + [ ^ \n ] + \n ) / g;
108- const imports = contents . match ( importPattern ) ;
109- if ( imports && imports . length > 0 ) {
110- const lastImport = imports [ imports . length - 1 ] ;
111- const lastImportIndex = contents . lastIndexOf ( lastImport ) ;
112- const insertPos = lastImportIndex + lastImport . length ;
113- contents =
114- contents . substring ( 0 , insertPos ) +
115- "import React\n" +
116- contents . substring ( insertPos ) ;
117- } else {
118- contents = "import React\n" + contents ;
119- }
151+ const insertPos = findSwiftImportInsertPosition ( contents ) ;
152+ contents =
153+ contents . substring ( 0 , insertPos ) +
154+ "import React\n" +
155+ contents . substring ( insertPos ) ;
120156 }
121157
122158 return contents ;
@@ -154,21 +190,16 @@ function patchSwiftAppDelegate(contents: string): string {
154190 return contents . replace ( methodPattern , targetImplementation ) ;
155191 }
156192 } else {
157- // Add new bundleURL method
158- // Try to find a good insertion point (after class declaration, before other methods)
159- const classMatch = contents . match ( / ( c l a s s \s + \w + A p p D e l e g a t e [ ^ { ] * \{ ) / ) ;
193+ // Add new bundleURL method right after the class opening brace.
194+ // \w* (not \w+) so SDK 55's bare `class AppDelegate` is matched in
195+ // addition to prefixed names like `RNAppDelegate` or `FooAppDelegate`.
196+ const classPattern = / ( c l a s s \s + \w * A p p D e l e g a t e \b [ ^ { ] * \{ ) / ;
197+ const classMatch = contents . match ( classPattern ) ;
160198 if ( classMatch && classMatch . index !== undefined ) {
161199 const insertPos = classMatch . index + classMatch [ 0 ] . length ;
162200 const before = contents . substring ( 0 , insertPos ) ;
163201 const after = contents . substring ( insertPos ) ;
164202 return before + "\n " + targetImplementation + "\n" + after ;
165- } else {
166- // Fallback: add at the beginning of the class body
167- return contents . replace (
168- / ( c l a s s \s + \w + A p p D e l e g a t e [ ^ { ] * \{ ) / ,
169- `$1
170- ${ targetImplementation } `
171- ) ;
172203 }
173204 }
174205
@@ -192,11 +223,10 @@ function addStallionObjCImports(contents: string): string {
192223 const insertPos = lastImportIndex + lastImport . length ;
193224 contents =
194225 contents . substring ( 0 , insertPos ) +
195- " #import <react_native_stallion/ StallionModule.h>\n" +
226+ ' #import " StallionModule.h"\n' +
196227 contents . substring ( insertPos ) ;
197228 } else {
198- contents =
199- "#import <react_native_stallion/StallionModule.h>\n" + contents ;
229+ contents = '#import "StallionModule.h"\n' + contents ;
200230 }
201231 }
202232
0 commit comments