Skip to content

Commit

Permalink
feat: support copy file/dir to FileSystemFileHandle/FileSystemDirecto…
Browse files Browse the repository at this point in the history
…ryHandle, remove return type for 'copyTo' and 'moveTo' [BREAKING]
  • Loading branch information
hughfenghen committed Jan 1, 2025
1 parent 7bc7124 commit 1b0844e
Show file tree
Hide file tree
Showing 5 changed files with 83 additions and 28 deletions.
5 changes: 5 additions & 0 deletions .changeset/dry-planets-burn.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'opfs-tools': minor
---

feat: support copy file/dir to FileSystemFileHandle/FileSystemDirectoryHandle, remove return type for 'copyTo' and 'moveTo' [BREAKING]
15 changes: 15 additions & 0 deletions src/__tests__/directory.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { afterEach, expect, test } from 'vitest';
import { dir } from '../directory';
import { file, write } from '../file';
import { joinPath } from '../common';

const filePath = '/unit-test/dir';

Expand Down Expand Up @@ -74,3 +75,17 @@ test('remove dir', async () => {
await d.remove();
expect(await d.exists()).toBe(false);
});

test('copy to dir handle', async () => {
const d = dir(filePath);
await write(joinPath(filePath, 'a'), 'aaa');
const dirHandle = await (
await navigator.storage.getDirectory()
).getDirectoryHandle('abc', { create: true });
await d.copyTo(dirHandle);

expect(
await (await (await dirHandle.getFileHandle('a')).getFile()).text()
).toBe('aaa');
await dir('/abc').remove();
});
35 changes: 24 additions & 11 deletions src/__tests__/file.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -167,9 +167,10 @@ test('file exists', async () => {

test('move file', async () => {
await write(filePath, 'foo');
const target = await file(filePath).moveTo(dir('/'));
const root = dir('/');
await file(filePath).moveTo(root);
expect(await file(filePath).exists()).toBe(false);
await target.remove();
await root.remove();
});

test('move file, current file not exists', async () => {
Expand All @@ -181,27 +182,39 @@ test('move file, current file not exists', async () => {
test('copy file to dir', async () => {
await write(filePath, 'foo');
const oldFile = file(filePath);
const copyed = await oldFile.copyTo(dir('/'));
expect(copyed.path).toBe(`/${oldFile.name}`);
const root = dir('/');
await oldFile.copyTo(root);
const newFile = file(`/${oldFile.name}`);
expect(await newFile.exists()).toBe(true);
expect(await oldFile.exists()).toBe(true);

await copyed.remove();
await newFile.remove();
});

test('copy file to another file', async () => {
await write(filePath, 'foo');
const oldFile = file(filePath);
let copyed = await oldFile.copyTo(file(filePath));
// selft
expect(copyed === oldFile).toBe(true);

copyed = await oldFile.copyTo(file('/abc'));
expect(copyed.path).toBe('/abc');
expect(await copyed.text()).toBe('foo');
const newFile = file('/abc');
await oldFile.copyTo(newFile);
expect(newFile.path).toBe('/abc');
expect(await newFile.text()).toBe('foo');

await file('/abc').remove();
});

test('copy to file handle', async () => {
await write(filePath, 'foo');
const oldFile = file(filePath);
const newFileHandle = await (
await navigator.storage.getDirectory()
).getFileHandle('bar', { create: true });

await oldFile.copyTo(newFileHandle);
expect(await (await newFileHandle.getFile()).text()).toBe('foo');
await file('/bar').remove();
});

test('close reader twice', async () => {
await write(filePath, 'foo');
const f = file(filePath);
Expand Down
40 changes: 29 additions & 11 deletions src/directory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -125,26 +125,44 @@ export class OPFSDirWrap {
* If the dest folder exists, copy the current directory into the dest folder;
* if the dest folder does not exist, rename the current directory to dest name.
*/
async copyTo(dest: OPFSDirWrap) {
async copyTo(dest: OPFSDirWrap | FileSystemDirectoryHandle) {
if (!(await this.exists())) {
throw Error(`dir ${this.path} not exists`);
}
const newDir = (await dest.exists())
? dir(joinPath(dest.path, this.name))
: dest;
await newDir.create();
await Promise.all((await this.children()).map((it) => it.copyTo(newDir)));

return newDir;
if (dest instanceof OPFSDirWrap) {
const newDir = (await dest.exists())
? dir(joinPath(dest.path, this.name))
: dest;
await newDir.create();
await Promise.all((await this.children()).map((it) => it.copyTo(newDir)));
return;
} else if (dest instanceof FileSystemDirectoryHandle) {
await Promise.all(
(
await this.children()
).map(async (it) => {
if (it.kind === 'file') {
await it.copyTo(
await dest.getFileHandle(it.name, { create: true })
);
} else {
await it.copyTo(
await dest.getDirectoryHandle(it.name, { create: true })
);
}
})
);
return;
}
throw Error('Illegal target type');
}

/**
* move directory, copy then remove current
*/
async moveTo(dest: OPFSDirWrap): Promise<OPFSDirWrap> {
const newDir = await this.copyTo(dest);
async moveTo(dest: OPFSDirWrap): Promise<void> {
await this.copyTo(dest);
await this.remove();

return newDir;
}
}
16 changes: 10 additions & 6 deletions src/file.ts
Original file line number Diff line number Diff line change
Expand Up @@ -287,27 +287,31 @@ export class OPFSFileWrap {
* If the target is a file, use current overwrite the target;
* if the target is a folder, copy the current file into that folder.
*/
async copyTo(target: OPFSDirWrap | OPFSFileWrap): Promise<OPFSFileWrap> {
async copyTo(
target: OPFSDirWrap | OPFSFileWrap | FileSystemFileHandle
): Promise<void> {
if (target instanceof OPFSFileWrap) {
if (target.path === this.path) return this;
if (target.path === this.path) return;

await write(target.path, this);
return file(target.path);
return;
} else if (target instanceof OPFSDirWrap) {
if (!(await this.exists())) {
throw Error(`file ${this.path} not exists`);
}
return await this.copyTo(file(joinPath(target.path, this.name)));
} else if (target instanceof FileSystemFileHandle) {
await (await this.stream()).pipeTo(await target.createWritable());
return;
}
throw Error('Illegal target type');
}

/**
* move file, copy then remove current
*/
async moveTo(target: OPFSDirWrap | OPFSFileWrap): Promise<OPFSFileWrap> {
const newFile = await this.copyTo(target);
async moveTo(target: OPFSDirWrap | OPFSFileWrap): Promise<void> {
await this.copyTo(target);
await this.remove();
return newFile;
}
}

0 comments on commit 1b0844e

Please sign in to comment.