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"