diff --git a/src/file-selector.spec.ts b/src/file-selector.spec.ts index d1a58e3..cf97813 100644 --- a/src/file-selector.spec.ts +++ b/src/file-selector.spec.ts @@ -312,13 +312,13 @@ it("should throw if reading file entry fails", (done) => { .catch(() => done()); }); -it("should throw if DataTransferItem is not a File", (done) => { +it("should throw if DataTransferItem is not a File", async () => { const item = dataTransferItem(null, "file"); - const evt = dragEvtFromFilesAndItems([], [item]); + const event = dragEvtFromFilesAndItems([], [item]); - fromEvent(evt) - .then(() => done.fail("Getting the files should have failed")) - .catch(() => done()); + await expect(fromEvent(event)).rejects.toThrow( + "Transferred item is not a file.", + ); }); it("should use getAsFileSystemHandle when available", async () => { @@ -344,6 +344,19 @@ it("should use getAsFileSystemHandle when available", async () => { expect(file.path).toBe(`./${name}`); }); +it("should throw if getAsFileSystemHandle does not return a file", (done) => { + const name = "test.json"; + const data = { ping: true }; + const options = { type: "application/json" }; + const file = createFile(name, data, options); + const handle = { kind: "unknown" } as unknown as FileSystemFileHandle; + const evt = dragEvtFromItems([dataTransferItemWithFsHandle(file, handle)]); + + fromEvent(evt) + .then(() => done.fail("Getting the files should have failed")) + .catch(() => done()); +}); + function dragEvtFromItems( items: DataTransferItem | DataTransferItem[], type: string = "drop", @@ -519,11 +532,11 @@ function createFileSystemFileHandle( data: T, options?: FilePropertyBag, ): [File, FileSystemFileHandle] { - const json = JSON.stringify(data); - const file = new File([json], name, options); + const file = createFile(name, data, options); return [ file, { + kind: "file", getFile() { return Promise.resolve(file); }, @@ -536,6 +549,7 @@ function sortFiles(files: T[]) { } interface FileSystemFileHandle { + kind: "file"; getFile(): Promise; } diff --git a/src/file-selector.ts b/src/file-selector.ts index 74963e1..deeb8a6 100644 --- a/src/file-selector.ts +++ b/src/file-selector.ts @@ -105,23 +105,29 @@ function flatten(items: any[]): T[] { ); } -function fromDataTransferItem( +async function fromDataTransferItem( item: DataTransferItem, entry?: FileSystemEntry | null, -) { - if (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); - }); +): Promise { + if (typeof item.getAsFileSystemHandle === "function") { + const handle = await item.getAsFileSystemHandle(); + + if (!handle || !isFileHandle(handle)) { + throw new Error("Transferred item is not a file."); + } + + const file = await handle.getFile(); + (file as any).handle = handle; + return toFileWithPath(file); } + const file = item.getAsFile(); + if (!file) { - return Promise.reject(`${item} is not a File`); + throw new Error("Transferred item is not a file."); } - const fwp = toFileWithPath(file, entry?.fullPath ?? undefined); - return Promise.resolve(fwp); + + return toFileWithPath(file, entry?.fullPath); } async function fromEntry( @@ -165,6 +171,10 @@ async function fromFileEntry( return fileWithPath; } +const isFileHandle = ( + handle: FileSystemHandle, +): handle is FileSystemFileHandle => handle.kind === "file"; + const isDirectoryEntry = ( entry: FileSystemEntry, ): entry is FileSystemDirectoryEntry => entry.isDirectory; @@ -180,6 +190,14 @@ const readEntries = ( ): Promise => new Promise((resolve, reject) => reader.readEntries(resolve, reject)); +declare global { + interface DataTransferItem { + // This method is not yet widely supported in all browsers, and is thus marked as optional. + // See: https://developer.mozilla.org/en-US/docs/Web/API/DataTransferItem/getAsFileSystemHandle + getAsFileSystemHandle?(): Promise; + } +} + // Infinite type recursion // https://github.com/Microsoft/TypeScript/issues/3496#issuecomment-128553540 interface FileArray extends Array {}