From 9a0ebe0abaec7b59bdefe63cd7d392737ab20160 Mon Sep 17 00:00:00 2001 From: Christoph Nakazawa Date: Fri, 13 Sep 2019 07:44:20 -0700 Subject: [PATCH] Move ART JS files to FB internal Summary: This is part of Lean Core. Reviewed By: rubennorte Differential Revision: D17343246 fbshipit-source-id: 1185e6c1f75e8272048ce1a24c2f195728d436c4 --- Libraries/ART/ARTSerializablePath.js | 84 --- Libraries/ART/ReactNativeART.js | 612 ------------------ .../RNTesterSnapshotTests.m | 14 +- RNTester/js/examples/ART/ARTExample.js | 105 --- RNTester/js/utils/RNTesterList.ios.js | 5 - index.js | 23 +- package.json | 1 - yarn.lock | 5 - 8 files changed, 19 insertions(+), 830 deletions(-) delete mode 100644 Libraries/ART/ARTSerializablePath.js delete mode 100644 Libraries/ART/ReactNativeART.js delete mode 100644 RNTester/js/examples/ART/ARTExample.js diff --git a/Libraries/ART/ARTSerializablePath.js b/Libraries/ART/ARTSerializablePath.js deleted file mode 100644 index aafb74c45d0..00000000000 --- a/Libraries/ART/ARTSerializablePath.js +++ /dev/null @@ -1,84 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - * - * @format - */ - -'use strict'; - -// TODO: Move this into an ART mode called "serialized" or something - -const Class = require('art/core/class.js'); -const Path = require('art/core/path.js'); - -const MOVE_TO = 0; -const CLOSE = 1; -const LINE_TO = 2; -const CURVE_TO = 3; -const ARC = 4; - -const SerializablePath = Class(Path, { - initialize: function(path) { - this.reset(); - if (path instanceof SerializablePath) { - this.path = path.path.slice(0); - } else if (path) { - if (path.applyToPath) { - path.applyToPath(this); - } else { - this.push(path); - } - } - }, - - onReset: function() { - this.path = []; - }, - - onMove: function(sx, sy, x, y) { - this.path.push(MOVE_TO, x, y); - }, - - onLine: function(sx, sy, x, y) { - this.path.push(LINE_TO, x, y); - }, - - onBezierCurve: function(sx, sy, p1x, p1y, p2x, p2y, x, y) { - this.path.push(CURVE_TO, p1x, p1y, p2x, p2y, x, y); - }, - - _arcToBezier: Path.prototype.onArc, - - onArc: function(sx, sy, ex, ey, cx, cy, rx, ry, sa, ea, ccw, rotation) { - if (rx !== ry || rotation) { - return this._arcToBezier( - sx, - sy, - ex, - ey, - cx, - cy, - rx, - ry, - sa, - ea, - ccw, - rotation, - ); - } - this.path.push(ARC, cx, cy, rx, sa, ea, ccw ? 0 : 1); - }, - - onClose: function() { - this.path.push(CLOSE); - }, - - toJSON: function() { - return this.path; - }, -}); - -module.exports = SerializablePath; diff --git a/Libraries/ART/ReactNativeART.js b/Libraries/ART/ReactNativeART.js deleted file mode 100644 index c97de5a34f2..00000000000 --- a/Libraries/ART/ReactNativeART.js +++ /dev/null @@ -1,612 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - * - * @format - */ - -'use strict'; - -const Color = require('art/core/color'); -const Path = require('./ARTSerializablePath'); -const Transform = require('art/core/transform'); - -const React = require('react'); -const PropTypes = require('prop-types'); -const ReactNativeViewAttributes = require('../Components/View/ReactNativeViewAttributes'); - -const createReactNativeComponentClass = require('../Renderer/shims/createReactNativeComponentClass'); -const merge = require('../vendor/core/merge'); -const invariant = require('invariant'); - -// Diff Helpers - -function arrayDiffer(a, b) { - if (a == null || b == null) { - return true; - } - if (a.length !== b.length) { - return true; - } - for (let i = 0; i < a.length; i++) { - if (a[i] !== b[i]) { - return true; - } - } - return false; -} - -function fontAndLinesDiffer(a, b) { - if (a === b) { - return false; - } - if (a.font !== b.font) { - if (a.font === null) { - return true; - } - if (b.font === null) { - return true; - } - - if ( - a.font.fontFamily !== b.font.fontFamily || - a.font.fontSize !== b.font.fontSize || - a.font.fontWeight !== b.font.fontWeight || - a.font.fontStyle !== b.font.fontStyle - ) { - return true; - } - } - return arrayDiffer(a.lines, b.lines); -} - -// Native Attributes - -const SurfaceViewAttributes = merge(ReactNativeViewAttributes.UIView, { - // This should contain pixel information such as width, height and - // resolution to know what kind of buffer needs to be allocated. - // Currently we rely on UIViews and style to figure that out. -}); - -const NodeAttributes = { - transform: {diff: arrayDiffer}, - opacity: true, -}; - -const GroupAttributes = merge(NodeAttributes, { - clipping: {diff: arrayDiffer}, -}); - -const RenderableAttributes = merge(NodeAttributes, { - fill: {diff: arrayDiffer}, - stroke: {diff: arrayDiffer}, - strokeWidth: true, - strokeCap: true, - strokeJoin: true, - strokeDash: {diff: arrayDiffer}, -}); - -const ShapeAttributes = merge(RenderableAttributes, { - d: {diff: arrayDiffer}, -}); - -const TextAttributes = merge(RenderableAttributes, { - alignment: true, - frame: {diff: fontAndLinesDiffer}, - path: {diff: arrayDiffer}, -}); - -// Native Components - -const NativeSurfaceView = createReactNativeComponentClass( - 'ARTSurfaceView', - () => ({ - validAttributes: SurfaceViewAttributes, - uiViewClassName: 'ARTSurfaceView', - }), -); - -const NativeGroup = createReactNativeComponentClass('ARTGroup', () => ({ - validAttributes: GroupAttributes, - uiViewClassName: 'ARTGroup', -})); - -const NativeShape = createReactNativeComponentClass('ARTShape', () => ({ - validAttributes: ShapeAttributes, - uiViewClassName: 'ARTShape', -})); - -const NativeText = createReactNativeComponentClass('ARTText', () => ({ - validAttributes: TextAttributes, - uiViewClassName: 'ARTText', -})); - -// Utilities - -function childrenAsString(children) { - if (!children) { - return ''; - } - if (typeof children === 'string') { - return children; - } - if (children.length) { - return children.join('\n'); - } - return ''; -} - -// Surface - Root node of all ART - -class Surface extends React.Component { - static childContextTypes = { - isInSurface: PropTypes.bool, - }; - - getChildContext() { - return {isInSurface: true}; - } - - render() { - const height = extractNumber(this.props.height, 0); - const width = extractNumber(this.props.width, 0); - - return ( - - {this.props.children} - - ); - } -} - -// Node Props - -// TODO: The desktop version of ART has title and cursor. We should have -// accessibility support here too even though hovering doesn't work. - -function extractNumber(value, defaultValue) { - if (value == null) { - return defaultValue; - } - return +value; -} - -const pooledTransform = new Transform(); - -function extractTransform(props) { - const scaleX = - props.scaleX != null ? props.scaleX : props.scale != null ? props.scale : 1; - const scaleY = - props.scaleY != null ? props.scaleY : props.scale != null ? props.scale : 1; - - pooledTransform - .transformTo(1, 0, 0, 1, 0, 0) - .move(props.x || 0, props.y || 0) - .rotate(props.rotation || 0, props.originX, props.originY) - .scale(scaleX, scaleY, props.originX, props.originY); - - if (props.transform != null) { - pooledTransform.transform(props.transform); - } - - return [ - pooledTransform.xx, - pooledTransform.yx, - pooledTransform.xy, - pooledTransform.yy, - pooledTransform.x, - pooledTransform.y, - ]; -} - -function extractOpacity(props) { - // TODO: visible === false should also have no hit detection - if (props.visible === false) { - return 0; - } - if (props.opacity == null) { - return 1; - } - return +props.opacity; -} - -// Groups - -// Note: ART has a notion of width and height on Group but AFAIK it's a noop in -// ReactART. - -class Group extends React.Component { - static contextTypes = { - isInSurface: PropTypes.bool.isRequired, - }; - - render() { - const props = this.props; - invariant( - this.context.isInSurface, - 'ART: must be a child of a ', - ); - return ( - - {this.props.children} - - ); - } -} - -class ClippingRectangle extends React.Component { - render() { - const props = this.props; - const x = extractNumber(props.x, 0); - const y = extractNumber(props.y, 0); - const w = extractNumber(props.width, 0); - const h = extractNumber(props.height, 0); - const clipping = [x, y, w, h]; - // The current clipping API requires x and y to be ignored in the transform - const propsExcludingXAndY = merge(props); - delete propsExcludingXAndY.x; - delete propsExcludingXAndY.y; - return ( - - {this.props.children} - - ); - } -} - -// Renderables - -const SOLID_COLOR = 0; -const LINEAR_GRADIENT = 1; -const RADIAL_GRADIENT = 2; -const PATTERN = 3; - -function insertColorIntoArray(color, targetArray, atIndex) { - const c = new Color(color); - targetArray[atIndex + 0] = c.red / 255; - targetArray[atIndex + 1] = c.green / 255; - targetArray[atIndex + 2] = c.blue / 255; - targetArray[atIndex + 3] = c.alpha; -} - -function insertColorsIntoArray(stops, targetArray, atIndex) { - let i = 0; - if ('length' in stops) { - while (i < stops.length) { - insertColorIntoArray(stops[i], targetArray, atIndex + i * 4); - i++; - } - } else { - for (const offset in stops) { - insertColorIntoArray(stops[offset], targetArray, atIndex + i * 4); - i++; - } - } - return atIndex + i * 4; -} - -function insertOffsetsIntoArray(stops, targetArray, atIndex, multi, reverse) { - let offsetNumber; - let i = 0; - if ('length' in stops) { - while (i < stops.length) { - offsetNumber = (i / (stops.length - 1)) * multi; - targetArray[atIndex + i] = reverse ? 1 - offsetNumber : offsetNumber; - i++; - } - } else { - for (const offsetString in stops) { - offsetNumber = +offsetString * multi; - targetArray[atIndex + i] = reverse ? 1 - offsetNumber : offsetNumber; - i++; - } - } - return atIndex + i; -} - -function insertColorStopsIntoArray(stops, targetArray, atIndex) { - const lastIndex = insertColorsIntoArray(stops, targetArray, atIndex); - insertOffsetsIntoArray(stops, targetArray, lastIndex, 1, false); -} - -function insertDoubleColorStopsIntoArray(stops, targetArray, atIndex) { - let lastIndex = insertColorsIntoArray(stops, targetArray, atIndex); - lastIndex = insertColorsIntoArray(stops, targetArray, lastIndex); - lastIndex = insertOffsetsIntoArray(stops, targetArray, lastIndex, 0.5, false); - insertOffsetsIntoArray(stops, targetArray, lastIndex, 0.5, true); -} - -function applyBoundingBoxToBrushData(brushData, props) { - const type = brushData[0]; - const width = +props.width; - const height = +props.height; - if (type === LINEAR_GRADIENT) { - brushData[1] *= width; - brushData[2] *= height; - brushData[3] *= width; - brushData[4] *= height; - } else if (type === RADIAL_GRADIENT) { - brushData[1] *= width; - brushData[2] *= height; - brushData[3] *= width; - brushData[4] *= height; - brushData[5] *= width; - brushData[6] *= height; - } else if (type === PATTERN) { - // todo - } -} - -function extractBrush(colorOrBrush, props) { - if (colorOrBrush == null) { - return null; - } - if (colorOrBrush._brush) { - if (colorOrBrush._bb) { - // The legacy API for Gradients allow for the bounding box to be used - // as a convenience for specifying gradient positions. This should be - // deprecated. It's not properly implemented in canvas mode. ReactART - // doesn't handle update to the bounding box correctly. That's why we - // mutate this so that if it's reused, we reuse the same resolved box. - applyBoundingBoxToBrushData(colorOrBrush._brush, props); - colorOrBrush._bb = false; - } - return colorOrBrush._brush; - } - const c = new Color(colorOrBrush); - return [SOLID_COLOR, c.red / 255, c.green / 255, c.blue / 255, c.alpha]; -} - -function extractColor(color) { - if (color == null) { - return null; - } - const c = new Color(color); - return [c.red / 255, c.green / 255, c.blue / 255, c.alpha]; -} - -function extractStrokeCap(strokeCap) { - switch (strokeCap) { - case 'butt': - return 0; - case 'square': - return 2; - default: - return 1; // round - } -} - -function extractStrokeJoin(strokeJoin) { - switch (strokeJoin) { - case 'miter': - return 0; - case 'bevel': - return 2; - default: - return 1; // round - } -} - -// Shape - -// Note: ART has a notion of width and height on Shape but AFAIK it's a noop in -// ReactART. - -export type ShapeProps = {| - fill?: mixed, - stroke?: mixed, - strokeCap?: mixed, - strokeDash?: mixed, - strokeJoin?: mixed, - strokeWidth?: mixed, - x?: number, - y?: number, - opacity?: mixed, -|}; - -class Shape extends React.Component { - render() { - const props = this.props; - const path = props.d || childrenAsString(props.children); - const d = (path instanceof Path ? path : new Path(path)).toJSON(); - return ( - - ); - } -} - -// Text - -const cachedFontObjectsFromString = {}; - -const fontFamilyPrefix = /^[\s"']*/; -const fontFamilySuffix = /[\s"']*$/; - -function extractSingleFontFamily(fontFamilyString) { - // ART on the web allows for multiple font-families to be specified. - // For compatibility, we extract the first font-family, hoping - // we'll get a match. - return fontFamilyString - .split(',')[0] - .replace(fontFamilyPrefix, '') - .replace(fontFamilySuffix, ''); -} - -function parseFontString(font) { - if (cachedFontObjectsFromString.hasOwnProperty(font)) { - return cachedFontObjectsFromString[font]; - } - const regexp = /^\s*((?:(?:normal|bold|italic)\s+)*)(?:(\d+(?:\.\d+)?)[ptexm\%]*(?:\s*\/.*?)?\s+)?\s*\"?([^\"]*)/i; - const match = regexp.exec(font); - if (!match) { - return null; - } - const fontFamily = extractSingleFontFamily(match[3]); - const fontSize = +match[2] || 12; - const isBold = /bold/.exec(match[1]); - const isItalic = /italic/.exec(match[1]); - cachedFontObjectsFromString[font] = { - fontFamily: fontFamily, - fontSize: fontSize, - fontWeight: isBold ? 'bold' : 'normal', - fontStyle: isItalic ? 'italic' : 'normal', - }; - return cachedFontObjectsFromString[font]; -} - -function extractFont(font) { - if (font == null) { - return null; - } - if (typeof font === 'string') { - return parseFontString(font); - } - const fontFamily = extractSingleFontFamily(font.fontFamily); - const fontSize = +font.fontSize || 12; - const fontWeight = - font.fontWeight != null ? font.fontWeight.toString() : '400'; - return { - // Normalize - fontFamily: fontFamily, - fontSize: fontSize, - fontWeight: fontWeight, - fontStyle: font.fontStyle, - }; -} - -const newLine = /\n/g; -function extractFontAndLines(font, text) { - return {font: extractFont(font), lines: text.split(newLine)}; -} - -function extractAlignment(alignment) { - switch (alignment) { - case 'right': - return 1; - case 'center': - return 2; - default: - return 0; - } -} - -class Text extends React.Component { - render() { - const props = this.props; - const path = props.path; - const textPath = path - ? (path instanceof Path ? path : new Path(path)).toJSON() - : null; - const textFrame = extractFontAndLines( - props.font, - childrenAsString(props.children), - ); - return ( - - ); - } -} - -// Declarative fill type objects - API design not finalized - -function LinearGradient(stops, x1, y1, x2, y2) { - const type = LINEAR_GRADIENT; - - if (arguments.length < 5) { - const angle = ((x1 == null ? 270 : x1) * Math.PI) / 180; - - let x = Math.cos(angle); - let y = -Math.sin(angle); - const l = (Math.abs(x) + Math.abs(y)) / 2; - - x *= l; - y *= l; - - x1 = 0.5 - x; - x2 = 0.5 + x; - y1 = 0.5 - y; - y2 = 0.5 + y; - this._bb = true; - } else { - this._bb = false; - } - - const brushData = [type, +x1, +y1, +x2, +y2]; - insertColorStopsIntoArray(stops, brushData, 5); - this._brush = brushData; -} - -function RadialGradient(stops, fx, fy, rx, ry, cx, cy) { - if (ry == null) { - ry = rx; - } - if (cx == null) { - cx = fx; - } - if (cy == null) { - cy = fy; - } - if (fx == null) { - // As a convenience we allow the whole radial gradient to cover the - // bounding box. We should consider dropping this API. - fx = fy = rx = ry = cx = cy = 0.5; - this._bb = true; - } else { - this._bb = false; - } - // The ART API expects the radial gradient to be repeated at the edges. - // To simulate this we render the gradient twice as large and add double - // color stops. Ideally this API would become more restrictive so that this - // extra work isn't needed. - const brushData = [RADIAL_GRADIENT, +fx, +fy, +rx * 2, +ry * 2, +cx, +cy]; - insertDoubleColorStopsIntoArray(stops, brushData, 7); - this._brush = brushData; -} - -function Pattern(url, width, height, left, top) { - this._brush = [PATTERN, url, +left || 0, +top || 0, +width, +height]; -} - -const ReactART = { - LinearGradient: LinearGradient, - RadialGradient: RadialGradient, - Pattern: Pattern, - Transform: Transform, - Path: Path, - Surface: Surface, - Group: Group, - ClippingRectangle: ClippingRectangle, - Shape: Shape, - Text: Text, -}; - -module.exports = ReactART; diff --git a/RNTester/RNTesterIntegrationTests/RNTesterSnapshotTests.m b/RNTester/RNTesterIntegrationTests/RNTesterSnapshotTests.m index 55998e0bfb8..3b12d4e6d63 100644 --- a/RNTester/RNTesterIntegrationTests/RNTesterSnapshotTests.m +++ b/RNTester/RNTesterIntegrationTests/RNTesterSnapshotTests.m @@ -11,8 +11,7 @@ #import -@interface RNTesterSnapshotTests : XCTestCase -{ +@interface RNTesterSnapshotTests : XCTestCase { RCTTestRunner *_runner; } @@ -29,15 +28,14 @@ _runner.recordMode = NO; } -#define RCT_TEST(name) \ -- (void)test##name \ -{ \ - [_runner runTest:_cmd module:@#name]; \ -} +#define RCT_TEST(name) \ + -(void)test##name \ + { \ + [_runner runTest:_cmd module:@ #name]; \ + } RCT_TEST(ViewExample) RCT_TEST(LayoutExample) -RCT_TEST(ARTExample) RCT_TEST(ScrollViewExample) RCT_TEST(TextExample) #if !TARGET_OS_TV diff --git a/RNTester/js/examples/ART/ARTExample.js b/RNTester/js/examples/ART/ARTExample.js deleted file mode 100644 index a3e294c54d6..00000000000 --- a/RNTester/js/examples/ART/ARTExample.js +++ /dev/null @@ -1,105 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - * - * @format - * @flow - */ - -'use strict'; - -const React = require('react'); -const {ART, Platform, View} = require('react-native'); - -const {Surface, Path, Group, Shape} = ART; - -const scale = Platform.isTV ? 4 : 1; - -type Props = $ReadOnly<{||}>; -class ARTExample extends React.Component { - render() { - const pathRect = new Path() - .moveTo(scale * 0, scale * 0) - .lineTo(scale * 0, scale * 110) - .lineTo(scale * 110, scale * 110) - .lineTo(scale * 110, scale * 0) - .close(); - - const pathCircle0 = new Path() - .moveTo(scale * 30, scale * 5) - .arc(scale * 0, scale * 50, scale * 25) - .arc(scale * 0, -scale * 50, scale * 25) - .close(); - - const pathCircle1 = new Path() - .moveTo(scale * 30, scale * 55) - .arc(scale * 0, scale * 50, scale * 25) - .arc(scale * 0, -scale * 50, scale * 25) - .close(); - - const pathCircle2 = new Path() - .moveTo(scale * 55, scale * 30) - .arc(scale * 50, scale * 0, scale * 25) - .arc(-scale * 50, scale * 0, scale * 25) - .close(); - - const pathCircle3 = new Path() - .moveTo(scale * 55, scale * 80) - .arc(scale * 50, scale * 0, scale * 25) - .arc(-scale * 50, scale * 0, scale * 25) - .close(); - - return ( - - - - - - - - - - - - ); - } -} - -exports.title = ''; -exports.displayName = 'ARTExample'; -exports.description = 'ART input for numeric values'; -exports.examples = [ - { - title: 'ART Example', - render(): React.Element { - return ; - }, - }, -]; diff --git a/RNTester/js/utils/RNTesterList.ios.js b/RNTester/js/utils/RNTesterList.ios.js index 2efa1878a69..c7cc018b6a8 100644 --- a/RNTester/js/utils/RNTesterList.ios.js +++ b/RNTester/js/utils/RNTesterList.ios.js @@ -18,11 +18,6 @@ const ComponentExamples: Array = [ module: require('../examples/ActivityIndicator/ActivityIndicatorExample'), supportsTVOS: true, }, - { - key: 'ARTExample', - module: require('../examples/ART/ARTExample'), - supportsTVOS: true, - }, { key: 'ButtonExample', module: require('../examples/Button/ButtonExample'), diff --git a/index.js b/index.js index 9e991220579..c2e3b86246d 100644 --- a/index.js +++ b/index.js @@ -12,7 +12,6 @@ import typeof AccessibilityInfo from './Libraries/Components/AccessibilityInfo/AccessibilityInfo'; import typeof ActivityIndicator from './Libraries/Components/ActivityIndicator/ActivityIndicator'; -import typeof ReactNativeART from './Libraries/ART/ReactNativeART'; import typeof Button from './Libraries/Components/Button'; import typeof CheckBox from './Libraries/Components/CheckBox/CheckBox'; import typeof DatePickerIOS from './Libraries/Components/DatePicker/DatePickerIOS'; @@ -112,15 +111,6 @@ module.exports = { get ActivityIndicator(): ActivityIndicator { return require('./Libraries/Components/ActivityIndicator/ActivityIndicator'); }, - get ART(): ReactNativeART { - warnOnce( - 'art-moved', - 'React Native ART has been extracted from react-native core and will be removed in a future release. ' + - "It can now be installed and imported from '@react-native-community/art' instead of 'react-native'. " + - 'See https://github.com/react-native-community/art', - ); - return require('./Libraries/ART/ReactNativeART'); - }, get Button(): Button { return require('./Libraries/Components/Button'); }, @@ -467,6 +457,19 @@ module.exports = { }; if (__DEV__) { + // $FlowFixMe This is intentional: Flow will error when attempting to access ART. + Object.defineProperty(module.exports, 'ART', { + configurable: true, + get() { + invariant( + false, + 'ART has been removed from React Native. ' + + "It can now be installed and imported from '@react-native-community/art' instead of 'react-native'. " + + 'See https://github.com/react-native-community/react-native-art', + ); + }, + }); + // $FlowFixMe This is intentional: Flow will error when attempting to access ListView. Object.defineProperty(module.exports, 'ListView', { configurable: true, diff --git a/package.json b/package.json index d49ae67f516..a2fb51e7020 100644 --- a/package.json +++ b/package.json @@ -90,7 +90,6 @@ "@react-native-community/cli-platform-android": "^3.0.0-alpha.1", "@react-native-community/cli-platform-ios": "^3.0.0-alpha.1", "abort-controller": "^3.0.0", - "art": "^0.10.0", "base64-js": "^1.1.2", "connect": "^3.6.5", "create-react-class": "^15.6.3", diff --git a/yarn.lock b/yarn.lock index af9515a57d5..0fb04d30310 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1496,11 +1496,6 @@ arrify@^1.0.1: resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d" integrity sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0= -art@^0.10.0: - version "0.10.3" - resolved "https://registry.yarnpkg.com/art/-/art-0.10.3.tgz#b01d84a968ccce6208df55a733838c96caeeaea2" - integrity sha512-HXwbdofRTiJT6qZX/FnchtldzJjS3vkLJxQilc3Xj+ma2MXjY4UAyQ0ls1XZYVnDvVIBiFZbC6QsvtW86TD6tQ== - asap@~2.0.3: version "2.0.6" resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46"