@hot-updater/react-native 0.29.2 → 0.29.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/HotUpdater.podspec +0 -4
- package/android/src/oldarch/HotUpdaterModule.kt +12 -4
- package/android/src/oldarch/HotUpdaterSpec.kt +3 -5
- package/ios/HotUpdater/Internal/ArchiveExtractionUtilities.swift +178 -0
- package/ios/HotUpdater/Internal/BundleFileStorageService.swift +82 -47
- package/ios/HotUpdater/Internal/StreamingTarArchiveExtractor.swift +359 -0
- package/ios/HotUpdater/Internal/TarArchiveExtractor.swift +386 -0
- package/ios/HotUpdater/Internal/TarBrDecompressionStrategy.swift +7 -213
- package/ios/HotUpdater/Internal/TarGzDecompressionStrategy.swift +8 -126
- package/ios/HotUpdater/Internal/URLSessionDownloadService.swift +13 -2
- package/ios/HotUpdater/Internal/ZipArchiveExtractor.swift +462 -0
- package/ios/HotUpdater/Internal/ZipDecompressionStrategy.swift +4 -113
- package/lib/commonjs/DefaultResolver.js.map +1 -1
- package/lib/commonjs/checkForUpdate.js.map +1 -1
- package/lib/commonjs/index.js +0 -7
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/native.js.map +1 -1
- package/lib/commonjs/native.spec.js.map +1 -1
- package/lib/commonjs/store.js.map +1 -1
- package/lib/commonjs/types.js.map +1 -1
- package/lib/commonjs/wrap.js.map +1 -1
- package/lib/module/DefaultResolver.js.map +1 -1
- package/lib/module/checkForUpdate.js.map +1 -1
- package/lib/module/index.js +0 -7
- package/lib/module/index.js.map +1 -1
- package/lib/module/native.js.map +1 -1
- package/lib/module/native.spec.js.map +1 -1
- package/lib/module/store.js.map +1 -1
- package/lib/module/types.js.map +1 -1
- package/lib/module/wrap.js.map +1 -1
- package/lib/typescript/commonjs/DefaultResolver.d.ts.map +1 -1
- package/lib/typescript/commonjs/checkForUpdate.d.ts.map +1 -1
- package/lib/typescript/commonjs/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/native.d.ts.map +1 -1
- package/lib/typescript/commonjs/store.d.ts.map +1 -1
- package/lib/typescript/commonjs/types.d.ts.map +1 -1
- package/lib/typescript/commonjs/wrap.d.ts.map +1 -1
- package/lib/typescript/module/DefaultResolver.d.ts.map +1 -1
- package/lib/typescript/module/checkForUpdate.d.ts.map +1 -1
- package/lib/typescript/module/index.d.ts.map +1 -1
- package/lib/typescript/module/native.d.ts.map +1 -1
- package/lib/typescript/module/store.d.ts.map +1 -1
- package/lib/typescript/module/types.d.ts.map +1 -1
- package/lib/typescript/module/wrap.d.ts.map +1 -1
- package/package.json +9 -9
- package/plugin/build/transformers.js +83 -97
- package/plugin/build/withHotUpdater.js +159 -239
- package/src/DefaultResolver.ts +1 -0
- package/src/checkForUpdate.ts +1 -0
- package/src/index.ts +0 -7
- package/src/native.spec.ts +4 -6
- package/src/native.ts +1 -0
- package/src/store.ts +1 -0
- package/src/types.ts +1 -0
- package/src/wrap.tsx +1 -0
|
@@ -3,22 +3,13 @@
|
|
|
3
3
|
* Pure transformation functions for HotUpdater code injection
|
|
4
4
|
* These utilities handle code transformations for different React Native patterns
|
|
5
5
|
*/
|
|
6
|
-
var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
|
|
7
|
-
if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
|
|
8
|
-
if (ar || !(i in from)) {
|
|
9
|
-
if (!ar) ar = Array.prototype.slice.call(from, 0, i);
|
|
10
|
-
ar[i] = from[i];
|
|
11
|
-
}
|
|
12
|
-
}
|
|
13
|
-
return to.concat(ar || Array.prototype.slice.call(from));
|
|
14
|
-
};
|
|
15
6
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
16
7
|
exports.transformAndroid = transformAndroid;
|
|
17
8
|
exports.transformIOS = transformIOS;
|
|
18
9
|
function findMatchingClosingParen(source, openParenIndex) {
|
|
19
|
-
|
|
20
|
-
for (
|
|
21
|
-
|
|
10
|
+
let depth = 0;
|
|
11
|
+
for (let i = openParenIndex; i < source.length; i += 1) {
|
|
12
|
+
const char = source[i];
|
|
22
13
|
if (char === "(") {
|
|
23
14
|
depth += 1;
|
|
24
15
|
continue;
|
|
@@ -36,7 +27,7 @@ function findMatchingClosingParen(source, openParenIndex) {
|
|
|
36
27
|
* Helper to add lines if they don't exist, anchored by a specific string.
|
|
37
28
|
*/
|
|
38
29
|
function addLinesOnce(contents, anchor, linesToAdd) {
|
|
39
|
-
if (linesToAdd.every(
|
|
30
|
+
if (linesToAdd.every((line) => contents.includes(line))) {
|
|
40
31
|
// All lines already exist, do nothing
|
|
41
32
|
return contents;
|
|
42
33
|
}
|
|
@@ -46,15 +37,15 @@ function addLinesOnce(contents, anchor, linesToAdd) {
|
|
|
46
37
|
return contents;
|
|
47
38
|
}
|
|
48
39
|
// Add lines after the anchor
|
|
49
|
-
return contents.replace(anchor,
|
|
40
|
+
return contents.replace(anchor, `${anchor}\n${linesToAdd.join("\n")}`);
|
|
50
41
|
}
|
|
51
42
|
/**
|
|
52
43
|
* Android: handle getDefaultReactHost pattern (RN 0.82+ style).
|
|
53
44
|
* Adds jsBundleFilePath parameter to the call.
|
|
54
45
|
*/
|
|
55
46
|
function transformAndroidReactHost(contents) {
|
|
56
|
-
|
|
57
|
-
|
|
47
|
+
const kotlinImport = "import com.hotupdater.HotUpdater";
|
|
48
|
+
const kotlinImportAnchor = "import com.facebook.react.ReactApplication";
|
|
58
49
|
// Quick pattern detection: only touch files using getDefaultReactHost
|
|
59
50
|
// with the new RN 0.82+ parameter style.
|
|
60
51
|
if (!contents.includes("getDefaultReactHost(") ||
|
|
@@ -62,27 +53,27 @@ function transformAndroidReactHost(contents) {
|
|
|
62
53
|
return contents;
|
|
63
54
|
}
|
|
64
55
|
// 1. Ensure HotUpdater import exists (idempotent via addLinesOnce)
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
56
|
+
const result = addLinesOnce(contents, kotlinImportAnchor, [kotlinImport]);
|
|
57
|
+
const callNeedle = "getDefaultReactHost(";
|
|
58
|
+
const callStartIndex = result.indexOf(callNeedle);
|
|
68
59
|
if (callStartIndex === -1) {
|
|
69
60
|
return result;
|
|
70
61
|
}
|
|
71
|
-
|
|
72
|
-
|
|
62
|
+
const openParenIndex = callStartIndex + callNeedle.length - 1;
|
|
63
|
+
const closeParenIndex = findMatchingClosingParen(result, openParenIndex);
|
|
73
64
|
if (closeParenIndex === -1) {
|
|
74
65
|
return result;
|
|
75
66
|
}
|
|
76
|
-
|
|
67
|
+
const callContents = result.slice(callStartIndex, closeParenIndex + 1);
|
|
77
68
|
if (callContents.includes("jsBundleFilePath")) {
|
|
78
69
|
return result;
|
|
79
70
|
}
|
|
80
|
-
|
|
71
|
+
const callLines = callContents.split("\n");
|
|
81
72
|
// Determine the indentation used for parameters (e.g. " ")
|
|
82
|
-
|
|
83
|
-
for (
|
|
84
|
-
|
|
85
|
-
|
|
73
|
+
let paramIndent = "";
|
|
74
|
+
for (let i = 1; i < callLines.length; i += 1) {
|
|
75
|
+
const line = callLines[i];
|
|
76
|
+
const trimmed = line.trim();
|
|
86
77
|
if (trimmed.length === 0) {
|
|
87
78
|
continue;
|
|
88
79
|
}
|
|
@@ -90,18 +81,18 @@ function transformAndroidReactHost(contents) {
|
|
|
90
81
|
// No parameters detected, give up safely.
|
|
91
82
|
return result;
|
|
92
83
|
}
|
|
93
|
-
|
|
84
|
+
const indentMatch = line.match(/^(\s*)/);
|
|
94
85
|
paramIndent = indentMatch ? indentMatch[1] : "";
|
|
95
86
|
break;
|
|
96
87
|
}
|
|
97
88
|
if (!paramIndent) {
|
|
98
89
|
return result;
|
|
99
90
|
}
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
91
|
+
const closingLineStartIndex = result.lastIndexOf("\n", closeParenIndex);
|
|
92
|
+
const insertionIndex = closingLineStartIndex === -1 ? 0 : closingLineStartIndex + 1;
|
|
93
|
+
let prefix = result.slice(0, insertionIndex);
|
|
94
|
+
const suffix = result.slice(insertionIndex);
|
|
95
|
+
let prevNonWhitespaceIndex = prefix.length - 1;
|
|
105
96
|
while (prevNonWhitespaceIndex >= 0 &&
|
|
106
97
|
/\s/.test(prefix[prevNonWhitespaceIndex])) {
|
|
107
98
|
prevNonWhitespaceIndex -= 1;
|
|
@@ -109,86 +100,81 @@ function transformAndroidReactHost(contents) {
|
|
|
109
100
|
if (prevNonWhitespaceIndex >= 0 &&
|
|
110
101
|
prefix[prevNonWhitespaceIndex] !== "," &&
|
|
111
102
|
prefix[prevNonWhitespaceIndex] !== "(") {
|
|
112
|
-
prefix =
|
|
103
|
+
prefix = `${prefix.slice(0, prevNonWhitespaceIndex + 1)},${prefix.slice(prevNonWhitespaceIndex + 1)}`;
|
|
113
104
|
}
|
|
114
|
-
|
|
115
|
-
return
|
|
105
|
+
const jsBundleLine = `${paramIndent}jsBundleFilePath = HotUpdater.getJSBundleFile(applicationContext),`;
|
|
106
|
+
return `${prefix}${jsBundleLine}\n${suffix}`;
|
|
116
107
|
}
|
|
117
108
|
/**
|
|
118
109
|
* Android: DefaultReactNativeHost pattern (RN 0.81 / Expo 54).
|
|
119
110
|
* Adds getJSBundleFile() override to the host.
|
|
120
111
|
*/
|
|
121
112
|
function transformAndroidDefaultHost(contents) {
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
var kotlinNewArchAnchor = "override val isNewArchEnabled: Boolean = BuildConfig.IS_NEW_ARCHITECTURE_ENABLED";
|
|
113
|
+
const kotlinImport = "import com.hotupdater.HotUpdater";
|
|
114
|
+
const kotlinImportAnchor = "import com.facebook.react.ReactApplication";
|
|
115
|
+
const kotlinReactNativeHostAnchor = "object : DefaultReactNativeHost(this) {";
|
|
116
|
+
const kotlinMethodCheck = "HotUpdater.getJSBundleFile(applicationContext)";
|
|
117
|
+
const kotlinExistingMethodRegex = /^\s*override fun getJSBundleFile\(\): String\?\s*\{[\s\S]*?^\s*\}/gm;
|
|
118
|
+
const kotlinHermesAnchor = "override val isHermesEnabled: Boolean = BuildConfig.IS_HERMES_ENABLED";
|
|
119
|
+
const kotlinNewArchAnchor = "override val isNewArchEnabled: Boolean = BuildConfig.IS_NEW_ARCHITECTURE_ENABLED";
|
|
130
120
|
// Check if this is the old pattern with DefaultReactNativeHost
|
|
131
121
|
if (!contents.includes(kotlinReactNativeHostAnchor)) {
|
|
132
122
|
return contents;
|
|
133
123
|
}
|
|
134
124
|
// 1. Add import if missing
|
|
135
|
-
|
|
125
|
+
let result = addLinesOnce(contents, kotlinImportAnchor, [kotlinImport]);
|
|
136
126
|
// 2. Add/Replace getJSBundleFile method if needed
|
|
137
127
|
if (!result.includes(kotlinMethodCheck)) {
|
|
138
128
|
// Remove potentially existing (different) override first
|
|
139
129
|
result = result.replace(kotlinExistingMethodRegex, "");
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
for (
|
|
143
|
-
if (
|
|
130
|
+
const lines = result.split("\n");
|
|
131
|
+
const findLineIndex = (needle) => {
|
|
132
|
+
for (let i = 0; i < lines.length; i += 1) {
|
|
133
|
+
if (lines[i].includes(needle)) {
|
|
144
134
|
return i;
|
|
145
135
|
}
|
|
146
136
|
}
|
|
147
137
|
return -1;
|
|
148
138
|
};
|
|
149
139
|
// Prefer inserting after Hermes line, then after new architecture line
|
|
150
|
-
|
|
140
|
+
let anchorIndex = findLineIndex(kotlinHermesAnchor);
|
|
151
141
|
if (anchorIndex === -1) {
|
|
152
142
|
anchorIndex = findLineIndex(kotlinNewArchAnchor);
|
|
153
143
|
}
|
|
154
144
|
if (anchorIndex !== -1) {
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
});
|
|
160
|
-
var indentSize = 2;
|
|
145
|
+
const indentMatch = lines[anchorIndex].match(/^\s*/);
|
|
146
|
+
const indent = indentMatch ? indentMatch[0] : "";
|
|
147
|
+
const objectLine = lines.find((line) => line.includes("object : DefaultReactNativeHost"));
|
|
148
|
+
let indentSize = 2;
|
|
161
149
|
if (objectLine) {
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
150
|
+
const objectIndent = (objectLine.match(/^\s*/)?.[0] || "").length;
|
|
151
|
+
const propertyIndent = indent.length;
|
|
152
|
+
const diff = propertyIndent - objectIndent;
|
|
165
153
|
if (diff > 0) {
|
|
166
154
|
indentSize = diff;
|
|
167
155
|
}
|
|
168
156
|
}
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
157
|
+
const spaces = indentSize === 2 ? " " : " ";
|
|
158
|
+
const bodyIndent = indent + spaces;
|
|
159
|
+
const methodLines = [
|
|
172
160
|
"", // blank line
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
161
|
+
`${indent}override fun getJSBundleFile(): String? {`,
|
|
162
|
+
`${bodyIndent}return HotUpdater.getJSBundleFile(applicationContext)`,
|
|
163
|
+
`${indent}}`,
|
|
176
164
|
];
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
result =
|
|
165
|
+
const insertIndex = anchorIndex + 1;
|
|
166
|
+
lines.splice(insertIndex, 0, ...methodLines);
|
|
167
|
+
result = lines.join("\n");
|
|
180
168
|
}
|
|
181
169
|
else {
|
|
182
170
|
// Fallback: insert before the closing brace of the object block
|
|
183
|
-
|
|
184
|
-
return line.includes("object : DefaultReactNativeHost");
|
|
185
|
-
});
|
|
171
|
+
const hostStartIndex = lines.findIndex((line) => line.includes("object : DefaultReactNativeHost"));
|
|
186
172
|
if (hostStartIndex === -1) {
|
|
187
173
|
throw new Error("[transformAndroidDefaultHost] Could not find DefaultReactNativeHost block.");
|
|
188
174
|
}
|
|
189
|
-
|
|
190
|
-
for (
|
|
191
|
-
if (
|
|
175
|
+
let hostEndIndex = -1;
|
|
176
|
+
for (let i = lines.length - 1; i > hostStartIndex; i -= 1) {
|
|
177
|
+
if (lines[i].trim() === "}") {
|
|
192
178
|
hostEndIndex = i;
|
|
193
179
|
break;
|
|
194
180
|
}
|
|
@@ -196,16 +182,16 @@ function transformAndroidDefaultHost(contents) {
|
|
|
196
182
|
if (hostEndIndex === -1) {
|
|
197
183
|
throw new Error("[transformAndroidDefaultHost] Could not find end of DefaultReactNativeHost block.");
|
|
198
184
|
}
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
185
|
+
const indentMatch = lines[hostEndIndex].match(/^\s*/);
|
|
186
|
+
const indent = indentMatch ? indentMatch[0] : "";
|
|
187
|
+
const bodyIndent = `${indent} `;
|
|
188
|
+
const methodLines = [
|
|
189
|
+
`${indent}override fun getJSBundleFile(): String? {`,
|
|
190
|
+
`${bodyIndent}return HotUpdater.getJSBundleFile(applicationContext)`,
|
|
191
|
+
`${indent}}`,
|
|
206
192
|
];
|
|
207
|
-
|
|
208
|
-
result =
|
|
193
|
+
lines.splice(hostEndIndex, 0, ...methodLines);
|
|
194
|
+
result = lines.join("\n");
|
|
209
195
|
}
|
|
210
196
|
}
|
|
211
197
|
return result;
|
|
@@ -214,7 +200,7 @@ function transformAndroidDefaultHost(contents) {
|
|
|
214
200
|
* Public Android transformer that applies all Android-specific transforms.
|
|
215
201
|
*/
|
|
216
202
|
function transformAndroid(contents) {
|
|
217
|
-
|
|
203
|
+
let result = contents;
|
|
218
204
|
result = transformAndroidReactHost(result);
|
|
219
205
|
result = transformAndroidDefaultHost(result);
|
|
220
206
|
return result;
|
|
@@ -224,15 +210,15 @@ function transformAndroid(contents) {
|
|
|
224
210
|
* Replaces NSBundle-based bundleURL with HotUpdater bundleURL.
|
|
225
211
|
*/
|
|
226
212
|
function transformIOSObjC(contents) {
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
213
|
+
const iosImport = "#import <HotUpdater/HotUpdater.h>";
|
|
214
|
+
const iosBundleUrl = "[HotUpdater bundleURL]";
|
|
215
|
+
const iosOriginalBundleUrlRegex = /\[\[NSBundle mainBundle\] URLForResource:@"main" withExtension:@"jsbundle"\]/g;
|
|
216
|
+
const iosAppDelegateHeader = '#import "AppDelegate.h"';
|
|
231
217
|
// Check if it's likely Obj-C
|
|
232
218
|
if (!contents.includes(iosAppDelegateHeader)) {
|
|
233
219
|
return contents;
|
|
234
220
|
}
|
|
235
|
-
|
|
221
|
+
let result = contents;
|
|
236
222
|
// 1. Ensure HotUpdater import is present
|
|
237
223
|
if (!result.includes(iosImport)) {
|
|
238
224
|
result = addLinesOnce(result, iosAppDelegateHeader, [iosImport]);
|
|
@@ -249,21 +235,21 @@ function transformIOSObjC(contents) {
|
|
|
249
235
|
* Replaces Bundle.main.url-based bundleURL with HotUpdater.bundleURL().
|
|
250
236
|
*/
|
|
251
237
|
function transformIOSSwift(contents) {
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
238
|
+
const swiftImport = "import HotUpdater";
|
|
239
|
+
const swiftBundleUrl = "HotUpdater.bundleURL()";
|
|
240
|
+
const swiftOriginalBundleUrlRegex = /Bundle\.main\.url\(forResource: "?main"?, withExtension: "jsbundle"\)/g;
|
|
255
241
|
// Check if it's likely Swift AppDelegate code
|
|
256
242
|
if (!contents.includes("import ")) {
|
|
257
243
|
return contents;
|
|
258
244
|
}
|
|
259
245
|
// 1. Add import if missing - find the last import statement and add after it
|
|
260
|
-
|
|
246
|
+
let result = contents;
|
|
261
247
|
if (!result.includes(swiftImport)) {
|
|
262
248
|
// Find the last import statement
|
|
263
|
-
|
|
249
|
+
const lastImportMatch = result.match(/^import .*$/gm);
|
|
264
250
|
if (lastImportMatch) {
|
|
265
|
-
|
|
266
|
-
result = result.replace(lastImport,
|
|
251
|
+
const lastImport = lastImportMatch[lastImportMatch.length - 1];
|
|
252
|
+
result = result.replace(lastImport, `${lastImport}\n${swiftImport}`);
|
|
267
253
|
}
|
|
268
254
|
}
|
|
269
255
|
// 2. Replace bundleURL provider if the original exists and hasn't been replaced
|
|
@@ -277,7 +263,7 @@ function transformIOSSwift(contents) {
|
|
|
277
263
|
* Public iOS transformer that applies both Objective-C and Swift transforms.
|
|
278
264
|
*/
|
|
279
265
|
function transformIOS(contents) {
|
|
280
|
-
|
|
266
|
+
let result = contents;
|
|
281
267
|
result = transformIOSObjC(result);
|
|
282
268
|
result = transformIOSSwift(result);
|
|
283
269
|
return result;
|