From 7b17ca335d4b1126592a65eedebf7546924ce829 Mon Sep 17 00:00:00 2001 From: Rafael Oleza Date: Mon, 4 Sep 2017 13:42:39 -0700 Subject: [PATCH] Add DeltaPatcher module to handle Delta bundles Reviewed By: jeanlauliac Differential Revision: D5761096 fbshipit-source-id: 8c8ef5fb720b08e3f4f097c5a76f793dc4f1c1da --- .../src/DeltaBundler/DeltaPatcher.js | 89 +++++++++++++++ .../__tests__/DeltaPatcher-test.js | 105 ++++++++++++++++++ .../__snapshots__/DeltaPatcher-test.js.snap | 28 +++++ 3 files changed, 222 insertions(+) create mode 100644 packages/metro-bundler/src/DeltaBundler/DeltaPatcher.js create mode 100644 packages/metro-bundler/src/DeltaBundler/__tests__/DeltaPatcher-test.js create mode 100644 packages/metro-bundler/src/DeltaBundler/__tests__/__snapshots__/DeltaPatcher-test.js.snap diff --git a/packages/metro-bundler/src/DeltaBundler/DeltaPatcher.js b/packages/metro-bundler/src/DeltaBundler/DeltaPatcher.js new file mode 100644 index 00000000..67281103 --- /dev/null +++ b/packages/metro-bundler/src/DeltaBundler/DeltaPatcher.js @@ -0,0 +1,89 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * @flow + * @format + */ + +'use strict'; + +import type {DeltaBundle} from './'; + +/** + * This is a reference client for the Delta Bundler: it maintains cached the + * last patched bundle delta and it's capable of applying new Deltas received + * from the Bundler and stringify them to convert them into a full bundle. + */ +class DeltaPatcher { + _lastBundle = { + pre: '', + post: '', + modules: {}, + }; + _initialized = false; + + /** + * Applies a Delta Bundle to the current bundle. + */ + applyDelta(deltaBundle: DeltaBundle) { + // Make sure that the first received delta is a fresh one. + if (!this._initialized && !deltaBundle.reset) { + throw new Error( + 'DeltaPatcher should receive a fresh Delta when being initialized', + ); + } + + this._initialized = true; + + // Reset the current delta when we receive a fresh delta. + if (deltaBundle.reset) { + this._lastBundle = { + pre: '', + post: '', + modules: {}, + }; + } + + // Override the prepended sources. + if (deltaBundle.pre) { + this._lastBundle.pre = deltaBundle.pre; + } + + // Override the appended sources. + if (deltaBundle.post) { + this._lastBundle.post = deltaBundle.post; + } + + // Patch the received modules. + for (const i in deltaBundle.delta) { + if (deltaBundle.delta[i] == null) { + delete this._lastBundle.modules[i]; + } else { + this._lastBundle.modules[i] = deltaBundle.delta[i]; + } + } + + return this; + } + + /** + * Converts the current delta bundle to a standard string bundle, ready to + * be interpreted by any JS VM. + */ + stringify() { + return [] + .concat( + this._lastBundle.pre, + Object.values(this._lastBundle.modules), + this._lastBundle.post, + ) + .join('\n;'); + } +} + +module.exports = DeltaPatcher; diff --git a/packages/metro-bundler/src/DeltaBundler/__tests__/DeltaPatcher-test.js b/packages/metro-bundler/src/DeltaBundler/__tests__/DeltaPatcher-test.js new file mode 100644 index 00000000..afb97bc4 --- /dev/null +++ b/packages/metro-bundler/src/DeltaBundler/__tests__/DeltaPatcher-test.js @@ -0,0 +1,105 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * @emails oncall+javascript_tools + * @format + */ + +'use strict'; + +const DeltaPatcher = require('../DeltaPatcher'); + +describe('DeltaPatcher', () => { + let deltaPatcher; + + beforeEach(() => { + deltaPatcher = new DeltaPatcher(); + }); + + it('should throw if received a non-reset delta as the initial one', () => { + expect(() => + deltaPatcher.applyDelta({ + pre: 'pre', + post: 'post', + delta: {}, + }), + ).toThrow(); + }); + + it('should apply an initial delta correctly', () => { + const result = deltaPatcher + .applyDelta({ + reset: 1, + pre: 'pre', + post: 'post', + delta: { + 1: 'middle', + }, + }) + .stringify(); + + expect(result).toMatchSnapshot(); + }); + + it('should apply many different patches correctly', () => { + const result = deltaPatcher + .applyDelta({ + reset: 1, + pre: 'pre', + post: 'post', + delta: { + 1: 'middle', + }, + }) + .applyDelta({ + delta: { + 2: 'another', + }, + }) + .applyDelta({ + delta: { + 2: 'another', + 87: 'third', + }, + }) + .stringify(); + + expect(result).toMatchSnapshot(); + + const anotherResult = deltaPatcher + .applyDelta({ + pre: 'new pre', + delta: { + 2: 'another', + 1: null, + }, + }) + .applyDelta({ + delta: { + 2: null, + 12: 'twelve', + }, + }) + .stringify(); + + expect(anotherResult).toMatchSnapshot(); + + expect( + deltaPatcher + .applyDelta({ + pre: '1', + post: '1', + delta: { + 12: 'ten', + }, + reset: true, + }) + .stringify(), + ).toMatchSnapshot(); + }); +}); diff --git a/packages/metro-bundler/src/DeltaBundler/__tests__/__snapshots__/DeltaPatcher-test.js.snap b/packages/metro-bundler/src/DeltaBundler/__tests__/__snapshots__/DeltaPatcher-test.js.snap new file mode 100644 index 00000000..473eca1f --- /dev/null +++ b/packages/metro-bundler/src/DeltaBundler/__tests__/__snapshots__/DeltaPatcher-test.js.snap @@ -0,0 +1,28 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`DeltaPatcher should apply an initial delta correctly 1`] = ` +"pre +;middle +;post" +`; + +exports[`DeltaPatcher should apply many different patches correctly 1`] = ` +"pre +;middle +;another +;third +;post" +`; + +exports[`DeltaPatcher should apply many different patches correctly 2`] = ` +"new pre +;twelve +;third +;post" +`; + +exports[`DeltaPatcher should apply many different patches correctly 3`] = ` +"1 +;ten +;1" +`;