Skip to content

Commit

Permalink
Local builds now are always enabled over duplicates, build step gener…
Browse files Browse the repository at this point in the history
…ates stable id for local builds
  • Loading branch information
Juan Tejada committed Oct 14, 2021
1 parent 91920c4 commit 29549d9
Show file tree
Hide file tree
Showing 4 changed files with 103 additions and 78 deletions.
4 changes: 1 addition & 3 deletions packages/react-devtools-extensions/build.js
Original file line number Diff line number Diff line change
Expand Up @@ -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));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,98 +14,75 @@ 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:<browser>: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:<browser>: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;
}

// If this extension wasn't built locally during development, 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.
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:<browser>: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;
}
Expand All @@ -114,3 +91,38 @@ export function checkForDuplicateInstallations(callback: boolean => void) {
}
}
}

function checkForInstalledExtensions(
extensionIds: string[],
): Promise<boolean[]> {
return Promise.all(
extensionIds.map(extensionId => checkForInstalledExtension(extensionId)),
);
}

function checkForInstalledExtension(extensionId: string): Promise<boolean> {
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);
}
},
);
});
}
16 changes: 12 additions & 4 deletions packages/react-devtools-extensions/src/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -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';
7 changes: 7 additions & 0 deletions packages/react-devtools-extensions/src/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down

0 comments on commit 29549d9

Please sign in to comment.