From 29549d95383d077c4de9dd90a7d9874c41523b39 Mon Sep 17 00:00:00 2001 From: Juan Tejada Date: Thu, 14 Oct 2021 14:18:37 -0400 Subject: [PATCH] Local builds now are always enabled over duplicates, build step generates stable id for local builds --- packages/react-devtools-extensions/build.js | 4 +- .../src/checkForDuplicateInstallations.js | 154 ++++++++++-------- .../src/constants.js | 16 +- .../react-devtools-extensions/src/main.js | 7 + 4 files changed, 103 insertions(+), 78 deletions(-) diff --git a/packages/react-devtools-extensions/build.js b/packages/react-devtools-extensions/build.js index dc61f5bc8eed9..c23e318f4b8b6 100644 --- a/packages/react-devtools-extensions/build.js +++ b/packages/react-devtools-extensions/build.js @@ -103,9 +103,7 @@ const build = async (tempPath, manifestPath) => { manifest.description += `\n\nCreated from revision ${commit} on ${dateString}.`; if (process.env.NODE_ENV === 'development') { - if (Array.isArray(manifest.permissions)) { - manifest.permissions.push('management'); - } + manifest.key = 'reactdevtoolslocalbuilduniquekey'; } writeFileSync(copiedManifestPath, JSON.stringify(manifest, null, 2)); diff --git a/packages/react-devtools-extensions/src/checkForDuplicateInstallations.js b/packages/react-devtools-extensions/src/checkForDuplicateInstallations.js index ea70b6ae37b83..f5f39991ee411 100644 --- a/packages/react-devtools-extensions/src/checkForDuplicateInstallations.js +++ b/packages/react-devtools-extensions/src/checkForDuplicateInstallations.js @@ -14,85 +14,52 @@ import { EXTENSION_INSTALL_CHECK_MESSAGE, EXTENSION_INSTALLATION_TYPE, INTERNAL_EXTENSION_ID, - EXTENSION_NAME, + LOCAL_EXTENSION_ID, } from './constants'; +const UNRECOGNIZED_EXTENSION_WARNING = + 'React Developer Tools: You are running an unrecognized installation of the React Developer Tools extension, which might conflict with other versions of the extension installed in your browser. ' + + 'Please make sure you only have a single version of the extension installed or enabled. ' + + 'If you are developing this extension locally, make sure to build the extension using the `yarn build::local` command.'; + export function checkForDuplicateInstallations(callback: boolean => void) { switch (EXTENSION_INSTALLATION_TYPE) { case 'public': { // If this is the public extension (e.g. from Chrome Web Store), check if an internal - // build of the extension is also installed, and if so, disable this extension. - chrome.runtime.sendMessage( + // or local build of the extension is also installed, and if so, disable this extension. + checkForInstalledExtensions([ INTERNAL_EXTENSION_ID, - EXTENSION_INSTALL_CHECK_MESSAGE, - response => { - if (__DEBUG__) { - console.log( - 'checkForDuplicateInstallations: Duplicate installation check responded with', - { - response, - error: chrome.runtime.lastError?.message, - currentExtension: EXTENSION_INSTALLATION_TYPE, - }, - ); - } - if (chrome.runtime.lastError != null) { - callback(false); - } else { - callback(true); - } - }, - ); + LOCAL_EXTENSION_ID, + ]).then(areExtensionsInstalled => { + if (areExtensionsInstalled.some(isInstalled => isInstalled)) { + callback(true); + } else { + callback(false); + } + }); break; } case 'internal': { - // If this is the internal extension, keep this one enabled. - // Other installations disable themselves if they detect this installation. + // If this is the internal extension, check if a local build of the extension + // is also installed, and if so, disable this extension. + // If the public version of the extension is also installed, that extension + // will disable itself. // TODO show warning if other installations are present. - callback(false); + checkForInstalledExtension(LOCAL_EXTENSION_ID).then(isInstalled => { + if (isInstalled) { + callback(true); + } else { + callback(false); + } + }); break; } - case 'unknown': { + case 'local': { if (__DEV__) { - // If this extension was built locally during development, then we check for other - // installations of the extension via the `chrome.management` API (which is only - // enabled in local development builds). - // If we detect other installations, we disable this one and show a warning - // for the developer to disable the other installations. - // NOTE: Ideally in this case we would disable any other extensions except the - // development one. However, since we don't have a stable extension ID for dev builds, - // doing so would require for other installations to wait for a message from this extension, - // which would unnecessarily delay initialization of those extensions. - chrome.management.getAll(extensions => { - if (chrome.runtime.lastError != null) { - const errorMessage = - 'React Developer Tools: Unable to access `chrome.management` to check for duplicate extensions. This extension will be disabled. ' + - 'If you are developing this extension locally, make sure to build the extension using the `yarn build::local` command.'; - console.error(errorMessage); - chrome.devtools.inspectedWindow.eval( - `console.error("${errorMessage}")`, - ); - callback(true); - return; - } - const devToolsExtensions = extensions.filter( - extension => extension.name === EXTENSION_NAME && extension.enabled, - ); - if (devToolsExtensions.length > 1) { - // TODO: Show warning in UI of extension that remains enabled - const errorMessage = - 'React Developer Tools: You are running multiple installations of the React Developer Tools extension, which will conflict with this development build of the extension. ' + - 'In order to prevent conflicts, this development build of the extension will be disabled. In order to continue local development, please disable or uninstall ' + - 'any other installations of the extension in your browser.'; - chrome.devtools.inspectedWindow.eval( - `console.error("${errorMessage}")`, - ); - console.error(errorMessage); - callback(true); - } else { - callback(false); - } - }); + // If this is the local extension (i.e. built locally during development), + // always keep this one enabled. Other installations disable themselves if + // they detect the local build is installed. + callback(false); break; } @@ -100,12 +67,22 @@ export function checkForDuplicateInstallations(callback: boolean => void) { // detect if there are other installations of DevTools present. // In this case, assume there are no duplicate exensions and show a warning about // potential conflicts. - const warnMessage = - 'React Developer Tools: You are running an unrecognized installation of the React Developer Tools extension, which might conflict with other versions of the extension installed in your browser. ' + - 'Please make sure you only have a single version of the extension installed or enabled. ' + - 'If you are developing this extension locally, make sure to build the extension using the `yarn build::local` command.'; - console.warn(warnMessage); - chrome.devtools.inspectedWindow.eval(`console.warn("${warnMessage}")`); + console.error(UNRECOGNIZED_EXTENSION_WARNING); + chrome.devtools.inspectedWindow.eval( + `console.error("${UNRECOGNIZED_EXTENSION_WARNING}")`, + ); + callback(false); + break; + } + case 'unknown': { + // If we don't know how this extension was built, we can't reliably detect if there + // are other installations of DevTools present. + // In this case, assume there are no duplicate exensions and show a warning about + // potential conflicts. + console.error(UNRECOGNIZED_EXTENSION_WARNING); + chrome.devtools.inspectedWindow.eval( + `console.error("${UNRECOGNIZED_EXTENSION_WARNING}")`, + ); callback(false); break; } @@ -114,3 +91,38 @@ export function checkForDuplicateInstallations(callback: boolean => void) { } } } + +function checkForInstalledExtensions( + extensionIds: string[], +): Promise { + return Promise.all( + extensionIds.map(extensionId => checkForInstalledExtension(extensionId)), + ); +} + +function checkForInstalledExtension(extensionId: string): Promise { + return new Promise(resolve => { + chrome.runtime.sendMessage( + extensionId, + EXTENSION_INSTALL_CHECK_MESSAGE, + response => { + if (__DEBUG__) { + console.log( + 'checkForDuplicateInstallations: Duplicate installation check responded with', + { + response, + error: chrome.runtime.lastError?.message, + currentExtension: EXTENSION_INSTALLATION_TYPE, + checkingExtension: extensionId, + }, + ); + } + if (chrome.runtime.lastError != null) { + resolve(false); + } else { + resolve(true); + } + }, + ); + }); +} diff --git a/packages/react-devtools-extensions/src/constants.js b/packages/react-devtools-extensions/src/constants.js index e6db00883e428..2aad30a92c813 100644 --- a/packages/react-devtools-extensions/src/constants.js +++ b/packages/react-devtools-extensions/src/constants.js @@ -11,15 +11,23 @@ declare var chrome: any; export const CURRENT_EXTENSION_ID = chrome.runtime.id; -export const EXTENSION_NAME = 'React Developer Tools'; export const EXTENSION_INSTALL_CHECK_MESSAGE = 'extension-install-check'; -export const CHROME_WEBSTORE_EXTENSION_ID = 'fmkadmapgofadopljbjfkapdkoienihi'; -export const INTERNAL_EXTENSION_ID = 'dnjnjgbfilfphmojnmhliehogmojhclc'; +// export const CHROME_WEBSTORE_EXTENSION_ID = 'fmkadmapgofadopljbjfkapdkoienihi'; +// export const INTERNAL_EXTENSION_ID = 'dnjnjgbfilfphmojnmhliehogmojhclc'; +export const CHROME_WEBSTORE_EXTENSION_ID = 'blaipngpacjhjmfmchjgmhmlhjemncgl'; +export const INTERNAL_EXTENSION_ID = 'bpocpipemfjjfjefjdnikdhpgmbanpla'; +export const LOCAL_EXTENSION_ID = 'ikiahnapldjmdmpkmfhjdjilojjhgcbf'; -export const EXTENSION_INSTALLATION_TYPE: 'public' | 'internal' | 'unknown' = +export const EXTENSION_INSTALLATION_TYPE: + | 'public' + | 'internal' + | 'local' + | 'unknown' = CURRENT_EXTENSION_ID === CHROME_WEBSTORE_EXTENSION_ID ? 'public' : CURRENT_EXTENSION_ID === INTERNAL_EXTENSION_ID ? 'internal' + : CURRENT_EXTENSION_ID === LOCAL_EXTENSION_ID + ? 'local' : 'unknown'; diff --git a/packages/react-devtools-extensions/src/main.js b/packages/react-devtools-extensions/src/main.js index 93af67fd337a3..8280158e1db70 100644 --- a/packages/react-devtools-extensions/src/main.js +++ b/packages/react-devtools-extensions/src/main.js @@ -85,6 +85,13 @@ function createPanelIfReactLoaded() { return; } + if (__DEBUG__) { + console.log( + '[main] createPanelIfReactLoaded: No duplicate installations detected, continuing with initialization.', + {currentExtension: EXTENSION_INSTALLATION_TYPE}, + ); + } + panelCreated = true; clearInterval(loadCheckInterval);