Преглед изворни кода

Merge branch 'main' into 4.x

* main:
  @uppy/remote-sources: migrate to TS (#5020)
  @uppy/dashboard: refine option types (#5022)
Murderlon пре 1 година
родитељ
комит
7329094049

+ 49 - 29
packages/@uppy/dashboard/src/Dashboard.tsx

@@ -112,61 +112,75 @@ interface DashboardState<M extends Meta, B extends Body> {
   [key: string]: unknown
 }
 
-export interface DashboardOptions<M extends Meta, B extends Body>
-  extends UIPluginOptions {
+export interface DashboardModalOptions {
+  inline?: false
   animateOpenClose?: boolean
   browserBackButtonClose?: boolean
   closeAfterFinish?: boolean
-  singleFileFullScreen?: boolean
   closeModalOnClickOutside?: boolean
-  disableInformer?: boolean
   disablePageScrollWhenModalOpen?: boolean
-  disableStatusBar?: boolean
-  disableThumbnailGenerator?: boolean
+}
+
+export interface DashboardInlineOptions {
+  inline: true
+
   height?: string | number
-  thumbnailWidth?: number
-  thumbnailHeight?: number
-  thumbnailType?: string
-  nativeCameraFacingMode?: ConstrainDOMString
-  waitForThumbnailsBeforeUpload?: boolean
+  width?: string | number
+}
+
+interface DashboardMiscOptions<M extends Meta, B extends Body>
+  extends UIPluginOptions {
+  autoOpen?: 'metaEditor' | 'imageEditor' | null
+  /** @deprecated use option autoOpen instead */
+  autoOpenFileEditor?: boolean
   defaultPickerIcon?: typeof defaultPickerIcon
+  disabled?: boolean
+  disableInformer?: boolean
+  disableLocalFiles?: boolean
+  disableStatusBar?: boolean
+  disableThumbnailGenerator?: boolean
+  doneButtonHandler?: () => void
+  fileManagerSelectionType?: 'files' | 'folders' | 'both'
   hideCancelButton?: boolean
   hidePauseResumeButton?: boolean
   hideProgressAfterFinish?: boolean
   hideRetryButton?: boolean
   hideUploadButton?: boolean
-  inline?: boolean
   metaFields?: MetaField[] | ((file: UppyFile<M, B>) => MetaField[])
+  nativeCameraFacingMode?: ConstrainDOMString
   note?: string | null
+  onDragLeave?: (event: DragEvent) => void
+  onDragOver?: (event: DragEvent) => void
+  onDrop?: (event: DragEvent) => void
+  onRequestCloseModal?: () => void
   plugins?: string[]
-  fileManagerSelectionType?: 'files' | 'folders' | 'both'
   proudlyDisplayPoweredByUppy?: boolean
   showLinkToFileUploadResult?: boolean
-  showProgressDetails?: boolean
-  showSelectedFiles?: boolean
-  showRemoveButtonAfterComplete?: boolean
   showNativePhotoCameraButton?: boolean
   showNativeVideoCameraButton?: boolean
+  showProgressDetails?: boolean
+  showRemoveButtonAfterComplete?: boolean
+  showSelectedFiles?: boolean
+  singleFileFullScreen?: boolean
   theme?: 'auto' | 'dark' | 'light'
+  thumbnailHeight?: number
+  thumbnailType?: string
+  thumbnailWidth?: number
   trigger?: string
-  width?: string | number
-  autoOpen?: 'metaEditor' | 'imageEditor' | null
-  /** @deprecated use option autoOpen instead */
-  autoOpenFileEditor?: boolean
-  disabled?: boolean
-  disableLocalFiles?: boolean
-  onRequestCloseModal?: () => void
-  doneButtonHandler?: () => void
-  onDragOver?: (event: DragEvent) => void
-  onDragLeave?: (event: DragEvent) => void
-  onDrop?: (event: DragEvent) => void
+  waitForThumbnailsBeforeUpload?: boolean
 }
 
+export type DashboardOptions<
+  M extends Meta,
+  B extends Body,
+> = DashboardMiscOptions<M, B> &
+  (DashboardModalOptions | DashboardInlineOptions)
+
 // set default options, must be kept in sync with packages/@uppy/react/src/DashboardModal.js
 const defaultOptions = {
   target: 'body',
   metaFields: [],
-  inline: false,
+  inline: false as boolean,
   width: 750,
   height: 550,
   thumbnailWidth: 280,
@@ -213,7 +227,13 @@ const defaultOptions = {
  * Dashboard UI with previews, metadata editing, tabs for various services and more
  */
 export default class Dashboard<M extends Meta, B extends Body> extends UIPlugin<
-  DefinePluginOpts<DashboardOptions<M, B>, keyof typeof defaultOptions>,
+  DefinePluginOpts<
+    // The options object inside the class is not the discriminated union but and intersection of the different subtypes.
+    DashboardMiscOptions<M, B> &
+      Omit<DashboardInlineOptions, 'inline'> &
+      Omit<DashboardModalOptions, 'inline'> & { inline?: boolean },
+    keyof typeof defaultOptions
+  >,
   M,
   B,
   DashboardState<M, B>

+ 1 - 0
packages/@uppy/remote-sources/.npmignore

@@ -0,0 +1 @@
+tsconfig.*

+ 0 - 76
packages/@uppy/remote-sources/src/index.js

@@ -1,76 +0,0 @@
-import { BasePlugin } from '@uppy/core'
-import Dropbox from '@uppy/dropbox'
-import GoogleDrive from '@uppy/google-drive'
-import Instagram from '@uppy/instagram'
-import Facebook from '@uppy/facebook'
-import OneDrive from '@uppy/onedrive'
-import Box from '@uppy/box'
-import Unsplash from '@uppy/unsplash'
-import Url from '@uppy/url'
-import Zoom from '@uppy/zoom'
-
-import packageJson from '../package.json'
-
-const availablePlugins = {
-  // Using a null-prototype object to avoid prototype pollution.
-  __proto__: null,
-  Box,
-  Dropbox,
-  Facebook,
-  GoogleDrive,
-  Instagram,
-  OneDrive,
-  Unsplash,
-  Url,
-  Zoom,
-}
-
-export default class RemoteSources extends BasePlugin {
-  static VERSION = packageJson.version
-
-  #installedPlugins = new Set()
-
-  constructor (uppy, opts) {
-    super(uppy, opts)
-    this.id = this.opts.id || 'RemoteSources'
-    this.type = 'preset'
-
-    const defaultOptions = {
-      sources: Object.keys(availablePlugins),
-    }
-    this.opts = { ...defaultOptions, ...opts }
-
-    if (this.opts.companionUrl == null) {
-      throw new Error('Please specify companionUrl for RemoteSources to work, see https://uppy.io/docs/remote-sources#companionUrl')
-    }
-  }
-
-  setOptions (newOpts) {
-    this.uninstall()
-    super.setOptions(newOpts)
-    this.install()
-  }
-
-  install () {
-    this.opts.sources.forEach((pluginId) => {
-      const optsForRemoteSourcePlugin = { ...this.opts, sources: undefined }
-      const plugin = availablePlugins[pluginId]
-      if (plugin == null) {
-        const pluginNames = Object.keys(availablePlugins)
-        const formatter = new Intl.ListFormat('en', { style: 'long', type: 'disjunction' })
-        throw new Error(`Invalid plugin: "${pluginId}" is not one of: ${formatter.format(pluginNames)}.`)
-      }
-      this.uppy.use(plugin, optsForRemoteSourcePlugin)
-      // `plugin` is a class, but we want to track the instance object
-      // so we have to do `getPlugin` here.
-      this.#installedPlugins.add(this.uppy.getPlugin(pluginId))
-    })
-  }
-
-  uninstall () {
-    for (const plugin of this.#installedPlugins) {
-      this.uppy.removePlugin(plugin)
-    }
-    this.#installedPlugins.clear()
-  }
-}

+ 15 - 4
packages/@uppy/remote-sources/src/index.test.js → packages/@uppy/remote-sources/src/index.test.ts

@@ -2,14 +2,17 @@ import { afterAll, beforeAll, describe, expect, it } from 'vitest'
 import resizeObserverPolyfill from 'resize-observer-polyfill'
 import Core from '@uppy/core'
 import Dashboard from '@uppy/dashboard'
-import RemoteSources from './index.js'
+import RemoteSources from './index.ts'
 
 describe('RemoteSources', () => {
   beforeAll(() => {
-    globalThis.ResizeObserver = resizeObserverPolyfill.default || resizeObserverPolyfill
+    globalThis.ResizeObserver =
+      // @ts-expect-error .default is fine
+      resizeObserverPolyfill.default || resizeObserverPolyfill
   })
 
   afterAll(() => {
+    // @ts-expect-error delete does not have to be conditional
     delete globalThis.ResizeObserver
   })
 
@@ -25,8 +28,13 @@ describe('RemoteSources', () => {
     expect(() => {
       const core = new Core()
       core.use(Dashboard)
+      // @ts-expect-error companionUrl is missing
       core.use(RemoteSources, { sources: ['Webcam'] })
-    }).toThrow(new Error('Please specify companionUrl for RemoteSources to work, see https://uppy.io/docs/remote-sources#companionUrl'))
+    }).toThrow(
+      new Error(
+        'Please specify companionUrl for RemoteSources to work, see https://uppy.io/docs/remote-sources#companionUrl',
+      ),
+    )
   })
 
   it('should throw when trying to use a plugin which is not included in RemoteSources', () => {
@@ -35,8 +43,11 @@ describe('RemoteSources', () => {
       core.use(Dashboard)
       core.use(RemoteSources, {
         companionUrl: 'https://example.com',
+        // @ts-expect-error test invalid
         sources: ['Webcam'],
       })
-    }).toThrow('Invalid plugin: "Webcam" is not one of: Box, Dropbox, Facebook, GoogleDrive, Instagram, OneDrive, Unsplash, Url, or Zoom.')
+    }).toThrow(
+      'Invalid plugin: "Webcam" is not one of: Box, Dropbox, Facebook, GoogleDrive, Instagram, OneDrive, Unsplash, Url, or Zoom.',
+    )
   })
 })

+ 105 - 0
packages/@uppy/remote-sources/src/index.ts

@@ -0,0 +1,105 @@
+import {
+  BasePlugin,
+  Uppy,
+  type UIPluginOptions,
+  type UnknownProviderPlugin,
+} from '@uppy/core'
+import Dropbox from '@uppy/dropbox'
+import GoogleDrive from '@uppy/google-drive'
+import Instagram from '@uppy/instagram'
+import Facebook from '@uppy/facebook'
+import OneDrive from '@uppy/onedrive'
+import Box from '@uppy/box'
+import Unsplash from '@uppy/unsplash'
+import Url from '@uppy/url'
+import Zoom from '@uppy/zoom'
+
+import type { DefinePluginOpts } from '@uppy/core/lib/BasePlugin'
+import type { Body, Meta } from '../../utils/src/UppyFile'
+// eslint-disable-next-line @typescript-eslint/ban-ts-comment
+// @ts-ignore We don't want TS to generate types for the package.json
+import packageJson from '../package.json'
+
+const availablePlugins = {
+  // Using a null-prototype object to avoid prototype pollution.
+  __proto__: null,
+  Box,
+  Dropbox,
+  Facebook,
+  GoogleDrive,
+  Instagram,
+  OneDrive,
+  Unsplash,
+  Url,
+  Zoom,
+}
+
+export interface RemoteSourcesOptions extends UIPluginOptions {
+  sources?: Array<keyof Omit<typeof availablePlugins, '__proto__'>>
+  companionUrl: string
+}
+
+const defaultOptions = {
+  sources: Object.keys(availablePlugins) as Array<
+    keyof Omit<typeof availablePlugins, '__proto__'>
+  >,
+} satisfies Partial<RemoteSourcesOptions>
+
+type Opts = DefinePluginOpts<RemoteSourcesOptions, keyof typeof defaultOptions>
+
+export default class RemoteSources<
+  M extends Meta,
+  B extends Body,
+> extends BasePlugin<Opts, M, B> {
+  static VERSION = packageJson.version
+
+  #installedPlugins: Set<UnknownProviderPlugin<M, B>> = new Set()
+
+  constructor(uppy: Uppy<M, B>, opts: RemoteSourcesOptions) {
+    super(uppy, { ...defaultOptions, ...opts })
+    this.id = this.opts.id || 'RemoteSources'
+    this.type = 'preset'
+
+    if (this.opts.companionUrl == null) {
+      throw new Error(
+        'Please specify companionUrl for RemoteSources to work, see https://uppy.io/docs/remote-sources#companionUrl',
+      )
+    }
+  }
+
+  setOptions(newOpts: Partial<Opts>): void {
+    this.uninstall()
+    super.setOptions(newOpts)
+    this.install()
+  }
+
+  install(): void {
+    this.opts.sources.forEach((pluginId) => {
+      const optsForRemoteSourcePlugin = { ...this.opts, sources: undefined }
+      const plugin = availablePlugins[pluginId]
+      if (plugin == null) {
+        const pluginNames = Object.keys(availablePlugins)
+        const formatter = new Intl.ListFormat('en', {
+          style: 'long',
+          type: 'disjunction',
+        })
+        throw new Error(
+          `Invalid plugin: "${pluginId}" is not one of: ${formatter.format(pluginNames)}.`,
+        )
+      }
+      this.uppy.use(plugin, optsForRemoteSourcePlugin)
+      // `plugin` is a class, but we want to track the instance object
+      // so we have to do `getPlugin` here.
+      this.#installedPlugins.add(
+        this.uppy.getPlugin(pluginId) as UnknownProviderPlugin<M, B>,
+      )
+    })
+  }
+
+  uninstall(): void {
+    for (const plugin of this.#installedPlugins) {
+      this.uppy.removePlugin(plugin)
+    }
+    this.#installedPlugins.clear()
+  }
+}

