From e5a197fa5d90a2ccdd17d0319682327bf7e8e4a3 Mon Sep 17 00:00:00 2001 From: Roland Groza Date: Tue, 26 Nov 2024 21:07:27 +0900 Subject: [PATCH 1/2] chore: use async/await in fromDataTransferItem --- src/file-selector.ts | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/file-selector.ts b/src/file-selector.ts index bfee1f7..1960f7b 100644 --- a/src/file-selector.ts +++ b/src/file-selector.ts @@ -105,7 +105,7 @@ function flatten(items: any[]): T[] { ); } -function fromDataTransferItem( +async function fromDataTransferItem( item: DataTransferItem, entry?: FileSystemEntry | null, ) { @@ -118,18 +118,17 @@ function fromDataTransferItem( globalThis.isSecureContext && typeof (item as any).getAsFileSystemHandle === "function" ) { - return (item as any).getAsFileSystemHandle().then(async (h: any) => { - const file = await h.getFile(); - file.handle = h; - return toFileWithPath(file); - }); + const h = await (item as any).getAsFileSystemHandle(); + const file = await h.getFile(); + file.handle = h; + return toFileWithPath(file); } const file = item.getAsFile(); if (!file) { - return Promise.reject(`${item} is not a File`); + throw new Error(`${item} is not a File`); } const fwp = toFileWithPath(file, entry?.fullPath ?? undefined); - return Promise.resolve(fwp); + return fwp; } async function fromEntry( From f166285204156f94a8adf491825696448829f165 Mon Sep 17 00:00:00 2001 From: Roland Groza Date: Tue, 26 Nov 2024 21:20:14 +0900 Subject: [PATCH 2/2] fix: handle getFile returning null and close #106, close #120 --- src/file-selector.spec.ts | 37 ++++++++++++++++++++++++++++++++++--- src/file-selector.ts | 13 ++++++++++--- 2 files changed, 44 insertions(+), 6 deletions(-) diff --git a/src/file-selector.spec.ts b/src/file-selector.spec.ts index 8fc85b1..f8e4e0e 100644 --- a/src/file-selector.spec.ts +++ b/src/file-selector.spec.ts @@ -379,6 +379,37 @@ it("should not use getAsFileSystemHandle when not in a secure context", async () globalThis.isSecureContext = true; }); +it("should reject when getAsFileSystemHandle resolves to null", async () => { + const evt = dragEvtFromItems([dataTransferItemWithFsHandle(null, null)]); + expect(fromEvent(evt)).rejects.toThrow("[object Object] is not a File"); +}); + +it("should fallback to getAsFile when getAsFileSystemHandle resolves to undefined", async () => { + const name = "test.nosec.json"; + const mockFile = createFile( + name, + { ping: false }, + { + type: "application/json", + }, + ); + const evt = dragEvtFromItems([ + dataTransferItemWithFsHandle(mockFile, undefined), + ]); + + const files = await fromEvent(evt); + expect(files).toHaveLength(1); + expect(files.every((file) => file instanceof File)).toBe(true); + + const [file] = files as FileWithPath[]; + + expect(file.name).toBe(mockFile.name); + expect(file.type).toBe(mockFile.type); + expect(file.size).toBe(mockFile.size); + expect(file.lastModified).toBe(mockFile.lastModified); + expect(file.path).toBe(`./${name}`); +}); + function dragEvtFromItems( items: DataTransferItem | DataTransferItem[], type: string = "drop", @@ -456,8 +487,8 @@ function dataTransferItemFromEntry( } function dataTransferItemWithFsHandle( - file?: File, - h?: FileSystemFileHandle, + file?: File | null, + h?: FileSystemFileHandle | null, ): DataTransferItem { return { kind: "file", @@ -570,7 +601,7 @@ function sortFiles(files: T[]) { } interface FileSystemFileHandle { - getFile(): Promise; + getFile(): Promise; } type FileOrDirEntry = FileEntry | DirEntry; diff --git a/src/file-selector.ts b/src/file-selector.ts index 1960f7b..c108a2a 100644 --- a/src/file-selector.ts +++ b/src/file-selector.ts @@ -119,9 +119,16 @@ async function fromDataTransferItem( typeof (item as any).getAsFileSystemHandle === "function" ) { const h = await (item as any).getAsFileSystemHandle(); - const file = await h.getFile(); - file.handle = h; - return toFileWithPath(file); + if (h === null) { + throw new Error(`${item} is not a File`); + } + // It seems that the handle can be `undefined` (see https://github.com/react-dropzone/file-selector/issues/120), + // so we check if it isn't; if it is, the code path continues to the next API (`getAsFile`). + if (h !== undefined) { + const file = await h.getFile(); + file.handle = h; + return toFileWithPath(file); + } } const file = item.getAsFile(); if (!file) {