Browse Source

Introduce `ValidateableFile` & move `MinimalRequiredUppyFile` into utils (#4944)

Antoine du Hamel 1 year ago
parent
commit
36b55b29a7

+ 6 - 2
packages/@uppy/audio/src/Audio.tsx

@@ -1,8 +1,12 @@
 import { h } from 'preact'
 
 import { UIPlugin, type UIPluginOptions } from '@uppy/core'
-import type { Body, Meta } from '@uppy/utils/lib/UppyFile'
-import type { Uppy, MinimalRequiredUppyFile } from '@uppy/core/lib/Uppy.ts'
+import type {
+  Body,
+  Meta,
+  MinimalRequiredUppyFile,
+} from '@uppy/utils/lib/UppyFile'
+import type { Uppy } from '@uppy/core/lib/Uppy.ts'
 
 import getFileTypeExtension from '@uppy/utils/lib/getFileTypeExtension'
 import supportsMediaRecorder from './supportsMediaRecorder.ts'

+ 19 - 9
packages/@uppy/core/src/Restricter.ts

@@ -18,6 +18,16 @@ export type Restrictions = {
   requiredMetaFields: string[]
 }
 
+/**
+ * The minimal required properties to be present from UppyFile in order to validate it.
+ */
+export type ValidateableFile<M extends Meta, B extends Body> = Pick<
+  UppyFile<M, B>,
+  'type' | 'extension' | 'size' | 'name'
+  // Both UppyFile and CompanionFile need to be passable as a ValidateableFile
+  // CompanionFile's do not have `isGhost`, so we mark it optional.
+> & { isGhost?: boolean }
+
 const defaultOptions = {
   maxFileSize: null,
   minFileSize: null,
@@ -69,8 +79,8 @@ class Restricter<M extends Meta, B extends Body> {
 
   // Because these operations are slow, we cannot run them for every file (if we are adding multiple files)
   validateAggregateRestrictions(
-    existingFiles: UppyFile<M, B>[],
-    addingFiles: UppyFile<M, B>[],
+    existingFiles: ValidateableFile<M, B>[],
+    addingFiles: ValidateableFile<M, B>[],
   ): void {
     const { maxTotalFileSize, maxNumberOfFiles } = this.getOpts().restrictions
 
@@ -109,7 +119,7 @@ class Restricter<M extends Meta, B extends Body> {
     }
   }
 
-  validateSingleFile(file: UppyFile<M, B>): void {
+  validateSingleFile(file: ValidateableFile<M, B>): void {
     const { maxFileSize, minFileSize, allowedFileTypes } =
       this.getOpts().restrictions
 
@@ -134,7 +144,7 @@ class Restricter<M extends Meta, B extends Body> {
           this.i18n('youCanOnlyUploadFileTypes', {
             types: allowedFileTypesString,
           }),
-          { file },
+          { file } as { file: UppyFile<M, B> },
         )
       }
     }
@@ -146,7 +156,7 @@ class Restricter<M extends Meta, B extends Body> {
           size: prettierBytes(maxFileSize),
           file: file.name,
         }),
-        { file },
+        { file } as { file: UppyFile<M, B> },
       )
     }
 
