about summary refs log tree commit diff
diff options
context:
space:
mode:
authorPaul Frazee <pfrazee@gmail.com>2023-01-26 12:36:27 -0600
committerPaul Frazee <pfrazee@gmail.com>2023-01-26 12:36:27 -0600
commit751dfb20fd0d316da396e3c4fc53aaaaa8041dd1 (patch)
tree55c23e901903cfa19b6b9acc264df0d0637d66f0
parentd6ec627c8cd32836e5ed494606318959ca17fca1 (diff)
downloadvoidsky-751dfb20fd0d316da396e3c4fc53aaaaa8041dd1.tar.zst
Add web polyfills
-rw-r--r--package.json1
-rw-r--r--src/lib/images.web.ts69
-rw-r--r--src/state/index.ts4
-rw-r--r--src/state/lib/api-polyfill.ts76
-rw-r--r--src/state/lib/api-polyfill.web.ts4
-rw-r--r--src/state/lib/api.ts75
-rw-r--r--src/state/lib/bg-scheduler.ts18
-rw-r--r--src/state/lib/bg-scheduler.web.ts13
-rw-r--r--src/state/models/profile-view.ts2
-rw-r--r--src/state/models/root-store.ts9
-rw-r--r--src/view/com/composer/PhotoCarouselPicker.tsx2
-rw-r--r--src/view/com/modals/EditProfile.tsx2
-rw-r--r--src/view/com/util/UserAvatar.tsx2
-rw-r--r--src/view/com/util/UserBanner.tsx8
-rw-r--r--src/view/com/util/images/ImageCropPicker.tsx6
-rw-r--r--src/view/com/util/images/ImageCropPicker.web.tsx32
-rw-r--r--web/webpack.config.js1
-rw-r--r--yarn.lock21
18 files changed, 240 insertions, 105 deletions
diff --git a/package.json b/package.json
index adedd032d..8b135531d 100644
--- a/package.json
+++ b/package.json
@@ -66,6 +66,7 @@
     "react-native-url-polyfill": "^1.3.0",
     "react-native-version-number": "^0.3.6",
     "react-native-web": "^0.18.11",
+    "react-native-web-linear-gradient": "^1.1.2",
     "rn-fetch-blob": "^0.12.0",
     "tlds": "^1.234.0",
     "zod": "^3.20.2"
diff --git a/src/lib/images.web.ts b/src/lib/images.web.ts
new file mode 100644
index 000000000..5158e005f
--- /dev/null
+++ b/src/lib/images.web.ts
@@ -0,0 +1,69 @@
+import {Share} from 'react-native'
+
+import * as Toast from '../view/com/util/Toast'
+
+export interface DownloadAndResizeOpts {
+  uri: string
+  width: number
+  height: number
+  mode: 'contain' | 'cover' | 'stretch'
+  maxSize: number
+  timeout: number
+}
+
+export interface Image {
+  path: string
+  mime: string
+  size: number
+  width: number
+  height: number
+}
+
+export async function downloadAndResize(_opts: DownloadAndResizeOpts) {
+  // TODO
+  throw new Error('TODO')
+}
+
+export interface ResizeOpts {
+  width: number
+  height: number
+  mode: 'contain' | 'cover' | 'stretch'
+  maxSize: number
+}
+
+export async function resize(
+  _localUri: string,
+  _opts: ResizeOpts,
+): Promise<Image> {
+  // TODO
+  throw new Error('TODO')
+}
+
+export async function compressIfNeeded(
+  _img: Image,
+  _maxSize: number,
+): Promise<Image> {
+  // TODO
+  throw new Error('TODO')
+}
+
+export interface Dim {
+  width: number
+  height: number
+}
+export function scaleDownDimensions(dim: Dim, max: Dim): Dim {
+  if (dim.width < max.width && dim.height < max.height) {
+    return dim
+  }
+  let wScale = dim.width > max.width ? max.width / dim.width : 1
+  let hScale = dim.height > max.height ? max.height / dim.height : 1
+  if (wScale < hScale) {
+    return {width: dim.width * wScale, height: dim.height * wScale}
+  }
+  return {width: dim.width * hScale, height: dim.height * hScale}
+}
+
+export const saveImageModal = async (_opts: {uri: string}) => {
+  // TODO
+  throw new Error('TODO')
+}
diff --git a/src/state/index.ts b/src/state/index.ts
index 78fba2ecf..654c15af7 100644
--- a/src/state/index.ts
+++ b/src/state/index.ts
@@ -2,7 +2,7 @@ import {autorun} from 'mobx'
 import {Platform} from 'react-native'
 import {sessionClient as AtpApi, SessionServiceClient} from '@atproto/api'
 import {RootStoreModel} from './models/root-store'
