From 06934f02ced38989d3e300f5bd2c34dde87bc767 Mon Sep 17 00:00:00 2001 From: Roland Groza Date: Sat, 11 Jul 2026 15:44:29 +0900 Subject: [PATCH] feat: use a custom error when items are not files Throw a dedicated UnexpectedObjectError (carrying the offending DataTransferItem) instead of a generic Error that stringified to the useless "[object Object] is not a File". --- .oxlintrc.json | 4 +--- src/error.spec.ts | 17 +++++++++++++++++ src/error.ts | 8 ++++++++ src/file-selector.spec.ts | 2 +- src/file-selector.ts | 5 +++-- 5 files changed, 30 insertions(+), 6 deletions(-) create mode 100644 src/error.spec.ts create mode 100644 src/error.ts diff --git a/.oxlintrc.json b/.oxlintrc.json index b14181a..e11a15a 100644 --- a/.oxlintrc.json +++ b/.oxlintrc.json @@ -9,9 +9,7 @@ "typescript/no-non-null-assertion": "off", "typescript/no-floating-promises": "error", "typescript/no-redundant-type-constituents": "off", - "typescript/require-array-sort-compare": "off", - "typescript/no-base-to-string": "off", - "typescript/restrict-template-expressions": "off" + "typescript/require-array-sort-compare": "off" }, "overrides": [ { diff --git a/src/error.spec.ts b/src/error.spec.ts new file mode 100644 index 0000000..d69e5b2 --- /dev/null +++ b/src/error.spec.ts @@ -0,0 +1,17 @@ +import {UnexpectedObjectError} from './error'; + +describe('UnexpectedObjectError', () => { + it('works as expected', () => { + const item = {it: 'works'}; + try { + throw new UnexpectedObjectError(item as any); + } catch (e: any) { + expect(e.name).toEqual('UnexpectedObjectError'); + expect(e.toString()).toEqual('UnexpectedObjectError: DataTransferItem is not a file'); + expect(e).toBeInstanceOf(Error); + expect(e).toBeInstanceOf(UnexpectedObjectError); + expect(e.item).toEqual(item); + expect(e.stack).toBeDefined(); + } + }); +}); diff --git a/src/error.ts b/src/error.ts new file mode 100644 index 0000000..ba27fa0 --- /dev/null +++ b/src/error.ts @@ -0,0 +1,8 @@ +export class UnexpectedObjectError extends Error { + item: DataTransferItem; + constructor(item: DataTransferItem) { + super('DataTransferItem is not a file'); + this.item = item; + this.name = 'UnexpectedObjectError'; + } +} diff --git a/src/file-selector.spec.ts b/src/file-selector.spec.ts index 71b3a1b..5c6f586 100644 --- a/src/file-selector.spec.ts +++ b/src/file-selector.spec.ts @@ -366,7 +366,7 @@ it('should not use getAsFileSystemHandle when not in a secure context', async () 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'); + expect(fromEvent(evt)).rejects.toThrow('DataTransferItem is not a file'); }); it('should fallback to getAsFile when getAsFileSystemHandle resolves to undefined', async () => { diff --git a/src/file-selector.ts b/src/file-selector.ts index 2d1db73..8331e5e 100644 --- a/src/file-selector.ts +++ b/src/file-selector.ts @@ -1,3 +1,4 @@ +import {UnexpectedObjectError} from './error'; import {type FileWithPath, toFileWithPath} from './file'; const FILES_TO_IGNORE = [ @@ -129,7 +130,7 @@ async function fromDataTransferItem(item: DataTransferItem, entry?: FileSystemEn if (globalThis.isSecureContext && typeof (item as any).getAsFileSystemHandle === 'function') { const h = await (item as any).getAsFileSystemHandle(); if (h === null) { - throw new Error(`${item} is not a File`); + throw new UnexpectedObjectError(item); } // 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`). @@ -141,7 +142,7 @@ async function fromDataTransferItem(item: DataTransferItem, entry?: FileSystemEn } const file = item.getAsFile(); if (!file) { - throw new Error(`${item} is not a File`); + throw new UnexpectedObjectError(item); } const fwp = toFileWithPath(file, entry?.fullPath ?? undefined); return fwp;