+ 71 - 0
packages/@uppy/remote-sources/tsconfig.build.json

@@ -0,0 +1,71 @@
+{
+  "extends": "../../../tsconfig.shared",
+  "compilerOptions": {
+    "noImplicitAny": false,
+    "outDir": "./lib",
+    "paths": {
+      "@uppy/box": ["../box/src/index.js"],
+      "@uppy/box/lib/*": ["../box/src/*"],
+      "@uppy/dashboard": ["../dashboard/src/index.js"],
+      "@uppy/dashboard/lib/*": ["../dashboard/src/*"],
+      "@uppy/dropbox": ["../dropbox/src/index.js"],
+      "@uppy/dropbox/lib/*": ["../dropbox/src/*"],
+      "@uppy/facebook": ["../facebook/src/index.js"],
+      "@uppy/facebook/lib/*": ["../facebook/src/*"],
+      "@uppy/google-drive": ["../google-drive/src/index.js"],
+      "@uppy/google-drive/lib/*": ["../google-drive/src/*"],
+      "@uppy/instagram": ["../instagram/src/index.js"],
+      "@uppy/instagram/lib/*": ["../instagram/src/*"],
+      "@uppy/onedrive": ["../onedrive/src/index.js"],
+      "@uppy/onedrive/lib/*": ["../onedrive/src/*"],
+      "@uppy/unsplash": ["../unsplash/src/index.js"],
+      "@uppy/unsplash/lib/*": ["../unsplash/src/*"],
+      "@uppy/url": ["../url/src/index.js"],
+      "@uppy/url/lib/*": ["../url/src/*"],
+      "@uppy/zoom": ["../zoom/src/index.js"],
+      "@uppy/zoom/lib/*": ["../zoom/src/*"],
+      "@uppy/core": ["../core/src/index.js"],
+      "@uppy/core/lib/*": ["../core/src/*"]
+    },
+    "resolveJsonModule": false,
+    "rootDir": "./src",
+    "skipLibCheck": true
+  },
+  "include": ["./src/**/*.*"],
+  "exclude": ["./src/**/*.test.ts"],
+  "references": [
+    {
+      "path": "../box/tsconfig.build.json"
+    },
+    {
+      "path": "../dashboard/tsconfig.build.json"
+    },
+    {
+      "path": "../dropbox/tsconfig.build.json"
+    },
+    {
+      "path": "../facebook/tsconfig.build.json"
+    },
+    {
+      "path": "../google-drive/tsconfig.build.json"
+    },
+    {
+      "path": "../instagram/tsconfig.build.json"
+    },
+    {
+      "path": "../onedrive/tsconfig.build.json"
+    },
+    {
+      "path": "../unsplash/tsconfig.build.json"
+    },
+    {
+      "path": "../url/tsconfig.build.json"
+    },
+    {
+      "path": "../zoom/tsconfig.build.json"
+    },
+    {
+      "path": "../core/tsconfig.build.json"
+    }
+  ]
+}

