diff --git a/src/file-selector.spec.ts b/src/file-selector.spec.ts index a590035..d64aee8 100644 --- a/src/file-selector.spec.ts +++ b/src/file-selector.spec.ts @@ -259,6 +259,26 @@ it('should throw if DataTransferItem is not a File', done => { .catch(() => done()); }); +it('should use getAsFileSystemHandle when available', async () => { + const name = 'test.json'; + const [f, h] = createFileSystemFileHandle(name, {ping: true}, { + type: 'application/json' + }); + const evt = dragEvtFromItems([ + dataTransferItemWithFsHandle(f, h) + ]); + 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(f.name); + expect(file.type).toBe(f.type); + expect(file.size).toBe(f.size); + expect(file.lastModified).toBe(f.lastModified); + expect(file.path).toBe(name); +}); function dragEvtFromItems(items: DataTransferItem | DataTransferItem[], type: string = 'drop'): DragEvent { return { @@ -330,6 +350,18 @@ function dataTransferItemFromEntry(entry: FileEntry | DirEntry, file?: File): Da } as any; } +function dataTransferItemWithFsHandle(file?: File, h?: FileSystemFileHandle): DataTransferItem { + return { + kind: 'file', + getAsFile() { + return file; + }, + getAsFileSystemHandle() { + return Promise.resolve(h); + } + } as any; +} + function fileSystemFileEntryFromFile(file: File, err?: any): FileEntry { return { isDirectory: false, diff --git a/src/file-selector.ts b/src/file-selector.ts index 31b6b36..f495d66 100644 --- a/src/file-selector.ts +++ b/src/file-selector.ts @@ -121,6 +121,14 @@ function flatten(items: any[]): T[] { } function fromDataTransferItem(item: DataTransferItem) { + 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); + }); + } const file = item.getAsFile(); if (!file) { return Promise.reject(`${item} is not a File`); diff --git a/src/file.spec.ts b/src/file.spec.ts index 09dd674..72c8f91 100644 --- a/src/file.spec.ts +++ b/src/file.spec.ts @@ -40,7 +40,7 @@ describe('toFile()', () => { expect(Object.keys(fileWithPath)).toContain('path'); - const keys = []; + const keys: string[] = []; for (const key in fileWithPath) { keys.push(key); } @@ -83,7 +83,7 @@ describe('toFile()', () => { expect(Object.keys(fileWithPath)).toContain('type'); - const keys = []; + const keys: string[] = []; for (const key in fileWithPath) { keys.push(key); } @@ -123,4 +123,38 @@ describe('toFile()', () => { reader.readAsText(fileWithPath); }); + + it('sets the {handle} if provided', () => { + const path = '/test/test.json'; + const file = new File([], 'test.json'); + const fileWithHandle = toFileWithPath(file, path, fsHandleFromFile(file)); + expect(fileWithHandle.handle).toBeDefined(); + expect(fileWithHandle.handle?.name).toEqual(file.name); + }); + + test('{handle} is enumerable', () => { + const path = '/test/test.json'; + const file = new File([], 'test.json'); + const fileWithHandle = toFileWithPath(file, path, fsHandleFromFile(file)); + + expect(Object.keys(fileWithHandle)).toContain('handle'); + + const keys: string[] = []; + for (const key in fileWithHandle) { + keys.push(key); + } + + expect(keys).toContain('handle'); + }); + }); + +function fsHandleFromFile(f: File): FileSystemHandle { + return { + kind: 'file', + name: f.name, + isSameEntry() { + return Promise.resolve(false) + } + } +} diff --git a/src/file.ts b/src/file.ts index 0896ed6..d022d2b 100644 --- a/src/file.ts +++ b/src/file.ts @@ -1201,7 +1201,7 @@ export const COMMON_MIME_TYPES = new Map([ ]); -export function toFileWithPath(file: FileWithPath, path?: string): FileWithPath { +export function toFileWithPath(file: FileWithPath, path?: string, h?: FileSystemHandle): FileWithPath { const f = withMimeType(file); if (typeof f.path !== 'string') { // on electron, path is already set to the absolute path const {webkitRelativePath} = file; @@ -1219,12 +1219,20 @@ export function toFileWithPath(file: FileWithPath, path?: string): FileWithPath enumerable: true }); } - + if (h !== undefined) { + Object.defineProperty(f, 'handle', { + value: h, + writable: false, + configurable: false, + enumerable: true + }); + } return f; } export interface FileWithPath extends File { readonly path?: string; + readonly handle?: FileSystemFileHandle; } function withMimeType(file: FileWithPath) {