@@ -156,14 +166,14 @@ class Restricter<M extends Meta, B extends Body> {
         this.i18n('inferiorSize', {
           size: prettierBytes(minFileSize),
         }),
-        { file },
+        { file } as { file: UppyFile<M, B> },
       )
     }
   }
 
   validate(
-    existingFiles: UppyFile<M, B>[],
-    addingFiles: UppyFile<M, B>[],
+    existingFiles: ValidateableFile<M, B>[],
+    addingFiles: ValidateableFile<M, B>[],
   ): void {
     addingFiles.forEach((addingFile) => {
       this.validateSingleFile(addingFile)
@@ -180,7 +190,7 @@ class Restricter<M extends Meta, B extends Body> {
     }
   }
 
-  getMissingRequiredMetaFields(file: UppyFile<M, B>): {
+  getMissingRequiredMetaFields(file: ValidateableFile<M, B> & { meta: M }): {
     missingFields: string[]
     error: RestrictionError<M, B>
   } {

+ 15 - 18
packages/@uppy/core/src/Uppy.ts

@@ -13,7 +13,12 @@ import DefaultStore from '@uppy/store-default'
 import getFileType from '@uppy/utils/lib/getFileType'
 import getFileNameAndExtension from '@uppy/utils/lib/getFileNameAndExtension'
 import { getSafeFileId } from '@uppy/utils/lib/generateFileID'
-import type { UppyFile, Meta, Body } from '@uppy/utils/lib/UppyFile'
+import type {
+  UppyFile,
+  Meta,
+  Body,
+  MinimalRequiredUppyFile,
+} from '@uppy/utils/lib/UppyFile'
 import type { CompanionFile } from '@uppy/utils/lib/CompanionFile'
 import type {
   CompanionClientProvider,
@@ -43,7 +48,7 @@ import locale from './locale.ts'
 
 import type BasePlugin from './BasePlugin.ts'
 import type UIPlugin from './UIPlugin.ts'
-import type { Restrictions } from './Restricter.ts'
+import type { Restrictions, ValidateableFile } from './Restricter.ts'
 
 type Processor = (fileIDs: string[], uploadID: string) => Promise<void> | void
 
@@ -128,17 +133,6 @@ export type UnknownSearchProviderPlugin<
   provider: CompanionClientSearchProvider
 }
 
-// The user facing type for UppyFile used in uppy.addFile() and uppy.setOptions()
-export type MinimalRequiredUppyFile<M extends Meta, B extends Body> = Required<
-  Pick<UppyFile<M, B>, 'name' | 'data' | 'type' | 'source'>
-> &
-  Partial<
-    Omit<UppyFile<M, B>, 'name' | 'data' | 'type' | 'source' | 'meta'>
-    // We want to omit the 'meta' from UppyFile because of internal metadata
-    // (see InternalMetadata in `UppyFile.ts`), as when adding a new file
-    // that is not required.
-  > & { meta?: M }
-
 interface UploadResult<M extends Meta, B extends Body> {
   successful?: UppyFile<M, B>[]
   failed?: UppyFile<M, B>[]
@@ -844,8 +838,8 @@ export class Uppy<M extends Meta, B extends Body> {
   }
 
   validateRestrictions(
-    file: UppyFile<M, B>,
-    files = this.getFiles(),
+    file: ValidateableFile<M, B>,
+    files: ValidateableFile<M, B>[] = this.getFiles(),
   ): RestrictionError<M, B> | null {
     try {
       this.#restricter.validate(files, [file])
@@ -882,9 +876,12 @@ export class Uppy<M extends Meta, B extends Body> {
     const { allowNewUpload } = this.getState()
 
     if (allowNewUpload === false) {
-      const error = new RestrictionError(this.i18n('noMoreFilesAllowed'), {
-        file,
-      })
+      const error = new RestrictionError<M, B>(
+        this.i18n('noMoreFilesAllowed'),
+        {
+          file,
+        },
+      )
       this.#informAndEmit([error])
       throw error
     }

+ 14 - 2
packages/@uppy/utils/src/UppyFile.ts

@@ -23,15 +23,16 @@ export interface UppyFile<M extends Meta, B extends Body> {
   remote?: {
     body?: Record<string, unknown>
     companionUrl: string
-    host: string
+    host?: string
     provider?: string
+    providerName?: string
     requestClientId: string
     url: string
   }
   serverToken?: string | null
   size: number | null
   source?: string
-  type?: string
+  type: string
   uploadURL?: string
   response?: {
     body: B
@@ -40,3 +41,14 @@ export interface UppyFile<M extends Meta, B extends Body> {
     uploadURL?: string
   }
 }
+
+// The user facing type for UppyFile used in uppy.addFile() and uppy.setOptions()
+export type MinimalRequiredUppyFile<M extends Meta, B extends Body> = Required<
+  Pick<UppyFile<M, B>, 'name' | 'data'>
+> &
+  Partial<
+    Omit<UppyFile<M, B>, 'name' | 'data' | 'meta'>
+    // We want to omit the 'meta' from UppyFile because of internal metadata
+    // (see InternalMetadata in `UppyFile.ts`), as when adding a new file
+    // that is not required.
+  > & { meta?: M }

+ 7 - 5
packages/@uppy/utils/src/generateFileID.ts

@@ -1,4 +1,4 @@
-import type { UppyFile } from './UppyFile'
+import type { MinimalRequiredUppyFile } from './UppyFile'
 import getFileType from './getFileType.ts'
 
 function encodeCharacter(character: string): string {
@@ -19,7 +19,9 @@ function encodeFilename(name: string): string {
  * Takes a file object and turns it into fileID, by converting file.name to lowercase,
  * removing extra characters and adding type, size and lastModified
  */
-export default function generateFileID(file: UppyFile<any, any>): string {
+export default function generateFileID(
+  file: MinimalRequiredUppyFile<any, any>,
+): string {
   // It's tempting to do `[items].filter(Boolean).join('-')` here, but that
   // is slower! simple string concatenation is fast
 
@@ -48,7 +50,7 @@ export default function generateFileID(file: UppyFile<any, any>): string {
 
 // If the provider has a stable, unique ID, then we can use that to identify the file.
 // Then we don't have to generate our own ID, and we can add the same file many times if needed (different path)
-function hasFileStableId(file: UppyFile<any, any>): boolean {
+function hasFileStableId(file: MinimalRequiredUppyFile<any, any>): boolean {
   if (!file.isRemote || !file.remote) return false
   // These are the providers that it seems like have stable IDs for their files. The other's I haven't checked yet.
   const stableIdProviders = new Set([
@@ -61,8 +63,8 @@ function hasFileStableId(file: UppyFile<any, any>): boolean {
   return stableIdProviders.has(file.remote.provider as any)
 }
 
-export function getSafeFileId(file: UppyFile<any, any>): string {
-  if (hasFileStableId(file)) return file.id
+export function getSafeFileId(file: MinimalRequiredUppyFile<any, any>): string {
+  if (hasFileStableId(file)) return file.id!
 
   const fileType = getFileType(file)
 

+ 4 - 2
packages/@uppy/utils/src/getFileType.ts

@@ -1,8 +1,10 @@
-import type { UppyFile } from './UppyFile'
 import getFileNameAndExtension from './getFileNameAndExtension.ts'
 import mimeTypes from './mimeTypes.ts'
 
-export default function getFileType(file: Partial<UppyFile<any, any>>): string {
+export default function getFileType(file: {
+  type?: string
+  name?: string
+}): string {
   if (file.type) return file.type
 
   const fileExtension =