-import * as libapi from './lib/api'
+import * as apiPolyfill from './lib/api-polyfill'
 import * as storage from './lib/storage'
 
 export const LOCAL_DEV_SERVICE =
@@ -17,7 +17,7 @@ export async function setupState(serviceUri = DEFAULT_SERVICE) {
   let rootStore: RootStoreModel
   let data: any
 
-  libapi.doPolyfill()
+  apiPolyfill.doPolyfill()
 
   const api = AtpApi.service(serviceUri) as SessionServiceClient
   rootStore = new RootStoreModel(api)
diff --git a/src/state/lib/api-polyfill.ts b/src/state/lib/api-polyfill.ts
new file mode 100644
index 000000000..be6f90f7a
--- /dev/null
+++ b/src/state/lib/api-polyfill.ts
@@ -0,0 +1,76 @@
+import {sessionClient as AtpApi} from '@atproto/api'
+
+export function doPolyfill() {
+  AtpApi.xrpc.fetch = fetchHandler
+}
+
+interface FetchHandlerResponse {
+  status: number
+  headers: Record<string, string>
+  body: ArrayBuffer | undefined
+}
+
+async function fetchHandler(
+  reqUri: string,
+  reqMethod: string,
+  reqHeaders: Record<string, string>,
+  reqBody: any,
+): Promise<FetchHandlerResponse> {
+  const reqMimeType = reqHeaders['Content-Type'] || reqHeaders['content-type']
+  if (reqMimeType && reqMimeType.startsWith('application/json')) {
+    reqBody = JSON.stringify(reqBody)
+  } else if (
+    typeof reqBody === 'string' &&
+    (reqBody.startsWith('/') || reqBody.startsWith('file:'))
+  ) {
+    if (reqBody.endsWith('.jpeg') || reqBody.endsWith('.jpg')) {
+      // HACK
+      // React native has a bug that inflates the size of jpegs on upload
+      // we get around that by renaming the file ext to .bin
+      // see https://github.com/facebook/react-native/issues/27099
+      // -prf
+      const newPath = reqBody.replace(/\.jpe?g$/, '.bin')
+      await RNFS.moveFile(reqBody, newPath)
+      reqBody = newPath
+    }
+    // NOTE
+    // React native treats bodies with {uri: string} as file uploads to pull from cache
+    // -prf
+    reqBody = {uri: reqBody}
+  }
+
+  const controller = new AbortController()
+  const to = setTimeout(() => controller.abort(), TIMEOUT)
+
+  const res = await fetch(reqUri, {
+    method: reqMethod,
+    headers: reqHeaders,
+    body: reqBody,
+    signal: controller.signal,
+  })
+
+  const resStatus = res.status
+  const resHeaders: Record<string, string> = {}
+  res.headers.forEach((value: string, key: string) => {
+    resHeaders[key] = value
+  })
+  const resMimeType = resHeaders['Content-Type'] || resHeaders['content-type']
+  let resBody
+  if (resMimeType) {
+    if (resMimeType.startsWith('application/json')) {
+      resBody = await res.json()
+    } else if (resMimeType.startsWith('text/')) {
+      resBody = await res.text()
+    } else {
+      throw new Error('TODO: non-textual response body')
+    }
+  }
+
+  clearTimeout(to)
+
+  return {
+    status: resStatus,
+    headers: resHeaders,
+    body: resBody,
+  }
+}
diff --git a/src/state/lib/api-polyfill.web.ts b/src/state/lib/api-polyfill.web.ts
new file mode 100644
index 000000000..1469cf905
--- /dev/null
+++ b/src/state/lib/api-polyfill.web.ts
@@ -0,0 +1,4 @@
+export function doPolyfill() {
+  // TODO needed? native fetch may work fine -prf
+  // AtpApi.xrpc.fetch = fetchHandler
+}
diff --git a/src/state/lib/api.ts b/src/state/lib/api.ts
index e498bef1b..c63f0e2f7 100644
--- a/src/state/lib/api.ts
+++ b/src/state/lib/api.ts
@@ -19,10 +19,6 @@ import {Image} from '../../lib/images'
 
 const TIMEOUT = 10e3 // 10s
 
-export function doPolyfill() {
-  AtpApi.xrpc.fetch = fetchHandler
-}
-
 export interface ExternalEmbedDraft {
   uri: string
   isLoading: boolean
@@ -199,74 +195,3 @@ export async function unfollow(store: RootStoreModel, followUri: string) {
     rkey: followUrip.rkey,
   })
 }
