Skip to content

Commit b5081ab

Browse files
martinbigiofacebook-github-bot-8
authored andcommitted
Send HMR updates only for files on the bundle
Summary: public Compute the dependencies of the bundle entry file just before sending HMR updates. In case the file that was changed doesn't belong to the bundle bail. Reviewed By: vjeux Differential Revision: D2793736 fb-gh-sync-id: f858e71b0dd5fe4f5b2307a22c6cef627eb66a22
1 parent 5f850fb commit b5081ab

5 files changed

Lines changed: 114 additions & 16 deletions

File tree

local-cli/server/util/attachHMRServer.js

Lines changed: 94 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -22,16 +22,76 @@ function attachHMRServer({httpServer, path, packagerServer}) {
2222
client = null;
2323
}
2424

25+
// Returns a promise with the full list of dependencies and the shallow
26+
// dependencies each file on the dependency list has for the give platform
27+
// and entry file.
28+
function getDependencies(platform, bundleEntry) {
29+
return packagerServer.getDependencies({
30+
platform: platform,
31+
dev: true,
32+
entryFile: bundleEntry,
33+
}).then(response => {
34+
// for each dependency builds the object:
35+
// `{path: '/a/b/c.js', deps: ['modA', 'modB', ...]}`
36+
return Promise.all(Object.values(response.dependencies).map(dep => {
37+
if (dep.isAsset() || dep.isAsset_DEPRECATED() || dep.isJSON()) {
38+
return Promise.resolve({path: dep.path, deps: []});
39+
}
40+
return packagerServer.getShallowDependencies(dep.path)
41+
.then(deps => {
42+
return {
43+
path: dep.path,
44+
deps,
45+
};
46+
});
47+
}))
48+
.then(deps => {
49+
// list with all the dependencies the bundle entry has
50+
const dependenciesCache = response.dependencies.map(dep => dep.path);
51+
52+
// map that indicates the shallow dependency each file included on the
53+
// bundle has
54+
const shallowDependencies = {};
55+
deps.forEach(dep => shallowDependencies[dep.path] = dep.deps);
56+
57+
return {dependenciesCache, shallowDependencies};
58+
});
59+
});
60+
}
61+
2562
packagerServer.addFileChangeListener(filename => {
2663
if (!client) {
2764
return;
2865
}
2966

30-
packagerServer.buildBundleForHMR({
31-
entryFile: filename,
32-
platform: client.platform,
33-
})
34-
.then(bundle => client.ws.send(bundle));
67+
packagerServer.getShallowDependencies(filename)
68+
.then(deps => {
69+
// if the file dependencies have change we need to invalidate the
70+
// dependencies caches because the list of files we need to send to the
71+
// client may have changed
72+
if (arrayEquals(deps, client.shallowDependencies[filename])) {
73+
return Promise.resolve();
74+
}
75+
return getDependencies(client.platform, client.bundleEntry)
76+
.then(({dependenciesCache, shallowDependencies}) => {
77+
// invalidate caches
78+
client.dependenciesCache = dependenciesCache;
79+
client.shallowDependencies = shallowDependencies;
80+
});
81+
})
82+
.then(() => {
83+
// make sure the file was modified is part of the bundle
84+
if (!client.shallowDependencies[filename]) {
85+
return;
86+
}
87+
88+
return packagerServer.buildBundleForHMR({
89+
platform: client.platform,
90+
entryFile: filename,
91+
})
92+
.then(bundle => client.ws.send(bundle));
93+
})
94+
.done();
3595
});
3696

3797
const WebSocketServer = require('ws').Server;
@@ -44,19 +104,37 @@ function attachHMRServer({httpServer, path, packagerServer}) {
44104
wss.on('connection', ws => {
45105
console.log('[Hot Module Replacement] Client connected');
46106
const params = querystring.parse(url.parse(ws.upgradeReq.url).query);
47-
client = {
48-
ws,
49-
platform: params.platform,
50-
bundleEntry: params.bundleEntry,
51-
};
52-
53-
client.ws.on('error', e => {
54-
console.error('[Hot Module Replacement] Unexpected error', e);
55-
disconnect();
56-
});
57107

58-
client.ws.on('close', () => disconnect());
108+
getDependencies(params.platform, params.bundleEntry)
109+
.then(({dependenciesCache, shallowDependencies}) => {
110+
client = {
111+
ws,
112+
platform: params.platform,
113+
bundleEntry: params.bundleEntry,
114+
dependenciesCache,
115+
shallowDependencies,
116+
};
117+
118+
client.ws.on('error', e => {
119+
console.error('[Hot Module Replacement] Unexpected error', e);
120+
disconnect();
121+
});
122+
123+
client.ws.on('close', () => disconnect());
124+
})
125+
.done();
59126
});
60127
}
61128

129+
function arrayEquals(arrayA, arrayB) {
130+
arrayA = arrayA || [];
131+
arrayB = arrayB || [];
132+
return (
133+
arrayA.length === arrayB.length &&
134+
arrayA.every((element, index) => {
135+
return element === arrayB[index];
136+
})
137+
);
138+
}
139+
62140
module.exports = attachHMRServer;

packager/react-packager/src/Bundler/index.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -310,6 +310,10 @@ class Bundler {
310310
this._transformer.invalidateFile(filePath);
311311
}
312312

313+
getShallowDependencies(entryFile) {
314+
return this._resolver.getShallowDependencies(entryFile);
315+
}
316+
313317
getDependencies(main, isDev, platform) {
314318
return this._resolver.getDependencies(main, { dev: isDev, platform });
315319
}

packager/react-packager/src/DependencyResolver/DependencyGraph/index.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,14 @@ class DependencyGraph {
133133
return this._loading;
134134
}
135135

136+
/**
137+
* Returns a promise with the direct dependencies the module associated to
138+
* the given entryPath has.
139+
*/
140+
getShallowDependencies(entryPath) {
141+
return this._moduleCache.getModule(entryPath).getDependencies();
142+
}
143+
136144
getDependencies(entryPath, platform) {
137145
return this.load().then(() => {
138146
platform = this._getRequestPlatform(entryPath, platform);

packager/react-packager/src/Resolver/index.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,10 @@ class Resolver {
9999
this._polyfillModuleNames = opts.polyfillModuleNames || [];
100100
}
101101

102+
getShallowDependencies(entryFile) {
103+
return this._depGraph.getShallowDependencies(entryFile);
104+
}
105+
102106
getDependencies(main, options) {
103107
const opts = getDependenciesValidateOpts(options);
104108

packager/react-packager/src/Server/index.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -246,6 +246,10 @@ class Server {
246246
});
247247
}
248248

249+
getShallowDependencies(entryFile) {
250+
return this._bundler.getShallowDependencies(entryFile);
251+
}
252+
249253
getDependencies(options) {
250254
return Promise.resolve().then(() => {
251255
if (!options.platform) {

0 commit comments

Comments
 (0)