Skip to content

Commit

Permalink
Merge branch 'main' into optimize-end-tag
Browse files Browse the repository at this point in the history
  • Loading branch information
yujunjung authored Oct 16, 2023
2 parents 31f0471 + 67cc9ba commit d9ca72a
Show file tree
Hide file tree
Showing 22 changed files with 216 additions and 118 deletions.
8 changes: 8 additions & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -441,6 +441,14 @@ module.exports = {
TaskController: 'readonly',
},
},
{
files: ['packages/react-devtools-extensions/**/*.js'],
globals: {
__IS_CHROME__: 'readonly',
__IS_FIREFOX__: 'readonly',
__IS_EDGE__: 'readonly',
},
},
],

env: {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
/* global chrome */

import {IS_FIREFOX} from '../utils';

// Firefox doesn't support ExecutionWorld.MAIN yet
// equivalent logic for Firefox is in prepareInjection.js
const contentScriptsToInject = IS_FIREFOX
const contentScriptsToInject = __IS_FIREFOX__
? [
{
id: '@react-devtools/proxy',
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
/* global chrome */

import {IS_FIREFOX} from '../utils';

// Firefox doesn't support ExecutionWorld.MAIN yet
// https://bugzilla.mozilla.org/show_bug.cgi?id=1736575
function executeScriptForFirefoxInMainWorld({target, files}) {
Expand Down Expand Up @@ -46,7 +44,7 @@ export function executeScriptInIsolatedWorld({target, files}) {
}

export function executeScriptInMainWorld({target, files}) {
if (IS_FIREFOX) {
if (__IS_FIREFOX__) {
return executeScriptForFirefoxInMainWorld({target, files});
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,8 @@

'use strict';

import {IS_FIREFOX} from 'react-devtools-extensions/src/utils';

function setExtensionIconAndPopup(reactBuildType, tabId) {
const action = IS_FIREFOX ? chrome.browserAction : chrome.action;
const action = __IS_FIREFOX__ ? chrome.browserAction : chrome.action;

action.setIcon({
tabId,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@

'use strict';

import {IS_FIREFOX} from 'react-devtools-extensions/src/utils';

import setExtensionIconAndPopup from './setExtensionIconAndPopup';

function isRestrictedBrowserPage(url) {
Expand All @@ -20,7 +18,7 @@ function checkAndHandleRestrictedPageIfSo(tab) {
// we can't update for any other types (prod,dev,outdated etc)
// as the content script needs to be injected at document_start itself for those kinds of detection
// TODO: Show a different popup page(to reload current page probably) for old tabs, opened before the extension is installed
if (!IS_FIREFOX) {
if (__IS_CHROME__ || __IS_EDGE__) {
chrome.tabs.query({}, tabs => tabs.forEach(checkAndHandleRestrictedPageIfSo));
chrome.tabs.onCreated.addListener((tabId, changeInfo, tab) =>
checkAndHandleRestrictedPageIfSo(tab),
Expand All @@ -29,7 +27,7 @@ if (!IS_FIREFOX) {

// Listen to URL changes on the active tab and update the DevTools icon.
chrome.tabs.onUpdated.addListener((tabId, changeInfo, tab) => {
if (IS_FIREFOX) {
if (__IS_FIREFOX__) {
// We don't properly detect protected URLs in Firefox at the moment.
// However, we can reset the DevTools icon to its loading state when the URL changes.
// It will be updated to the correct icon by the onMessage callback below.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
/* global chrome */

import nullthrows from 'nullthrows';
import {IS_FIREFOX} from '../utils';

// We run scripts on the page via the service worker (background/index.js) for
// Manifest V3 extensions (Chrome & Edge).
Expand Down Expand Up @@ -62,7 +61,7 @@ window.addEventListener('pageshow', function ({target}) {
chrome.runtime.sendMessage(lastSentDevToolsHookMessage);
});

if (IS_FIREFOX) {
if (__IS_FIREFOX__) {
injectScriptSync(chrome.runtime.getURL('build/renderer.js'));

// Inject a __REACT_DEVTOOLS_GLOBAL_HOOK__ global for React to interact with.
Expand Down
16 changes: 8 additions & 8 deletions packages/react-devtools-extensions/src/main/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {flushSync} from 'react-dom';
import {createRoot} from 'react-dom/client';
import Bridge from 'react-devtools-shared/src/bridge';
import Store from 'react-devtools-shared/src/devtools/store';
import {IS_CHROME, IS_EDGE, getBrowserTheme, IS_FIREFOX} from '../utils';
import {getBrowserTheme} from '../utils';
import {
localStorageGetItem,
localStorageSetItem,
Expand Down Expand Up @@ -93,10 +93,10 @@ function createBridgeAndStore() {

store = new Store(bridge, {
isProfiling,
supportsReloadAndProfile: IS_CHROME || IS_EDGE,
supportsReloadAndProfile: __IS_CHROME__ || __IS_EDGE__,
supportsProfiling,
// At this time, the timeline can only parse Chrome performance profiles.
supportsTimeline: IS_CHROME,
supportsTimeline: __IS_CHROME__,
supportsTraceUpdates: true,
});

Expand Down Expand Up @@ -218,8 +218,8 @@ function createComponentsPanel() {
}

chrome.devtools.panels.create(
IS_CHROME || IS_EDGE ? '⚛️ Components' : 'Components',
IS_EDGE ? 'icons/production.svg' : '',
__IS_CHROME__ || __IS_EDGE__ ? '⚛️ Components' : 'Components',
__IS_EDGE__ ? 'icons/production.svg' : '',
'panel.html',
createdPanel => {
componentsPanel = createdPanel;
Expand Down Expand Up @@ -257,8 +257,8 @@ function createProfilerPanel() {
}

chrome.devtools.panels.create(
IS_CHROME || IS_EDGE ? '⚛️ Profiler' : 'Profiler',
IS_EDGE ? 'icons/production.svg' : '',
__IS_CHROME__ || __IS_EDGE__ ? '⚛️ Profiler' : 'Profiler',
__IS_EDGE__ ? 'icons/production.svg' : '',
'panel.html',
createdPanel => {
profilerPanel = createdPanel;
Expand Down Expand Up @@ -444,7 +444,7 @@ const debouncedOnNavigatedListener = debounce(() => {
chrome.devtools.network.onNavigated.addListener(debouncedOnNavigatedListener);

// Should be emitted when browser DevTools are closed
if (IS_FIREFOX) {
if (__IS_FIREFOX__) {
// For some reason Firefox doesn't emit onBeforeUnload event
window.addEventListener('unload', performFullCleanup);
} else {
Expand Down
6 changes: 1 addition & 5 deletions packages/react-devtools-extensions/src/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,8 @@

import type {BrowserTheme} from 'react-devtools-shared/src/devtools/views/DevTools';

export const IS_EDGE: boolean = process.env.IS_EDGE;
export const IS_FIREFOX: boolean = process.env.IS_FIREFOX;
export const IS_CHROME: boolean = process.env.IS_CHROME;

export function getBrowserTheme(): BrowserTheme {
if (IS_CHROME) {
if (__IS_CHROME__) {
// chrome.devtools.panels added in Chrome 18.
// chrome.devtools.panels.themeName added in Chrome 54.
return chrome.devtools.panels.themeName === 'dark' ? 'dark' : 'light';
Expand Down
11 changes: 7 additions & 4 deletions packages/react-devtools-extensions/webpack.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,10 @@ module.exports = {
minimizer: [
new TerserPlugin({
terserOptions: {
compress: false,
compress: {
unused: true,
dead_code: true,
},
mangle: {
keep_fnames: true,
},
Expand All @@ -113,6 +116,9 @@ module.exports = {
__EXTENSION__: true,
__PROFILE__: false,
__TEST__: NODE_ENV === 'test',
__IS_CHROME__: IS_CHROME,
__IS_FIREFOX__: IS_FIREFOX,
__IS_EDGE__: IS_EDGE,
'process.env.DEVTOOLS_PACKAGE': `"react-devtools-extensions"`,
'process.env.DEVTOOLS_VERSION': `"${DEVTOOLS_VERSION}"`,
'process.env.EDITOR_URL': EDITOR_URL != null ? `"${EDITOR_URL}"` : null,
Expand All @@ -125,9 +131,6 @@ module.exports = {
'process.env.LIGHT_MODE_DIMMED_WARNING_COLOR': `"${LIGHT_MODE_DIMMED_WARNING_COLOR}"`,
'process.env.LIGHT_MODE_DIMMED_ERROR_COLOR': `"${LIGHT_MODE_DIMMED_ERROR_COLOR}"`,
'process.env.LIGHT_MODE_DIMMED_LOG_COLOR': `"${LIGHT_MODE_DIMMED_LOG_COLOR}"`,
'process.env.IS_CHROME': IS_CHROME,
'process.env.IS_FIREFOX': IS_FIREFOX,
'process.env.IS_EDGE': IS_EDGE,
}),
],
module: {
Expand Down
2 changes: 1 addition & 1 deletion packages/react-dom/src/__tests__/ReactDOMAttribute-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ describe('ReactDOM unknown attribute', () => {
expect(test).toThrowError(new TypeError('prod message')),
).toErrorDev(
'Warning: The provided `unknown` attribute is an unsupported type TemporalLike.' +
' This value must be coerced to a string before before using it here.',
' This value must be coerced to a string before using it here.',
);
});

Expand Down
2 changes: 1 addition & 1 deletion packages/react-dom/src/__tests__/ReactDOMComponent-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -276,7 +276,7 @@ describe('ReactDOMComponent', () => {
expect(test).toThrowError(new TypeError('prod message')),
).toErrorDev(
'Warning: The provided `fontSize` CSS property is an unsupported type TemporalLike.' +
' This value must be coerced to a string before before using it here.',
' This value must be coerced to a string before using it here.',
);
});

Expand Down
98 changes: 72 additions & 26 deletions packages/react-dom/src/__tests__/ReactDOMFiberAsync-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ let ReactDOMClient;
let Scheduler;
let act;
let waitForAll;
let waitFor;
let waitForMicrotasks;
let assertLog;

const setUntrackedInputValue = Object.getOwnPropertyDescriptor(
Expand All @@ -36,6 +38,8 @@ describe('ReactDOMFiberAsync', () => {

const InternalTestUtils = require('internal-test-utils');
waitForAll = InternalTestUtils.waitForAll;
waitFor = InternalTestUtils.waitFor;
waitForMicrotasks = InternalTestUtils.waitForMicrotasks;
assertLog = InternalTestUtils.assertLog;

document.body.appendChild(container);
Expand Down Expand Up @@ -653,52 +657,94 @@ describe('ReactDOMFiberAsync', () => {
});
});

it('transition lane in popState should yield if it suspends', async () => {
const never = {then() {}};
let _setText;
it('transition lane in popState should be allowed to suspend', async () => {
let resolvePromise;
const promise = new Promise(res => {
resolvePromise = res;
});

function Text({text}) {
Scheduler.log(text);
return text;
}

function App() {
const [shouldSuspend, setShouldSuspend] = React.useState(false);
const [text, setText] = React.useState('0');
_setText = setText;
if (shouldSuspend) {
Scheduler.log('Suspend!');
throw never;
}
function onPopstate() {
React.startTransition(() => {
setShouldSuspend(val => !val);
});
const [pathname, setPathname] = React.useState('/path/a');

if (pathname !== '/path/a') {
try {
React.use(promise);
} catch (e) {
Scheduler.log(`Suspend! [${pathname}]`);
throw e;
}
}

React.useEffect(() => {
function onPopstate() {
React.startTransition(() => {
setPathname('/path/b');
});
}
window.addEventListener('popstate', onPopstate);
return () => window.removeEventListener('popstate', onPopstate);
}, []);
Scheduler.log(`Child:${shouldSuspend}/${text}`);
return text;

return (
<>
<Text text="Before" />
<div>
<Text text={pathname} />
</div>
<Text text="After" />
</>
);
}

const root = ReactDOMClient.createRoot(container);
await act(async () => {
root.render(<App />);
});
assertLog(['Child:false/0']);
assertLog(['Before', '/path/a', 'After']);

await act(() => {
const div = container.getElementsByTagName('div')[0];
expect(div.textContent).toBe('/path/a');

// Simulate a popstate event
await act(async () => {
const popStateEvent = new Event('popstate');

// Simulate a popstate event
window.event = popStateEvent;
window.dispatchEvent(popStateEvent);
queueMicrotask(() => {
window.event = undefined;
});
await waitForMicrotasks();
window.event = undefined;

// The transition lane should have been attempted synchronously (in
// a microtask)
assertLog(['Suspend! [/path/b]']);
// Because it suspended, it remains on the current path
expect(div.textContent).toBe('/path/a');
});
assertLog(['Suspend!']);
assertLog(['Suspend! [/path/b]']);

await act(async () => {
_setText('1');
});
assertLog(['Child:false/1', 'Suspend!']);
resolvePromise();

// Since the transition previously suspended, there's no need for this
// transition to be rendered synchronously on susbequent attempts; if we
// fail to commit synchronously the first time, the scroll restoration
// state won't be restored anyway.
//
// Yield in between each child to prove that it's concurrent.
await waitForMicrotasks();
assertLog([]);

root.unmount();
await waitFor(['Before']);
await waitFor(['/path/b']);
await waitFor(['After']);
});
assertLog([]);
expect(div.textContent).toBe('/path/b');
});
});
8 changes: 4 additions & 4 deletions packages/react-dom/src/__tests__/ReactDOMInput-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -631,7 +631,7 @@ describe('ReactDOMInput', () => {
expect(test).toThrowError(new TypeError('prod message')),
).toErrorDev(
'Form field values (value, checked, defaultValue, or defaultChecked props) must be ' +
'strings, not TemporalLike. This value must be coerced to a string before before using it here.',
'strings, not TemporalLike. This value must be coerced to a string before using it here.',
);
});

Expand All @@ -655,7 +655,7 @@ describe('ReactDOMInput', () => {
expect(test).toThrowError(new TypeError('prod message')),
).toErrorDev(
'Form field values (value, checked, defaultValue, or defaultChecked props) must be ' +
'strings, not TemporalLike. This value must be coerced to a string before before using it here.',
'strings, not TemporalLike. This value must be coerced to a string before using it here.',
);
});

Expand All @@ -679,7 +679,7 @@ describe('ReactDOMInput', () => {
expect(test).toThrowError(new TypeError('prod message')),
).toErrorDev(
'Form field values (value, checked, defaultValue, or defaultChecked props) must be ' +
'strings, not TemporalLike. This value must be coerced to a string before before using it here.',
'strings, not TemporalLike. This value must be coerced to a string before using it here.',
);
});

Expand All @@ -703,7 +703,7 @@ describe('ReactDOMInput', () => {
expect(test).toThrowError(new TypeError('prod message')),
).toErrorDev(
'Form field values (value, checked, defaultValue, or defaultChecked props) must be ' +
'strings, not TemporalLike. This value must be coerced to a string before before using it here.',
'strings, not TemporalLike. This value must be coerced to a string before using it here.',
);
});

Expand Down
Loading

0 comments on commit d9ca72a

Please sign in to comment.