-
-interface FetchHandlerResponse {
-  status: number
-  headers: Record<string, string>
-  body: ArrayBuffer | undefined
-}
-
-async function fetchHandler(
-  reqUri: string,
-  reqMethod: string,
-  reqHeaders: Record<string, string>,
-  reqBody: any,
-): Promise<FetchHandlerResponse> {
-  const reqMimeType = reqHeaders['Content-Type'] || reqHeaders['content-type']
-  if (reqMimeType && reqMimeType.startsWith('application/json')) {
-    reqBody = JSON.stringify(reqBody)
-  } else if (
-    typeof reqBody === 'string' &&
-    (reqBody.startsWith('/') || reqBody.startsWith('file:'))
-  ) {
-    if (reqBody.endsWith('.jpeg') || reqBody.endsWith('.jpg')) {
-      // HACK
-      // React native has a bug that inflates the size of jpegs on upload
-      // we get around that by renaming the file ext to .bin
-      // see https://github.com/facebook/react-native/issues/27099
-      // -prf
-      const newPath = reqBody.replace(/\.jpe?g$/, '.bin')
-      await RNFS.moveFile(reqBody, newPath)
-      reqBody = newPath
-    }
-    // NOTE
-    // React native treats bodies with {uri: string} as file uploads to pull from cache
-    // -prf
-    reqBody = {uri: reqBody}
-  }
-
-  const controller = new AbortController()
-  const to = setTimeout(() => controller.abort(), TIMEOUT)
-
-  const res = await fetch(reqUri, {
-    method: reqMethod,
-    headers: reqHeaders,
-    body: reqBody,
-    signal: controller.signal,
-  })
-
-  const resStatus = res.status
-  const resHeaders: Record<string, string> = {}
-  res.headers.forEach((value: string, key: string) => {
-    resHeaders[key] = value
-  })
-  const resMimeType = resHeaders['Content-Type'] || resHeaders['content-type']
-  let resBody
-  if (resMimeType) {
-    if (resMimeType.startsWith('application/json')) {
-      resBody = await res.json()
-    } else if (resMimeType.startsWith('text/')) {
-      resBody = await res.text()
-    } else {
-      throw new Error('TODO: non-textual response body')
-    }
-  }
-
-  clearTimeout(to)
-
-  return {
-    status: resStatus,
-    headers: resHeaders,
-    body: resBody,
-  }
-}
diff --git a/src/state/lib/bg-scheduler.ts b/src/state/lib/bg-scheduler.ts
new file mode 100644
index 000000000..97ccb78b2
--- /dev/null
+++ b/src/state/lib/bg-scheduler.ts
@@ -0,0 +1,18 @@
+import BackgroundFetch, {
+  BackgroundFetchStatus,
+} from 'react-native-background-fetch'
+
+export function configure(
+  handler: (taskId: string) => Promise<void>,
+  timeoutHandler: (taskId: string) => Promise<void>,
+): Promise<BackgroundFetchStatus> {
+  return BackgroundFetch.configure(
+    {minimumFetchInterval: 15},
+    handler,
+    timeoutHandler,
+  )
+}
+
+export function finish(taskId: string) {
+  return BackgroundFetch.finish(taskId)
+}
diff --git a/src/state/lib/bg-scheduler.web.ts b/src/state/lib/bg-scheduler.web.ts
new file mode 100644
index 000000000..91ec9428f
--- /dev/null
+++ b/src/state/lib/bg-scheduler.web.ts
@@ -0,0 +1,13 @@
+type BackgroundFetchStatus = 0 | 1 | 2
+
+export async function configure(
+  _handler: (taskId: string) => Promise<void>,
+  _timeoutHandler: (taskId: string) => Promise<void>,
+): Promise<BackgroundFetchStatus> {
+  // TODO
+  return 0
+}
+
+export function finish(_taskId: string) {
+  // TODO
+}
diff --git a/src/state/models/profile-view.ts b/src/state/models/profile-view.ts
index a1535693c..3228c57e8 100644
--- a/src/state/models/profile-view.ts
+++ b/src/state/models/profile-view.ts
@@ -1,5 +1,5 @@
 import {makeAutoObservable, runInAction} from 'mobx'
