From 4681e49f18b1b27c71c234e2d50d8be94234bbbb Mon Sep 17 00:00:00 2001 From: ufolux Date: Mon, 27 Jul 2026 15:58:09 -0400 Subject: [PATCH] fix(metro): replace unmaintained image-size dependency --- packages/metro/package.json | 2 +- packages/metro/src/Assets.js | 34 ++- packages/metro/src/__tests__/Assets-test.js | 25 +- .../src/lib/__tests__/getAssetSize-test.js | 214 ++++++++++++++++++ packages/metro/src/lib/getAssetSize.js | 112 +++++++++ packages/metro/types/lib/getAssetSize.d.ts | 19 ++ yarn.lock | 62 +++-- 7 files changed, 426 insertions(+), 42 deletions(-) create mode 100644 packages/metro/src/lib/__tests__/getAssetSize-test.js create mode 100644 packages/metro/src/lib/getAssetSize.js create mode 100644 packages/metro/types/lib/getAssetSize.d.ts diff --git a/packages/metro/package.json b/packages/metro/package.json index 444ac4cd3c..5acd919658 100644 --- a/packages/metro/package.json +++ b/packages/metro/package.json @@ -34,7 +34,6 @@ "flow-enums-runtime": "^0.0.6", "graceful-fs": "^4.2.4", "hermes-parser": "0.37.0", - "image-size": "^1.0.2", "invariant": "^2.2.4", "jest-worker": "^29.7.0", "jsc-safe-url": "^0.2.2", @@ -53,6 +52,7 @@ "metro-transform-worker": "0.87.0", "mime-types": "^3.0.1", "nullthrows": "^1.1.1", + "probe-image-size": "7.3.0", "serialize-error": "^2.1.0", "source-map": "^0.5.6", "throat": "^5.0.0", diff --git a/packages/metro/src/Assets.js b/packages/metro/src/Assets.js index f4816836ba..40860bc47f 100644 --- a/packages/metro/src/Assets.js +++ b/packages/metro/src/Assets.js @@ -11,10 +11,9 @@ import type {AssetPath} from './node-haste/lib/AssetPaths'; +import {getAssetSize as parseAssetSize} from './lib/getAssetSize'; import {normalizePathSeparatorsToPosix} from './lib/pathUtils'; import * as AssetPaths from './node-haste/lib/AssetPaths'; -// $FlowFixMe[untyped-import] image-size -import getImageSize from 'image-size'; import crypto from 'node:crypto'; import fs from 'node:fs'; import path from 'node:path'; @@ -51,8 +50,8 @@ export type AssetDataFiltered = { ... }; -// Test extension against all types supported by image-size module. -// If it's not one of these, we won't treat it as an image. +// If an extension isn't explicitly supported here, we won't treat it as an +// image. export function isAssetTypeAnImage(type: string): boolean { return ( [ @@ -78,11 +77,7 @@ export function getAssetSize( if (!isAssetTypeAnImage(type)) { return null; } - if (content.length === 0) { - throw new Error(`Image asset \`${filePath}\` cannot be an empty file.`); - } - const {width, height} = getImageSize(content); - return {width, height}; + return parseAssetSize(type, content, filePath); } export type AssetData = AssetDataWithoutFiles & { @@ -180,7 +175,7 @@ async function getAbsoluteAssetRecord( async function getAbsoluteAssetInfo( assetPath: string, platform: ?string = null, -): Promise { +): Promise<{assetInfo: AssetInfo, firstFileContent: Buffer}> { const nameData = AssetPaths.parse( assetPath, new Set(platform != null ? [platform] : []), @@ -198,7 +193,10 @@ async function getAbsoluteAssetInfo( hasher.update(data); } - return {files, hash: hasher.digest('hex'), name, scales, type}; + return { + assetInfo: {files, hash: hasher.digest('hex'), name, scales, type}, + firstFileContent: fileData[0], + }; } export async function getAssetData( @@ -218,13 +216,13 @@ export async function getAssetData( // On Windows, change backslashes to slashes to get proper URL path from file path. assetUrlPath = normalizePathSeparatorsToPosix(assetUrlPath); - const isImage = isAssetTypeAnImage(path.extname(assetPath).slice(1)); - const assetInfo = await getAbsoluteAssetInfo(assetPath, platform ?? null); - - const isImageInput = assetInfo.files[0].includes('.zip/') - ? fs.readFileSync(assetInfo.files[0]) - : assetInfo.files[0]; - const dimensions = isImage ? getImageSize(isImageInput) : null; + const {assetInfo, firstFileContent} = await getAbsoluteAssetInfo( + assetPath, + platform ?? null, + ); + const dimensions = isAssetTypeAnImage(assetInfo.type) + ? getAssetSize(assetInfo.type, firstFileContent, assetInfo.files[0]) + : null; const scale = assetInfo.scales[0]; const assetData = { diff --git a/packages/metro/src/__tests__/Assets-test.js b/packages/metro/src/__tests__/Assets-test.js index 66f91d46c9..865e3bd048 100644 --- a/packages/metro/src/__tests__/Assets-test.js +++ b/packages/metro/src/__tests__/Assets-test.js @@ -11,11 +11,17 @@ 'use strict'; jest.mock('node:fs', () => new (require('metro-memory-fs'))()); -jest.mock('image-size'); +jest.mock('../lib/getAssetSize', () => ({ + getAssetSize: jest.fn(() => ({ + width: mockImageWidth, + height: mockImageHeight, + })), +})); jest.useRealTimers(); const {getAsset, getAssetData} = require('../Assets'); +const getImageSize = require('../lib/getAssetSize').getAssetSize; const crypto = require('node:crypto'); const path = require('node:path'); @@ -24,11 +30,6 @@ const fs = jest.requireMock('node:fs'); const mockImageWidth = 300; const mockImageHeight = 200; -require('image-size').mockReturnValue({ - width: mockImageWidth, - height: mockImageHeight, -}); - describe('getAsset', () => { beforeEach(() => { fs.reset(); @@ -284,6 +285,18 @@ describe('getAssetData', () => { }); }); + test('parses dimensions from the first asset file buffer', async () => { + writeImages({'b@1x.png': 'b1 image', 'b@2x.png': 'b2 image'}); + + await getAssetData('/root/imgs/b.png', 'imgs/b.png', [], null, '/assets'); + + expect(getImageSize).toHaveBeenCalledWith( + 'png', + Buffer.from('b1 image'), + '/root/imgs/b@1x.png', + ); + }); + test('should get assetData for non-png images', async () => { writeImages({ 'b@1x.jpg': 'b1 image', diff --git a/packages/metro/src/lib/__tests__/getAssetSize-test.js b/packages/metro/src/lib/__tests__/getAssetSize-test.js new file mode 100644 index 0000000000..d3ae68ae12 --- /dev/null +++ b/packages/metro/src/lib/__tests__/getAssetSize-test.js @@ -0,0 +1,214 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @format + * @oncall react_native + */ + +'use strict'; + +const {getAssetSize} = require('../getAssetSize'); + +const WIDTH = 300; +const HEIGHT = 200; +const KTX1_IDENTIFIER = [ + 0xab, 0x4b, 0x54, 0x58, 0x20, 0x31, 0x31, 0xbb, 0x0d, 0x0a, 0x1a, 0x0a, +]; +const KTX2_IDENTIFIER = [ + 0xab, 0x4b, 0x54, 0x58, 0x20, 0x32, 0x30, 0xbb, 0x0d, 0x0a, 0x1a, 0x0a, +]; + +describe('getAssetSize', () => { + test.each([ + ['bmp', createBmp()], + ['gif', createGif()], + ['jpg', createJpeg()], + ['jpeg', createJpeg()], + ['png', createPng()], + ['psd', createPsd()], + ['svg', createSvg()], + ['tiff', createTiff()], + ['webp', createWebp()], + ['ktx', createKtx1('little')], + ['ktx', createKtx1('big')], + ['ktx', createKtx2()], + ])('parses a valid %s image', (type, content) => { + expect(getAssetSize(type, content, `/root/image.${type}`)).toEqual({ + width: WIDTH, + height: HEIGHT, + }); + }); + + test('rejects an empty image', () => { + expect(() => + getAssetSize('png', Buffer.alloc(0), '/root/empty.png'), + ).toThrow('Invalid png image asset: /root/empty.png'); + }); + + test('rejects content that does not match the asset type', () => { + expect(() => + getAssetSize('png', createJpeg(), '/root/disguised.png'), + ).toThrow('Invalid png image asset: /root/disguised.png'); + }); + + test('rejects a truncated image header', () => { + expect(() => + getAssetSize( + 'png', + Buffer.from([0x89, 0x50, 0x4e, 0x47]), + '/root/truncated.png', + ), + ).toThrow('Invalid png image asset: /root/truncated.png'); + }); + + test.each([ + ['KTX1', createKtx1('little').subarray(0, 44)], + ['KTX2', createKtx2().subarray(0, 28)], + ])('rejects a truncated %s header', (_, content) => { + expect(() => getAssetSize('ktx', content, '/root/truncated.ktx')).toThrow( + 'Invalid ktx image asset: /root/truncated.ktx', + ); + }); + + test('rejects non-positive dimensions', () => { + const content = createPng(); + content.writeUInt32BE(0, 16); + + expect(() => getAssetSize('png', content, '/root/zero-width.png')).toThrow( + 'Invalid png image asset: /root/zero-width.png', + ); + }); + + test.each([ + ['JXL', createZeroLengthJxlBox()], + ['HEIF', createZeroLengthHeifBox()], + ['ICNS', createZeroLengthIcnsEntry()], + ])( + 'rejects a malformed %s payload without auto-detecting it', + (_, content) => { + expect(() => getAssetSize('png', content, '/root/malicious.png')).toThrow( + 'Invalid png image asset: /root/malicious.png', + ); + }, + ); +}); + +function createBmp() { + const content = Buffer.alloc(26); + content.write('BM', 0, 'ascii'); + content.writeUInt32LE(40, 14); + content.writeInt32LE(WIDTH, 18); + content.writeInt32LE(HEIGHT, 22); + return content; +} + +function createGif() { + const content = Buffer.alloc(10); + content.write('GIF89a', 0, 'ascii'); + content.writeUInt16LE(WIDTH, 6); + content.writeUInt16LE(HEIGHT, 8); + return content; +} + +function createJpeg() { + const content = Buffer.alloc(21); + content.set([0xff, 0xd8, 0xff, 0xc0, 0x00, 0x11, 0x08]); + content.writeUInt16BE(HEIGHT, 7); + content.writeUInt16BE(WIDTH, 9); + return content; +} + +function createPng() { + const content = Buffer.alloc(24); + content.set([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]); + content.write('IHDR', 12, 'ascii'); + content.writeUInt32BE(WIDTH, 16); + content.writeUInt32BE(HEIGHT, 20); + return content; +} + +function createPsd() { + const content = Buffer.alloc(22); + content.set([0x38, 0x42, 0x50, 0x53, 0x00, 0x01]); + content.writeUInt32BE(HEIGHT, 14); + content.writeUInt32BE(WIDTH, 18); + return content; +} + +function createSvg() { + return Buffer.from( + ``, + ); +} + +function createTiff() { + const content = Buffer.alloc(34); + content.set([0x49, 0x49, 0x2a, 0x00]); + content.writeUInt32LE(8, 4); + content.writeUInt16LE(2, 8); + writeTiffEntry(content, 10, 256, WIDTH); + writeTiffEntry(content, 22, 257, HEIGHT); + return content; +} + +function writeTiffEntry(content, offset, tag, value) { + content.writeUInt16LE(tag, offset); + content.writeUInt16LE(4, offset + 2); + content.writeUInt32LE(1, offset + 4); + content.writeUInt32LE(value, offset + 8); +} + +function createWebp() { + const content = Buffer.alloc(30); + content.write('RIFF', 0, 'ascii'); + content.writeUInt32LE(content.length - 8, 4); + content.write('WEBP', 8, 'ascii'); + content.write('VP8X', 12, 'ascii'); + content.writeUInt32LE(10, 16); + content.writeUIntLE(WIDTH - 1, 24, 3); + content.writeUIntLE(HEIGHT - 1, 27, 3); + return content; +} + +function createKtx1(endianness) { + const content = Buffer.alloc(64); + content.set(KTX1_IDENTIFIER); + if (endianness === 'little') { + content.set([0x01, 0x02, 0x03, 0x04], 12); + content.writeUInt32LE(WIDTH, 36); + content.writeUInt32LE(HEIGHT, 40); + } else { + content.set([0x04, 0x03, 0x02, 0x01], 12); + content.writeUInt32BE(WIDTH, 36); + content.writeUInt32BE(HEIGHT, 40); + } + return content; +} + +function createKtx2() { + const content = Buffer.alloc(80); + content.set(KTX2_IDENTIFIER); + content.writeUInt32LE(WIDTH, 20); + content.writeUInt32LE(HEIGHT, 24); + return content; +} + +function createZeroLengthJxlBox() { + return Buffer.from([0x00, 0x00, 0x00, 0x00, 0x4a, 0x58, 0x4c, 0x20]); +} + +function createZeroLengthHeifBox() { + return Buffer.from([ + 0x00, 0x00, 0x00, 0x00, 0x66, 0x74, 0x79, 0x70, 0x61, 0x76, 0x69, 0x66, + ]); +} + +function createZeroLengthIcnsEntry() { + return Buffer.from([ + 0x69, 0x63, 0x6e, 0x73, 0x00, 0x00, 0x00, 0x10, 0x69, 0x63, 0x30, 0x37, + 0x00, 0x00, 0x00, 0x00, + ]); +} diff --git a/packages/metro/src/lib/getAssetSize.js b/packages/metro/src/lib/getAssetSize.js new file mode 100644 index 0000000000..cade1667d1 --- /dev/null +++ b/packages/metro/src/lib/getAssetSize.js @@ -0,0 +1,112 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @flow strict-local + * @format + * @oncall react_native + */ + +// $FlowFixMe[untyped-import] probe-image-size does not provide Flow types. +import probeImageSize from 'probe-image-size/sync'; + +type Dimensions = { + readonly width: number, + readonly height: number, +}; + +type ImageParser = (content: Buffer) => ?Dimensions; + +const probeParsers: {[string]: ImageParser} = probeImageSize.parsers; + +const parsers: {[string]: ImageParser} = { + bmp: probeParsers.bmp, + gif: probeParsers.gif, + jpeg: probeParsers.jpeg, + jpg: probeParsers.jpeg, + png: probeParsers.png, + psd: probeParsers.psd, + svg: probeParsers.svg, + tiff: probeParsers.tiff, + webp: probeParsers.webp, + ktx: parseKtx, +}; + +const KTX1_IDENTIFIER = Buffer.from([ + 0xab, 0x4b, 0x54, 0x58, 0x20, 0x31, 0x31, 0xbb, 0x0d, 0x0a, 0x1a, 0x0a, +]); +const KTX2_IDENTIFIER = Buffer.from([ + 0xab, 0x4b, 0x54, 0x58, 0x20, 0x32, 0x30, 0xbb, 0x0d, 0x0a, 0x1a, 0x0a, +]); +const KTX1_HEADER_LENGTH = 64; +const KTX2_HEADER_LENGTH = 80; + +export function getAssetSize( + type: string, + content: Buffer, + filePath: string, +): Dimensions { + const parser = parsers[type]; + let dimensions; + + try { + dimensions = parser?.(content); + } catch { + throw createInvalidImageError(type, filePath); + } + + if ( + dimensions == null || + !Number.isFinite(dimensions.width) || + !Number.isFinite(dimensions.height) || + dimensions.width <= 0 || + dimensions.height <= 0 + ) { + throw createInvalidImageError(type, filePath); + } + + return { + width: dimensions.width, + height: dimensions.height, + }; +} + +function parseKtx(content: Buffer): ?Dimensions { + if ( + content.length >= KTX1_HEADER_LENGTH && + content.subarray(0, KTX1_IDENTIFIER.length).equals(KTX1_IDENTIFIER) + ) { + const endianness = content.subarray(12, 16); + if (endianness.equals(Buffer.from([0x01, 0x02, 0x03, 0x04]))) { + return { + width: content.readUInt32LE(36), + height: content.readUInt32LE(40), + }; + } + if (endianness.equals(Buffer.from([0x04, 0x03, 0x02, 0x01]))) { + return { + width: content.readUInt32BE(36), + height: content.readUInt32BE(40), + }; + } + return null; + } + + if ( + content.length >= KTX2_HEADER_LENGTH && + content.subarray(0, KTX2_IDENTIFIER.length).equals(KTX2_IDENTIFIER) + ) { + return { + width: content.readUInt32LE(20), + height: content.readUInt32LE(24), + }; + } + + return null; +} + +function createInvalidImageError(type: string, filePath: string): Error { + return new Error(`Invalid ${type} image asset: ${filePath}`); +} diff --git a/packages/metro/types/lib/getAssetSize.d.ts b/packages/metro/types/lib/getAssetSize.d.ts new file mode 100644 index 0000000000..7b825124f2 --- /dev/null +++ b/packages/metro/types/lib/getAssetSize.d.ts @@ -0,0 +1,19 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @noformat + * @oncall react_native + * @generated SignedSource<<4d3913569e22fa7afa4d9d1a60d6bc53>> + * + * This file was translated from Flow by scripts/generateTypeScriptDefinitions.js + * Original file: packages/metro/src/lib/getAssetSize.js + * To regenerate, run: + * js1 build metro-ts-defs (internal) OR + * yarn run build-ts-defs (OSS) + */ + +type Dimensions = {readonly width: number; readonly height: number}; +export declare function getAssetSize(type: string, content: Buffer, filePath: string): Dimensions; diff --git a/yarn.lock b/yarn.lock index 00249b51a8..0e66912b54 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2367,7 +2367,7 @@ data-view-byte-offset@^1.0.1: es-errors "^1.3.0" is-data-view "^1.0.1" -debug@2.6.9: +debug@2, debug@2.6.9: version "2.6.9" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== @@ -2381,7 +2381,7 @@ debug@4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.4, d dependencies: ms "^2.1.3" -debug@^3.2.7: +debug@^3.2.6, debug@^3.2.7: version "3.2.7" resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a" integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ== @@ -3394,6 +3394,13 @@ hyperdyperid@^1.2.0: resolved "https://registry.yarnpkg.com/hyperdyperid/-/hyperdyperid-1.2.0.tgz#59668d323ada92228d2a869d3e474d5a33b69e6b" integrity sha512-Y93lCzHYgGWdrJ66yIktxiaGULYc6oGiABxhcO5AufBeOyoIdZF7bIfLaOrbM0iGIOXQQgxxRrFEnb+Y6w1n4A== +iconv-lite@^0.4.4: + version "0.4.24" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" + integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== + dependencies: + safer-buffer ">= 2.1.2 < 3" + ignore@^5.2.0: version "5.2.0" resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.0.tgz#6d3bac8fa7fe0d45d9f9be7bac2fc279577e345a" @@ -3404,13 +3411,6 @@ ignore@^7.0.5: resolved "https://registry.yarnpkg.com/ignore/-/ignore-7.0.5.tgz#4cb5f6cd7d4c7ab0365738c7aea888baa6d7efd9" integrity sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg== -image-size@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/image-size/-/image-size-1.0.2.tgz#d778b6d0ab75b2737c1556dd631652eb963bc486" - integrity sha512-xfOoWjceHntRb3qFCrh5ZFORYH8XCdYpASltMhZ/Q0KZiOwjdE/Yl2QCiWdwD+lygV5bMCvauzgu5PxBX/Yerg== - dependencies: - queue "6.0.2" - import-fresh@^3.2.1: version "3.2.2" resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.2.2.tgz#fc129c160c5d68235507f4331a6baad186bdbc3e" @@ -3440,7 +3440,7 @@ inflight@^1.0.4: once "^1.3.0" wrappy "1" -inherits@2, inherits@~2.0.3: +inherits@2: version "2.0.4" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== @@ -4552,6 +4552,15 @@ natural-compare@^1.4.0: resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc= +needle@^2.5.2: + version "2.9.1" + resolved "https://registry.yarnpkg.com/needle/-/needle-2.9.1.tgz#22d1dffbe3490c2b83e301f7709b6736cd8f2684" + integrity sha512-6R9fqJ5Zcmf+uYaFgdIHmLwNldn5HbK8L5ybn7Uz+ylX/rnOsSp1AHcvQSrCaFN+qNM1wpymHqD7mVasEOlHGQ== + dependencies: + debug "^3.2.6" + iconv-lite "^0.4.4" + sax "^1.2.4" + negotiator@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-1.0.0.tgz#b6c91bb47172d69f93cfd7c357bbb529019b5f6a" @@ -4859,6 +4868,15 @@ pretty-format@^29.7.0: ansi-styles "^5.0.0" react-is "^18.0.0" +probe-image-size@7.3.0: + version "7.3.0" + resolved "https://registry.yarnpkg.com/probe-image-size/-/probe-image-size-7.3.0.tgz#a07df2e2cffc1057026d5a1bda3a5b11ee970034" + integrity sha512-7CaDeBwiAbh6ohXsvLbAZhO7wzsZAmaevfxe39qvCwRh8LyaZfDlBGGLU1CCTgrTLtCOdwBBhjOrIHaIIimHfQ== + dependencies: + lodash.merge "^4.6.2" + needle "^2.5.2" + stream-parser "~0.3.1" + progress@^2.0.0: version "2.0.3" resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8" @@ -4908,13 +4926,6 @@ queue-microtask@^1.2.2: resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== -queue@6.0.2: - version "6.0.2" - resolved "https://registry.yarnpkg.com/queue/-/queue-6.0.2.tgz#b91525283e2315c7553d2efa18d83e76432fed65" - integrity sha512-iHZWu+q3IdFZFX36ro/lKBkSvfkztY5Y7HMiPlOUjhupPcG2JMfst2KKEpu5XndviX/3UhFbRngUPNKtgvtZiA== - dependencies: - inherits "~2.0.3" - react-is@^16.13.1: version "16.13.1" resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" @@ -5142,6 +5153,16 @@ safe-regex-test@^1.1.0: es-errors "^1.3.0" is-regex "^1.2.1" +"safer-buffer@>= 2.1.2 < 3": + version "2.1.2" + resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" + integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== + +sax@^1.2.4: + version "1.6.1" + resolved "https://registry.yarnpkg.com/sax/-/sax-1.6.1.tgz#4c23cf608c0b693ab54b4b5888e92cfe977b9843" + integrity sha512-42tBVwLWnaQvW5zc4HbZrTuWccECCZfBi92FDuwtqxasH+JbPB3/FOKb1m222K42R4WxuxzzMsTswfzgtSu64Q== + scheduler@^0.27.0: version "0.27.0" resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.27.0.tgz#0c4ef82d67d1e5c1e359e8fc76d3a87f045fe5bd" @@ -5361,6 +5382,13 @@ stop-iteration-iterator@^1.1.0: es-errors "^1.3.0" internal-slot "^1.1.0" +stream-parser@~0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/stream-parser/-/stream-parser-0.3.1.tgz#1618548694420021a1182ff0af1911c129761773" + integrity sha512-bJ/HgKq41nlKvlhccD5kaCr/P+Hu0wPNKPJOH7en+YrJu/9EgqUF+88w5Jb6KNcjOFMhfX4B2asfeAtIGuHObQ== + dependencies: + debug "2" + string-length@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/string-length/-/string-length-4.0.1.tgz#4a973bf31ef77c4edbceadd6af2611996985f8a1"