From 3054d8cf871ed9ea17ce4b0558e27034909ea72d Mon Sep 17 00:00:00 2001 From: David Alsh <12656294+alshdavid@users.noreply.github.com> Date: Wed, 24 Jul 2024 09:57:33 +1000 Subject: [PATCH] Add retriable dynamic imports behind a feature flag (#9860) * import retries * cache result if succesful --- packages/core/core/test/test-utils.js | 1 + packages/core/feature-flags/src/index.js | 1 + packages/core/feature-flags/src/types.js | 4 +++ packages/runtimes/js/src/JSRuntime.js | 18 +++++++++++++ .../helpers/browser/esm-js-loader-retry.js | 26 +++++++++++++++++++ 5 files changed, 50 insertions(+) create mode 100644 packages/runtimes/js/src/helpers/browser/esm-js-loader-retry.js diff --git a/packages/core/core/test/test-utils.js b/packages/core/core/test/test-utils.js index 0fa89c3315f..b9331e652f8 100644 --- a/packages/core/core/test/test-utils.js +++ b/packages/core/core/test/test-utils.js @@ -55,6 +55,7 @@ export const DEFAULT_OPTIONS: ParcelOptions = { featureFlags: { exampleFeature: false, parcelV3: false, + importRetry: false, }, }; diff --git a/packages/core/feature-flags/src/index.js b/packages/core/feature-flags/src/index.js index 4bcc5d71993..96819652129 100644 --- a/packages/core/feature-flags/src/index.js +++ b/packages/core/feature-flags/src/index.js @@ -8,6 +8,7 @@ export type FeatureFlags = _FeatureFlags; export const DEFAULT_FEATURE_FLAGS: FeatureFlags = { exampleFeature: false, parcelV3: false, + importRetry: false, }; let featureFlagValues: FeatureFlags = {...DEFAULT_FEATURE_FLAGS}; diff --git a/packages/core/feature-flags/src/types.js b/packages/core/feature-flags/src/types.js index fa78c4c7b81..73ef3f63611 100644 --- a/packages/core/feature-flags/src/types.js +++ b/packages/core/feature-flags/src/types.js @@ -7,4 +7,8 @@ export type FeatureFlags = {| * Rust backed requests */ +parcelV3: boolean, + /** + * Configure runtime to enable retriable dynamic imports + */ + importRetry: boolean, |}; diff --git a/packages/runtimes/js/src/JSRuntime.js b/packages/runtimes/js/src/JSRuntime.js index d66e1d6b530..f5058c1c542 100644 --- a/packages/runtimes/js/src/JSRuntime.js +++ b/packages/runtimes/js/src/JSRuntime.js @@ -484,6 +484,24 @@ function getLoaderRuntime({ )}'))`; } + if (needsEsmLoadPrelude && options.featureFlags.importRetry) { + loaderCode = ` + Object.defineProperty(module, 'exports', { get: () => { + let load = require('./helpers/browser/esm-js-loader-retry'); + return ${loaderCode}.then((v) => { + Object.defineProperty(module, "exports", { value: Promise.resolve(v) }) + return v + }); + }})`; + + return { + filePath: __filename, + code: loaderCode, + dependency, + env: {sourceType: 'module'}, + }; + } + let code = []; if (needsEsmLoadPrelude) { diff --git a/packages/runtimes/js/src/helpers/browser/esm-js-loader-retry.js b/packages/runtimes/js/src/helpers/browser/esm-js-loader-retry.js new file mode 100644 index 00000000000..1f729b6b914 --- /dev/null +++ b/packages/runtimes/js/src/helpers/browser/esm-js-loader-retry.js @@ -0,0 +1,26 @@ +async function load(id) { + if (!parcelRequire.retryState) { + parcelRequire.retryState = {}; + } + + if (!globalThis.navigator.onLine) { + await new Promise(res => + globalThis.addEventListener('online', res, {once: true}), + ); + } + + let url = require('../bundle-manifest').resolve(id); + + if (parcelRequire.retryState[id] != undefined) { + url = `${url}?retry=${parcelRequire.retryState[id]}`; + } + + try { + // eslint-disable-next-line no-undef + return await __parcel__import__(url); + } catch (error) { + parcelRequire.retryState[id] = Date.now(); + } +} + +module.exports = load;