Add X-Metro-Files-Changed-Count HTTP header to the response when using Delta Bundler

Reviewed By: mjesun

Differential Revision: D5793977

fbshipit-source-id: 9e0783356ca7574077d4eb06489e9837f033d986
This commit is contained in:
Rafael Oleza 2017-09-11 08:22:35 -07:00 committed by Facebook Github Bot
parent bf64ba58d3
commit 197d885ec1
4 changed files with 64 additions and 16 deletions

View File

@ -28,6 +28,7 @@ class DeltaPatcher {
modules: new Map(), modules: new Map(),
}; };
_initialized = false; _initialized = false;
_lastNumModifiedFiles = 0;
/** /**
* Applies a Delta Bundle to the current bundle. * Applies a Delta Bundle to the current bundle.
@ -51,6 +52,9 @@ class DeltaPatcher {
}; };
} }
this._lastNumModifiedFiles =
deltaBundle.pre.size + deltaBundle.post.size + deltaBundle.delta.size;
this._patchMap(this._lastBundle.pre, deltaBundle.pre); this._patchMap(this._lastBundle.pre, deltaBundle.pre);
this._patchMap(this._lastBundle.post, deltaBundle.post); this._patchMap(this._lastBundle.post, deltaBundle.post);
this._patchMap(this._lastBundle.modules, deltaBundle.delta); this._patchMap(this._lastBundle.modules, deltaBundle.delta);
@ -58,17 +62,27 @@ class DeltaPatcher {
return this; return this;
} }
/**
* Returns the number of modified files in the last received Delta. This is
* currently used to populate the `X-Metro-Files-Changed-Count` HTTP header
* when metro serves the whole JS bundle, and can potentially be removed once
* we only send the actual deltas to clients.
*/
getLastNumModifiedFiles(): number {
return this._lastNumModifiedFiles;
}
/** /**
* Converts the current delta bundle to a standard string bundle, ready to * Converts the current delta bundle to a standard string bundle, ready to
* be interpreted by any JS VM. * be interpreted by any JS VM.
*/ */
stringifyCode() { stringifyCode(): string {
const code = this._getAllModules().map(m => m.code); const code = this._getAllModules().map(m => m.code);
return code.join('\n;'); return code.join('\n;');
} }
stringifyMap({excludeSource}: {excludeSource?: boolean}) { stringifyMap({excludeSource}: {excludeSource?: boolean}): string {
const mappings = fromRawMappings(this._getAllModules()); const mappings = fromRawMappings(this._getAllModules());
return mappings.toString(undefined, {excludeSource}); return mappings.toString(undefined, {excludeSource});

View File

@ -92,4 +92,29 @@ describe('DeltaPatcher', () => {
.stringifyCode(), .stringifyCode(),
).toMatchSnapshot(); ).toMatchSnapshot();
}); });
it('should return the number of modified files in the last Delta', () => {
deltaPatcher
.applyDelta({
reset: 1,
pre: new Map([[1, {code: 'pre'}]]),
post: new Map([[2, {code: 'post'}]]),
delta: new Map([[3, {code: 'middle'}]]),
})
.stringifyCode();
expect(deltaPatcher.getLastNumModifiedFiles()).toEqual(3);
deltaPatcher
.applyDelta({
reset: 1,
pre: new Map([[1, null]]),
post: new Map(),
delta: new Map([[3, {code: 'different'}]]),
})
.stringifyCode();
// A deleted module counts as a modified file.
expect(deltaPatcher.getLastNumModifiedFiles()).toEqual(2);
});
}); });

View File

@ -105,14 +105,20 @@ class DeltaBundler {
}; };
} }
async buildFullBundle(options: FullBuildOptions): Promise<string> { async buildFullBundle(
let output = (await this._getDeltaPatcher(options)).stringifyCode(); options: FullBuildOptions,
): Promise<{bundle: string, numModifiedFiles: number}> {
const deltaPatcher = await this._getDeltaPatcher(options);
let bundle = deltaPatcher.stringifyCode();
if (options.sourceMapUrl) { if (options.sourceMapUrl) {
output += '//# sourceMappingURL=' + options.sourceMapUrl; bundle += '//# sourceMappingURL=' + options.sourceMapUrl;
} }
return output; return {
bundle,
numModifiedFiles: deltaPatcher.getLastNumModifiedFiles(),
};
} }
async buildFullSourceMap(options: FullBuildOptions): Promise<string> { async buildFullSourceMap(options: FullBuildOptions): Promise<string> {

View File

@ -924,12 +924,13 @@ class Server {
); );
let bundle; let bundle;
let numModifiedFiles;
try { try {
bundle = await this._deltaBundler.buildFullBundle({ ({bundle, numModifiedFiles} = await this._deltaBundler.buildFullBundle({
...options, ...options,
deltaBundleId: this.optionsHash(options), deltaBundleId: this.optionsHash(options),
}); }));
} catch (error) { } catch (error) {
this._handleError(res, this.optionsHash(options), error); this._handleError(res, this.optionsHash(options), error);
@ -947,14 +948,13 @@ class Server {
debug('Responding with 304'); debug('Responding with 304');
res.writeHead(304); res.writeHead(304);
res.end(); res.end();
} else {
return; res.setHeader(FILES_CHANGED_COUNT_HEADER, String(numModifiedFiles));
}
res.setHeader('Content-Type', 'application/javascript'); res.setHeader('Content-Type', 'application/javascript');
res.setHeader('ETag', etag); res.setHeader('ETag', etag);
res.setHeader('Content-Length', String(Buffer.byteLength(bundle))); res.setHeader('Content-Length', String(Buffer.byteLength(bundle)));
res.end(bundle); res.end(bundle);
}
this._reporter.update({ this._reporter.update({
buildID, buildID,
@ -962,7 +962,10 @@ class Server {
}); });
debug('Finished response'); debug('Finished response');
log(createActionEndEntry(requestingBundleLogEntry)); log({
...createActionEndEntry(requestingBundleLogEntry),
outdated_modules: numModifiedFiles,
});
} }
async _processSourceMapUsingDeltaBundler( async _processSourceMapUsingDeltaBundler(