mirror of https://github.com/status-im/metro.git
Adding ETag handling to packager improves local development
Summary: Closes https://github.com/facebook/react-native/pull/2473 Reviewed By: svcscm Differential Revision: D2669385 Pulled By: mkonicek fb-gh-sync-id: bb9ee4a309a05e0da0ccc7663a373f4a53b9a0a2
This commit is contained in:
parent
d1ae909bbb
commit
0dbc239685
|
@ -185,7 +185,7 @@ describe('AssetServer', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('assetSerer.getAssetData', () => {
|
||||
describe('assetServer.getAssetData', () => {
|
||||
pit('should get assetData', () => {
|
||||
const hash = {
|
||||
update: jest.genMockFn(),
|
||||
|
|
|
@ -14,6 +14,7 @@ const BundleBase = require('./BundleBase');
|
|||
const UglifyJS = require('uglify-js');
|
||||
const ModuleTransport = require('../lib/ModuleTransport');
|
||||
const Activity = require('../Activity');
|
||||
const crypto = require('crypto');
|
||||
|
||||
const SOURCEMAPPING_URL = '\n\/\/# sourceMappingURL=';
|
||||
|
||||
|
@ -256,6 +257,11 @@ class Bundle extends BundleBase {
|
|||
return map;
|
||||
}
|
||||
|
||||
getEtag() {
|
||||
var eTag = crypto.createHash('md5').update(this.getSource()).digest('hex');
|
||||
return eTag;
|
||||
}
|
||||
|
||||
_getMappings() {
|
||||
const modules = super.getModules();
|
||||
|
||||
|
|
|
@ -15,6 +15,7 @@ const ModuleTransport = require('../../lib/ModuleTransport');
|
|||
const Promise = require('Promise');
|
||||
const SourceMapGenerator = require('source-map').SourceMapGenerator;
|
||||
const UglifyJS = require('uglify-js');
|
||||
const crypto = require('crypto');
|
||||
|
||||
describe('Bundle', () => {
|
||||
var bundle;
|
||||
|
@ -301,6 +302,15 @@ describe('Bundle', () => {
|
|||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('getEtag()', function() {
|
||||
it('should return an etag', function() {
|
||||
var bundle = new Bundle('test_url');
|
||||
bundle.finalize({});
|
||||
var eTag = crypto.createHash('md5').update(bundle.getSource()).digest('hex');
|
||||
expect(bundle.getEtag()).toEqual(eTag);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
|
|
|
@ -14,7 +14,8 @@ jest.setMock('worker-farm', function() { return () => {}; })
|
|||
.dontMock('url')
|
||||
.setMock('timers', { setImmediate: (fn) => setTimeout(fn, 0) })
|
||||
.setMock('uglify-js')
|
||||
.dontMock('../');
|
||||
.dontMock('../')
|
||||
.setMock('crypto');
|
||||
|
||||
const Promise = require('promise');
|
||||
|
||||
|
@ -34,12 +35,17 @@ describe('processRequest', () => {
|
|||
polyfillModuleNames: null
|
||||
};
|
||||
|
||||
const makeRequest = (reqHandler, requrl) => new Promise(resolve =>
|
||||
const makeRequest = (reqHandler, requrl, reqOptions) => new Promise(resolve =>
|
||||
reqHandler(
|
||||
{ url: requrl },
|
||||
{ url: requrl, headers:{}, ...reqOptions },
|
||||
{
|
||||
setHeader: jest.genMockFunction(),
|
||||
end: res => resolve(res),
|
||||
headers: {},
|
||||
getHeader(header) { return this.headers[header]; },
|
||||
setHeader(header, value) { this.headers[header] = value; },
|
||||
end(body) {
|
||||
this.body = body;
|
||||
resolve(this);
|
||||
},
|
||||
},
|
||||
{ next: () => {} },
|
||||
)
|
||||
|
@ -55,6 +61,7 @@ describe('processRequest', () => {
|
|||
Promise.resolve({
|
||||
getSource: () => 'this is the source',
|
||||
getSourceMap: () => 'this is the source map',
|
||||
getEtag: () => 'this is an etag',
|
||||
})
|
||||
);
|
||||
|
||||
|
@ -76,9 +83,10 @@ describe('processRequest', () => {
|
|||
pit('returns JS bundle source on request of *.bundle', () => {
|
||||
return makeRequest(
|
||||
requestHandler,
|
||||
'mybundle.bundle?runModule=true'
|
||||
'mybundle.bundle?runModule=true',
|
||||
null
|
||||
).then(response =>
|
||||
expect(response).toEqual('this is the source')
|
||||
expect(response.body).toEqual('this is the source')
|
||||
);
|
||||
});
|
||||
|
||||
|
@ -87,16 +95,35 @@ describe('processRequest', () => {
|
|||
requestHandler,
|
||||
'mybundle.runModule.bundle'
|
||||
).then(response =>
|
||||
expect(response).toEqual('this is the source')
|
||||
expect(response.body).toEqual('this is the source')
|
||||
);
|
||||
});
|
||||
|
||||
pit('returns ETag header on request of *.bundle', () => {
|
||||
return makeRequest(
|
||||
requestHandler,
|
||||
'mybundle.bundle?runModule=true'
|
||||
).then(response => {
|
||||
expect(response.getHeader('ETag')).toBeDefined()
|
||||
});
|
||||
});
|
||||
|
||||
pit('returns 304 on request of *.bundle when if-none-match equals the ETag', () => {
|
||||
return makeRequest(
|
||||
requestHandler,
|
||||
'mybundle.bundle?runModule=true',
|
||||
{ headers : { 'if-none-match' : 'this is an etag' } }
|
||||
).then(response => {
|
||||
expect(response.statusCode).toEqual(304)
|
||||
});
|
||||
});
|
||||
|
||||
pit('returns sourcemap on request of *.map', () => {
|
||||
return makeRequest(
|
||||
requestHandler,
|
||||
'mybundle.map?runModule=true'
|
||||
).then(response =>
|
||||
expect(response).toEqual('this is the source map')
|
||||
expect(response.body).toEqual('this is the source map')
|
||||
);
|
||||
});
|
||||
|
||||
|
@ -105,7 +132,7 @@ describe('processRequest', () => {
|
|||
requestHandler,
|
||||
'index.ios.includeRequire.bundle'
|
||||
).then(response => {
|
||||
expect(response).toEqual('this is the source');
|
||||
expect(response.body).toEqual('this is the source');
|
||||
expect(Bundler.prototype.bundle).toBeCalledWith({
|
||||
entryFile: 'index.ios.js',
|
||||
inlineSourceMap: false,
|
||||
|
@ -126,7 +153,7 @@ describe('processRequest', () => {
|
|||
requestHandler,
|
||||
'index.bundle?platform=ios'
|
||||
).then(function(response) {
|
||||
expect(response).toEqual('this is the source');
|
||||
expect(response.body).toEqual('this is the source');
|
||||
expect(Bundler.prototype.bundle).toBeCalledWith({
|
||||
entryFile: 'index.js',
|
||||
inlineSourceMap: false,
|
||||
|
@ -183,12 +210,14 @@ describe('processRequest', () => {
|
|||
Promise.resolve({
|
||||
getSource: () => 'this is the first source',
|
||||
getSourceMap: () => {},
|
||||
getEtag: () => () => 'this is an etag',
|
||||
})
|
||||
)
|
||||
.mockReturnValue(
|
||||
Promise.resolve({
|
||||
getSource: () => 'this is the rebuilt source',
|
||||
getSourceMap: () => {},
|
||||
getEtag: () => () => 'this is an etag',
|
||||
})
|
||||
);
|
||||
|
||||
|
@ -200,7 +229,7 @@ describe('processRequest', () => {
|
|||
|
||||
makeRequest(requestHandler, 'mybundle.bundle?runModule=true')
|
||||
.done(response => {
|
||||
expect(response).toEqual('this is the first source');
|
||||
expect(response.body).toEqual('this is the first source');
|
||||
expect(bundleFunc.mock.calls.length).toBe(1);
|
||||
});
|
||||
|
||||
|
@ -214,7 +243,7 @@ describe('processRequest', () => {
|
|||
|
||||
makeRequest(requestHandler, 'mybundle.bundle?runModule=true')
|
||||
.done(response =>
|
||||
expect(response).toEqual('this is the rebuilt source')
|
||||
expect(response.body).toEqual('this is the rebuilt source')
|
||||
);
|
||||
jest.runAllTicks();
|
||||
}
|
||||
|
|
|
@ -430,7 +430,13 @@ class Server {
|
|||
dev: options.dev,
|
||||
});
|
||||
res.setHeader('Content-Type', 'application/javascript');
|
||||
res.end(bundleSource);
|
||||
res.setHeader('ETag', p.getEtag());
|
||||
if (req.headers['if-none-match'] === res.getHeader('ETag')){
|
||||
res.statusCode = 304;
|
||||
res.end();
|
||||
} else {
|
||||
res.end(bundleSource);
|
||||
}
|
||||
Activity.endEvent(startReqEventId);
|
||||
} else if (requestType === 'map') {
|
||||
var sourceMap = p.getSourceMap({
|
||||
|
|
Loading…
Reference in New Issue