-import {Image as PickedImage} from 'react-native-image-crop-picker'
+import {Image as PickedImage} from '../../view/com/util/images/ImageCropPicker'
 import {
   AppBskyActorGetProfile as GetProfile,
   AppBskyActorProfile as Profile,
diff --git a/src/state/models/root-store.ts b/src/state/models/root-store.ts
index c4798ad0b..2f6931cdc 100644
--- a/src/state/models/root-store.ts
+++ b/src/state/models/root-store.ts
@@ -6,7 +6,7 @@ import {makeAutoObservable} from 'mobx'
 import {sessionClient as AtpApi, SessionServiceClient} from '@atproto/api'
 import {createContext, useContext} from 'react'
 import {DeviceEventEmitter, EmitterSubscription} from 'react-native'
-import BackgroundFetch from 'react-native-background-fetch'
+import * as BgScheduler from '../lib/bg-scheduler'
 import {isObj, hasProp} from '../lib/type-guards'
 import {LogModel} from './log'
 import {SessionModel} from './session'
@@ -124,8 +124,7 @@ export class RootStoreModel {
     // background fetch runs every 15 minutes *at most* and will get slowed down
     // based on some heuristics run by iOS, meaning it is not a reliable form of delivery
     // -prf
-    BackgroundFetch.configure(
-      {minimumFetchInterval: 15},
+    BgScheduler.configure(
       this.onBgFetch.bind(this),
       this.onBgFetchTimeout.bind(this),
     ).then(status => {
@@ -138,12 +137,12 @@ export class RootStoreModel {
     if (this.session.hasSession) {
       await this.me.bgFetchNotifications()
     }
-    BackgroundFetch.finish(taskId)
+    BgScheduler.finish(taskId)
   }
 
   onBgFetchTimeout(taskId: string) {
     this.log.debug(`Background fetch timed out for task ${taskId}`)
-    BackgroundFetch.finish(taskId)
+    BgScheduler.finish(taskId)
   }
 }
 
diff --git a/src/view/com/composer/PhotoCarouselPicker.tsx b/src/view/com/composer/PhotoCarouselPicker.tsx
index 383027de3..eb5b4dcf2 100644
--- a/src/view/com/composer/PhotoCarouselPicker.tsx
+++ b/src/view/com/composer/PhotoCarouselPicker.tsx
@@ -8,7 +8,7 @@ import {
   openPicker,
   openCamera,
   openCropper,
-} from 'react-native-image-crop-picker'
+} from '../util/images/ImageCropPicker'
 import {
   UserLocalPhotosModel,
   PhotoIdentifier,
diff --git a/src/view/com/modals/EditProfile.tsx b/src/view/com/modals/EditProfile.tsx
index 8449fda68..380e76e79 100644
--- a/src/view/com/modals/EditProfile.tsx
+++ b/src/view/com/modals/EditProfile.tsx
@@ -8,7 +8,7 @@ import {
 } from 'react-native'
 import LinearGradient from 'react-native-linear-gradient'
 import {BottomSheetScrollView, BottomSheetTextInput} from '@gorhom/bottom-sheet'
-import {Image as PickedImage} from 'react-native-image-crop-picker'
+import {Image as PickedImage} from '../util/images/ImageCropPicker'
 import {Text} from '../util/text/Text'
 import {ErrorMessage} from '../util/error/ErrorMessage'
 import {useStores} from '../../../state'
diff --git a/src/view/com/util/UserAvatar.tsx b/src/view/com/util/UserAvatar.tsx
index c9c255f46..d91607b6c 100644
--- a/src/view/com/util/UserAvatar.tsx
+++ b/src/view/com/util/UserAvatar.tsx
@@ -7,7 +7,7 @@ import {
   openCropper,
   openPicker,
   Image as PickedImage,
-} from 'react-native-image-crop-picker'
+} from './images/ImageCropPicker'
 import {colors, gradients} from '../../lib/styles'
 
 export function UserAvatar({
diff --git a/src/view/com/util/UserBanner.tsx b/src/view/com/util/UserBanner.tsx
index 5ce638351..fe606bc55 100644
--- a/src/view/com/util/UserBanner.tsx
+++ b/src/view/com/util/UserBanner.tsx
@@ -2,13 +2,9 @@ import React, {useCallback} from 'react'
 import {StyleSheet, View, TouchableOpacity, Alert, Image} from 'react-native'
 import Svg, {Rect, Defs, LinearGradient, Stop} from 'react-native-svg'
 import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
-import {Image as PickedImage} from 'react-native-image-crop-picker'
+import {Image as PickedImage} from './images/ImageCropPicker'
 import {colors, gradients} from '../../lib/styles'
-import {
-  openCamera,
-  openCropper,
-  openPicker,
-} from 'react-native-image-crop-picker'
+import {openCamera, openCropper, openPicker} from './images/ImageCropPicker'
 
 export function UserBanner({
   banner,
diff --git a/src/view/com/util/images/ImageCropPicker.tsx b/src/view/com/util/images/ImageCropPicker.tsx
new file mode 100644
index 000000000..9cd4da9f5
--- /dev/null
+++ b/src/view/com/util/images/ImageCropPicker.tsx
@@ -0,0 +1,6 @@
+export {
+  openPicker,
+  openCamera,
+  openCropper,
+} from 'react-native-image-crop-picker'
+export type {Image} from 'react-native-image-crop-picker'
diff --git a/src/view/com/util/images/ImageCropPicker.web.tsx b/src/view/com/util/images/ImageCropPicker.web.tsx
new file mode 100644
index 000000000..a385e2e93
--- /dev/null
+++ b/src/view/com/util/images/ImageCropPicker.web.tsx
@@ -0,0 +1,32 @@
+import type {
+  Image,
+  Video,
+  ImageOrVideo,
+  Options,
+  PossibleArray,
+} from 'react-native-image-crop-picker'
+
+export type {Image} from 'react-native-image-crop-picker'
+
+type MediaType<O> = O extends {mediaType: 'photo'}
+  ? Image
+  : O extends {mediaType: 'video'}
+  ? Video
+  : ImageOrVideo
+
+export async function openPicker<O extends Options>(
+  _options: O,
+): Promise<PossibleArray<O, MediaType<O>>> {
+  // TODO
+  throw new Error('TODO')
+}
+export async function openCamera<O extends Options>(
+  _options: O,
+): Promise<PossibleArray<O, MediaType<O>>> {
+  // TODO
+  throw new Error('TODO')
+}
+export async function openCropper(_options: Options): Promise<Image> {
+  // TODO
+  throw new Error('TODO')
+}
diff --git a/web/webpack.config.js b/web/webpack.config.js
index 222703968..c7b248d87 100644
--- a/web/webpack.config.js
+++ b/web/webpack.config.js
@@ -70,6 +70,7 @@ module.exports = {
   resolve: {
     alias: {
       'react-native$': 'react-native-web',
+      'react-native-linear-gradient': 'react-native-web-linear-gradient',
     },
     extensions: [
       '.web.tsx',
diff --git a/yarn.lock b/yarn.lock
index bec4c873e..84d6e3ff2 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -5471,7 +5471,7 @@ end-of-stream@^1.1.0, end-of-stream@^1.4.1:
   dependencies:
     once "^1.4.0"
 
-enhanced-resolve@^5.0.0, enhanced-resolve@^5.10.0:
+enhanced-resolve@^5.10.0:
   version "5.12.0"
   resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.12.0.tgz#300e1c90228f5b570c4d35babf263f6da7155634"
   integrity sha512-QHTXI/sZQmko1cbDoNAa3mJ5qhWUUNAq3vR0/YiD379fWQrcfuoX1+HW2S0MTt7XmoPLapdaDKUtelUSPic7hQ==
@@ -9450,7 +9450,7 @@ micromatch@^3.1.10:
     snapdragon "^0.8.1"
     to-regex "^3.0.2"
 
-micromatch@^4.0.0, micromatch@^4.0.2, micromatch@^4.0.4, micromatch@^4.0.5:
+micromatch@^4.0.2, micromatch@^4.0.4, micromatch@^4.0.5:
   version "4.0.5"
   resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.5.tgz#bc8999a7cbbf77cdc89f132f6e467051b49090c6"
   integrity sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==
@@ -11401,6 +11401,11 @@ react-native-version-number@^0.3.6:
   resolved "https://registry.yarnpkg.com/react-native-version-number/-/react-native-version-number-0.3.6.tgz#dd8b1435fc217df0a166d7e4a61fdc993f3e7437"
   integrity sha512-TdyXiK90NiwmSbmAUlUBOV6WI1QGoqtvZZzI5zQY4fKl67B3ZrZn/h+Wy/OYIKKFMfePSiyfeIs8LtHGOZ/NgA==
 
+react-native-web-linear-gradient@^1.1.2:
+  version "1.1.2"
+  resolved "https://registry.yarnpkg.com/react-native-web-linear-gradient/-/react-native-web-linear-gradient-1.1.2.tgz#33f85f7085a0bb5ffa5106faf02ed105b92a9ed7"
+  integrity sha512-SmUnpwT49CEe78pXvIvYf72Es8Pv+ZYKCnEOgb2zAKpEUDMo0+xElfRJhwt5nfI8krJ5WbFPKnoDgD0uUjAN1A==
+
 react-native-web@^0.18.11:
   version "0.18.12"
   resolved "https://registry.yarnpkg.com/react-native-web/-/react-native-web-0.18.12.tgz#d4bb3a783ece2514ba0508d7805b09c0a98f5a8e"
@@ -12045,7 +12050,7 @@ semver-compare@^1.0.0:
   resolved "https://registry.yarnpkg.com/semver-compare/-/semver-compare-1.0.0.tgz#0dee216a1c941ab37e9efb1788f6afc5ff5537fc"
   integrity sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow==
 
-semver@7.3.8, semver@^7.3.2, semver@^7.3.4, semver@^7.3.5, semver@^7.3.7, semver@^7.3.8:
+semver@7.3.8, semver@^7.3.2, semver@^7.3.5, semver@^7.3.7, semver@^7.3.8:
   version "7.3.8"
   resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.8.tgz#07a78feafb3f7b32347d725e33de7e2a2df67798"
   integrity sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==
@@ -13002,16 +13007,6 @@ tryer@^1.0.1:
   resolved "https://registry.yarnpkg.com/tryer/-/tryer-1.0.1.tgz#f2c85406800b9b0f74c9f7465b81eaad241252f8"
   integrity sha512-c3zayb8/kWWpycWYg87P71E1S1ZL6b6IJxfb5fvsUgsf0S2MVGaDhDXXjDMpdCpfWXqptc+4mXwmiy1ypXqRAA==
 
-ts-loader@^9.4.2:
-  version "9.4.2"
-  resolved "https://registry.yarnpkg.com/ts-loader/-/ts-loader-9.4.2.tgz#80a45eee92dd5170b900b3d00abcfa14949aeb78"
-  integrity sha512-OmlC4WVmFv5I0PpaxYb+qGeGOdm5giHU7HwDDUjw59emP2UYMHy9fFSDcYgSNoH8sXcj4hGCSEhlDZ9ULeDraA==
-  dependencies:
-    chalk "^4.1.0"
-    enhanced-resolve "^5.0.0"
-    micromatch "^4.0.0"
-    semver "^7.3.4"
-
 tsconfig-paths@^3.14.1:
   version "3.14.1"
   resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz#ba0734599e8ea36c862798e920bcf163277b137a"