From d0f1f4e129926316edab8e75768d640649ad4313 Mon Sep 17 00:00:00 2001 From: "Robert Hurst (Dialpad)" <43556103+rhurstdialpad@users.noreply.github.com> Date: Mon, 25 Feb 2019 10:44:14 -0800 Subject: [PATCH] Prevent circular deps from causing a stack overflow in HMR runtime (#2660) --- packages/core/integration-tests/test/hmr.js | 31 +++++++++++++++++++ .../test/integration/hmr-circular/index.js | 9 ++++++ .../test/integration/hmr-circular/local.js | 4 +++ .../src/builtins/hmr-runtime.js | 8 +++++ 4 files changed, 52 insertions(+) create mode 100644 packages/core/integration-tests/test/integration/hmr-circular/index.js create mode 100644 packages/core/integration-tests/test/integration/hmr-circular/local.js diff --git a/packages/core/integration-tests/test/hmr.js b/packages/core/integration-tests/test/hmr.js index e06276c6e30..4766bac154b 100644 --- a/packages/core/integration-tests/test/hmr.js +++ b/packages/core/integration-tests/test/hmr.js @@ -287,6 +287,37 @@ describe('hmr', function() { assert.deepEqual(outputs, [3, 10]); }); + it('should work with circular dependencies', async function() { + await ncp( + path.join(__dirname, '/integration/hmr-circular'), + path.join(__dirname, '/input') + ); + + b = bundler(path.join(__dirname, '/input/index.js'), { + watch: true, + hmr: true + }); + let bundle = await b.bundle(); + let outputs = []; + + await run(bundle, { + output(o) { + outputs.push(o); + } + }); + + assert.deepEqual(outputs, [3]); + + await sleep(100); + fs.writeFile( + path.join(__dirname, '/input/local.js'), + "var other = require('./index.js'); exports.a = 5; exports.b = 5;" + ); + + await nextEvent(b, 'bundled'); + assert.deepEqual(outputs, [3, 10]); + }); + it('should call dispose and accept callbacks', async function() { await ncp( path.join(__dirname, '/integration/hmr-callbacks'), diff --git a/packages/core/integration-tests/test/integration/hmr-circular/index.js b/packages/core/integration-tests/test/integration/hmr-circular/index.js new file mode 100644 index 00000000000..72533c84aa2 --- /dev/null +++ b/packages/core/integration-tests/test/integration/hmr-circular/index.js @@ -0,0 +1,9 @@ +var local = require('./local'); + +function run() { + output(local.a + local.b); +} + +run(); + +module.exports = 'value'; diff --git a/packages/core/integration-tests/test/integration/hmr-circular/local.js b/packages/core/integration-tests/test/integration/hmr-circular/local.js new file mode 100644 index 00000000000..3bb6f4669b5 --- /dev/null +++ b/packages/core/integration-tests/test/integration/hmr-circular/local.js @@ -0,0 +1,4 @@ +var other = require('./index.js'); + +exports.a = 1; +exports.b = 2; diff --git a/packages/core/parcel-bundler/src/builtins/hmr-runtime.js b/packages/core/parcel-bundler/src/builtins/hmr-runtime.js index 74efb86d4d6..766bb1592ee 100644 --- a/packages/core/parcel-bundler/src/builtins/hmr-runtime.js +++ b/packages/core/parcel-bundler/src/builtins/hmr-runtime.js @@ -20,6 +20,7 @@ function Module(moduleName) { } module.bundle.Module = Module; +var updatedAssets; var parent = module.bundle.parent; if ((!parent || !parent.isParcelRequire) && typeof WebSocket !== 'undefined') { @@ -27,6 +28,8 @@ if ((!parent || !parent.isParcelRequire) && typeof WebSocket !== 'undefined') { var protocol = location.protocol === 'https:' ? 'wss' : 'ws'; var ws = new WebSocket(protocol + '://' + hostname + ':' + process.env.HMR_PORT + '/'); ws.onmessage = function(event) { + updatedAssets = {}; + var data = JSON.parse(event.data); if (data.type === 'update') { @@ -147,6 +150,11 @@ function hmrAccept(bundle, id) { return hmrAccept(bundle.parent, id); } + if (updatedAssets[id]) { + return; + } + updatedAssets[id] = true; + var cached = bundle.cache[id]; bundle.hotData = {}; if (cached) {