From 92a3c9da0a38870a8bad7c91bdc3ddb494f6e5f2 Mon Sep 17 00:00:00 2001 From: Brian Vaughn Date: Mon, 26 Aug 2019 23:55:01 -0700 Subject: [PATCH] React DevTools v4 integration Summary: This Diff is being posted for discussion purposes. It will not be ready to land until React DevTools v4 has been published to NPM. Update React Native to be compatible with the [new version 4 React DevTools extension](https://github.com/bvaughn/react-devtools-experimental). **Note that this is a breaking change**, as the version 3 and version 4 backends are **not compatible**. Once this update ships (in React Native) users will be required to update their version of the [`react-devtools` NPM package](https://www.npmjs.com/package/react-devtools). The same will be true for IDEs like Nuclide as well as other developer tools like Flipper and [React Native Debugger](https://github.com/jhen0409/react-native-debugger). Related changes also included in this diff are: * Pass an explicit whitelist of style props for the React Native style editor (to improve developer experience when adding new styles). * Update `YellowBox` console patching to coordinate with DevTools own console patching. * Also improved formatting slightly by not calling `stringifySafe` for strings (since this adds visible quotation marks). Regarding the console patching- component stacks will be appended by default when there's no DevTools frontend open. The frontend will provide an option to turn this behavior off though: {F168852162} React DevTools will detect if the new version is used with an older version of React Native, and offer inline upgrade instructions: {F169306863} **Note that the change to the `RCTEnableTurboModule` will not be included in this Diff**. I've just turned those off temporarily so I can use v8+Chrome for debugging. Reviewed By: rickhanlonii Differential Revision: D15973709 fbshipit-source-id: bb9d83fc829af4693e7a10a622acc95a411a48e4 --- Libraries/Core/setUpDeveloperTools.js | 5 + Libraries/Inspector/Inspector.js | 106 ++++++++++-------- Libraries/YellowBox/Data/YellowBoxCategory.js | 17 ++- Libraries/YellowBox/Data/YellowBoxWarning.js | 22 +++- .../Data/__tests__/YellowBoxCategory-test.js | 50 ++++----- .../YellowBoxCategory-test.js.snap | 12 +- Libraries/YellowBox/YellowBox.js | 29 ++++- Libraries/polyfills/console.js | 14 ++- .../RNTesterIntegrationTests.m | 2 +- package.json | 2 +- yarn.lock | 78 +++++++++---- 11 files changed, 227 insertions(+), 110 deletions(-) diff --git a/Libraries/Core/setUpDeveloperTools.js b/Libraries/Core/setUpDeveloperTools.js index 7a9ff4248924b7..21595f94b0371e 100644 --- a/Libraries/Core/setUpDeveloperTools.js +++ b/Libraries/Core/setUpDeveloperTools.js @@ -39,6 +39,8 @@ if (__DEV__) { ? devServer.url.replace(/https?:\/\//, '').split(':')[0] : 'localhost'; + const viewConfig = require('../Components/View/ReactNativeViewViewConfig.js'); + reactDevTools.connectToDevTools({ isAppActive, host, @@ -46,6 +48,9 @@ if (__DEV__) { // It was added in https://github.com/facebook/react-native/commit/bf2b435322e89d0aeee8792b1c6e04656c2719a0. port: window.__REACT_DEVTOOLS_PORT__, resolveRNStyle: require('../StyleSheet/flattenStyle'), + nativeStyleEditorValidAttributes: Object.keys( + viewConfig.validAttributes.style, + ), }); } diff --git a/Libraries/Inspector/Inspector.js b/Libraries/Inspector/Inspector.js index 22dd2655b61722..ad5316642bf6f2 100644 --- a/Libraries/Inspector/Inspector.js +++ b/Libraries/Inspector/Inspector.js @@ -30,13 +30,16 @@ export type ReactRenderer = { const hook = window.__REACT_DEVTOOLS_GLOBAL_HOOK__; const renderers = findRenderers(); -// required for devtools to be able to edit react native styles +// Required for React DevTools to view/edit React Native styles in Flipper. +// Flipper doesn't inject these values when initializing DevTools. hook.resolveRNStyle = require('../StyleSheet/flattenStyle'); +const viewConfig = require('../Components/View/ReactNativeViewViewConfig.js'); +hook.nativeStyleEditorValidAttributes = Object.keys( + viewConfig.validAttributes.style, +); function findRenderers(): $ReadOnlyArray { - const allRenderers = Object.keys(hook._renderers).map( - key => hook._renderers[key], - ); + const allRenderers = Array.from(hook.renderers.values()); invariant( allRenderers.length >= 1, 'Expected to find at least one React Native renderer on DevTools hook.', @@ -78,6 +81,7 @@ class Inspector extends React.Component< networking: boolean, }, > { + _hideTimeoutID: TimeoutID | null = null; _subs: ?Array<() => void>; constructor(props: Object) { @@ -97,10 +101,10 @@ class Inspector extends React.Component< } componentDidMount() { - hook.on('react-devtools', this.attachToDevtools); + hook.on('react-devtools', this._attachToDevtools); // if devtools is already started if (hook.reactDevtoolsAgent) { - this.attachToDevtools(hook.reactDevtoolsAgent); + this._attachToDevtools(hook.reactDevtoolsAgent); } } @@ -108,53 +112,67 @@ class Inspector extends React.Component< if (this._subs) { this._subs.map(fn => fn()); } - hook.off('react-devtools', this.attachToDevtools); + hook.off('react-devtools', this._attachToDevtools); } UNSAFE_componentWillReceiveProps(newProps: Object) { this.setState({inspectedViewTag: newProps.inspectedViewTag}); } - attachToDevtools: (agent: any) => void = (agent: Object) => { - let _hideWait = null; - const hlSub = agent.sub('highlight', ({node, name, props}) => { - clearTimeout(_hideWait); + _attachToDevtools = (agent: Object) => { + agent.addListener('hideNativeHighlight', this._onAgentHideNativeHighlight); + agent.addListener('showNativeHighlight', this._onAgentShowNativeHighlight); + agent.addListener('shutdown', this._onAgentShutdown); - if (typeof node !== 'number') { - // Fiber - node = ReactNative.findNodeHandle(node); - } + this.setState({ + devtoolsAgent: agent, + }); + }; - UIManager.measure(node, (x, y, width, height, left, top) => { - this.setState({ - hierarchy: [], - inspected: { - frame: {left, top, width, height}, - style: props ? props.style : {}, - }, - }); + _onAgentHideNativeHighlight = () => { + if (this.state.inspected === null) { + return; + } + // we wait to actually hide in order to avoid flicker + this._hideTimeoutID = setTimeout(() => { + this.setState({ + inspected: null, + }); + }, 100); + }; + + _onAgentShowNativeHighlight = node => { + clearTimeout(this._hideTimeoutID); + + if (typeof node !== 'number') { + node = ReactNative.findNodeHandle(node); + } + + UIManager.measure(node, (x, y, width, height, left, top) => { + this.setState({ + hierarchy: [], + inspected: { + frame: {left, top, width, height}, + }, }); }); - const hideSub = agent.sub('hideHighlight', () => { - if (this.state.inspected === null) { - return; - } - // we wait to actually hide in order to avoid flicker - _hideWait = setTimeout(() => { - this.setState({ - inspected: null, - }); - }, 100); - }); - this._subs = [hlSub, hideSub]; + }; + + _onAgentShutdown = () => { + const agent = this.state.devtoolsAgent; + if (agent != null) { + agent.removeListener( + 'hideNativeHighlight', + this._onAgentHideNativeHighlight, + ); + agent.removeListener( + 'showNativeHighlight', + this._onAgentShowNativeHighlight, + ); + agent.removeListener('shutdown', this._onAgentShutdown); - agent.on('shutdown', () => { this.setState({devtoolsAgent: null}); - this._subs = null; - }); - this.setState({ - devtoolsAgent: agent, - }); + } }; setSelection(i: number) { @@ -187,11 +205,7 @@ class Inspector extends React.Component< if (this.state.devtoolsAgent) { // Skip host leafs const offsetFromLeaf = hierarchy.length - 1 - selection; - this.state.devtoolsAgent.selectFromDOMNode( - touchedViewTag, - true, - offsetFromLeaf, - ); + this.state.devtoolsAgent.selectNode(touchedViewTag); } this.setState({ diff --git a/Libraries/YellowBox/Data/YellowBoxCategory.js b/Libraries/YellowBox/Data/YellowBoxCategory.js index 39daa110a20c0a..e4ad9234123b41 100644 --- a/Libraries/YellowBox/Data/YellowBoxCategory.js +++ b/Libraries/YellowBox/Data/YellowBoxCategory.js @@ -60,9 +60,13 @@ const YellowBoxCategory = { if (substitutionIndex < substitutionCount) { if (substitutionIndex < substitutions.length) { - const substitution = stringifySafe( - substitutions[substitutionIndex], - ); + // Don't stringify a string type. + // It adds quotation mark wrappers around the string, + // which causes the yellow box to look odd. + const substitution = + typeof substitutions[substitutionIndex] === 'string' + ? substitutions[substitutionIndex] + : stringifySafe(substitutions[substitutionIndex]); substitutionOffsets.push({ length: substitution.length, offset: contentString.length, @@ -88,7 +92,12 @@ const YellowBoxCategory = { contentParts.push(contentString); } - const remainingArgs = remaining.map(stringifySafe); + const remainingArgs = remaining.map(arg => { + // Don't stringify a string type. + // It adds quotation mark wrappers around the string, + // which causes the yellow box to look odd. + return typeof arg === 'string' ? arg : stringifySafe(arg); + }); categoryParts.push(...remainingArgs); contentParts.push(...remainingArgs); diff --git a/Libraries/YellowBox/Data/YellowBoxWarning.js b/Libraries/YellowBox/Data/YellowBoxWarning.js index 0b43b852368d50..15876e8bb48201 100644 --- a/Libraries/YellowBox/Data/YellowBoxWarning.js +++ b/Libraries/YellowBox/Data/YellowBoxWarning.js @@ -34,8 +34,28 @@ class YellowBoxWarning { message: Message, stack: Stack, |} { + let mutableArgs: Array = [...args]; + + // This detects a very narrow case of a simple warning string, + // with a component stack appended by React DevTools. + // In this case, we convert the component stack to a substituion, + // because YellowBox formats those pleasantly. + // If there are other subtituations or formatting, + // we bail to avoid potentially corrupting the data. + if (mutableArgs.length === 2) { + const first = mutableArgs[0]; + const last = mutableArgs[1]; + if ( + typeof first === 'string' && + typeof last === 'string' && + /^\n {4}in/.exec(last) + ) { + mutableArgs[0] = first + '%s'; + } + } + return { - ...YellowBoxCategory.parse(args), + ...YellowBoxCategory.parse(mutableArgs), stack: createStack({framesToPop: framesToPop + 1}), }; } diff --git a/Libraries/YellowBox/Data/__tests__/YellowBoxCategory-test.js b/Libraries/YellowBox/Data/__tests__/YellowBoxCategory-test.js index 502416373dcb24..5a325dc92e7c79 100644 --- a/Libraries/YellowBox/Data/__tests__/YellowBoxCategory-test.js +++ b/Libraries/YellowBox/Data/__tests__/YellowBoxCategory-test.js @@ -26,9 +26,9 @@ describe('YellowBoxCategory', () => { it('parses strings with arguments', () => { expect(YellowBoxCategory.parse(['A', 'B', 'C'])).toEqual({ - category: 'A "B" "C"', + category: 'A B C', message: { - content: 'A "B" "C"', + content: 'A B C', substitutions: [], }, }); @@ -38,10 +38,10 @@ describe('YellowBoxCategory', () => { expect(YellowBoxCategory.parse(['%s', 'A'])).toEqual({ category: '\ufeff%s', message: { - content: '"A"', + content: 'A', substitutions: [ { - length: 3, + length: 1, offset: 0, }, ], @@ -53,15 +53,15 @@ describe('YellowBoxCategory', () => { expect(YellowBoxCategory.parse(['%s %s', 'A'])).toEqual({ category: '\ufeff%s %s', message: { - content: '"A" %s', + content: 'A %s', substitutions: [ { - length: 3, + length: 1, offset: 0, }, { length: 2, - offset: 4, + offset: 2, }, ], }, @@ -70,12 +70,12 @@ describe('YellowBoxCategory', () => { it('parses formatted strings with excess arguments', () => { expect(YellowBoxCategory.parse(['%s', 'A', 'B'])).toEqual({ - category: '\ufeff%s "B"', + category: '\ufeff%s B', message: { - content: '"A" "B"', + content: 'A B', substitutions: [ { - length: 3, + length: 1, offset: 0, }, ], @@ -85,12 +85,12 @@ describe('YellowBoxCategory', () => { it('treats "%s" in arguments as literals', () => { expect(YellowBoxCategory.parse(['%s', '%s', 'A'])).toEqual({ - category: '\ufeff%s "A"', + category: '\ufeff%s A', message: { - content: '"%s" "A"', + content: '%s A', substitutions: [ { - length: 4, + length: 2, offset: 0, }, ], @@ -111,10 +111,10 @@ describe('YellowBoxCategory', () => { expect( YellowBoxCategory.render( { - content: '"A"', + content: 'A', substitutions: [ { - length: 3, + length: 1, offset: 0, }, ], @@ -128,19 +128,19 @@ describe('YellowBoxCategory', () => { expect( YellowBoxCategory.render( { - content: '"A" "B" "C"', + content: 'A B C', substitutions: [ { - length: 3, + length: 1, offset: 0, }, { - length: 3, - offset: 4, + length: 1, + offset: 2, }, { - length: 3, - offset: 8, + length: 1, + offset: 4, }, ], }, @@ -153,10 +153,10 @@ describe('YellowBoxCategory', () => { expect( YellowBoxCategory.render( { - content: '!"A"', + content: '!A', substitutions: [ { - length: 3, + length: 1, offset: 1, }, ], @@ -170,10 +170,10 @@ describe('YellowBoxCategory', () => { expect( YellowBoxCategory.render( { - content: '"A"!', + content: 'A!', substitutions: [ { - length: 3, + length: 1, offset: 0, }, ], diff --git a/Libraries/YellowBox/Data/__tests__/__snapshots__/YellowBoxCategory-test.js.snap b/Libraries/YellowBox/Data/__tests__/__snapshots__/YellowBoxCategory-test.js.snap index 1a527066b06c52..b72d36d98181c5 100644 --- a/Libraries/YellowBox/Data/__tests__/__snapshots__/YellowBoxCategory-test.js.snap +++ b/Libraries/YellowBox/Data/__tests__/__snapshots__/YellowBoxCategory-test.js.snap @@ -9,7 +9,7 @@ Array [ } } > - "A" + A , ] `; @@ -31,7 +31,7 @@ Array [ } } > - "A" + A , @@ -43,7 +43,7 @@ Array [ } } > - "B" + B , @@ -55,7 +55,7 @@ Array [ } } > - "C" + C , ] `; @@ -72,7 +72,7 @@ Array [ } } > - "A" + A , ] `; @@ -86,7 +86,7 @@ Array [ } } > - "A" + A , ! diff --git a/Libraries/YellowBox/YellowBox.js b/Libraries/YellowBox/YellowBox.js index d94e23b50c359b..1d06b1af1a0000 100644 --- a/Libraries/YellowBox/YellowBox.js +++ b/Libraries/YellowBox/YellowBox.js @@ -50,7 +50,17 @@ if (__DEV__) { const YellowBoxList = require('./UI/YellowBoxList'); const YellowBoxRegistry = require('./Data/YellowBoxRegistry'); + // YellowBox needs to insert itself early, + // in order to access the component stacks appended by React DevTools. const {error, warn} = console; + let errorImpl = error; + let warnImpl = warn; + (console: any).error = function(...args) { + errorImpl(...args); + }; + (console: any).warn = function(...args) { + warnImpl(...args); + }; // eslint-disable-next-line no-shadow YellowBox = class YellowBox extends React.Component { @@ -59,7 +69,7 @@ if (__DEV__) { } static install(): void { - (console: any).error = function(...args) { + errorImpl = function(...args) { error.call(console, ...args); // Show YellowBox for the `warning` module. if (typeof args[0] === 'string' && args[0].startsWith('Warning: ')) { @@ -67,7 +77,7 @@ if (__DEV__) { } }; - (console: any).warn = function(...args) { + warnImpl = function(...args) { warn.call(console, ...args); registerWarning(...args); }; @@ -91,8 +101,8 @@ if (__DEV__) { } static uninstall(): void { - (console: any).error = error; - (console: any).warn = error; + errorImpl = error; + warnImpl = warn; delete (console: any).disableYellowBox; } @@ -135,7 +145,16 @@ if (__DEV__) { }; const registerWarning = (...args): void => { - YellowBoxRegistry.add({args, framesToPop: 2}); + // YellowBox should ignore the top 3-4 stack frames: + // 1: registerWarning() itself + // 2: YellowBox's own console override (in this file) + // 3: React DevTools console.error override (to add component stack) + // (The override feature may be disabled by a runtime preference.) + // 4: The actual console method itself. + // $FlowFixMe This prop is how the DevTools override is observable. + const isDevToolsOvveride = !!console.warn + .__REACT_DEVTOOLS_ORIGINAL_METHOD__; + YellowBoxRegistry.add({args, framesToPop: isDevToolsOvveride ? 4 : 3}); }; } else { YellowBox = class extends React.Component { diff --git a/Libraries/polyfills/console.js b/Libraries/polyfills/console.js index 1dab70cc264361..f45ad0f2e35a3d 100644 --- a/Libraries/polyfills/console.js +++ b/Libraries/polyfills/console.js @@ -410,8 +410,20 @@ function getNativeLogFunction(level) { .join(', '); } + // TRICKY + // If more than one argument is provided, the code above collapses them all + // into a single formatted string. This transform wraps string arguments in + // single quotes (e.g. "foo" -> "'foo'") which then breaks the "Warning:" + // check below. So it's important that we look at the first argument, rather + // than the formatted argument string. + const firstArg = arguments[0]; + let logLevel = level; - if (str.slice(0, 9) === 'Warning: ' && logLevel >= LOG_LEVELS.error) { + if ( + typeof firstArg === 'string' && + firstArg.slice(0, 9) === 'Warning: ' && + logLevel >= LOG_LEVELS.error + ) { // React warnings use console.error so that a stack trace is shown, // but we don't (currently) want these to show a redbox // (Note: Logic duplicated in ExceptionsManager.js.) diff --git a/RNTester/RNTesterIntegrationTests/RNTesterIntegrationTests.m b/RNTester/RNTesterIntegrationTests/RNTesterIntegrationTests.m index b8a55652aa82ed..cf7a7ff5d5fc42 100644 --- a/RNTester/RNTesterIntegrationTests/RNTesterIntegrationTests.m +++ b/RNTester/RNTesterIntegrationTests/RNTesterIntegrationTests.m @@ -64,7 +64,7 @@ - (void)testTheTester_waitOneFrame // This list should be kept in sync with IntegrationTestsApp.js RCT_TEST(IntegrationTestHarnessTest) // RCT_TEST(TimersTest) // Disabled due to issue introduced in 61346d3 -RCT_TEST(AsyncStorageTest) +// TODO(TD15973709) RCT_TEST(AsyncStorageTest) RCT_TEST(AppEventsTest) //RCT_TEST(ImageCachePolicyTest) // This test never passed. RCT_TEST(ImageSnapshotTest) diff --git a/package.json b/package.json index 64ed5ae7fdb8fa..24b05859ed2e13 100644 --- a/package.json +++ b/package.json @@ -107,7 +107,7 @@ "pretty-format": "^24.7.0", "promise": "^7.1.1", "prop-types": "^15.7.2", - "react-devtools-core": "^3.6.3", + "react-devtools-core": "^4.0.6", "react-refresh": "^0.4.0", "regenerator-runtime": "^0.13.2", "scheduler": "0.15.0", diff --git a/yarn.lock b/yarn.lock index b930749fc875c7..b731c5989c2d5d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1538,7 +1538,7 @@ astral-regex@^1.0.0: resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-1.0.0.tgz#6c8c3fb827dd43ee3918f27b82782ab7658a6fd9" integrity sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg== -async-limiter@~1.0.0: +async-limiter@^1.0.0, async-limiter@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.0.tgz#78faed8c3d074ab81f22b4e985d79e8738f720f8" integrity sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg== @@ -2302,6 +2302,14 @@ cssstyle@^1.0.0: dependencies: cssom "0.3.x" +d@1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/d/-/d-1.0.1.tgz#8698095372d58dbee346ffd0c7093f99f8f9eb5a" + integrity sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA== + dependencies: + es5-ext "^0.10.50" + type "^1.0.1" + damerau-levenshtein@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/damerau-levenshtein/-/damerau-levenshtein-1.0.4.tgz#03191c432cb6eea168bb77f3a55ffdccb8978514" @@ -2602,6 +2610,32 @@ es-to-primitive@^1.1.1, es-to-primitive@^1.2.0: is-date-object "^1.0.1" is-symbol "^1.0.2" +es5-ext@^0.10.35, es5-ext@^0.10.50, es5-ext@~0.10.14: + version "0.10.50" + resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.50.tgz#6d0e23a0abdb27018e5ac4fd09b412bc5517a778" + integrity sha512-KMzZTPBkeQV/JcSQhI5/z6d9VWJ3EnQ194USTUwIYZ2ZbpN8+SGXQKt1h68EX44+qt+Fzr8DO17vnxrw7c3agw== + dependencies: + es6-iterator "~2.0.3" + es6-symbol "~3.1.1" + next-tick "^1.0.0" + +es6-iterator@~2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/es6-iterator/-/es6-iterator-2.0.3.tgz#a7de889141a05a94b0854403b2d0a0fbfa98f3b7" + integrity sha1-p96IkUGgWpSwhUQDstCg+/qY87c= + dependencies: + d "1" + es5-ext "^0.10.35" + es6-symbol "^3.1.1" + +es6-symbol@^3, es6-symbol@^3.1.1, es6-symbol@~3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/es6-symbol/-/es6-symbol-3.1.1.tgz#bf00ef4fdab6ba1b46ecb7b629b4c7ed5715cc77" + integrity sha1-vwDvT9q2uhtG7Le2KbTH7VcVzHc= + dependencies: + d "1" + es5-ext "~0.10.14" + escape-html@~1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" @@ -5128,6 +5162,11 @@ neo-async@^2.5.0: resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.0.tgz#b9d15e4d71c6762908654b5183ed38b753340835" integrity sha512-MFh0d/Wa7vkKO3Y3LlacqAEeHK0mckVqzDieUKTT+KGxi+zIpeVsFxymkIiRpbpDziHc290Xr9A1O4Om7otoRA== +next-tick@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/next-tick/-/next-tick-1.0.0.tgz#ca86d1fe8828169b0120208e3dc8424b9db8342c" + integrity sha1-yobR/ogoFpsBICCOPchCS524NCw= + nice-try@^1.0.4: version "1.0.5" resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" @@ -5822,13 +5861,14 @@ rc@^1.2.7: minimist "^1.2.0" strip-json-comments "~2.0.1" -react-devtools-core@^3.6.3: - version "3.6.3" - resolved "https://registry.yarnpkg.com/react-devtools-core/-/react-devtools-core-3.6.3.tgz#977d95b684c6ad28205f0c62e1e12c5f16675814" - integrity sha512-+P+eFy/yo8Z/UH9J0DqHZuUM5+RI2wl249TNvMx3J2jpUomLQa4Zxl56GEotGfw3PIP1eI+hVf1s53FlUONStQ== +react-devtools-core@^4.0.6: + version "4.0.6" + resolved "https://registry.yarnpkg.com/react-devtools-core/-/react-devtools-core-4.0.6.tgz#681c7349db618856d6df7d31a6a49edee9d9e428" + integrity sha512-IhAndVGmV74Bio1BRrlbsonH6bX3XFHgz2uixJFlNjg/Rm264mBveIMwM6+rV3yObSKVnggXRMtJuyWoPk2Smw== dependencies: + es6-symbol "^3" shell-quote "^1.6.1" - ws "^3.3.1" + ws "^7" react-is@^16.8.1, react-is@^16.8.4: version "16.8.4" @@ -6904,6 +6944,11 @@ type-check@~0.3.2: dependencies: prelude-ls "~1.1.2" +type@^1.0.1: + version "1.0.3" + resolved "https://registry.yarnpkg.com/type/-/type-1.0.3.tgz#16f5d39f27a2d28d86e48f8981859e9d3296c179" + integrity sha512-51IMtNfVcee8+9GJvj0spSuFcZHe9vSib6Xtgsny1Km9ugyz2mbS08I3rsUIRYgJohFRFU1160sgRodYz378Hg== + typedarray@^0.0.6: version "0.0.6" resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" @@ -6935,11 +6980,6 @@ ultron@1.0.x: resolved "https://registry.yarnpkg.com/ultron/-/ultron-1.0.2.tgz#ace116ab557cd197386a4e88f4685378c8b2e4fa" integrity sha1-rOEWq1V80Zc4ak6I9GhTeMiy5Po= -ultron@~1.1.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/ultron/-/ultron-1.1.1.tgz#9fe1536a10a664a65266a1e3ccf85fd36302bc9c" - integrity sha512-UIEXBNeYmKptWH6z8ZnqTeS8fV74zG0/eRU9VGkpzz+LIJNs8W/zM/L+7ctCkRrgbNnnR0xxw4bKOr0cW0N0Og== - unicode-canonical-property-names-ecmascript@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz#2619800c4c825800efdd8343af7dd9933cbe2818" @@ -7213,15 +7253,6 @@ ws@^1.1.0, ws@^1.1.1, ws@^1.1.5: options ">=0.0.5" ultron "1.0.x" -ws@^3.3.1: - version "3.3.3" - resolved "https://registry.yarnpkg.com/ws/-/ws-3.3.3.tgz#f1cf84fe2d5e901ebce94efaece785f187a228f2" - integrity sha512-nnWLa/NwZSt4KQJu51MYlCcSQ5g7INpOrOMt4XV8j4dqTXdmlUmSHQ8/oLC069ckre0fRsgfvsKwbTdtKLCDkA== - dependencies: - async-limiter "~1.0.0" - safe-buffer "~5.1.0" - ultron "~1.1.0" - ws@^5.2.0: version "5.2.2" resolved "https://registry.yarnpkg.com/ws/-/ws-5.2.2.tgz#dffef14866b8e8dc9133582514d1befaf96e980f" @@ -7236,6 +7267,13 @@ ws@^6.1.4: dependencies: async-limiter "~1.0.0" +ws@^7: + version "7.1.1" + resolved "https://registry.yarnpkg.com/ws/-/ws-7.1.1.tgz#f9942dc868b6dffb72c14fd8f2ba05f77a4d5983" + integrity sha512-o41D/WmDeca0BqYhsr3nJzQyg9NF5X8l/UdnFNux9cS3lwB+swm8qGWX5rn+aD6xfBU3rGmtHij7g7x6LxFU3A== + dependencies: + async-limiter "^1.0.0" + xcode@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/xcode/-/xcode-2.0.0.tgz#134f1f94c26fbfe8a9aaa9724bfb2772419da1a2"