Skip to content

Commit 1eedf05

Browse files
Alexandre Kirszenbergfacebook-github-bot
authored andcommitted
Update the Delta/HMR format
Summary: Makes the delta bundle data structures more consistent. The changes are as follows: * There are now two types of JSON bundles that can be downloaded from the delta endpoint. Base bundles (`Bundle` type), and Delta bundles (`DeltaBundle` type). * The `reset` boolean is renamed to `base`. * `pre` and `post` properties are now strings. * Only `Bundle` can define `pre` and `post` properties. * The `delta` property is renamed to `modules`. * Deleted modules are now listed inside of the `deleted` property, which is only defined by `DeltaBundle`. Reviewed By: mjesun Differential Revision: D10446831 fbshipit-source-id: 40e229a2811d48950f0bad8dd341ece189089e9b
1 parent bb93abf commit 1eedf05

File tree

4 files changed

+87
-80
lines changed

4 files changed

+87
-80
lines changed

ReactAndroid/src/main/java/com/facebook/react/devsupport/BundleDeltaClient.java

Lines changed: 45 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
public abstract class BundleDeltaClient {
2626

2727
private static final String METRO_DELTA_ID_HEADER = "X-Metro-Delta-ID";
28-
@Nullable private String mDeltaId;
28+
@Nullable private String mRevisionId;
2929

3030
public enum ClientType {
3131
NONE,
@@ -54,58 +54,59 @@ abstract protected Pair<Boolean, NativeDeltaClient> processDelta(
5454
BufferedSource body,
5555
File outputFile) throws IOException;
5656

57-
final public String extendUrlForDelta(String bundleURL) {
58-
return mDeltaId != null ? bundleURL + "&deltaBundleId=" + mDeltaId : bundleURL;
57+
final public synchronized String extendUrlForDelta(String bundleURL) {
58+
return mRevisionId != null ? bundleURL + "&revisionId=" + mRevisionId : bundleURL;
5959
}
6060

61-
public void reset() {
62-
mDeltaId = null;
61+
public synchronized void reset() {
62+
mRevisionId = null;
6363
}
6464

65-
public Pair<Boolean, NativeDeltaClient> processDelta(
65+
public synchronized Pair<Boolean, NativeDeltaClient> processDelta(
6666
Headers headers,
6767
BufferedSource body,
6868
File outputFile) throws IOException {
6969

70-
mDeltaId = headers.get(METRO_DELTA_ID_HEADER);
70+
mRevisionId = headers.get(METRO_DELTA_ID_HEADER);
7171
return processDelta(body, outputFile);
7272
}
7373

7474
private static class BundleDeltaJavaClient extends BundleDeltaClient {
7575

76-
final LinkedHashMap<Number, byte[]> mPreModules = new LinkedHashMap<Number, byte[]>();
77-
final LinkedHashMap<Number, byte[]> mDeltaModules = new LinkedHashMap<Number, byte[]>();
78-
final LinkedHashMap<Number, byte[]> mPostModules = new LinkedHashMap<Number, byte[]>();
76+
byte[] mPreCode;
77+
byte[] mPostCode;
78+
final LinkedHashMap<Number, byte[]> mModules = new LinkedHashMap<Number, byte[]>();
7979

8080
@Override
8181
public boolean canHandle(ClientType type) {
8282
return type == ClientType.DEV_SUPPORT;
8383
}
8484

85-
public void reset() {
85+
public synchronized void reset() {
8686
super.reset();
87-
mDeltaModules.clear();
88-
mPreModules.clear();
89-
mPostModules.clear();
87+
mPreCode = null;
88+
mPostCode = null;
89+
mModules.clear();
9090
}
9191

9292
@Override
9393
public synchronized Pair<Boolean, NativeDeltaClient> processDelta(
9494
BufferedSource body,
9595
File outputFile) throws IOException {
96-
9796
JsonReader jsonReader = new JsonReader(new InputStreamReader(body.inputStream()));
9897
jsonReader.beginObject();
9998
int numChangedModules = 0;
10099

101100
while (jsonReader.hasNext()) {
102101
String name = jsonReader.nextName();
103102
if (name.equals("pre")) {
104-
numChangedModules += patchDelta(jsonReader, mPreModules);
103+
mPreCode = jsonReader.nextString().getBytes();
105104
} else if (name.equals("post")) {
106-
numChangedModules += patchDelta(jsonReader, mPostModules);
107-
} else if (name.equals("delta")) {
108-
numChangedModules += patchDelta(jsonReader, mDeltaModules);
105+
mPostCode = jsonReader.nextString().getBytes();
106+
} else if (name.equals("modules")) {
107+
numChangedModules += setModules(jsonReader, mModules);
108+
} else if (name.equals("deleted")) {
109+
numChangedModules += removeModules(jsonReader, mModules);
109110
} else {
110111
jsonReader.skipValue();
111112
}
@@ -123,20 +124,16 @@ public synchronized Pair<Boolean, NativeDeltaClient> processDelta(
123124
FileOutputStream fileOutputStream = new FileOutputStream(outputFile);
124125

125126
try {
126-
for (byte[] code : mPreModules.values()) {
127-
fileOutputStream.write(code);
128-
fileOutputStream.write('\n');
129-
}
127+
fileOutputStream.write(mPreCode);
128+
fileOutputStream.write('\n');
130129

131-
for (byte[] code : mDeltaModules.values()) {
130+
for (byte[] code : mModules.values()) {
132131
fileOutputStream.write(code);
133132
fileOutputStream.write('\n');
134133
}
135134

136-
for (byte[] code : mPostModules.values()) {
137-
fileOutputStream.write(code);
138-
fileOutputStream.write('\n');
139-
}
135+
fileOutputStream.write(mPostCode);
136+
fileOutputStream.write('\n');
140137
} finally {
141138
fileOutputStream.flush();
142139
fileOutputStream.close();
@@ -145,7 +142,7 @@ public synchronized Pair<Boolean, NativeDeltaClient> processDelta(
145142
return Pair.create(Boolean.TRUE, null);
146143
}
147144

148-
private static int patchDelta(JsonReader jsonReader, LinkedHashMap<Number, byte[]> map)
145+
private static int setModules(JsonReader jsonReader, LinkedHashMap<Number, byte[]> map)
149146
throws IOException {
150147
jsonReader.beginArray();
151148

@@ -155,12 +152,7 @@ private static int patchDelta(JsonReader jsonReader, LinkedHashMap<Number, byte[
155152

156153
int moduleId = jsonReader.nextInt();
157154

158-
if (jsonReader.peek() == JsonToken.NULL) {
159-
jsonReader.skipValue();
160-
map.remove(moduleId);
161-
} else {
162-
map.put(moduleId, jsonReader.nextString().getBytes());
163-
}
155+
map.put(moduleId, jsonReader.nextString().getBytes());
164156

165157
jsonReader.endArray();
166158
numModules++;
@@ -170,6 +162,24 @@ private static int patchDelta(JsonReader jsonReader, LinkedHashMap<Number, byte[
170162

171163
return numModules;
172164
}
165+
166+
private static int removeModules(JsonReader jsonReader, LinkedHashMap<Number, byte[]> map)
167+
throws IOException {
168+
jsonReader.beginArray();
169+
170+
int numModules = 0;
171+
while (jsonReader.hasNext()) {
172+
int moduleId = jsonReader.nextInt();
173+
174+
map.remove(moduleId);
175+
176+
numModules++;
177+
}
178+
179+
jsonReader.endArray();
180+
181+
return numModules;
182+
}
173183
}
174184

175185
private static class BundleDeltaNativeClient extends BundleDeltaClient {

ReactCommon/cxxreact/JSDeltaBundleClient.cpp

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,7 @@ namespace {
1313

1414
for (auto section : {pre, post}) {
1515
if (section != nullptr) {
16-
for (folly::dynamic pair : *section) {
17-
startupCode << pair[1].getString() << '\n';
18-
}
16+
startupCode << section->getString() << '\n';
1917
}
2018
}
2119

@@ -24,28 +22,30 @@ namespace {
2422
} // namespace
2523

2624
void JSDeltaBundleClient::patch(const folly::dynamic& delta) {
27-
auto const reset = delta.get_ptr("reset");
28-
if (reset != nullptr && reset->asBool()) {
25+
auto const base = delta.get_ptr("base");
26+
27+
if (base != nullptr && base->asBool()) {
2928
clear();
30-
}
3129

32-
auto const pre = delta.get_ptr("pre");
33-
auto const post = delta.get_ptr("post");
30+
auto const pre = delta.get_ptr("pre");
31+
auto const post = delta.get_ptr("post");
3432

35-
if ((pre != nullptr && pre->size() > 0) || (post != nullptr && post->size() > 0)) {
3633
startupCode_ = startupCode(pre, post);
34+
} else {
35+
const folly::dynamic *deleted = delta.get_ptr("deleted");
36+
if (deleted != nullptr) {
37+
for (const folly::dynamic id : *deleted) {
38+
modules_.erase(id.getInt());
39+
}
40+
}
3741
}
3842

39-
const folly::dynamic *modules = delta.get_ptr("delta");
43+
const folly::dynamic *modules = delta.get_ptr("modules");
4044
if (modules != nullptr) {
4145
for (const folly::dynamic pair : *modules) {
4246
auto id = pair[0].getInt();
4347
auto module = pair[1];
44-
if (module.isNull()) {
45-
modules_.erase(id);
46-
} else {
47-
modules_.emplace(id, module.getString());
48-
}
48+
modules_.emplace(id, module.getString());
4949
}
5050
}
5151
}

local-cli/server/util/debugger-ui/DeltaPatcher.js

Lines changed: 19 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,10 @@
2727
class DeltaPatcher {
2828
constructor() {
2929
this._lastBundle = {
30-
pre: new Map(),
31-
post: new Map(),
30+
revisionId: undefined,
31+
pre: '',
32+
post: '',
3233
modules: new Map(),
33-
id: undefined,
3434
};
3535
this._initialized = false;
3636
this._lastNumModifiedFiles = 0;
@@ -51,44 +51,41 @@
5151
/**
5252
* Applies a Delta Bundle to the current bundle.
5353
*/
54-
applyDelta(deltaBundle) {
55-
// Make sure that the first received delta is a fresh one.
56-
if (!this._initialized && !deltaBundle.reset) {
54+
applyDelta(bundle) {
55+
// Make sure that the first received bundle is a base.
56+
if (!this._initialized && !bundle.base) {
5757
throw new Error(
58-
'DeltaPatcher should receive a fresh Delta when being initialized',
58+
'DeltaPatcher should receive a base Bundle when being initialized',
5959
);
6060
}
6161

6262
this._initialized = true;
6363

64-
// Reset the current delta when we receive a fresh delta.
65-
if (deltaBundle.reset) {
64+
// Reset the current bundle when we receive a base bundle.
65+
if (bundle.base) {
6666
this._lastBundle = {
67-
pre: new Map(),
68-
post: new Map(),
67+
revisionId: undefined,
68+
pre: bundle.pre,
69+
post: bundle.post,
6970
modules: new Map(),
70-
id: undefined,
7171
};
7272
}
7373

74-
this._lastNumModifiedFiles =
75-
deltaBundle.pre.size + deltaBundle.post.size + deltaBundle.delta.size;
74+
this._lastNumModifiedFiles = bundle.modules.size;
7675

7776
if (this._lastNumModifiedFiles > 0) {
7877
this._lastModifiedDate = new Date();
7978
}
8079

81-
this._patchMap(this._lastBundle.pre, deltaBundle.pre);
82-
this._patchMap(this._lastBundle.post, deltaBundle.post);
83-
this._patchMap(this._lastBundle.modules, deltaBundle.delta);
80+
this._patchMap(this._lastBundle.modules, bundle.modules);
8481

85-
this._lastBundle.id = deltaBundle.id;
82+
this._lastBundle.revisionId = bundle.revisionId;
8683

8784
return this;
8885
}
8986

90-
getLastBundleId() {
91-
return this._lastBundle.id;
87+
getLastRevisionId() {
88+
return this._lastBundle.revisionId;
9289
}
9390

9491
/**
@@ -107,9 +104,9 @@
107104

108105
getAllModules() {
109106
return [].concat(
110-
Array.from(this._lastBundle.pre.values()),
107+
this._lastBundle.pre,
111108
Array.from(this._lastBundle.modules.values()),
112-
Array.from(this._lastBundle.post.values()),
109+
this._lastBundle.post,
113110
);
114111
}
115112

local-cli/server/util/debugger-ui/deltaUrlToBlobUrl.js

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -22,19 +22,19 @@
2222
async function deltaUrlToBlobUrl(deltaUrl) {
2323
const client = global.DeltaPatcher.get(deltaUrl);
2424

25-
const deltaBundleId = client.getLastBundleId()
26-
? `&deltaBundleId=${client.getLastBundleId()}`
25+
const revisionId = client.getLastRevisionId()
26+
? `&revisionId=${client.getLastRevisionId()}`
2727
: '';
2828

29-
const data = await fetch(deltaUrl + deltaBundleId);
29+
const data = await fetch(deltaUrl + revisionId);
3030
const bundle = await data.json();
3131

3232
const deltaPatcher = client.applyDelta({
33-
id: bundle.id,
34-
pre: new Map(bundle.pre),
35-
post: new Map(bundle.post),
36-
delta: new Map(bundle.delta),
37-
reset: bundle.reset,
33+
base: bundle.base,
34+
revisionId: bundle.revisionId,
35+
pre: bundle.pre,
36+
post: bundle.post,
37+
modules: new Map(bundle.modules),
3838
});
3939

4040
let cachedBundle = cachedBundleUrls.get(deltaUrl);

0 commit comments

Comments
 (0)