├── .eslintrc ├── .gitignore ├── .npmignore ├── .vscode └── settings.json ├── bundle-html.js ├── example ├── .gitignore ├── README.md ├── app.json ├── app │ ├── _layout.tsx │ └── index.tsx ├── assets │ └── images │ │ ├── adaptive-icon.png │ │ ├── embed-html.png │ │ ├── gradient.png │ │ ├── icon.png │ │ ├── image-rect.png │ │ ├── panorama.png │ │ ├── path.png │ │ ├── purple-black-rect.png │ │ ├── purple-rect.png │ │ ├── red-circle.png │ │ └── splash.png ├── package.json ├── tsconfig.json └── yarn.lock ├── license.txt ├── package.json ├── readme.md ├── src ├── Bus.js ├── Canvas.js ├── CanvasGradient.js ├── CanvasRenderingContext2D.js ├── Image.js ├── ImageData.js ├── Path2D.js ├── autoscale-canvas.ts ├── index.html ├── index.html.js ├── webview-binders.js └── webview.js ├── tsconfig.json └── yarn.lock /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["fbjs-opensource"], 3 | "rules": { 4 | "quotes": "off", 5 | "arrow-parens": "off", 6 | "object-curly-spacing": "off" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | dist/ 2 | node_modules/ 3 | .vscode/ 4 | .history/ 5 | .expo/ 6 | .DS_Store -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | src/ 3 | bundle-html.js 4 | example/ -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "prettier.singleQuote": true, 3 | "prettier.trailingComma": "always", 4 | "javascript.validate.enable": false, 5 | "prettier.eslintIntegration": true, 6 | "[javascriptreact]": { 7 | "editor.formatOnSave": true 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /bundle-html.js: -------------------------------------------------------------------------------- 1 | const fs = require("fs"); 2 | const path = require("path"); 3 | const parse5 = require("parse5"); 4 | const typescript = require("typescript"); 5 | 6 | const Node = { 7 | map: (transform) => (node) => { 8 | const transformed = transform(node); 9 | const { childNodes } = transformed; 10 | return { 11 | ...transformed, 12 | childNodes: childNodes && childNodes.map(Node.map(transform)), 13 | }; 14 | }, 15 | }; 16 | 17 | const ENTRY = process.argv[2]; 18 | 19 | const entryPath = require.resolve(ENTRY); 20 | const entryContent = fs.readFileSync(entryPath, "utf-8"); 21 | 22 | const parsed = parse5.parse(entryContent); 23 | const transformed = Node.map((node) => { 24 | if (node.nodeName === "script") { 25 | const src = node.attrs.find((attr) => attr.name === "src"); 26 | if (src.value) { 27 | const scriptPath = path.resolve(path.dirname(entryPath), src.value); 28 | const scriptRawContent = fs.readFileSync(scriptPath, "utf-8"); 29 | const transpileOutput = typescript.transpileModule(scriptRawContent, { 30 | compilerOptions: { 31 | allowJs: true, 32 | checkJs: false, 33 | removeComments: true, 34 | }, 35 | }); 36 | const scriptContent = transpileOutput.outputText; 37 | const [newScript] = parse5.parseFragment( 38 | ``, 39 | node.parent, 40 | ).childNodes; 41 | return newScript; 42 | } 43 | } 44 | return node; 45 | })(parsed); 46 | const newContent = parse5.serialize(transformed); 47 | 48 | fs.writeFileSync(`${entryPath}.js`, `export default \`${newContent}\``); 49 | -------------------------------------------------------------------------------- /example/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | .expo/ 3 | dist/ 4 | npm-debug.* 5 | *.jks 6 | *.p8 7 | *.p12 8 | *.key 9 | *.mobileprovision 10 | *.orig.* 11 | web-build/ 12 | 13 | # macOS 14 | .DS_Store 15 | 16 | # @generated expo-cli sync-2b81b286409207a5da26e14c78851eb30d8ccbdb 17 | # The following patterns were generated by expo-cli 18 | 19 | expo-env.d.ts 20 | # @end expo-cli -------------------------------------------------------------------------------- /example/README.md: -------------------------------------------------------------------------------- 1 | # Welcome to your Expo app 👋 2 | 3 | This is an [Expo](https://expo.dev) project created with [`create-expo-app`](https://www.npmjs.com/package/create-expo-app). 4 | 5 | ## Get started 6 | 7 | 1. Install dependencies 8 | 9 | ```bash 10 | npm install 11 | ``` 12 | 13 | 2. Start the app 14 | 15 | ```bash 16 | npx expo start 17 | ``` 18 | 19 | In the output, you'll find options to open the app in a 20 | 21 | - [development build](https://docs.expo.dev/develop/development-builds/introduction/) 22 | - [Android emulator](https://docs.expo.dev/workflow/android-studio-emulator/) 23 | - [iOS simulator](https://docs.expo.dev/workflow/ios-simulator/) 24 | - [Expo Go](https://expo.dev/go), a limited sandbox for trying out app development with Expo 25 | 26 | You can start developing by editing the files inside the **app** directory. This project uses [file-based routing](https://docs.expo.dev/router/introduction). 27 | 28 | ## Learn more 29 | 30 | To learn more about developing your project with Expo, look at the following resources: 31 | 32 | - [Expo documentation](https://docs.expo.dev/): Learn fundamentals, or go into advanced topics with our [guides](https://docs.expo.dev/guides). 33 | - [Learn Expo tutorial](https://docs.expo.dev/learn): Follow a step-by-step tutorial where you'll create a project that runs on Android, iOS, and the web. 34 | 35 | ## Join the community 36 | 37 | Join our community of developers creating universal apps. 38 | 39 | - [Expo on GitHub](https://github.com/expo/expo): View our open source platform and contribute. 40 | - [Discord community](https://chat.expo.dev): Chat with Expo users and ask questions. 41 | -------------------------------------------------------------------------------- /example/app.json: -------------------------------------------------------------------------------- 1 | { 2 | "expo": { 3 | "name": "example", 4 | "slug": "example", 5 | "version": "1.0.0", 6 | "orientation": "portrait", 7 | "icon": "./assets/images/icon.png", 8 | "scheme": "myapp", 9 | "userInterfaceStyle": "automatic", 10 | "splash": { 11 | "image": "./assets/images/splash.png", 12 | "resizeMode": "contain", 13 | "backgroundColor": "#ffffff" 14 | }, 15 | "ios": { 16 | "supportsTablet": true 17 | }, 18 | "android": { 19 | "adaptiveIcon": { 20 | "foregroundImage": "./assets/images/adaptive-icon.png", 21 | "backgroundColor": "#ffffff" 22 | } 23 | }, 24 | "plugins": ["expo-router"], 25 | "experiments": { 26 | "typedRoutes": true 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /example/app/_layout.tsx: -------------------------------------------------------------------------------- 1 | import { Stack } from "expo-router"; 2 | 3 | export default function RootLayout() { 4 | return ( 5 | 6 | 7 | 8 | ); 9 | } 10 | -------------------------------------------------------------------------------- /example/app/index.tsx: -------------------------------------------------------------------------------- 1 | import React, { ReactElement, useCallback } from "react"; 2 | import { 3 | Text, 4 | Image, 5 | ScrollView, 6 | StatusBar, 7 | View, 8 | StyleSheet, 9 | ImageSourcePropType, 10 | } from "react-native"; 11 | import Canvas, { 12 | Image as CanvasImage, 13 | Path2D, 14 | ImageData, 15 | } from "react-native-canvas"; 16 | 17 | export default function HomeScreen() { 18 | const handleImageData = useCallback((canvas: Canvas | null) => { 19 | if (!canvas) return; 20 | canvas.width = 100; 21 | canvas.height = 100; 22 | 23 | const context = canvas.getContext("2d"); 24 | context.fillStyle = "purple"; 25 | context.fillRect(0, 0, 100, 100); 26 | 27 | context.getImageData(0, 0, 100, 100).then((imageData) => { 28 | const data = Object.values(imageData.data); 29 | const length = Object.keys(data).length; 30 | for (let i = 0; i < length; i += 4) { 31 | data[i] = 0; 32 | data[i + 1] = 0; 33 | data[i + 2] = 0; 34 | } 35 | const imgData = new ImageData(canvas, data, 100, 100); 36 | context.putImageData(imgData, 0, 0); 37 | }); 38 | }, []); 39 | 40 | const handlePurpleRect = useCallback(async (canvas: Canvas | null) => { 41 | if (!canvas) return; 42 | canvas.width = 100; 43 | canvas.height = 100; 44 | 45 | const context = canvas.getContext("2d"); 46 | 47 | context.fillStyle = "purple"; 48 | context.fillRect(0, 0, 100, 100); 49 | 50 | const { width } = await context.measureText("yo"); 51 | }, []); 52 | 53 | const handleRedCircle = useCallback((canvas: Canvas | null) => { 54 | if (!canvas) return; 55 | canvas.width = 100; 56 | canvas.height = 100; 57 | 58 | const context = canvas.getContext("2d"); 59 | 60 | context.fillStyle = "red"; 61 | context.arc(50, 50, 49, 0, Math.PI * 2, true); 62 | context.fill(); 63 | }, []); 64 | 65 | const handleImageRect = useCallback((canvas: Canvas | null) => { 66 | if (!canvas) return; 67 | const image = new CanvasImage(canvas); 68 | canvas.width = 100; 69 | canvas.height = 100; 70 | 71 | const context = canvas.getContext("2d"); 72 | 73 | image.src = 74 | "https://upload.wikimedia.org/wikipedia/commons/6/63/Biho_Takashi._Bat_Before_the_Moon%2C_ca._1910.jpg"; 75 | image.addEventListener("load", () => { 76 | context.drawImage(image, 0, 0, 100, 100); 77 | }); 78 | }, []); 79 | 80 | const handlePath = useCallback((canvas: Canvas | null) => { 81 | if (!canvas) return; 82 | canvas.width = 100; 83 | canvas.height = 100; 84 | const context = canvas.getContext("2d"); 85 | 86 | context.fillStyle = "red"; 87 | context.fillRect(0, 0, 100, 100); 88 | 89 | const ellipse = new Path2D(canvas); 90 | ellipse.ellipse(50, 50, 25, 35, (45 * Math.PI) / 180, 0, 2 * Math.PI); 91 | context.fillStyle = "purple"; 92 | context.fill(ellipse); 93 | 94 | context.save(); 95 | context.scale(0.5, 0.5); 96 | context.translate(50, 20); 97 | const rectPath = new Path2D(canvas, "M10 10 h 80 v 80 h -80 Z"); 98 | 99 | context.fillStyle = "pink"; 100 | context.fill(rectPath); 101 | context.restore(); 102 | }, []); 103 | 104 | const handleGradient = useCallback(async (canvas: Canvas | null) => { 105 | if (!canvas) return; 106 | canvas.width = 100; 107 | canvas.height = 100; 108 | const ctx = canvas.getContext("2d"); 109 | const gradient = await ctx.createLinearGradient(0, 0, 200, 0); 110 | gradient.addColorStop(0, "green"); 111 | gradient.addColorStop(1, "white"); 112 | ctx.fillStyle = gradient; 113 | ctx.fillRect(0, 0, 100, 100); 114 | }, []); 115 | 116 | const handleEmbedHTML = useCallback((canvas: Canvas | null) => { 117 | if (!canvas) return; 118 | const image = new CanvasImage(canvas); 119 | canvas.width = 100; 120 | canvas.height = 100; 121 | 122 | const context = canvas.getContext("2d"); 123 | 124 | const htmlString = "Hello, World!"; 125 | const svgString = ` 126 | 127 | 128 |
129 | 130 | ${htmlString} 131 | 132 |
133 |
134 |
135 | `; 136 | image.src = `data:image/svg+xml,${encodeURIComponent(svgString)}`; 137 | 138 | image.addEventListener("load", () => { 139 | context.drawImage(image, 0, 0, 100, 100); 140 | }); 141 | }, []); 142 | 143 | return ( 144 | 145 | 178 | ); 179 | } 180 | 181 | const Example = ({ 182 | sample, 183 | children, 184 | }: { 185 | sample: ImageSourcePropType; 186 | children: ReactElement; 187 | }) => ( 188 | 189 | {children} 190 | 191 | 192 | 193 | 194 | ); 195 | 196 | const commonStyles = StyleSheet.create({ 197 | full: { 198 | position: "absolute", 199 | top: 0, 200 | left: 0, 201 | width: "100%", 202 | height: "100%", 203 | }, 204 | cell: { 205 | flex: 1, 206 | padding: 10, 207 | justifyContent: "center", 208 | alignItems: "center", 209 | }, 210 | }); 211 | 212 | const styles = StyleSheet.create({ 213 | container: { 214 | backgroundColor: "white", 215 | ...commonStyles.full, 216 | }, 217 | examples: { 218 | ...commonStyles.full, 219 | padding: 5, 220 | paddingBottom: 0, 221 | }, 222 | example: { 223 | paddingBottom: 5, 224 | flex: 1, 225 | flexDirection: "row", 226 | }, 227 | exampleLeft: { 228 | ...commonStyles.cell, 229 | }, 230 | exampleRight: { 231 | ...commonStyles.cell, 232 | }, 233 | }); 234 | -------------------------------------------------------------------------------- /example/assets/images/adaptive-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iddan/react-native-canvas/619bf5d8208139c374f3043f6f3e461a73c3eb77/example/assets/images/adaptive-icon.png -------------------------------------------------------------------------------- /example/assets/images/embed-html.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iddan/react-native-canvas/619bf5d8208139c374f3043f6f3e461a73c3eb77/example/assets/images/embed-html.png -------------------------------------------------------------------------------- /example/assets/images/gradient.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iddan/react-native-canvas/619bf5d8208139c374f3043f6f3e461a73c3eb77/example/assets/images/gradient.png -------------------------------------------------------------------------------- /example/assets/images/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iddan/react-native-canvas/619bf5d8208139c374f3043f6f3e461a73c3eb77/example/assets/images/icon.png -------------------------------------------------------------------------------- /example/assets/images/image-rect.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iddan/react-native-canvas/619bf5d8208139c374f3043f6f3e461a73c3eb77/example/assets/images/image-rect.png -------------------------------------------------------------------------------- /example/assets/images/panorama.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iddan/react-native-canvas/619bf5d8208139c374f3043f6f3e461a73c3eb77/example/assets/images/panorama.png -------------------------------------------------------------------------------- /example/assets/images/path.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iddan/react-native-canvas/619bf5d8208139c374f3043f6f3e461a73c3eb77/example/assets/images/path.png -------------------------------------------------------------------------------- /example/assets/images/purple-black-rect.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iddan/react-native-canvas/619bf5d8208139c374f3043f6f3e461a73c3eb77/example/assets/images/purple-black-rect.png -------------------------------------------------------------------------------- /example/assets/images/purple-rect.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iddan/react-native-canvas/619bf5d8208139c374f3043f6f3e461a73c3eb77/example/assets/images/purple-rect.png -------------------------------------------------------------------------------- /example/assets/images/red-circle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iddan/react-native-canvas/619bf5d8208139c374f3043f6f3e461a73c3eb77/example/assets/images/red-circle.png -------------------------------------------------------------------------------- /example/assets/images/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iddan/react-native-canvas/619bf5d8208139c374f3043f6f3e461a73c3eb77/example/assets/images/splash.png -------------------------------------------------------------------------------- /example/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "example", 3 | "main": "expo-router/entry", 4 | "version": "1.0.0", 5 | "scripts": { 6 | "start": "expo start", 7 | "android": "expo start --android", 8 | "ios": "expo start --ios", 9 | "test": "jest --watchAll", 10 | "lint": "expo lint" 11 | }, 12 | "jest": { 13 | "preset": "jest-expo" 14 | }, 15 | "dependencies": { 16 | "expo": "~51.0.9", 17 | "expo-constants": "^16.0.2", 18 | "expo-linking": "^6.3.1", 19 | "expo-router": "~3.5.14", 20 | "expo-status-bar": "^1.12.1", 21 | "react": "18.2.0", 22 | "react-dom": "18.2.0", 23 | "react-native": "^0.74.1", 24 | "react-native-canvas": "file:../.", 25 | "react-native-reanimated": "^3.11.0", 26 | "react-native-safe-area-context": "^4.10.3", 27 | "react-native-screens": "^3.31.1", 28 | "react-native-webview": "^13.8.6" 29 | }, 30 | "devDependencies": { 31 | "@babel/core": "^7.20.0", 32 | "@types/jest": "^29.5.12", 33 | "@types/react": "~18.2.45", 34 | "@types/react-native-canvas": "^0.1.13", 35 | "jest": "^29.2.1", 36 | "jest-expo": "~51.0.1", 37 | "typescript": "~5.3.3" 38 | }, 39 | "private": true 40 | } 41 | -------------------------------------------------------------------------------- /example/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "expo/tsconfig.base", 3 | "compilerOptions": { 4 | "strict": true, 5 | "paths": { 6 | "@/*": ["./*"] 7 | } 8 | }, 9 | "include": ["**/*.ts", "**/*.tsx", ".expo/types/**/*.ts", "expo-env.d.ts"] 10 | } 11 | -------------------------------------------------------------------------------- /license.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Iddan Aaronsohn 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-native-canvas", 3 | "license": "MIT", 4 | "version": "0.1.40", 5 | "main": "dist/Canvas", 6 | "scripts": { 7 | "build": "node bundle-html.js ./src/index.html && tsc", 8 | "copy-to-example": "rsync -rv dist example/node_modules/react-native-canvas", 9 | "build-to-example": "npm run build; npm run copy-to-example;", 10 | "prepare": "npm run build" 11 | }, 12 | "devDependencies": { 13 | "eslint": "^4", 14 | "eslint-config-fbjs-opensource": "^1.0.0", 15 | "parse5": "^5.0.0", 16 | "prettier": "^3.3.0", 17 | "typescript": "^5.4.5" 18 | }, 19 | "dependencies": { 20 | "ctx-polyfill": "^1.1.4" 21 | }, 22 | "repository": "https://github.com/iddan/react-native-canvas", 23 | "peerDependencies": { 24 | "react-native-webview": ">=5.10.0 || >=6.1.0" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # 🎆 react-native-canvas 2 | 3 | A Canvas component for React Native 4 | 5 | ```bash 6 | npm install react-native-webview 7 | react-native link react-native-webview 8 | npm install react-native-canvas 9 | ``` 10 | 11 | ### Usage 12 | 13 | ```JSX 14 | import React, { Component } from 'react'; 15 | import Canvas from 'react-native-canvas'; 16 | 17 | const App = () => { 18 | const handleCanvas = (canvas) => { 19 | if (!canvas) return; 20 | 21 | const ctx = canvas.getContext('2d'); 22 | ctx.fillStyle = 'purple'; 23 | ctx.fillRect(0, 0, 100, 100); 24 | }; 25 | 26 | return ( 27 | 28 | ); 29 | } 30 | ``` 31 | 32 | ### API 33 | 34 | #### Canvas 35 | 36 | ###### Canvas#height 37 | 38 | Reflects the height of the canvas in pixels 39 | 40 | ###### Canvas#width 41 | 42 | Reflects the width of the canvas in pixels 43 | 44 | ###### Canvas#getContext() 45 | 46 | Returns a canvas rendering context. Currently only supports 2d context. 47 | 48 | ###### Canvas#toDataURL() 49 | 50 | Returns a `Promise` that resolves to DataURL. 51 | 52 | #### CanvasRenderingContext2D 53 | 54 | Standard CanvasRenderingContext2D. [MDN](https://developer.mozilla.org/en/docs/Web/API/CanvasRenderingContext2D). Only difference is `await` should be used to retrieve values from methods. 55 | 56 | ```javascript 57 | const ctx = canvas.getContext("2d"); 58 | ``` 59 | 60 | #### Image 61 | 62 | WebView Image constructor. Unlike in the browsers accepts canvas as first argument. [MDN](https://developer.mozilla.org/en-US/docs/Web/API/HTMLImageElement/Image) 63 | 64 | ```javascript 65 | const image = new Image(canvas, height, width); 66 | ``` 67 | 68 | #### Path2D 69 | 70 | Path2D API constructor. Unlike in the browsers, this requires the canvas as first argument. See also https://developer.mozilla.org/en-US/docs/Web/API/Path2D/Path2D. 71 | 72 | ```javascript 73 | const path = new Path2D(canvas); 74 | ``` 75 | -------------------------------------------------------------------------------- /src/Bus.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @typedef {Object} Message 3 | * @property {string} id 4 | */ 5 | 6 | export default class Bus { 7 | _paused = false; 8 | /** 9 | * @type {Object.} 10 | */ 11 | _messageListeners = {}; 12 | /** 13 | * @type {Message[]} 14 | */ 15 | _queue = []; 16 | /** 17 | * @param {function} send 18 | */ 19 | constructor(send) { 20 | this._send = send; 21 | } 22 | /** 23 | * @param {Message} message 24 | * @return {Promise.} 25 | */ 26 | post(message) { 27 | return new Promise((resolve) => { 28 | /** 29 | * Currently, 'set' is the only message type that's not resolved 30 | * back to the caller. If we store it here, it will leak memory 31 | * because the entry won't get removed from this._messageListeners. 32 | */ 33 | if (message.type !== "set") { 34 | this._messageListeners[message.id] = resolve; 35 | } 36 | 37 | if (!this._paused) { 38 | this._send(message); 39 | } else { 40 | this._queue.push(message); 41 | } 42 | }); 43 | } 44 | /** 45 | * @param {Message} message 46 | * @return {void} 47 | */ 48 | handle(message) { 49 | const handler = this._messageListeners[message.id]; 50 | 51 | // Delete the message listener from the cache as soon as it's handled. 52 | delete this._messageListeners[message.id]; 53 | 54 | if (handler) { 55 | handler(message); 56 | } else { 57 | console.warn("Received unexpected message", message); 58 | } 59 | } 60 | /** 61 | * @returns {void} 62 | */ 63 | pause() { 64 | this._paused = true; 65 | } 66 | /** 67 | * @returns {void} 68 | */ 69 | resume() { 70 | this._paused = false; 71 | this._send(this._queue); 72 | this._queue = []; 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/Canvas.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | import { View, Platform, StyleSheet } from "react-native"; 3 | import { WebView } from "react-native-webview"; 4 | import Bus from "./Bus"; 5 | import { 6 | webviewTarget, 7 | webviewProperties, 8 | webviewMethods, 9 | constructors, 10 | WEBVIEW_TARGET, 11 | } from "./webview-binders"; 12 | import CanvasRenderingContext2D from "./CanvasRenderingContext2D"; 13 | import html from "./index.html.js"; 14 | export { default as Image } from "./Image"; 15 | export { default as ImageData } from "./ImageData"; 16 | export { default as Path2D } from "./Path2D"; 17 | import "./CanvasGradient"; 18 | 19 | const stylesheet = StyleSheet.create({ 20 | container: { overflow: "hidden", flex: 0 }, 21 | webview: { 22 | overflow: "hidden", 23 | backgroundColor: "transparent", 24 | flex: 0, 25 | }, 26 | webviewAndroid9: { 27 | overflow: "hidden", 28 | backgroundColor: "transparent", 29 | flex: 0, 30 | opacity: 0.99, 31 | }, 32 | }); 33 | 34 | @webviewTarget("canvas") 35 | @webviewProperties({ width: 300, height: 150 }) 36 | @webviewMethods(["toDataURL"]) 37 | export default class Canvas extends Component { 38 | state = { 39 | isLoaded: false, 40 | }; 41 | 42 | addMessageListener = (listener) => { 43 | this.listeners.push(listener); 44 | return () => this.removeMessageListener(listener); 45 | }; 46 | 47 | removeMessageListener = (listener) => { 48 | this.listeners.splice(this.listeners.indexOf(listener), 1); 49 | }; 50 | 51 | /** 52 | * in the mounting process this.webview can be set to null 53 | */ 54 | webviewPostMessage = (message) => { 55 | if (this.webview) { 56 | this.webview.postMessage(JSON.stringify(message)); 57 | } 58 | }; 59 | 60 | bus = new Bus(this.webviewPostMessage); 61 | listeners = []; 62 | context2D = new CanvasRenderingContext2D(this); 63 | 64 | constructor() { 65 | super(); 66 | this.bus.pause(); 67 | } 68 | 69 | getContext = (contextType, contextAttributes) => { 70 | switch (contextType) { 71 | case "2d": { 72 | return this.context2D; 73 | } 74 | } 75 | return null; 76 | }; 77 | 78 | postMessage = async (message) => { 79 | const { stack } = new Error(); 80 | const { type, payload } = await this.bus.post({ 81 | id: Math.random(), 82 | ...message, 83 | }); 84 | switch (type) { 85 | case "error": { 86 | const error = new Error(payload.message); 87 | error.stack = stack; 88 | throw error; 89 | } 90 | case "json": { 91 | return payload; 92 | } 93 | case "blob": { 94 | return atob(payload); 95 | } 96 | } 97 | }; 98 | 99 | handleMessage = (e) => { 100 | let data = JSON.parse(e.nativeEvent.data); 101 | switch (data.type) { 102 | case "log": { 103 | // eslint-disable-line no-console 104 | console.log(...data.payload); 105 | break; 106 | } 107 | case "error": { 108 | throw new Error(data.payload.message); 109 | } 110 | default: { 111 | if (data.payload) { 112 | const constructor = constructors[data.meta.constructor]; 113 | if (constructor) { 114 | const { args, payload } = data; 115 | const object = constructor.constructLocally(this, ...args); 116 | Object.assign(object, payload, { 117 | [WEBVIEW_TARGET]: data.meta.target, 118 | }); 119 | data = { 120 | ...data, 121 | payload: object, 122 | }; 123 | } 124 | for (const listener of this.listeners) { 125 | listener(data.payload); 126 | } 127 | } 128 | this.bus.handle(data); 129 | } 130 | } 131 | }; 132 | 133 | handleRef = (element) => { 134 | this.webview = element; 135 | }; 136 | 137 | handleLoad = () => { 138 | this.setState({ isLoaded: true }); 139 | this.bus.resume(); 140 | }; 141 | 142 | render() { 143 | const { width, height } = this; 144 | const { style, baseUrl = "", originWhitelist = ["*"] } = this.props; 145 | const { isLoaded } = this.state; 146 | if (Platform.OS === "android") { 147 | const isAndroid9 = Platform.Version >= 28; 148 | return ( 149 | 150 | 168 | 169 | ); 170 | } 171 | return ( 172 | 179 | 188 | 189 | ); 190 | } 191 | } 192 | -------------------------------------------------------------------------------- /src/CanvasGradient.js: -------------------------------------------------------------------------------- 1 | import { webviewConstructor, webviewMethods } from "./webview-binders"; 2 | 3 | @webviewMethods(["addColorStop"]) 4 | @webviewConstructor("CanvasGradient") 5 | export default class CanvasGradient { 6 | constructor(canvas) { 7 | this.canvas = canvas; 8 | } 9 | 10 | postMessage = (message) => { 11 | return this.canvas.postMessage(message); 12 | }; 13 | } 14 | -------------------------------------------------------------------------------- /src/CanvasRenderingContext2D.js: -------------------------------------------------------------------------------- 1 | import { 2 | webviewTarget, 3 | webviewProperties, 4 | webviewMethods, 5 | } from "./webview-binders"; 6 | 7 | @webviewTarget("context2D") 8 | @webviewProperties({ 9 | fillStyle: "#000", 10 | font: "10px sans-serif", 11 | globalAlpha: 1.0, 12 | globalCompositeOperation: "source-over", 13 | lineCap: "butt", 14 | lineDashOffset: 0.0, 15 | lineJoin: "miter", 16 | lineWidth: 1.0, 17 | miterLimit: 10.0, 18 | shadowBlur: 0, 19 | shadowColor: "rgba(0,0,0,0)", 20 | shadowOffsetX: 0, 21 | shadowOffsetY: 0, 22 | strokeStyle: "#000", 23 | textAlign: "start", 24 | textBaseline: "alphabetic", 25 | }) 26 | @webviewMethods([ 27 | "arc", 28 | "arcTo", 29 | "beginPath", 30 | "bezierCurveTo", 31 | "clearRect", 32 | "clip", 33 | "closePath", 34 | "createImageData", 35 | "createLinearGradient", 36 | "createPattern", 37 | "createRadialGradient", 38 | "drawFocusIfNeeded", 39 | "drawImage", 40 | "drawWidgetAsOnScreen", 41 | "drawWindow", 42 | "ellipse", 43 | "fill", 44 | "fillRect", 45 | "fillText", 46 | "getImageData", 47 | "getLineDash", 48 | "isPointInPath", 49 | "isPointInStroke", 50 | "lineTo", 51 | "measureText", 52 | "moveTo", 53 | "putImageData", 54 | "quadraticCurveTo", 55 | "rect", 56 | "restore", 57 | "rotate", 58 | "save", 59 | "scale", 60 | "setLineDash", 61 | "setTransform", 62 | "stroke", 63 | "strokeRect", 64 | "strokeText", 65 | "transform", 66 | "translate", 67 | ]) 68 | export default class CanvasRenderingContext2D { 69 | constructor(canvas) { 70 | this.canvas = canvas; 71 | } 72 | 73 | postMessage(message) { 74 | return this.canvas.postMessage(message); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/Image.js: -------------------------------------------------------------------------------- 1 | import Canvas from "./Canvas"; 2 | import { 3 | webviewConstructor, 4 | webviewProperties, 5 | webviewEvents, 6 | } from "./webview-binders"; 7 | 8 | @webviewProperties({ 9 | crossOrigin: undefined, 10 | height: undefined, 11 | src: undefined, 12 | width: undefined, 13 | }) 14 | @webviewEvents(["load", "error"]) 15 | @webviewConstructor("Image") 16 | export default class Image { 17 | constructor(canvas, width, height, noOnConstruction) { 18 | if (!(canvas instanceof Canvas)) { 19 | throw new Error("Image must be initialized with a Canvas instance"); 20 | } 21 | this.canvas = canvas; 22 | if (this.onConstruction && !noOnConstruction) { 23 | this.onConstruction(); 24 | } 25 | if (this.width) { 26 | this.width = width; 27 | } 28 | if (this.height) { 29 | this.height = height; 30 | } 31 | } 32 | 33 | postMessage = (message) => { 34 | return this.canvas.postMessage(message); 35 | }; 36 | 37 | addMessageListener = (listener) => { 38 | return this.canvas.addMessageListener(listener); 39 | }; 40 | } 41 | -------------------------------------------------------------------------------- /src/ImageData.js: -------------------------------------------------------------------------------- 1 | import Canvas from "./Canvas"; 2 | import { webviewConstructor } from "./webview-binders"; 3 | 4 | @webviewConstructor("ImageData") 5 | export default class ImageData { 6 | constructor(canvas, array, width, height, noOnConstruction) { 7 | if (!(canvas instanceof Canvas)) { 8 | throw new Error("ImageData must be initialized with a Canvas instance"); 9 | } 10 | this.canvas = canvas; 11 | if (this.onConstruction && !noOnConstruction) { 12 | this.onConstruction(array, width, height); 13 | } 14 | } 15 | 16 | postMessage = (message) => { 17 | return this.canvas.postMessage(message); 18 | }; 19 | 20 | addMessageListener = (listener) => { 21 | return this.canvas.addMessageListener(listener); 22 | }; 23 | } 24 | -------------------------------------------------------------------------------- /src/Path2D.js: -------------------------------------------------------------------------------- 1 | import { webviewConstructor, webviewMethods } from "./webview-binders"; 2 | 3 | /** 4 | * Currently doesn't support passing an SVGMatrix in addPath as SVGMatrix is deprecated 5 | */ 6 | @webviewMethods([ 7 | "addPath", 8 | "closePath", 9 | "moveTo", 10 | "lineTo", 11 | "bezierCurveTo", 12 | "quadraticCurveTo", 13 | "arc", 14 | "arcTo", 15 | "ellipse", 16 | "rect", 17 | ]) 18 | @webviewConstructor("Path2D") 19 | export default class Path2D { 20 | constructor(canvas, pathOrD, noOnConstruction) { 21 | this.canvas = canvas; 22 | if (this.onConstruction && !noOnConstruction) { 23 | this.onConstruction(pathOrD); 24 | } 25 | } 26 | 27 | postMessage = (message) => { 28 | return this.canvas.postMessage(message); 29 | }; 30 | 31 | addMessageListener = (listener) => { 32 | return this.canvas.addMessageListener(listener); 33 | }; 34 | } 35 | -------------------------------------------------------------------------------- /src/autoscale-canvas.ts: -------------------------------------------------------------------------------- 1 | const scale = 2 | (ratio: number) => 3 | (item: unknown): unknown => { 4 | if (typeof item === "number") { 5 | return item * ratio; 6 | } 7 | return item; 8 | }; 9 | 10 | /** 11 | * Extracted from https://github.com/component/autoscale-canvas 12 | * @param {Canvas} canvas 13 | * @return {Canvas} 14 | */ 15 | function autoScaleCanvas(canvas: HTMLCanvasElement): HTMLCanvasElement { 16 | const ctx = canvas.getContext("2d"); 17 | const ratio = window.devicePixelRatio || 1; 18 | if (ratio !== 1) { 19 | canvas.style.width = canvas.width + "px"; 20 | canvas.style.height = canvas.height + "px"; 21 | canvas.width *= ratio; 22 | canvas.height *= ratio; 23 | ctx.scale(ratio, ratio); 24 | ctx.isPointInPath = (...args: unknown[]) => 25 | CanvasRenderingContext2D.prototype.isPointInPath.apply( 26 | ctx, 27 | args.map(scale(ratio)), 28 | ); 29 | } 30 | return canvas; 31 | } 32 | 33 | window.autoScaleCanvas = autoScaleCanvas; 34 | -------------------------------------------------------------------------------- /src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 29 | 30 | 31 | 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /src/index.html.js: -------------------------------------------------------------------------------- 1 | export default ` 2 | 3 | 25 | 26 | 27 | 329 | 358 | 642 | 643 | 644 | ` -------------------------------------------------------------------------------- /src/webview-binders.js: -------------------------------------------------------------------------------- 1 | export const WEBVIEW_TARGET = "@@WEBVIEW_TARGET"; 2 | 3 | /** 4 | * @mutable 5 | */ 6 | export const constructors = {}; 7 | 8 | export const webviewTarget = (targetName) => (target) => { 9 | target.prototype[WEBVIEW_TARGET] = targetName; 10 | }; 11 | 12 | const ID = () => Math.random().toString(32).slice(2); 13 | 14 | /** 15 | * These are where objects need other objects as an argument. 16 | * Because when the data is sent as JSON it removes the class. 17 | * 18 | * One example being ImageData which requires the Uint8ClampedArray 19 | * object as the first parameter. 20 | */ 21 | const SPECIAL_CONSTRUCTOR = { 22 | ImageData: { 23 | className: "Uint8ClampedArray", 24 | paramNum: 0, 25 | }, 26 | }; 27 | 28 | export const webviewConstructor = (constructorName) => (target) => { 29 | constructors[constructorName] = target; 30 | target.constructLocally = function (...args) { 31 | // Pass noOnConstruction 32 | return new target(...args, true); 33 | }; 34 | /** 35 | * Arguments should be identical to the arguments passed to the constructor 36 | * just without the canvas instance 37 | */ 38 | target.prototype.onConstruction = function (...args) { 39 | if (SPECIAL_CONSTRUCTOR[constructorName] !== undefined) { 40 | const { className, paramNum } = SPECIAL_CONSTRUCTOR[constructorName]; 41 | args[paramNum] = { className, classArgs: [args[paramNum]] }; 42 | } 43 | this[WEBVIEW_TARGET] = ID(); 44 | this.postMessage({ 45 | type: "construct", 46 | payload: { 47 | constructor: constructorName, 48 | id: this[WEBVIEW_TARGET], 49 | args, 50 | }, 51 | }); 52 | }; 53 | target.prototype.toJSON = function () { 54 | return { __ref__: this[WEBVIEW_TARGET] }; 55 | }; 56 | }; 57 | 58 | export const webviewMethods = (methods) => (target) => { 59 | for (const method of methods) { 60 | target.prototype[method] = function (...args) { 61 | return this.postMessage({ 62 | type: "exec", 63 | payload: { 64 | target: this[WEBVIEW_TARGET], 65 | method, 66 | args, 67 | }, 68 | }); 69 | }; 70 | } 71 | }; 72 | 73 | export const webviewProperties = (properties) => (target) => { 74 | for (const key of Object.keys(properties)) { 75 | const initialValue = properties[key]; 76 | const privateKey = `__${key}__`; 77 | target.prototype[privateKey] = initialValue; 78 | Object.defineProperty(target.prototype, key, { 79 | get() { 80 | return this[privateKey]; 81 | }, 82 | set(value) { 83 | this.postMessage({ 84 | type: "set", 85 | payload: { 86 | target: this[WEBVIEW_TARGET], 87 | key, 88 | value, 89 | }, 90 | }); 91 | 92 | if (this.forceUpdate) { 93 | this.forceUpdate(); 94 | } 95 | 96 | return (this[privateKey] = value); 97 | }, 98 | }); 99 | } 100 | }; 101 | 102 | export const webviewEvents = (types) => (target) => { 103 | const { onConstruction } = target.prototype; 104 | target.prototype.onConstruction = function () { 105 | if (onConstruction) { 106 | onConstruction.call(this); 107 | } 108 | this.postMessage({ 109 | type: "listen", 110 | payload: { 111 | types, 112 | target: this[WEBVIEW_TARGET], 113 | }, 114 | }); 115 | }; 116 | target.prototype.addEventListener = function (type, callback) { 117 | this.addMessageListener((message) => { 118 | if ( 119 | message && 120 | message.type === "event" && 121 | message.payload.target[WEBVIEW_TARGET] === this[WEBVIEW_TARGET] && 122 | message.payload.type === type 123 | ) { 124 | for (const key in message.payload.target) { 125 | const value = message.payload.target[key]; 126 | if (key in this && this[key] !== value) { 127 | this[key] = value; 128 | } 129 | } 130 | callback({ 131 | ...message.payload, 132 | target: this, 133 | }); 134 | } 135 | }); 136 | }; 137 | }; 138 | -------------------------------------------------------------------------------- /src/webview.js: -------------------------------------------------------------------------------- 1 | const WEBVIEW_TARGET = "@@WEBVIEW_TARGET"; 2 | 3 | const ID = () => Math.random().toString(32).slice(2); 4 | 5 | const flattenObjectCopyValue = (flatObj, srcObj, key) => { 6 | const value = srcObj[key]; 7 | if (typeof value === "function") { 8 | return; 9 | } 10 | if (typeof value === "object" && value instanceof Node) { 11 | return; 12 | } 13 | flatObj[key] = flattenObject(value); 14 | }; 15 | 16 | const flattenObject = (object) => { 17 | if (typeof object !== "object" || object === null) { 18 | return object; 19 | } 20 | const flatObject = {}; 21 | for (const key in object) { 22 | flattenObjectCopyValue(flatObject, object, key); 23 | } 24 | for (const key in Object.getOwnPropertyNames(object)) { 25 | flattenObjectCopyValue(flatObject, object, key); 26 | } 27 | return flatObject; 28 | }; 29 | 30 | class AutoScaledCanvas { 31 | constructor(element) { 32 | this.element = element; 33 | } 34 | 35 | toDataURL(...args) { 36 | return this.element.toDataURL(...args); 37 | } 38 | 39 | autoScale() { 40 | if (this.savedHeight !== undefined) { 41 | this.element.height = this.savedHeight; 42 | } 43 | if (this.savedWidth !== undefined) { 44 | this.element.width = this.savedWidth; 45 | } 46 | window.autoScaleCanvas(this.element); 47 | } 48 | 49 | get width() { 50 | return this.element.width; 51 | } 52 | 53 | set width(value) { 54 | this.savedWidth = value; 55 | this.autoScale(); 56 | return value; 57 | } 58 | 59 | get height() { 60 | return this.element.height; 61 | } 62 | 63 | set height(value) { 64 | this.savedHeight = value; 65 | this.autoScale(); 66 | return value; 67 | } 68 | } 69 | 70 | const toMessage = (result) => { 71 | if (result instanceof Blob) { 72 | return { 73 | type: "blob", 74 | payload: btoa(result), 75 | meta: {}, 76 | }; 77 | } 78 | if (result instanceof Object) { 79 | if (!result[WEBVIEW_TARGET]) { 80 | const id = ID(); 81 | result[WEBVIEW_TARGET] = id; 82 | targets[id] = result; 83 | } 84 | return { 85 | type: "json", 86 | payload: flattenObject(result), 87 | args: toArgs(flattenObject(result)), 88 | meta: { 89 | target: result[WEBVIEW_TARGET], 90 | constructor: result.__constructorName__ || result.constructor.name, 91 | }, 92 | }; 93 | } 94 | return { 95 | type: "json", 96 | payload: JSON.stringify(result), 97 | meta: {}, 98 | }; 99 | }; 100 | 101 | /** 102 | * Gets the all the args required for creating the object. 103 | * Also converts typed arrays to normal arrays. 104 | * 105 | * For example with ImageData we need a Uint8ClampedArray, 106 | * but if we sent it as JSON it will be sent as an object 107 | * not an array. So we convert any typed arrays into arrays 108 | * first, they will be converted to Uint8ClampedArrays in 109 | * `webview-binders.js`. 110 | * 111 | */ 112 | const toArgs = (result) => { 113 | const args = []; 114 | for (const key in result) { 115 | if (result[key] !== undefined && key !== "@@WEBVIEW_TARGET") { 116 | if (typedArrays[result[key].constructor.name] !== undefined) { 117 | result[key] = Array.from(result[key]); 118 | } 119 | args.push(result[key]); 120 | } 121 | } 122 | return args; 123 | }; 124 | 125 | /** 126 | * Creates objects from args. If any argument have the object 127 | * which contains `className` it means we need to convert that 128 | * argument into an object. 129 | * 130 | * We need to do this because when we pass data between the WebView 131 | * and RN using JSON, it strips/removes the class data from the object. 132 | * So this will raise errors as the WebView will expect arguments to be 133 | * of a certain class. 134 | * 135 | * For example for ImageData we expect to receive 136 | * [{className: Uint8ClampedArray, classArgs: [Array(4)]}, 100, 100] 137 | * We need to convert the first parameter into an object first. 138 | * 139 | */ 140 | const createObjectsFromArgs = (args) => { 141 | for (let index = 0; index < args.length; index += 1) { 142 | const currentArg = args[index]; 143 | if (currentArg && currentArg.className !== undefined) { 144 | const { className, classArgs } = currentArg; 145 | const object = new constructors[className](...classArgs); 146 | args[index] = object; 147 | } 148 | } 149 | return args; 150 | }; 151 | 152 | // const print = (...args) => { 153 | // const message = JSON.stringify({ 154 | // type: 'log', 155 | // payload: args, 156 | // }); 157 | // window.ReactNativeWebView.postMessage(message); 158 | // }; 159 | 160 | const canvas = document.createElement("canvas"); 161 | const autoScaledCanvas = new AutoScaledCanvas(canvas); 162 | 163 | const targets = { 164 | canvas: autoScaledCanvas, 165 | context2D: canvas.getContext("2d"), 166 | }; 167 | 168 | const constructors = { 169 | Image, 170 | Path2D, 171 | CanvasGradient, 172 | ImageData, 173 | Uint8ClampedArray, 174 | }; 175 | 176 | const typedArrays = { 177 | Uint8ClampedArray, 178 | }; 179 | 180 | /** 181 | * In iOS 9 constructors doesn't have bind defined which fails 182 | * Babel object constructors utility function 183 | */ 184 | Image.bind = 185 | Image.bind || 186 | function () { 187 | return Image; 188 | }; 189 | 190 | Path2D.bind = 191 | Path2D.bind || 192 | function () { 193 | return Path2D; 194 | }; 195 | 196 | ImageData.bind = 197 | ImageData.bind || 198 | function () { 199 | return ImageData; 200 | }; 201 | 202 | Uint8ClampedArray.bind = 203 | Uint8ClampedArray.bind || 204 | function () { 205 | return Uint8ClampedArray; 206 | }; 207 | 208 | const populateRefs = (arg) => { 209 | if (arg && arg.__ref__) { 210 | return targets[arg.__ref__]; 211 | } 212 | return arg; 213 | }; 214 | 215 | document.body.appendChild(canvas); 216 | 217 | /** 218 | * NOTE: Depending on the message type, the message sender will potentially get a callback via 219 | * window.ReactNativeWebView.postMessage(...). The postMessage function causes Bus to resolve 220 | * a Promise to the caller. 221 | * 222 | * For example, ctx.fillRect(...) returns a Promise that is then resolved from the code below. 223 | * 224 | * 'set' is currently the exception - it doesn't resolve at all. 225 | * Therefore, Bus should not be saving message ids for 'set' messages. 226 | * See the function 'post' in Bus.js. 227 | */ 228 | function handleMessage({ id, type, payload }) { 229 | switch (type) { 230 | case "exec": { 231 | const { target, method, args } = payload; 232 | const result = targets[target][method](...args.map(populateRefs)); 233 | const message = toMessage(result); 234 | 235 | /** 236 | * In iOS 9 some classes name are not defined so we compare to 237 | * known constructors to find the name. 238 | */ 239 | if (typeof result === "object" && !message.meta.constructor) { 240 | for (const constructorName in constructors) { 241 | if (result instanceof constructors[constructorName]) { 242 | message.meta.constructor = constructorName; 243 | } 244 | } 245 | } 246 | window.ReactNativeWebView.postMessage(JSON.stringify({ id, ...message })); 247 | break; 248 | } 249 | case "set": { 250 | const { target, key, value } = payload; 251 | targets[target][key] = populateRefs(value); 252 | break; 253 | } 254 | case "construct": { 255 | const { constructor, id: target, args = [] } = payload; 256 | const newArgs = createObjectsFromArgs(args); 257 | let object; 258 | try { 259 | object = new constructors[constructor](...newArgs); 260 | } catch (error) { 261 | throw new Error( 262 | `Error while constructing ${constructor} ${error.message}`, 263 | ); 264 | } 265 | object.__constructorName__ = constructor; 266 | const message = toMessage({}); 267 | targets[target] = object; 268 | window.ReactNativeWebView.postMessage(JSON.stringify({ id, ...message })); 269 | break; 270 | } 271 | case "listen": { 272 | const { types, target } = payload; 273 | for (const eventType of types) { 274 | targets[target].addEventListener(eventType, (e) => { 275 | const message = toMessage({ 276 | type: "event", 277 | payload: { 278 | type: e.type, 279 | target: { 280 | ...flattenObject(targets[target]), 281 | [WEBVIEW_TARGET]: target, 282 | }, 283 | }, 284 | }); 285 | window.ReactNativeWebView.postMessage( 286 | JSON.stringify({ id, ...message }), 287 | ); 288 | }); 289 | } 290 | break; 291 | } 292 | } 293 | } 294 | 295 | const handleError = (err, message) => { 296 | window.ReactNativeWebView.postMessage( 297 | JSON.stringify({ 298 | id: message.id, 299 | type: "error", 300 | payload: { 301 | message: err.message, 302 | stack: err.stack, 303 | }, 304 | }), 305 | ); 306 | document.removeEventListener("message", handleIncomingMessage); 307 | }; 308 | 309 | function handleIncomingMessage(e) { 310 | const data = JSON.parse(e.data); 311 | if (Array.isArray(data)) { 312 | for (const message of data) { 313 | try { 314 | handleMessage(message); 315 | } catch (err) { 316 | handleError(err, message); 317 | } 318 | } 319 | } else { 320 | try { 321 | handleMessage(data); 322 | } catch (err) { 323 | handleError(err, data); 324 | } 325 | } 326 | } 327 | 328 | // iOS 329 | window.addEventListener("message", handleIncomingMessage); 330 | // Android 331 | document.addEventListener("message", handleIncomingMessage); 332 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "strict": false, 4 | "target": "ES5", 5 | "experimentalDecorators": true, 6 | "declaration": false, 7 | "allowJs": true, 8 | "checkJs": false, 9 | "outDir": "./dist", 10 | "jsx": "react-jsx" 11 | }, 12 | "include": ["src"] 13 | } 14 | -------------------------------------------------------------------------------- /yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | acorn-jsx@^3.0.0: 6 | version "3.0.1" 7 | resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-3.0.1.tgz#afdf9488fb1ecefc8348f6fb22f464e32a58b36b" 8 | dependencies: 9 | acorn "^3.0.4" 10 | 11 | acorn@^3.0.4: 12 | version "3.3.0" 13 | resolved "https://registry.yarnpkg.com/acorn/-/acorn-3.3.0.tgz#45e37fb39e8da3f25baee3ff5369e2bb5f22017a" 14 | 15 | acorn@^5.5.0: 16 | version "5.7.1" 17 | resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.7.1.tgz#f095829297706a7c9776958c0afc8930a9b9d9d8" 18 | 19 | ajv-keywords@^2.1.0: 20 | version "2.1.1" 21 | resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-2.1.1.tgz#617997fc5f60576894c435f940d819e135b80762" 22 | integrity sha1-YXmX/F9gV2iUxDX5QNgZ4TW4B2I= 23 | 24 | ajv@^5.2.3, ajv@^5.3.0: 25 | version "5.5.2" 26 | resolved "https://registry.yarnpkg.com/ajv/-/ajv-5.5.2.tgz#73b5eeca3fab653e3d3f9422b341ad42205dc965" 27 | integrity sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU= 28 | dependencies: 29 | co "^4.6.0" 30 | fast-deep-equal "^1.0.0" 31 | fast-json-stable-stringify "^2.0.0" 32 | json-schema-traverse "^0.3.0" 33 | 34 | ansi-escapes@^3.0.0: 35 | version "3.2.0" 36 | resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-3.2.0.tgz#8780b98ff9dbf5638152d1f1fe5c1d7b4442976b" 37 | integrity sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ== 38 | 39 | ansi-regex@^2.0.0: 40 | version "2.1.1" 41 | resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" 42 | 43 | ansi-regex@^3.0.0: 44 | version "3.0.0" 45 | resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998" 46 | 47 | ansi-styles@^2.2.1: 48 | version "2.2.1" 49 | resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" 50 | 51 | ansi-styles@^3.2.1: 52 | version "3.2.1" 53 | resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" 54 | integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== 55 | dependencies: 56 | color-convert "^1.9.0" 57 | 58 | argparse@^1.0.7: 59 | version "1.0.10" 60 | resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" 61 | integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== 62 | dependencies: 63 | sprintf-js "~1.0.2" 64 | 65 | array-union@^1.0.1: 66 | version "1.0.2" 67 | resolved "https://registry.yarnpkg.com/array-union/-/array-union-1.0.2.tgz#9a34410e4f4e3da23dea375be5be70f24778ec39" 68 | dependencies: 69 | array-uniq "^1.0.1" 70 | 71 | array-uniq@^1.0.1: 72 | version "1.0.3" 73 | resolved "https://registry.yarnpkg.com/array-uniq/-/array-uniq-1.0.3.tgz#af6ac877a25cc7f74e058894753858dfdb24fdb6" 74 | 75 | array.prototype.find@^2.0.1: 76 | version "2.0.4" 77 | resolved "https://registry.yarnpkg.com/array.prototype.find/-/array.prototype.find-2.0.4.tgz#556a5c5362c08648323ddaeb9de9d14bc1864c90" 78 | dependencies: 79 | define-properties "^1.1.2" 80 | es-abstract "^1.7.0" 81 | 82 | arrify@^1.0.0: 83 | version "1.0.1" 84 | resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d" 85 | 86 | babel-code-frame@^6.22.0, babel-code-frame@^6.26.0: 87 | version "6.26.0" 88 | resolved "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.26.0.tgz#63fd43f7dc1e3bb7ce35947db8fe369a3f58c74b" 89 | dependencies: 90 | chalk "^1.1.3" 91 | esutils "^2.0.2" 92 | js-tokens "^3.0.2" 93 | 94 | babel-eslint@^7.1.1: 95 | version "7.2.3" 96 | resolved "https://registry.yarnpkg.com/babel-eslint/-/babel-eslint-7.2.3.tgz#b2fe2d80126470f5c19442dc757253a897710827" 97 | dependencies: 98 | babel-code-frame "^6.22.0" 99 | babel-traverse "^6.23.1" 100 | babel-types "^6.23.0" 101 | babylon "^6.17.0" 102 | 103 | babel-messages@^6.23.0: 104 | version "6.23.0" 105 | resolved "https://registry.yarnpkg.com/babel-messages/-/babel-messages-6.23.0.tgz#f3cdf4703858035b2a2951c6ec5edf6c62f2630e" 106 | dependencies: 107 | babel-runtime "^6.22.0" 108 | 109 | babel-runtime@^6.22.0, babel-runtime@^6.26.0: 110 | version "6.26.0" 111 | resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.26.0.tgz#965c7058668e82b55d7bfe04ff2337bc8b5647fe" 112 | dependencies: 113 | core-js "^2.4.0" 114 | regenerator-runtime "^0.11.0" 115 | 116 | babel-traverse@^6.23.1: 117 | version "6.26.0" 118 | resolved "https://registry.yarnpkg.com/babel-traverse/-/babel-traverse-6.26.0.tgz#46a9cbd7edcc62c8e5c064e2d2d8d0f4035766ee" 119 | dependencies: 120 | babel-code-frame "^6.26.0" 121 | babel-messages "^6.23.0" 122 | babel-runtime "^6.26.0" 123 | babel-types "^6.26.0" 124 | babylon "^6.18.0" 125 | debug "^2.6.8" 126 | globals "^9.18.0" 127 | invariant "^2.2.2" 128 | lodash "^4.17.4" 129 | 130 | babel-types@^6.23.0, babel-types@^6.26.0: 131 | version "6.26.0" 132 | resolved "https://registry.yarnpkg.com/babel-types/-/babel-types-6.26.0.tgz#a3b073f94ab49eb6fa55cd65227a334380632497" 133 | dependencies: 134 | babel-runtime "^6.26.0" 135 | esutils "^2.0.2" 136 | lodash "^4.17.4" 137 | to-fast-properties "^1.0.3" 138 | 139 | babylon@^6.17.0, babylon@^6.18.0: 140 | version "6.18.0" 141 | resolved "https://registry.yarnpkg.com/babylon/-/babylon-6.18.0.tgz#af2f3b88fa6f5c1e4c634d1a0f8eac4f55b395e3" 142 | 143 | balanced-match@^1.0.0: 144 | version "1.0.0" 145 | resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" 146 | 147 | brace-expansion@^1.1.7: 148 | version "1.1.8" 149 | resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.8.tgz#c07b211c7c952ec1f8efd51a77ef0d1d3990a292" 150 | dependencies: 151 | balanced-match "^1.0.0" 152 | concat-map "0.0.1" 153 | 154 | buffer-from@^1.0.0: 155 | version "1.1.1" 156 | resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef" 157 | 158 | caller-path@^0.1.0: 159 | version "0.1.0" 160 | resolved "https://registry.yarnpkg.com/caller-path/-/caller-path-0.1.0.tgz#94085ef63581ecd3daa92444a8fe94e82577751f" 161 | dependencies: 162 | callsites "^0.2.0" 163 | 164 | callsites@^0.2.0: 165 | version "0.2.0" 166 | resolved "https://registry.yarnpkg.com/callsites/-/callsites-0.2.0.tgz#afab96262910a7f33c19a5775825c69f34e350ca" 167 | 168 | chalk@^1.1.3: 169 | version "1.1.3" 170 | resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" 171 | dependencies: 172 | ansi-styles "^2.2.1" 173 | escape-string-regexp "^1.0.2" 174 | has-ansi "^2.0.0" 175 | strip-ansi "^3.0.0" 176 | supports-color "^2.0.0" 177 | 178 | chalk@^2.0.0, chalk@^2.1.0: 179 | version "2.4.2" 180 | resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" 181 | integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== 182 | dependencies: 183 | ansi-styles "^3.2.1" 184 | escape-string-regexp "^1.0.5" 185 | supports-color "^5.3.0" 186 | 187 | chardet@^0.4.0: 188 | version "0.4.2" 189 | resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.4.2.tgz#b5473b33dc97c424e5d98dc87d55d4d8a29c8bf2" 190 | integrity sha1-tUc7M9yXxCTl2Y3IfVXU2KKci/I= 191 | 192 | circular-json@^0.3.1: 193 | version "0.3.3" 194 | resolved "https://registry.yarnpkg.com/circular-json/-/circular-json-0.3.3.tgz#815c99ea84f6809529d2f45791bdf82711352d66" 195 | 196 | cli-cursor@^2.1.0: 197 | version "2.1.0" 198 | resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-2.1.0.tgz#b35dac376479facc3e94747d41d0d0f5238ffcb5" 199 | integrity sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU= 200 | dependencies: 201 | restore-cursor "^2.0.0" 202 | 203 | cli-width@^2.0.0: 204 | version "2.2.0" 205 | resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.2.0.tgz#ff19ede8a9a5e579324147b0c11f0fbcbabed639" 206 | 207 | co@^4.6.0: 208 | version "4.6.0" 209 | resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" 210 | 211 | color-convert@^1.9.0: 212 | version "1.9.0" 213 | resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.0.tgz#1accf97dd739b983bf994d56fec8f95853641b7a" 214 | dependencies: 215 | color-name "^1.1.1" 216 | 217 | color-name@^1.1.1: 218 | version "1.1.3" 219 | resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" 220 | 221 | concat-map@0.0.1: 222 | version "0.0.1" 223 | resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" 224 | 225 | concat-stream@^1.6.0: 226 | version "1.6.2" 227 | resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34" 228 | integrity sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw== 229 | dependencies: 230 | buffer-from "^1.0.0" 231 | inherits "^2.0.3" 232 | readable-stream "^2.2.2" 233 | typedarray "^0.0.6" 234 | 235 | core-js@^2.4.0: 236 | version "2.5.1" 237 | resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.5.1.tgz#ae6874dc66937789b80754ff5428df66819ca50b" 238 | 239 | core-util-is@~1.0.0: 240 | version "1.0.2" 241 | resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" 242 | 243 | cross-spawn@^5.1.0: 244 | version "5.1.0" 245 | resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-5.1.0.tgz#e8bd0efee58fcff6f8f94510a0a554bbfa235449" 246 | integrity sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk= 247 | dependencies: 248 | lru-cache "^4.0.1" 249 | shebang-command "^1.2.0" 250 | which "^1.2.9" 251 | 252 | ctx-polyfill@^1.1.4: 253 | version "1.1.4" 254 | resolved "https://registry.yarnpkg.com/ctx-polyfill/-/ctx-polyfill-1.1.4.tgz#08420bc5c540d08ac36d05720ca503c65e302d65" 255 | 256 | debug@^2.6.8: 257 | version "2.6.9" 258 | resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" 259 | dependencies: 260 | ms "2.0.0" 261 | 262 | debug@^3.1.0: 263 | version "3.1.0" 264 | resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261" 265 | dependencies: 266 | ms "2.0.0" 267 | 268 | deep-is@~0.1.3: 269 | version "0.1.3" 270 | resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" 271 | 272 | define-properties@^1.1.2: 273 | version "1.1.2" 274 | resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.2.tgz#83a73f2fea569898fb737193c8f873caf6d45c94" 275 | dependencies: 276 | foreach "^2.0.5" 277 | object-keys "^1.0.8" 278 | 279 | del@^2.0.2: 280 | version "2.2.2" 281 | resolved "https://registry.yarnpkg.com/del/-/del-2.2.2.tgz#c12c981d067846c84bcaf862cff930d907ffd1a8" 282 | dependencies: 283 | globby "^5.0.0" 284 | is-path-cwd "^1.0.0" 285 | is-path-in-cwd "^1.0.0" 286 | object-assign "^4.0.1" 287 | pify "^2.0.0" 288 | pinkie-promise "^2.0.0" 289 | rimraf "^2.2.8" 290 | 291 | doctrine@^1.2.2: 292 | version "1.5.0" 293 | resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-1.5.0.tgz#379dce730f6166f76cefa4e6707a159b02c5a6fa" 294 | dependencies: 295 | esutils "^2.0.2" 296 | isarray "^1.0.0" 297 | 298 | doctrine@^2.1.0: 299 | version "2.1.0" 300 | resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-2.1.0.tgz#5cd01fc101621b42c4cd7f5d1a66243716d3f39d" 301 | integrity sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw== 302 | dependencies: 303 | esutils "^2.0.2" 304 | 305 | es-abstract@^1.7.0: 306 | version "1.9.0" 307 | resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.9.0.tgz#690829a07cae36b222e7fd9b75c0d0573eb25227" 308 | dependencies: 309 | es-to-primitive "^1.1.1" 310 | function-bind "^1.1.1" 311 | has "^1.0.1" 312 | is-callable "^1.1.3" 313 | is-regex "^1.0.4" 314 | 315 | es-to-primitive@^1.1.1: 316 | version "1.1.1" 317 | resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.1.1.tgz#45355248a88979034b6792e19bb81f2b7975dd0d" 318 | dependencies: 319 | is-callable "^1.1.1" 320 | is-date-object "^1.0.1" 321 | is-symbol "^1.0.1" 322 | 323 | escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5: 324 | version "1.0.5" 325 | resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" 326 | 327 | eslint-config-fbjs-opensource@^1.0.0: 328 | version "1.0.0" 329 | resolved "https://registry.yarnpkg.com/eslint-config-fbjs-opensource/-/eslint-config-fbjs-opensource-1.0.0.tgz#9f6c88bed517c060eea9c43406a057330db08497" 330 | dependencies: 331 | babel-eslint "^7.1.1" 332 | eslint-plugin-babel "^4.0.1" 333 | eslint-plugin-flowtype "^2.30.0" 334 | eslint-plugin-jasmine "^2.2.0" 335 | eslint-plugin-prefer-object-spread "^1.1.0" 336 | eslint-plugin-react "^6.9.0" 337 | fbjs-eslint-utils "^1.0.0" 338 | 339 | eslint-plugin-babel@^4.0.1: 340 | version "4.1.2" 341 | resolved "https://registry.yarnpkg.com/eslint-plugin-babel/-/eslint-plugin-babel-4.1.2.tgz#79202a0e35757dd92780919b2336f1fa2fe53c1e" 342 | 343 | eslint-plugin-flowtype@^2.30.0: 344 | version "2.38.0" 345 | resolved "https://registry.yarnpkg.com/eslint-plugin-flowtype/-/eslint-plugin-flowtype-2.38.0.tgz#9134849d6f92e434a9adca0215b6b9f49a3b24d7" 346 | dependencies: 347 | lodash "^4.15.0" 348 | 349 | eslint-plugin-jasmine@^2.2.0: 350 | version "2.8.5" 351 | resolved "https://registry.yarnpkg.com/eslint-plugin-jasmine/-/eslint-plugin-jasmine-2.8.5.tgz#0e41d050fc31ca8a9a0f0ff56977fe42b3e7bc5c" 352 | 353 | eslint-plugin-prefer-object-spread@^1.1.0: 354 | version "1.2.1" 355 | resolved "https://registry.yarnpkg.com/eslint-plugin-prefer-object-spread/-/eslint-plugin-prefer-object-spread-1.2.1.tgz#27fb91853690cceb3ae6101d9c8aecc6a67a402c" 356 | 357 | eslint-plugin-react@^6.9.0: 358 | version "6.10.3" 359 | resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-6.10.3.tgz#c5435beb06774e12c7db2f6abaddcbf900cd3f78" 360 | dependencies: 361 | array.prototype.find "^2.0.1" 362 | doctrine "^1.2.2" 363 | has "^1.0.1" 364 | jsx-ast-utils "^1.3.4" 365 | object.assign "^4.0.4" 366 | 367 | eslint-scope@^3.7.1: 368 | version "3.7.3" 369 | resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-3.7.3.tgz#bb507200d3d17f60247636160b4826284b108535" 370 | integrity sha512-W+B0SvF4gamyCTmUc+uITPY0989iXVfKvhwtmJocTaYoc/3khEHmEmvfY/Gn9HA9VV75jrQECsHizkNw1b68FA== 371 | dependencies: 372 | esrecurse "^4.1.0" 373 | estraverse "^4.1.1" 374 | 375 | eslint-visitor-keys@^1.0.0: 376 | version "1.0.0" 377 | resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz#3f3180fb2e291017716acb4c9d6d5b5c34a6a81d" 378 | integrity sha512-qzm/XxIbxm/FHyH341ZrbnMUpe+5Bocte9xkmFMzPMjRaZMcXww+MpBptFvtU+79L362nqiLhekCxCxDPaUMBQ== 379 | 380 | eslint@^4: 381 | version "4.18.2" 382 | resolved "https://registry.yarnpkg.com/eslint/-/eslint-4.18.2.tgz#0f81267ad1012e7d2051e186a9004cc2267b8d45" 383 | integrity sha512-qy4i3wODqKMYfz9LUI8N2qYDkHkoieTbiHpMrYUI/WbjhXJQr7lI4VngixTgaG+yHX+NBCv7nW4hA0ShbvaNKw== 384 | dependencies: 385 | ajv "^5.3.0" 386 | babel-code-frame "^6.22.0" 387 | chalk "^2.1.0" 388 | concat-stream "^1.6.0" 389 | cross-spawn "^5.1.0" 390 | debug "^3.1.0" 391 | doctrine "^2.1.0" 392 | eslint-scope "^3.7.1" 393 | eslint-visitor-keys "^1.0.0" 394 | espree "^3.5.2" 395 | esquery "^1.0.0" 396 | esutils "^2.0.2" 397 | file-entry-cache "^2.0.0" 398 | functional-red-black-tree "^1.0.1" 399 | glob "^7.1.2" 400 | globals "^11.0.1" 401 | ignore "^3.3.3" 402 | imurmurhash "^0.1.4" 403 | inquirer "^3.0.6" 404 | is-resolvable "^1.0.0" 405 | js-yaml "^3.9.1" 406 | json-stable-stringify-without-jsonify "^1.0.1" 407 | levn "^0.3.0" 408 | lodash "^4.17.4" 409 | minimatch "^3.0.2" 410 | mkdirp "^0.5.1" 411 | natural-compare "^1.4.0" 412 | optionator "^0.8.2" 413 | path-is-inside "^1.0.2" 414 | pluralize "^7.0.0" 415 | progress "^2.0.0" 416 | require-uncached "^1.0.3" 417 | semver "^5.3.0" 418 | strip-ansi "^4.0.0" 419 | strip-json-comments "~2.0.1" 420 | table "4.0.2" 421 | text-table "~0.2.0" 422 | 423 | espree@^3.5.2: 424 | version "3.5.4" 425 | resolved "https://registry.yarnpkg.com/espree/-/espree-3.5.4.tgz#b0f447187c8a8bed944b815a660bddf5deb5d1a7" 426 | integrity sha512-yAcIQxtmMiB/jL32dzEp2enBeidsB7xWPLNiw3IIkpVds1P+h7qF9YwJq1yUNzp2OKXgAprs4F61ih66UsoD1A== 427 | dependencies: 428 | acorn "^5.5.0" 429 | acorn-jsx "^3.0.0" 430 | 431 | esprima@^4.0.0: 432 | version "4.0.1" 433 | resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" 434 | integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== 435 | 436 | esquery@^1.0.0: 437 | version "1.0.1" 438 | resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.0.1.tgz#406c51658b1f5991a5f9b62b1dc25b00e3e5c708" 439 | dependencies: 440 | estraverse "^4.0.0" 441 | 442 | esrecurse@^4.1.0: 443 | version "4.2.0" 444 | resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.2.0.tgz#fa9568d98d3823f9a41d91e902dcab9ea6e5b163" 445 | dependencies: 446 | estraverse "^4.1.0" 447 | object-assign "^4.0.1" 448 | 449 | estraverse@^4.0.0, estraverse@^4.1.0, estraverse@^4.1.1: 450 | version "4.2.0" 451 | resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.2.0.tgz#0dee3fed31fcd469618ce7342099fc1afa0bdb13" 452 | 453 | esutils@^2.0.2: 454 | version "2.0.2" 455 | resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.2.tgz#0abf4f1caa5bcb1f7a9d8acc6dea4faaa04bac9b" 456 | 457 | external-editor@^2.0.4: 458 | version "2.2.0" 459 | resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-2.2.0.tgz#045511cfd8d133f3846673d1047c154e214ad3d5" 460 | integrity sha512-bSn6gvGxKt+b7+6TKEv1ZycHleA7aHhRHyAqJyp5pbUFuYYNIzpZnQDk7AsYckyWdEnTeAnay0aCy2aV6iTk9A== 461 | dependencies: 462 | chardet "^0.4.0" 463 | iconv-lite "^0.4.17" 464 | tmp "^0.0.33" 465 | 466 | fast-deep-equal@^1.0.0: 467 | version "1.1.0" 468 | resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz#c053477817c86b51daa853c81e059b733d023614" 469 | integrity sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ= 470 | 471 | fast-json-stable-stringify@^2.0.0: 472 | version "2.0.0" 473 | resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz#d5142c0caee6b1189f87d3a76111064f86c8bbf2" 474 | integrity sha1-1RQsDK7msRifh9OnYREGT4bIu/I= 475 | 476 | fast-levenshtein@~2.0.4: 477 | version "2.0.6" 478 | resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" 479 | 480 | fbjs-eslint-utils@^1.0.0: 481 | version "1.0.0" 482 | resolved "https://registry.yarnpkg.com/fbjs-eslint-utils/-/fbjs-eslint-utils-1.0.0.tgz#e6d09d14e9360b30e0840d1609f769b70f56c3ea" 483 | 484 | figures@^2.0.0: 485 | version "2.0.0" 486 | resolved "https://registry.yarnpkg.com/figures/-/figures-2.0.0.tgz#3ab1a2d2a62c8bfb431a0c94cb797a2fce27c962" 487 | integrity sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI= 488 | dependencies: 489 | escape-string-regexp "^1.0.5" 490 | 491 | file-entry-cache@^2.0.0: 492 | version "2.0.0" 493 | resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-2.0.0.tgz#c392990c3e684783d838b8c84a45d8a048458361" 494 | dependencies: 495 | flat-cache "^1.2.1" 496 | object-assign "^4.0.1" 497 | 498 | flat-cache@^1.2.1: 499 | version "1.3.0" 500 | resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-1.3.0.tgz#d3030b32b38154f4e3b7e9c709f490f7ef97c481" 501 | dependencies: 502 | circular-json "^0.3.1" 503 | del "^2.0.2" 504 | graceful-fs "^4.1.2" 505 | write "^0.2.1" 506 | 507 | foreach@^2.0.5: 508 | version "2.0.5" 509 | resolved "https://registry.yarnpkg.com/foreach/-/foreach-2.0.5.tgz#0bee005018aeb260d0a3af3ae658dd0136ec1b99" 510 | 511 | fs.realpath@^1.0.0: 512 | version "1.0.0" 513 | resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" 514 | 515 | function-bind@^1.0.2, function-bind@^1.1.0, function-bind@^1.1.1: 516 | version "1.1.1" 517 | resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" 518 | 519 | functional-red-black-tree@^1.0.1: 520 | version "1.0.1" 521 | resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327" 522 | integrity sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc= 523 | 524 | glob@^7.0.3, glob@^7.0.5, glob@^7.1.2: 525 | version "7.1.4" 526 | resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.4.tgz#aa608a2f6c577ad357e1ae5a5c26d9a8d1969255" 527 | integrity sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A== 528 | dependencies: 529 | fs.realpath "^1.0.0" 530 | inflight "^1.0.4" 531 | inherits "2" 532 | minimatch "^3.0.4" 533 | once "^1.3.0" 534 | path-is-absolute "^1.0.0" 535 | 536 | globals@^11.0.1: 537 | version "11.12.0" 538 | resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" 539 | integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== 540 | 541 | globals@^9.18.0: 542 | version "9.18.0" 543 | resolved "https://registry.yarnpkg.com/globals/-/globals-9.18.0.tgz#aa3896b3e69b487f17e31ed2143d69a8e30c2d8a" 544 | 545 | globby@^5.0.0: 546 | version "5.0.0" 547 | resolved "https://registry.yarnpkg.com/globby/-/globby-5.0.0.tgz#ebd84667ca0dbb330b99bcfc68eac2bc54370e0d" 548 | dependencies: 549 | array-union "^1.0.1" 550 | arrify "^1.0.0" 551 | glob "^7.0.3" 552 | object-assign "^4.0.1" 553 | pify "^2.0.0" 554 | pinkie-promise "^2.0.0" 555 | 556 | graceful-fs@^4.1.2: 557 | version "4.1.11" 558 | resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.11.tgz#0e8bdfe4d1ddb8854d64e04ea7c00e2a026e5658" 559 | 560 | has-ansi@^2.0.0: 561 | version "2.0.0" 562 | resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91" 563 | dependencies: 564 | ansi-regex "^2.0.0" 565 | 566 | has-flag@^3.0.0: 567 | version "3.0.0" 568 | resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" 569 | integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0= 570 | 571 | has@^1.0.1: 572 | version "1.0.1" 573 | resolved "https://registry.yarnpkg.com/has/-/has-1.0.1.tgz#8461733f538b0837c9361e39a9ab9e9704dc2f28" 574 | dependencies: 575 | function-bind "^1.0.2" 576 | 577 | iconv-lite@^0.4.17: 578 | version "0.4.24" 579 | resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" 580 | integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== 581 | dependencies: 582 | safer-buffer ">= 2.1.2 < 3" 583 | 584 | ignore@^3.3.3: 585 | version "3.3.10" 586 | resolved "https://registry.yarnpkg.com/ignore/-/ignore-3.3.10.tgz#0a97fb876986e8081c631160f8f9f389157f0043" 587 | integrity sha512-Pgs951kaMm5GXP7MOvxERINe3gsaVjUWFm+UZPSq9xYriQAksyhg0csnS0KXSNRD5NmNdapXEpjxG49+AKh/ug== 588 | 589 | imurmurhash@^0.1.4: 590 | version "0.1.4" 591 | resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" 592 | 593 | inflight@^1.0.4: 594 | version "1.0.6" 595 | resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" 596 | dependencies: 597 | once "^1.3.0" 598 | wrappy "1" 599 | 600 | inherits@2, inherits@^2.0.3, inherits@~2.0.3: 601 | version "2.0.3" 602 | resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" 603 | 604 | inquirer@^3.0.6: 605 | version "3.3.0" 606 | resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-3.3.0.tgz#9dd2f2ad765dcab1ff0443b491442a20ba227dc9" 607 | integrity sha512-h+xtnyk4EwKvFWHrUYsWErEVR+igKtLdchu+o0Z1RL7VU/jVMFbYir2bp6bAj8efFNxWqHX0dIss6fJQ+/+qeQ== 608 | dependencies: 609 | ansi-escapes "^3.0.0" 610 | chalk "^2.0.0" 611 | cli-cursor "^2.1.0" 612 | cli-width "^2.0.0" 613 | external-editor "^2.0.4" 614 | figures "^2.0.0" 615 | lodash "^4.3.0" 616 | mute-stream "0.0.7" 617 | run-async "^2.2.0" 618 | rx-lite "^4.0.8" 619 | rx-lite-aggregates "^4.0.8" 620 | string-width "^2.1.0" 621 | strip-ansi "^4.0.0" 622 | through "^2.3.6" 623 | 624 | invariant@^2.2.2: 625 | version "2.2.2" 626 | resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.2.tgz#9e1f56ac0acdb6bf303306f338be3b204ae60360" 627 | dependencies: 628 | loose-envify "^1.0.0" 629 | 630 | is-callable@^1.1.1, is-callable@^1.1.3: 631 | version "1.1.3" 632 | resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.1.3.tgz#86eb75392805ddc33af71c92a0eedf74ee7604b2" 633 | 634 | is-date-object@^1.0.1: 635 | version "1.0.1" 636 | resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.1.tgz#9aa20eb6aeebbff77fbd33e74ca01b33581d3a16" 637 | 638 | is-fullwidth-code-point@^2.0.0: 639 | version "2.0.0" 640 | resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" 641 | 642 | is-path-cwd@^1.0.0: 643 | version "1.0.0" 644 | resolved "https://registry.yarnpkg.com/is-path-cwd/-/is-path-cwd-1.0.0.tgz#d225ec23132e89edd38fda767472e62e65f1106d" 645 | 646 | is-path-in-cwd@^1.0.0: 647 | version "1.0.0" 648 | resolved "https://registry.yarnpkg.com/is-path-in-cwd/-/is-path-in-cwd-1.0.0.tgz#6477582b8214d602346094567003be8a9eac04dc" 649 | dependencies: 650 | is-path-inside "^1.0.0" 651 | 652 | is-path-inside@^1.0.0: 653 | version "1.0.0" 654 | resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-1.0.0.tgz#fc06e5a1683fbda13de667aff717bbc10a48f37f" 655 | dependencies: 656 | path-is-inside "^1.0.1" 657 | 658 | is-promise@^2.1.0: 659 | version "2.1.0" 660 | resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-2.1.0.tgz#79a2a9ece7f096e80f36d2b2f3bc16c1ff4bf3fa" 661 | integrity sha1-eaKp7OfwlugPNtKy87wWwf9L8/o= 662 | 663 | is-regex@^1.0.4: 664 | version "1.0.4" 665 | resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.0.4.tgz#5517489b547091b0930e095654ced25ee97e9491" 666 | dependencies: 667 | has "^1.0.1" 668 | 669 | is-resolvable@^1.0.0: 670 | version "1.1.0" 671 | resolved "https://registry.yarnpkg.com/is-resolvable/-/is-resolvable-1.1.0.tgz#fb18f87ce1feb925169c9a407c19318a3206ed88" 672 | 673 | is-symbol@^1.0.1: 674 | version "1.0.1" 675 | resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.1.tgz#3cc59f00025194b6ab2e38dbae6689256b660572" 676 | 677 | isarray@^1.0.0, isarray@~1.0.0: 678 | version "1.0.0" 679 | resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" 680 | 681 | isexe@^2.0.0: 682 | version "2.0.0" 683 | resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" 684 | integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= 685 | 686 | js-tokens@^3.0.0, js-tokens@^3.0.2: 687 | version "3.0.2" 688 | resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.2.tgz#9866df395102130e38f7f996bceb65443209c25b" 689 | 690 | js-yaml@^3.9.1: 691 | version "3.13.1" 692 | resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.13.1.tgz#aff151b30bfdfa8e49e05da22e7415e9dfa37847" 693 | integrity sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw== 694 | dependencies: 695 | argparse "^1.0.7" 696 | esprima "^4.0.0" 697 | 698 | json-schema-traverse@^0.3.0: 699 | version "0.3.1" 700 | resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz#349a6d44c53a51de89b40805c5d5e59b417d3340" 701 | integrity sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A= 702 | 703 | json-stable-stringify-without-jsonify@^1.0.1: 704 | version "1.0.1" 705 | resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" 706 | integrity sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE= 707 | 708 | jsx-ast-utils@^1.3.4: 709 | version "1.4.1" 710 | resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-1.4.1.tgz#3867213e8dd79bf1e8f2300c0cfc1efb182c0df1" 711 | 712 | levn@^0.3.0, levn@~0.3.0: 713 | version "0.3.0" 714 | resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" 715 | dependencies: 716 | prelude-ls "~1.1.2" 717 | type-check "~0.3.2" 718 | 719 | lodash@^4.15.0, lodash@^4.17.4, lodash@^4.3.0: 720 | version "4.17.21" 721 | resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" 722 | integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== 723 | 724 | loose-envify@^1.0.0: 725 | version "1.3.1" 726 | resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.3.1.tgz#d1a8ad33fa9ce0e713d65fdd0ac8b748d478c848" 727 | dependencies: 728 | js-tokens "^3.0.0" 729 | 730 | lru-cache@^4.0.1: 731 | version "4.1.5" 732 | resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.5.tgz#8bbe50ea85bed59bc9e33dcab8235ee9bcf443cd" 733 | integrity sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g== 734 | dependencies: 735 | pseudomap "^1.0.2" 736 | yallist "^2.1.2" 737 | 738 | mimic-fn@^1.0.0: 739 | version "1.2.0" 740 | resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.2.0.tgz#820c86a39334640e99516928bd03fca88057d022" 741 | integrity sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ== 742 | 743 | minimatch@^3.0.2, minimatch@^3.0.4: 744 | version "3.0.4" 745 | resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" 746 | dependencies: 747 | brace-expansion "^1.1.7" 748 | 749 | minimist@^1.2.5: 750 | version "1.2.6" 751 | resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.6.tgz#8637a5b759ea0d6e98702cfb3a9283323c93af44" 752 | integrity sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q== 753 | 754 | mkdirp@^0.5.1: 755 | version "0.5.5" 756 | resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def" 757 | integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ== 758 | dependencies: 759 | minimist "^1.2.5" 760 | 761 | ms@2.0.0: 762 | version "2.0.0" 763 | resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" 764 | 765 | mute-stream@0.0.7: 766 | version "0.0.7" 767 | resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.7.tgz#3075ce93bc21b8fab43e1bc4da7e8115ed1e7bab" 768 | integrity sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s= 769 | 770 | natural-compare@^1.4.0: 771 | version "1.4.0" 772 | resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" 773 | 774 | object-assign@^4.0.1: 775 | version "4.1.1" 776 | resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" 777 | 778 | object-keys@^1.0.10, object-keys@^1.0.8: 779 | version "1.0.11" 780 | resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.0.11.tgz#c54601778ad560f1142ce0e01bcca8b56d13426d" 781 | 782 | object.assign@^4.0.4: 783 | version "4.0.4" 784 | resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.0.4.tgz#b1c9cc044ef1b9fe63606fc141abbb32e14730cc" 785 | dependencies: 786 | define-properties "^1.1.2" 787 | function-bind "^1.1.0" 788 | object-keys "^1.0.10" 789 | 790 | once@^1.3.0: 791 | version "1.4.0" 792 | resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" 793 | dependencies: 794 | wrappy "1" 795 | 796 | onetime@^2.0.0: 797 | version "2.0.1" 798 | resolved "https://registry.yarnpkg.com/onetime/-/onetime-2.0.1.tgz#067428230fd67443b2794b22bba528b6867962d4" 799 | integrity sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ= 800 | dependencies: 801 | mimic-fn "^1.0.0" 802 | 803 | optionator@^0.8.2: 804 | version "0.8.2" 805 | resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.2.tgz#364c5e409d3f4d6301d6c0b4c05bba50180aeb64" 806 | dependencies: 807 | deep-is "~0.1.3" 808 | fast-levenshtein "~2.0.4" 809 | levn "~0.3.0" 810 | prelude-ls "~1.1.2" 811 | type-check "~0.3.2" 812 | wordwrap "~1.0.0" 813 | 814 | os-tmpdir@~1.0.2: 815 | version "1.0.2" 816 | resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" 817 | 818 | parse5@^5.0.0: 819 | version "5.0.0" 820 | resolved "https://registry.yarnpkg.com/parse5/-/parse5-5.0.0.tgz#4d02710d44f3c3846197a11e205d4ef17842b81a" 821 | 822 | path-is-absolute@^1.0.0: 823 | version "1.0.1" 824 | resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" 825 | 826 | path-is-inside@^1.0.1, path-is-inside@^1.0.2: 827 | version "1.0.2" 828 | resolved "https://registry.yarnpkg.com/path-is-inside/-/path-is-inside-1.0.2.tgz#365417dede44430d1c11af61027facf074bdfc53" 829 | 830 | pify@^2.0.0: 831 | version "2.3.0" 832 | resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" 833 | 834 | pinkie-promise@^2.0.0: 835 | version "2.0.1" 836 | resolved "https://registry.yarnpkg.com/pinkie-promise/-/pinkie-promise-2.0.1.tgz#2135d6dfa7a358c069ac9b178776288228450ffa" 837 | dependencies: 838 | pinkie "^2.0.0" 839 | 840 | pinkie@^2.0.0: 841 | version "2.0.4" 842 | resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870" 843 | 844 | pluralize@^7.0.0: 845 | version "7.0.0" 846 | resolved "https://registry.yarnpkg.com/pluralize/-/pluralize-7.0.0.tgz#298b89df8b93b0221dbf421ad2b1b1ea23fc6777" 847 | integrity sha512-ARhBOdzS3e41FbkW/XWrTEtukqqLoK5+Z/4UeDaLuSW+39JPeFgs4gCGqsrJHVZX0fUrx//4OF0K1CUGwlIFow== 848 | 849 | prelude-ls@~1.1.2: 850 | version "1.1.2" 851 | resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" 852 | 853 | prettier@^3.3.0: 854 | version "3.3.0" 855 | resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.3.0.tgz#d173ea0524a691d4c0b1181752f2b46724328cdf" 856 | integrity sha512-J9odKxERhCQ10OC2yb93583f6UnYutOeiV5i0zEDS7UGTdUt0u+y8erxl3lBKvwo/JHyyoEdXjwp4dke9oyZ/g== 857 | 858 | process-nextick-args@~2.0.0: 859 | version "2.0.0" 860 | resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.0.tgz#a37d732f4271b4ab1ad070d35508e8290788ffaa" 861 | 862 | progress@^2.0.0: 863 | version "2.0.3" 864 | resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8" 865 | integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA== 866 | 867 | pseudomap@^1.0.2: 868 | version "1.0.2" 869 | resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3" 870 | integrity sha1-8FKijacOYYkX7wqKw0wa5aaChrM= 871 | 872 | readable-stream@^2.2.2: 873 | version "2.3.6" 874 | resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.6.tgz#b11c27d88b8ff1fbe070643cf94b0c79ae1b0aaf" 875 | dependencies: 876 | core-util-is "~1.0.0" 877 | inherits "~2.0.3" 878 | isarray "~1.0.0" 879 | process-nextick-args "~2.0.0" 880 | safe-buffer "~5.1.1" 881 | string_decoder "~1.1.1" 882 | util-deprecate "~1.0.1" 883 | 884 | regenerator-runtime@^0.11.0: 885 | version "0.11.0" 886 | resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.11.0.tgz#7e54fe5b5ccd5d6624ea6255c3473be090b802e1" 887 | 888 | require-uncached@^1.0.3: 889 | version "1.0.3" 890 | resolved "https://registry.yarnpkg.com/require-uncached/-/require-uncached-1.0.3.tgz#4e0d56d6c9662fd31e43011c4b95aa49955421d3" 891 | integrity sha1-Tg1W1slmL9MeQwEcS5WqSZVUIdM= 892 | dependencies: 893 | caller-path "^0.1.0" 894 | resolve-from "^1.0.0" 895 | 896 | resolve-from@^1.0.0: 897 | version "1.0.1" 898 | resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-1.0.1.tgz#26cbfe935d1aeeeabb29bc3fe5aeb01e93d44226" 899 | 900 | restore-cursor@^2.0.0: 901 | version "2.0.0" 902 | resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-2.0.0.tgz#9f7ee287f82fd326d4fd162923d62129eee0dfaf" 903 | integrity sha1-n37ih/gv0ybU/RYpI9YhKe7g368= 904 | dependencies: 905 | onetime "^2.0.0" 906 | signal-exit "^3.0.2" 907 | 908 | rimraf@^2.2.8: 909 | version "2.6.2" 910 | resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.2.tgz#2ed8150d24a16ea8651e6d6ef0f47c4158ce7a36" 911 | dependencies: 912 | glob "^7.0.5" 913 | 914 | run-async@^2.2.0: 915 | version "2.3.0" 916 | resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.3.0.tgz#0371ab4ae0bdd720d4166d7dfda64ff7a445a6c0" 917 | integrity sha1-A3GrSuC91yDUFm19/aZP96RFpsA= 918 | dependencies: 919 | is-promise "^2.1.0" 920 | 921 | rx-lite-aggregates@^4.0.8: 922 | version "4.0.8" 923 | resolved "https://registry.yarnpkg.com/rx-lite-aggregates/-/rx-lite-aggregates-4.0.8.tgz#753b87a89a11c95467c4ac1626c4efc4e05c67be" 924 | integrity sha1-dTuHqJoRyVRnxKwWJsTvxOBcZ74= 925 | dependencies: 926 | rx-lite "*" 927 | 928 | rx-lite@*, rx-lite@^4.0.8: 929 | version "4.0.8" 930 | resolved "https://registry.yarnpkg.com/rx-lite/-/rx-lite-4.0.8.tgz#0b1e11af8bc44836f04a6407e92da42467b79444" 931 | integrity sha1-Cx4Rr4vESDbwSmQH6S2kJGe3lEQ= 932 | 933 | safe-buffer@~5.1.0, safe-buffer@~5.1.1: 934 | version "5.1.1" 935 | resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.1.tgz#893312af69b2123def71f57889001671eeb2c853" 936 | 937 | "safer-buffer@>= 2.1.2 < 3": 938 | version "2.1.2" 939 | resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" 940 | 941 | semver@^5.3.0: 942 | version "5.5.0" 943 | resolved "https://registry.yarnpkg.com/semver/-/semver-5.5.0.tgz#dc4bbc7a6ca9d916dee5d43516f0092b58f7b8ab" 944 | 945 | shebang-command@^1.2.0: 946 | version "1.2.0" 947 | resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" 948 | integrity sha1-RKrGW2lbAzmJaMOfNj/uXer98eo= 949 | dependencies: 950 | shebang-regex "^1.0.0" 951 | 952 | shebang-regex@^1.0.0: 953 | version "1.0.0" 954 | resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" 955 | integrity sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM= 956 | 957 | signal-exit@^3.0.2: 958 | version "3.0.2" 959 | resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d" 960 | 961 | slice-ansi@1.0.0: 962 | version "1.0.0" 963 | resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-1.0.0.tgz#044f1a49d8842ff307aad6b505ed178bd950134d" 964 | integrity sha512-POqxBK6Lb3q6s047D/XsDVNPnF9Dl8JSaqe9h9lURl0OdNqy/ujDrOiIHtsqXMGbWWTIomRzAMaTyawAU//Reg== 965 | dependencies: 966 | is-fullwidth-code-point "^2.0.0" 967 | 968 | sprintf-js@~1.0.2: 969 | version "1.0.3" 970 | resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" 971 | integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw= 972 | 973 | string-width@^2.1.0, string-width@^2.1.1: 974 | version "2.1.1" 975 | resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" 976 | integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw== 977 | dependencies: 978 | is-fullwidth-code-point "^2.0.0" 979 | strip-ansi "^4.0.0" 980 | 981 | string_decoder@~1.1.1: 982 | version "1.1.1" 983 | resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" 984 | dependencies: 985 | safe-buffer "~5.1.0" 986 | 987 | strip-ansi@^3.0.0: 988 | version "3.0.1" 989 | resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" 990 | dependencies: 991 | ansi-regex "^2.0.0" 992 | 993 | strip-ansi@^4.0.0: 994 | version "4.0.0" 995 | resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f" 996 | dependencies: 997 | ansi-regex "^3.0.0" 998 | 999 | strip-json-comments@~2.0.1: 1000 | version "2.0.1" 1001 | resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" 1002 | 1003 | supports-color@^2.0.0: 1004 | version "2.0.0" 1005 | resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" 1006 | 1007 | supports-color@^5.3.0: 1008 | version "5.5.0" 1009 | resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" 1010 | integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== 1011 | dependencies: 1012 | has-flag "^3.0.0" 1013 | 1014 | table@4.0.2: 1015 | version "4.0.2" 1016 | resolved "https://registry.yarnpkg.com/table/-/table-4.0.2.tgz#a33447375391e766ad34d3486e6e2aedc84d2e36" 1017 | integrity sha512-UUkEAPdSGxtRpiV9ozJ5cMTtYiqz7Ni1OGqLXRCynrvzdtR1p+cfOWe2RJLwvUG8hNanaSRjecIqwOjqeatDsA== 1018 | dependencies: 1019 | ajv "^5.2.3" 1020 | ajv-keywords "^2.1.0" 1021 | chalk "^2.1.0" 1022 | lodash "^4.17.4" 1023 | slice-ansi "1.0.0" 1024 | string-width "^2.1.1" 1025 | 1026 | text-table@~0.2.0: 1027 | version "0.2.0" 1028 | resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" 1029 | 1030 | through@^2.3.6: 1031 | version "2.3.8" 1032 | resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" 1033 | 1034 | tmp@^0.0.33: 1035 | version "0.0.33" 1036 | resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" 1037 | integrity sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw== 1038 | dependencies: 1039 | os-tmpdir "~1.0.2" 1040 | 1041 | to-fast-properties@^1.0.3: 1042 | version "1.0.3" 1043 | resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-1.0.3.tgz#b83571fa4d8c25b82e231b06e3a3055de4ca1a47" 1044 | 1045 | type-check@~0.3.2: 1046 | version "0.3.2" 1047 | resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72" 1048 | dependencies: 1049 | prelude-ls "~1.1.2" 1050 | 1051 | typedarray@^0.0.6: 1052 | version "0.0.6" 1053 | resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" 1054 | 1055 | typescript@^5.4.5: 1056 | version "5.4.5" 1057 | resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.4.5.tgz#42ccef2c571fdbd0f6718b1d1f5e6e5ef006f611" 1058 | integrity sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ== 1059 | 1060 | util-deprecate@~1.0.1: 1061 | version "1.0.2" 1062 | resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" 1063 | 1064 | which@^1.2.9: 1065 | version "1.3.1" 1066 | resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" 1067 | integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== 1068 | dependencies: 1069 | isexe "^2.0.0" 1070 | 1071 | wordwrap@~1.0.0: 1072 | version "1.0.0" 1073 | resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb" 1074 | 1075 | wrappy@1: 1076 | version "1.0.2" 1077 | resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" 1078 | 1079 | write@^0.2.1: 1080 | version "0.2.1" 1081 | resolved "https://registry.yarnpkg.com/write/-/write-0.2.1.tgz#5fc03828e264cea3fe91455476f7a3c566cb0757" 1082 | dependencies: 1083 | mkdirp "^0.5.1" 1084 | 1085 | yallist@^2.1.2: 1086 | version "2.1.2" 1087 | resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52" 1088 | integrity sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI= 1089 | --------------------------------------------------------------------------------