Skip to content

Commit 7182c73

Browse files
fix: fixed import react_native_stallion issue on rn 0.83+ (#9)
* fix: fixed import react_native_stallion issue on RN 0.83+ * fix: use quoted import for StallionModule.h in Obj-C AppDelegate
1 parent fc043d5 commit 7182c73

2 files changed

Lines changed: 72 additions & 42 deletions

File tree

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "expo-stallion-plugin",
3-
"version": "1.0.3",
3+
"version": "1.0.4",
44
"description": "Expo Config Plugin for react-native-stallion - automatically configures Stallion as the JS bundle provider",
55
"main": "build/index.js",
66
"types": "build/index.d.ts",

src/ios.ts

Lines changed: 71 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -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+(\([^)]*\))?|public|private|internal|fileprivate|open)\s+)*import\s+\S/;
99+
const stopLine =
100+
/^(@main\b|class\s|struct\s|enum\s|protocol\s|extension\s|actor\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 (/^#if\b/.test(trimmed)) {
118+
conditionalDepth++;
119+
} else if (/^#endif\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
*/
87140
function addStallionSwiftImports(contents: string): string {
88-
// Add react_native_stallion import if missing
89141
if (!contents.includes("import react_native_stallion")) {
90-
const importPattern = /(import\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 = /(import\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(/(class\s+\w+AppDelegate[^{]*\{)/);
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 = /(class\s+\w*AppDelegate\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-
/(class\s+\w+AppDelegate[^{]*\{)/,
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

Comments
 (0)