diff --git a/index.d.ts b/index.d.ts index 093875e25..5bc702e2e 100644 --- a/index.d.ts +++ b/index.d.ts @@ -93,7 +93,7 @@ declare module '@react-pdf/renderer' { type SourceObject = | string - | { data: Buffer; format: 'png' | 'jpg' } + | { data: Buffer; format: 'png' | 'jpg' | 'gif' } | { uri: string; method: HTTPMethod; body: any; headers: any }; interface BaseImageProps extends NodeProps { @@ -114,8 +114,8 @@ declare module '@react-pdf/renderer' { type ImageProps = ImageWithSrcProp | ImageWithSourceProp; /** - * A React component for displaying network or local (Node only) JPG or - * PNG images, as well as base64 encoded image strings. + * A React component for displaying network or local (Node only) JPG, + * PNG or GIF images, as well as base64 encoded image strings. */ class Image extends React.Component {} diff --git a/package.json b/package.json index 6a1652c81..eeec4eb15 100644 --- a/package.json +++ b/package.json @@ -33,10 +33,12 @@ "@textkit/pdf-renderer": "^0.1.13", "@textkit/text-decoration-engine": "^0.1.10", "blob-stream": "^0.1.3", + "blob-to-buffer": "^1.2.8", "cross-fetch": "^3.0.1", "emoji-regex": "^7.0.3", "hyphen": "^1.1.1", "is-url": "^1.2.4", + "jpegasus": "^1.9.54", "lodash.groupby": "^4.6.0", "lodash.isfunction": "^3.0.9", "lodash.merge": "^4.6.1", @@ -46,6 +48,8 @@ "page-wrapping": "^1.1.0", "react": "^16.8.3", "react-reconciler": "^0.20.1", + "sharp": "^0.22.0", + "to-arraybuffer": "^1.0.1", "yoga-layout-prebuilt": "^1.9.3" }, "devDependencies": { diff --git a/src/utils/gif.js b/src/utils/gif.js new file mode 100644 index 000000000..4bf5d3e85 --- /dev/null +++ b/src/utils/gif.js @@ -0,0 +1,69 @@ +/* global File */ + +import jpegasus from 'jpegasus'; +import toArrayBuffer from 'to-arraybuffer'; +import toBufferCb from 'blob-to-buffer'; +import sharp from 'sharp'; +import JPEG from './jpeg'; +import PNG from './png'; + +const toBuffer = blob => + new Promise((resolve, reject) => + toBufferCb(blob, (err, buffer) => (err ? reject(err) : resolve(buffer))), + ); + +const renderGIF = async data => { + if (!GIF.isValid(data)) { + throw new TypeError( + 'Image passed to GIF decoder appears not to be in GIF format', + ); + } + + if (!BROWSER) { + const pngBuffer = await sharp(data).toBuffer(); + return new PNG(pngBuffer); + } + + const jpegBlob = await jpegasus.compress( + new File([toArrayBuffer(data)], 'image.gif', { + type: 'image/gif', + }), + { + quality: 0.8, + }, + ); + const jpegBuffer = await toBuffer(jpegBlob); + return new JPEG(jpegBuffer); +}; + +// Instantiate a “thenable” object — an object that behaves like a Promise +// https://samdanielson.com/blog/2016/09/15/subclassing-promises-not.html +// by implementing the Promise prototype’s public methods, and applying them to a real Promise +// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise#Promise_prototype +class GIF { + constructor(data) { + this.imagePromise = renderGIF(data); + } + + then(...args) { + return this.imagePromise.then.apply(this.imagePromise, args); + } + + catch(...args) { + return this.imagePromise.catch.apply(this.imagePromise, args); + } + + finally(...args) { + return this.imagePromise.finally.apply(this.imagePromise, args); + } +} + +GIF.isValid = function(data) { + try { + return data[0] === 71 && data[1] === 73 && data[2] === 70; + } catch (e) { + return false; + } +}; + +export default GIF; diff --git a/src/utils/image.js b/src/utils/image.js index 546671957..cea70d8e2 100644 --- a/src/utils/image.js +++ b/src/utils/image.js @@ -5,6 +5,7 @@ import fetch from 'cross-fetch'; import PNG from './png'; import JPEG from './jpeg'; +import GIF from './gif'; import createCache from './cache'; export const IMAGE_CACHE = createCache({ limit: 30 }); @@ -74,7 +75,9 @@ const fetchRemoteFile = async (uri, options) => { const isValidFormat = format => { const lower = format.toLowerCase(); - return lower === 'jpg' || lower === 'jpeg' || lower === 'png'; + return ( + lower === 'jpg' || lower === 'jpeg' || lower === 'png' || lower === 'gif' + ); }; const guessFormat = buffer => { @@ -84,13 +87,15 @@ const guessFormat = buffer => { format = 'jpg'; } else if (PNG.isValid(buffer)) { format = 'png'; + } else if (GIF.isValid(buffer)) { + format = 'gif'; } return format; }; const isCompatibleBase64 = ({ uri }) => - /^data:image\/[a-zA-Z]*;base64,[^"]*/g.test(uri); + /^data:image\/[a-zA-Z]*;base64,[^"]*/.test(uri); function getImage(body, extension) { switch (extension.toLowerCase()) { @@ -99,38 +104,36 @@ function getImage(body, extension) { return new JPEG(body); case 'png': return new PNG(body); + case 'gif': + return new GIF(body); default: return null; } } -const resolveBase64Image = ({ uri }) => { - const match = /^data:image\/([a-zA-Z]*);base64,([^"]*)/g.exec(uri); - const format = match[1]; - const data = match[2]; +const resolveBase64Image = async ({ uri }) => { + const [, format, data] = /^data:image\/([a-zA-Z]*);base64,([^"]*)/.exec(uri); if (!isValidFormat(format)) { throw new Error(`Base64 image invalid format: ${format}`); } - return new Promise(resolve => { - return resolve(getImage(Buffer.from(data, 'base64'), format)); - }); + return getImage(Buffer.from(data, 'base64'), format); }; -const resolveImageFromData = src => { +const resolveImageFromData = async src => { if (src.data && src.format) { - return new Promise(resolve => resolve(getImage(src.data, src.format))); + return getImage(src.data, src.format); } throw new Error(`Invalid data given for local file: ${JSON.stringify(src)}`); }; -const resolveBufferImage = buffer => { +const resolveBufferImage = async buffer => { const format = guessFormat(buffer); if (format) { - return new Promise(resolve => resolve(getImage(buffer, format))); + return getImage(buffer, format); } }; @@ -147,11 +150,16 @@ const getImageFormat = body => { const isJpg = body[0] === 255 && body[1] === 216 && body[2] === 255; + // based on https://github.com/sindresorhus/file-type/blob/master/index.js#L65 + const isGif = body[0] === 71 && body[1] === 73 && body[2] === 70; + let extension = ''; if (isPng) { extension = 'png'; } else if (isJpg) { extension = 'jpg'; + } else if (isGif) { + extension = 'gif'; } else { throw new Error('Not valid image extension'); } diff --git a/tests/assets/test.gif b/tests/assets/test.gif new file mode 100644 index 000000000..67a53d84f Binary files /dev/null and b/tests/assets/test.gif differ diff --git a/tests/gif.test.js b/tests/gif.test.js new file mode 100644 index 000000000..493a6f526 --- /dev/null +++ b/tests/gif.test.js @@ -0,0 +1,24 @@ +import fs from 'fs'; +import path from 'path'; +import GIF from '../src/utils/gif'; + +const localGIFImage = fs.readFileSync(path.join(__dirname, 'assets/test.gif')); +const localPNGImage = fs.readFileSync(path.join(__dirname, 'assets/test.png')); + +describe('gif', () => { + test('should return true for valid gif images', () => { + expect(GIF.isValid(localGIFImage)).toBeTruthy(); + }); + + test('should return false for non gif images', () => { + expect(GIF.isValid(localPNGImage)).toBeFalsy(); + }); + + test('should return false for empty argument', () => { + expect(GIF.isValid()).toBeFalsy(); + }); + + test('should return false for non buffer argument', () => { + expect(GIF.isValid('some data')).toBeFalsy(); + }); +}); diff --git a/tests/image.test.js b/tests/image.test.js index 912b674d4..a7465ee2d 100644 --- a/tests/image.test.js +++ b/tests/image.test.js @@ -16,8 +16,10 @@ let dummyRoot; const jpgImageUrl = 'https://react-pdf.org/static/images/quijote1.jpg'; const pngImageUrl = 'https://react-pdf.org/static/images/quijote2.png'; +const gifImageUrl = 'https://react-pdf.org/static/images/quijote3.gif'; const localJPGImage = fs.readFileSync(path.join(__dirname, 'assets/test.jpg')); const localPNGImage = fs.readFileSync(path.join(__dirname, 'assets/test.png')); +const localGIFImage = fs.readFileSync(path.join(__dirname, 'assets/test.gif')); jest.mock('../src/utils/warning'); @@ -141,6 +143,19 @@ describe('Image', () => { expect(dummyRoot.instance.image.mock.calls[0][0]).toBe(image.image.data); }); + test('Should render a gif image over http', async () => { + fetch.once(localGIFImage); + + const image = new Image(dummyRoot, { src: gifImageUrl }); + + await image.fetch(); + await image.render(); + + expect(image.image.data).toBeTruthy(); + expect(dummyRoot.instance.image.mock.calls).toHaveLength(1); + expect(dummyRoot.instance.image.mock.calls[0][0]).toBe(image.image.data); + }); + test('Should render a remote image from src object', async () => { fetch.once(localJPGImage); @@ -254,6 +269,17 @@ describe('Image', () => { expect(dummyRoot.instance.image.mock.calls[0][0]).toBe(image.image.data); }); + /* test('Should render a buffer gif image', async () => { + const image = new Image(dummyRoot, { src: localGIFImage }); + + await image.fetch(); + await image.render(); + + expect(image.image.data).toBeTruthy(); + expect(dummyRoot.instance.image.mock.calls).toHaveLength(1); + expect(dummyRoot.instance.image.mock.calls[0][0]).toBe(image.image.data); + }); */ + test('Should correctly resolve objectFit styles given a flat `style` prop', async () => { const resolveObjectFitSpy = jest.spyOn(objectFit, 'resolveObjectFit'); resolveObjectFitSpy.mockClear(); diff --git a/yarn.lock b/yarn.lock index 28156b0d1..1d8d7607f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1438,6 +1438,13 @@ binary-extensions@^1.0.0: resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.13.0.tgz#9523e001306a32444b907423f1de2164222f6ab1" integrity sha512-EgmjVLMn22z7eGGv3kcnHwSnJXmFHjISTY9E/S5lIcTD3Oxw05QTcBLNkJFzcb3cNueUdF/IN4U+d78V0zO8Hw== +bindings@^1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/bindings/-/bindings-1.5.0.tgz#10353c9e945334bc0511a6d90b38fbc7c9c504df" + integrity sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ== + dependencies: + file-uri-to-path "1.0.0" + bl@^1.0.0: version "1.2.2" resolved "https://registry.yarnpkg.com/bl/-/bl-1.2.2.tgz#a160911717103c07410cef63ef51b397c025af9c" @@ -1453,6 +1460,11 @@ blob-stream@^0.1.3: dependencies: blob "0.0.4" +blob-to-buffer@^1.2.8: + version "1.2.8" + resolved "https://registry.yarnpkg.com/blob-to-buffer/-/blob-to-buffer-1.2.8.tgz#78eeeb332f1280ed0ca6fb2b60693a8c6d36903a" + integrity sha512-re0AIxakF504MgeMtIyJkVcZ8T5aUxtp/QmTMlmjyb3P44E1BEv5x3LATBGApWAJATyXHtkXRD+gWTmeyYLiQA== + blob@0.0.4: version "0.0.4" resolved "https://registry.yarnpkg.com/blob/-/blob-0.0.4.tgz#bcf13052ca54463f30f9fc7e95b9a47630a94921" @@ -1798,7 +1810,7 @@ collection-visit@^1.0.0: map-visit "^1.0.0" object-visit "^1.0.0" -color-convert@^1.9.0: +color-convert@^1.9.0, color-convert@^1.9.1: version "1.9.3" resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== @@ -1810,6 +1822,27 @@ color-name@1.1.3: resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU= +color-name@^1.0.0: + version "1.1.4" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" + integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== + +color-string@^1.5.2: + version "1.5.3" + resolved "https://registry.yarnpkg.com/color-string/-/color-string-1.5.3.tgz#c9bbc5f01b58b5492f3d6857459cb6590ce204cc" + integrity sha512-dC2C5qeWoYkxki5UAXapdjqO672AM4vZuPGRQfO8b5HKuKGBbKWpITyDYN7TOFKvRW7kOgAn3746clDBMDJyQw== + dependencies: + color-name "^1.0.0" + simple-swizzle "^0.2.2" + +color@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/color/-/color-3.1.0.tgz#d8e9fb096732875774c84bf922815df0308d0ffc" + integrity sha512-CwyopLkuRYO5ei2EpzpIh6LqJMt6Mt+jZhO5VI5f/wJLZriXQE32/SSqzmrh+QB+AZT81Cj8yv+7zwToW8ahZg== + dependencies: + color-convert "^1.9.1" + color-string "^1.5.2" + combined-stream@^1.0.6, combined-stream@~1.0.6: version "1.0.7" resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.7.tgz#2d1d24317afb8abe95d6d2c0b07b57813539d828" @@ -2519,6 +2552,11 @@ expand-template@^1.0.2: resolved "https://registry.yarnpkg.com/expand-template/-/expand-template-1.1.1.tgz#981f188c0c3a87d2e28f559bc541426ff94f21dd" integrity sha512-cebqLtV8KOZfw0UI8TEFWxtczxxC1jvyUvx6H4fyp1K1FN7A4Q+uggVUlOsI1K8AGU0rwOGqP8nCapdrw8CYQg== +expand-template@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/expand-template/-/expand-template-2.0.3.tgz#6e14b3fcee0f3a6340ecb57d2e8918692052a47c" + integrity sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg== + expect@^24.5.0: version "24.5.0" resolved "https://registry.yarnpkg.com/expect/-/expect-24.5.0.tgz#492fb0df8378d8474cc84b827776b069f46294ed" @@ -2628,6 +2666,11 @@ file-entry-cache@^5.0.1: dependencies: flat-cache "^2.0.1" +file-uri-to-path@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd" + integrity sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw== + fileset@^2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/fileset/-/fileset-2.0.3.tgz#8e7548a96d3cc2327ee5e674168723a333bba2a0" @@ -2738,6 +2781,11 @@ fs-constants@^1.0.0: resolved "https://registry.yarnpkg.com/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad" integrity sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow== +fs-copy-file-sync@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/fs-copy-file-sync/-/fs-copy-file-sync-1.1.1.tgz#11bf32c096c10d126e5f6b36d06eece776062918" + integrity sha512-2QY5eeqVv4m2PfyMiEuy9adxNP+ajf+8AR05cEi+OAzPcOj90hvFImeZhTmKLBgSd9EvG33jsD7ZRxsx9dThkQ== + fs-minipass@^1.2.5: version "1.2.5" resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-1.2.5.tgz#06c277218454ec288df77ada54a03b8702aacb9d" @@ -3215,6 +3263,11 @@ is-arrayish@^0.2.1: resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" integrity sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0= +is-arrayish@^0.3.1: + version "0.3.2" + resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.3.2.tgz#4574a2ae56f7ab206896fb431eaeed066fdf8f03" + integrity sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ== + is-binary-path@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-1.0.1.tgz#75f16642b480f187a711c814161fd3a4a7655898" @@ -3917,6 +3970,11 @@ jest@^24.5.0: import-local "^2.0.0" jest-cli "^24.5.0" +jpegasus@^1.9.54: + version "1.9.54" + resolved "https://registry.yarnpkg.com/jpegasus/-/jpegasus-1.9.54.tgz#f28e504776683d364f28c57d266f330703fb6bc3" + integrity sha512-LDLQhUs4xonKo5LlR6cbImwauZ7hW9eKDmS/R+ptMrI/QCIW+rEXkfX90PF+5GOq4fnmgxOMzDzjjvST6csIRA== + js-levenshtein@^1.1.3: version "1.1.6" resolved "https://registry.yarnpkg.com/js-levenshtein/-/js-levenshtein-1.1.6.tgz#c6cee58eb3550372df8deb85fad5ce66ce01d59d" @@ -4487,6 +4545,11 @@ mute-stream@0.0.7: resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.7.tgz#3075ce93bc21b8fab43e1bc4da7e8115ed1e7bab" integrity sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s= +nan@^2.13.1: + version "2.13.2" + resolved "https://registry.yarnpkg.com/nan/-/nan-2.13.2.tgz#f51dc7ae66ba7d5d55e1e6d4d8092e802c9aefe7" + integrity sha512-TghvYc72wlMGMVMluVo9WRJc0mB8KxxF/gZ4YYFy7V2ZQX9l7rgbPg7vjS9mt6U5HXODVFVI2bOduCzwOMv/lw== + nan@^2.6.2, nan@^2.9.2: version "2.12.1" resolved "https://registry.yarnpkg.com/nan/-/nan-2.12.1.tgz#7b1aa193e9aa86057e3c7bbd0ac448e770925552" @@ -4509,6 +4572,11 @@ nanomatch@^1.2.9: snapdragon "^0.8.1" to-regex "^3.0.1" +napi-build-utils@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/napi-build-utils/-/napi-build-utils-1.0.1.tgz#1381a0f92c39d66bf19852e7873432fc2123e508" + integrity sha512-boQj1WFgQH3v4clhu3mTNfP+vOBxorDlE8EKiMjUlLG3C4qAESnn9AxIOkFgTR2c9LtzNjPrjS60cT27ZKBhaA== + natural-compare@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" @@ -4539,7 +4607,7 @@ nice-try@^1.0.4: resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ== -node-abi@^2.2.0: +node-abi@^2.2.0, node-abi@^2.7.0: version "2.7.1" resolved "https://registry.yarnpkg.com/node-abi/-/node-abi-2.7.1.tgz#a8997ae91176a5fbaa455b194976e32683cda643" integrity sha512-OV8Bq1OrPh6z+Y4dqwo05HqrRL9YNF7QVMRfq1/pguwKLG+q9UB/Lk0x5qXjO23JjJg+/jqCHSTaG1P3tfKfuw== @@ -4696,7 +4764,7 @@ npm-which@^3.0.1: npm-path "^2.0.2" which "^1.2.10" -"npmlog@0 || 1 || 2 || 3 || 4", npmlog@^4.0.1, npmlog@^4.0.2: +"npmlog@0 || 1 || 2 || 3 || 4", npmlog@^4.0.1, npmlog@^4.0.2, npmlog@^4.1.2: version "4.1.2" resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b" integrity sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg== @@ -5134,6 +5202,28 @@ prebuild-install@^2.3.0: tunnel-agent "^0.6.0" which-pm-runs "^1.0.0" +prebuild-install@^5.2.5: + version "5.2.5" + resolved "https://registry.yarnpkg.com/prebuild-install/-/prebuild-install-5.2.5.tgz#c7485911fe98950b7f7cd15bb9daee11b875cc44" + integrity sha512-6uZgMVg7yDfqlP5CPurVhtq3hUKBFNufiar4J5hZrlHTo59DDBEtyxw01xCdFss9j0Zb9+qzFVf/s4niayba3w== + dependencies: + detect-libc "^1.0.3" + expand-template "^2.0.3" + github-from-package "0.0.0" + minimist "^1.2.0" + mkdirp "^0.5.1" + napi-build-utils "^1.0.1" + node-abi "^2.7.0" + noop-logger "^0.1.1" + npmlog "^4.0.1" + os-homedir "^1.0.1" + pump "^2.0.1" + rc "^1.2.7" + simple-get "^2.7.0" + tar-fs "^1.13.0" + tunnel-agent "^0.6.0" + which-pm-runs "^1.0.0" + prelude-ls@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" @@ -5815,6 +5905,23 @@ set-value@^2.0.0: is-plain-object "^2.0.3" split-string "^3.0.1" +sharp@^0.22.0: + version "0.22.0" + resolved "https://registry.yarnpkg.com/sharp/-/sharp-0.22.0.tgz#cf4cfcb019941fd06ac24555d9f5bc84536d29be" + integrity sha512-yInpiWYvVbE0hJylso2Q2A7QaYFBxGdSlVVHGeUf1F9JsQNAUpmaqdnX54TImgKbSCy9mQpEAoGm1pcKCZhCsQ== + dependencies: + bindings "^1.5.0" + color "^3.1.0" + detect-libc "^1.0.3" + fs-copy-file-sync "^1.1.1" + nan "^2.13.1" + npmlog "^4.1.2" + prebuild-install "^5.2.5" + semver "^5.6.0" + simple-get "^3.0.3" + tar "^4.4.8" + tunnel-agent "^0.6.0" + shebang-command@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" @@ -5851,6 +5958,15 @@ simple-get@^2.7.0: once "^1.3.1" simple-concat "^1.0.0" +simple-get@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/simple-get/-/simple-get-3.0.3.tgz#924528ac3f9d7718ce5e9ec1b1a69c0be4d62efa" + integrity sha512-Wvre/Jq5vgoz31Z9stYWPLn0PqRqmBDpFSdypAnHu5AvRVCYPRYGnvryNLiXu8GOBNDH82J2FRHUGMjjHUpXFw== + dependencies: + decompress-response "^3.3.0" + once "^1.3.1" + simple-concat "^1.0.0" + simple-git@^1.85.0: version "1.107.0" resolved "https://registry.yarnpkg.com/simple-git/-/simple-git-1.107.0.tgz#12cffaf261c14d6f450f7fdb86c21ccee968b383" @@ -5858,6 +5974,13 @@ simple-git@^1.85.0: dependencies: debug "^4.0.1" +simple-swizzle@^0.2.2: + version "0.2.2" + resolved "https://registry.yarnpkg.com/simple-swizzle/-/simple-swizzle-0.2.2.tgz#a4da6b635ffcccca33f70d17cb92592de95e557a" + integrity sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo= + dependencies: + is-arrayish "^0.3.1" + sisteransi@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/sisteransi/-/sisteransi-1.0.0.tgz#77d9622ff909080f1c19e5f4a1df0c1b0a27b88c" @@ -6232,7 +6355,7 @@ tar@^2.0.0: fstream "^1.0.2" inherits "2" -tar@^4: +tar@^4, tar@^4.4.8: version "4.4.8" resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.8.tgz#b19eec3fde2a96e64666df9fdb40c5ca1bc3747d" integrity sha512-LzHF64s5chPQQS0IYBn9IN5h3i98c12bo4NCO7e0sGM2llXQ3p2FGC5sdENN4cTW48O915Sh+x+EXx7XW96xYQ== @@ -6296,6 +6419,11 @@ tmpl@1.0.x: resolved "https://registry.yarnpkg.com/tmpl/-/tmpl-1.0.4.tgz#23640dd7b42d00433911140820e5cf440e521dd1" integrity sha1-I2QN17QtAEM5ERQIIOXPRA5SHdE= +to-arraybuffer@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz#7d229b1fcc637e466ca081180836a7aabff83f43" + integrity sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M= + to-buffer@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/to-buffer/-/to-buffer-1.1.1.tgz#493bd48f62d7c43fcded313a03dcadb2e1213a80"