+ 67 - 0
packages/@uppy/remote-sources/tsconfig.json

@@ -0,0 +1,67 @@
+{
+  "extends": "../../../tsconfig.shared",
+  "compilerOptions": {
+    "emitDeclarationOnly": false,
+    "noEmit": true,
+    "paths": {
+      "@uppy/box": ["../box/src/index.js"],
+      "@uppy/box/lib/*": ["../box/src/*"],
+      "@uppy/dashboard": ["../dashboard/src/index.js"],
+      "@uppy/dashboard/lib/*": ["../dashboard/src/*"],
+      "@uppy/dropbox": ["../dropbox/src/index.js"],
+      "@uppy/dropbox/lib/*": ["../dropbox/src/*"],
+      "@uppy/facebook": ["../facebook/src/index.js"],
+      "@uppy/facebook/lib/*": ["../facebook/src/*"],
+      "@uppy/google-drive": ["../google-drive/src/index.js"],
+      "@uppy/google-drive/lib/*": ["../google-drive/src/*"],
+      "@uppy/instagram": ["../instagram/src/index.js"],
+      "@uppy/instagram/lib/*": ["../instagram/src/*"],
+      "@uppy/onedrive": ["../onedrive/src/index.js"],
+      "@uppy/onedrive/lib/*": ["../onedrive/src/*"],
+      "@uppy/unsplash": ["../unsplash/src/index.js"],
+      "@uppy/unsplash/lib/*": ["../unsplash/src/*"],
+      "@uppy/url": ["../url/src/index.js"],
+      "@uppy/url/lib/*": ["../url/src/*"],
+      "@uppy/zoom": ["../zoom/src/index.js"],
+      "@uppy/zoom/lib/*": ["../zoom/src/*"],
+      "@uppy/core": ["../core/src/index.js"],
+      "@uppy/core/lib/*": ["../core/src/*"],
+    },
+  },
+  "include": ["./package.json", "./src/**/*.*"],
+  "references": [
+    {
+      "path": "../box/tsconfig.build.json",
+    },
+    {
+      "path": "../dashboard/tsconfig.build.json",
+    },
+    {
+      "path": "../dropbox/tsconfig.build.json",
+    },
+    {
+      "path": "../facebook/tsconfig.build.json",
+    },
+    {
+      "path": "../google-drive/tsconfig.build.json",
+    },
+    {
+      "path": "../instagram/tsconfig.build.json",
+    },
+    {
+      "path": "../onedrive/tsconfig.build.json",
+    },
+    {
+      "path": "../unsplash/tsconfig.build.json",
+    },
+    {
+      "path": "../url/tsconfig.build.json",
+    },
+    {
+      "path": "../zoom/tsconfig.build.json",
+    },
+    {
+      "path": "../core/tsconfig.build.json",
+    },
+  ],
+}