DeltaPatcher: better support for the new Delta format

Summary: Adds support for the `deleted` key to remove modules from the bundle. Without this, source maps would break after removing a module, since it would still end up in the patched bundle but not in the source map.

Reviewed By: mjesun

Differential Revision: D12874011

fbshipit-source-id: 79239756854cb2c02f14ec8b0bb2b649766393fe
This commit is contained in:
Alexandre Kirszenberg 2018-11-12 08:38:41 -08:00 committed by Facebook Github Bot
parent 0436bfce3e
commit bea57d871f
3 changed files with 131 additions and 27 deletions

View File

@ -63,24 +63,33 @@
// Reset the current bundle when we receive a base bundle.
if (bundle.base) {
this._lastNumModifiedFiles = bundle.modules.length;
this._lastBundle = {
revisionId: undefined,
revisionId: bundle.revisionId,
pre: bundle.pre,
post: bundle.post,
modules: new Map(),
modules: new Map(bundle.modules),
};
}
} else {
this._lastNumModifiedFiles =
bundle.modules.length + bundle.deleted.length;
this._lastNumModifiedFiles = bundle.modules.size;
this._lastBundle.revisionId = bundle.revisionId;
for (const [key, value] of bundle.modules) {
this._lastBundle.modules.set(key, value);
}
for (const id of bundle.deleted) {
this._lastBundle.modules.delete(id);
}
}
if (this._lastNumModifiedFiles > 0) {
this._lastModifiedDate = new Date();
}
this._patchMap(this._lastBundle.modules, bundle.modules);
this._lastBundle.revisionId = bundle.revisionId;
return this;
}
@ -104,21 +113,11 @@
getAllModules() {
return [].concat(
this._lastBundle.pre,
[this._lastBundle.pre],
Array.from(this._lastBundle.modules.values()),
this._lastBundle.post,
[this._lastBundle.post],
);
}
_patchMap(original, patch) {
for (const [key, value] of patch.entries()) {
if (value == null) {
original.delete(key);
} else {
original.set(key, value);
}
}
}
}
DeltaPatcher._deltaPatchers = new Map();

View File

@ -0,0 +1,111 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @format
* @emails oncall+javascript_foundation
*/
'use strict';
describe('DeltaPatcher', () => {
const window = (global.window = {});
require('../DeltaPatcher');
it('should initialize to an empty bundle', () => {
global.Date = jest.fn();
const dp = new window.DeltaPatcher();
expect(dp.getLastRevisionId()).toBe(undefined);
expect(dp.getLastModifiedDate()).toBe(global.Date.mock.instances[0]);
expect(dp.getLastNumModifiedFiles()).toBe(0);
// Empty pre and post.
expect(dp.getAllModules()).toEqual(['', '']);
});
it('should expect a base bundle at initialization', () => {
const dp = new window.DeltaPatcher();
expect(() => {
dp.applyDelta({
base: false,
revisionId: 'hello',
modules: [],
deleted: [],
});
}).toThrow();
});
it('should accept a base bundle at initialization', () => {
const dp = new window.DeltaPatcher();
global.Date = jest.fn();
dp.applyDelta({
base: true,
revisionId: 'rev0',
pre: 'pre0',
post: 'post0',
modules: [[0, '__d(0);']],
});
expect(dp.getLastRevisionId()).toBe('rev0');
expect(dp.getLastModifiedDate()).toBe(global.Date.mock.instances[0]);
expect(dp.getLastNumModifiedFiles()).toBe(1);
expect(dp.getAllModules()).toEqual(['pre0', '__d(0);', 'post0']);
});
it('should accept a delta bundle after a base bundle', () => {
const dp = new window.DeltaPatcher();
dp.applyDelta({
base: true,
revisionId: 'rev0',
pre: 'pre0',
post: 'post0',
modules: [[0, '__d(0);'], [1, '__d(1);'], [2, '__d(2);']],
});
global.Date = jest.fn();
dp.applyDelta({
base: false,
revisionId: 'rev1',
modules: [[1, '__d(1.1);'], [3, '__d(3);']],
deleted: [0],
});
expect(dp.getLastRevisionId()).toBe('rev1');
expect(dp.getLastModifiedDate()).toBe(global.Date.mock.instances[0]);
expect(dp.getLastNumModifiedFiles()).toBe(3);
expect(dp.getAllModules()).toEqual([
'pre0',
'__d(1.1);',
'__d(2);',
'__d(3);',
'post0',
]);
});
it('should accept a base bundle after initialization', () => {
const dp = new window.DeltaPatcher();
dp.applyDelta({
base: true,
revisionId: 'rev0',
pre: 'pre0',
post: 'post0',
modules: [[0, '__d(0);'], [1, '__d(1);'], [2, '__d(2);']],
});
dp.applyDelta({
base: false,
revisionId: 'rev1',
modules: [[1, '__d(1.1);'], [3, '__d(3);']],
deleted: [0],
});
global.Date = jest.fn();
dp.applyDelta({
base: true,
revisionId: 'rev2',
pre: 'pre2',
post: 'post2',
modules: [[4, '__d(4);'], [5, '__d(5);']],
});
expect(dp.getLastRevisionId()).toBe('rev2');
expect(dp.getLastModifiedDate()).toBe(global.Date.mock.instances[0]);
expect(dp.getLastNumModifiedFiles()).toBe(2);
expect(dp.getAllModules()).toEqual(['pre2', '__d(4);', '__d(5);', 'post2']);
});
});

View File

@ -29,13 +29,7 @@
const data = await fetch(deltaUrl + revisionId);
const bundle = await data.json();
const deltaPatcher = client.applyDelta({
base: bundle.base,
revisionId: bundle.revisionId,
pre: bundle.pre,
post: bundle.post,
modules: new Map(bundle.modules),
});
const deltaPatcher = client.applyDelta(bundle);
let cachedBundle = cachedBundleUrls.get(deltaUrl);