From 5a5a509d4d943dbd0e2ca48c8d074d2db872ab9b Mon Sep 17 00:00:00 2001 From: Anton Kastritskiy Date: Tue, 10 Oct 2023 03:37:21 -0700 Subject: [PATCH] fallback to handle plugins as commonjs if dynamic import fails Summary: This should help us to make flipper more reliable and avoid failing loading plugins if they are loaded from cache(old ones) Reviewed By: lblasa Differential Revision: D50081726 fbshipit-source-id: edef9999ad44660331153a082e15c6f3f5de9b05 --- .../src/initializeRenderHost.tsx | 41 ++++++++++++++++++- 1 file changed, 39 insertions(+), 2 deletions(-) diff --git a/desktop/flipper-ui-browser/src/initializeRenderHost.tsx b/desktop/flipper-ui-browser/src/initializeRenderHost.tsx index 9c0f9d043..626d3362f 100644 --- a/desktop/flipper-ui-browser/src/initializeRenderHost.tsx +++ b/desktop/flipper-ui-browser/src/initializeRenderHost.tsx @@ -185,11 +185,48 @@ export function initializeRenderHost( // Typescript transpiles dynamic import calls to `require` in the browser bundle // We want to explicilty use dynamic import here const importStr = `import('/${staticPath}?ts=${Date.now()}')`; + const nonEsmPluginSymbol = Symbol.for('nonEsmPlugin'); const source = await flipperServer.exec('plugin-source', path); // eslint-disable-next-line no-eval - const importRes = await eval(importStr); + const importRes = await eval(importStr).catch((e: unknown) => { + if ( + e instanceof ReferenceError && + e.message.includes('module is not defined') + ) { + return nonEsmPluginSymbol; + } else { + throw e; + } + }); - return {plugin: importRes, css: source.css}; + if (importRes !== nonEsmPluginSymbol) { + return {plugin: importRes, css: source.css}; + } + // handle commonjs plugin + else { + console.log( + '[browser-ui][requirePlugin] handle plugin js as CJS', + // only log path inside of flipper project + path.split('/desktop/', 2).pop(), + ); + let js = source.js; + // append source url (to make sure a file entry shows up in the debugger) + js += `\n//# sourceURL=file://${path}`; + if (isProduction()) { + // and source map url (to get source code if available) + js += `\n//# sourceMappingURL=file://${path}.map`; + } + + // Plugins are compiled as typical CJS modules, referring to the global + // 'module', which we'll make available by loading the source into a closure that captures 'module'. + // Note that we use 'eval', and not 'new Function', because the latter will cause the source maps + // to be off by two lines (as the function declaration uses two lines in the generated source) + // eslint-disable-next-line no-eval + const cjsLoader = eval('(module) => {' + js + '\n}'); + const theModule = {exports: {}}; + cjsLoader(theModule); + return {plugin: theModule.exports, css: source.css}; + } }, getStaticResourceUrl(path): string { // the 'static' folder is mounted as static middleware in Express at the root