Skip to content

Commit

Permalink
feat: 🎸 add initial writign implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
streamich committed Jun 20, 2023
1 parent a7061eb commit 2f9542c
Show file tree
Hide file tree
Showing 6 changed files with 148 additions and 7 deletions.
6 changes: 4 additions & 2 deletions src/node-to-fsa/NodeFileSystemDirectoryHandle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,14 @@ import type Dirent from '../Dirent';
* @see https://developer.mozilla.org/en-US/docs/Web/API/FileSystemDirectoryHandle
*/
export class NodeFileSystemDirectoryHandle extends NodeFileSystemHandle {
protected readonly ctx: Partial<NodeFsaContext>;
constructor(
protected readonly fs: NodeFsaFs,
public readonly __path: string,
protected readonly ctx: Partial<NodeFsaContext> = createCtx(ctx),
ctx: Partial<NodeFsaContext> = {},
) {
super('directory', basename(__path, ctx.separator!));
super('directory', basename(__path, ctx.separator || '/'));
this.ctx = createCtx(ctx);
}

/**
Expand Down
5 changes: 3 additions & 2 deletions src/node-to-fsa/NodeFileSystemFileHandle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { NodeFileSystemHandle } from './NodeFileSystemHandle';
import { NodeFileSystemSyncAccessHandle } from './NodeFileSystemSyncAccessHandle';
import { basename, ctx as createCtx, newNotAllowedError } from './util';
import type { NodeFsaContext, NodeFsaFs } from './types';
import {NodeFileSystemWritableFileStream} from './NodeFileSystemWritableFileStream';

export class NodeFileSystemFileHandle extends NodeFileSystemHandle {
constructor(
Expand Down Expand Up @@ -51,7 +52,7 @@ export class NodeFileSystemFileHandle extends NodeFileSystemHandle {
*/
public async createWritable(
{ keepExistingData = false }: { keepExistingData?: boolean } = { keepExistingData: false },
): Promise<NodeFileSystemSyncAccessHandle> {
throw new Error('Not implemented');
): Promise<NodeFileSystemWritableFileStream> {
return new NodeFileSystemWritableFileStream(this.fs, this.__path);
}
}
125 changes: 124 additions & 1 deletion src/node-to-fsa/NodeFileSystemWritableFileStream.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,127 @@
import type {IFileHandle} from "../promises";
import type {NodeFsaFs} from "./types";

/**
* Is a WritableStream object with additional convenience methods, which
* operates on a single file on disk. The interface is accessed through the
* `FileSystemFileHandle.createWritable()` method.
*
* @see https://developer.mozilla.org/en-US/docs/Web/API/FileSystemWritableFileStream
*/
export class NodeFileSystemWritableFileStream extends WritableStream {}
export class NodeFileSystemWritableFileStream extends WritableStream {
protected handle: IFileHandle | undefined = undefined;

constructor(
protected readonly fs: NodeFsaFs,
protected readonly path: string,
) {
const ref: {handle: IFileHandle | undefined} = {handle: undefined};
super({
async start() {
ref.handle = await fs.promises.open(path, 'w');
},
async write(chunk: Data) {
const handle = ref.handle;
if (!handle) throw new Error('Invalid state');
const buffer = Buffer.from(
typeof chunk === 'string'
? chunk
: chunk instanceof Blob
? await chunk.arrayBuffer()
: chunk);
await handle.write(buffer);
},
async close() {
if (ref.handle) await ref.handle.close();
},
async abort() {
if (ref.handle) await ref.handle.close();
},
});
}

/**
* @sse https://developer.mozilla.org/en-US/docs/Web/API/FileSystemWritableFileStream/seek
* @param position An `unsigned long` describing the byte position from the top
* (beginning) of the file.
*/
public async seek(position: number): Promise<void> {
throw new Error('Not implemented');
}

/**
* @see https://developer.mozilla.org/en-US/docs/Web/API/FileSystemWritableFileStream/truncate
* @param size An `unsigned long` of the amount of bytes to resize the stream to.
*/
public async truncate(size: number): Promise<void> {
throw new Error('Not implemented');
}

protected async writeBase(chunk: Data): Promise<void> {
const writer = this.getWriter();
try {
await writer.write(chunk);
} finally {
writer.releaseLock();
}
}

/**
* @see https://developer.mozilla.org/en-US/docs/Web/API/FileSystemWritableFileStream/write
*/
public async write(chunk: Data): Promise<void>;
public async write(params: FileSystemWritableFileStreamParams): Promise<void>;
public async write(params): Promise<void> {
if (!params) throw new TypeError('Missing required argument: params');
switch (typeof params) {
case 'string': {
return this.writeBase(params);
}
case 'object': {
const constructor = params.constructor;
switch (constructor) {
case ArrayBuffer:
case Blob:
case DataView: return this.writeBase(params);
default: {
if (ArrayBuffer.isView(params)) return this.writeBase(params);
else {
switch (params.type) {
case 'write': return this.writeBase(params.data);
case 'truncate': return this.truncate(params.size);
case 'seek': return this.seek(params.position);
default: throw new TypeError('Invalid argument: params');
}
}
}
}
}
default: throw new TypeError('Invalid argument: params');
}
}
}

export interface FileSystemWritableFileStreamParams {
type: 'write' | 'truncate' | 'seek';
data?: Data;
position?: number;
size?: number;
}

export type Data =
| ArrayBuffer
| ArrayBufferView
| Uint8Array
| Uint8ClampedArray
| Int8Array
| Uint16Array
| Int16Array
| Uint32Array
| Int32Array
| Float32Array
| Float64Array
| BigUint64Array
| BigInt64Array
| DataView
| Blob
| string;
15 changes: 15 additions & 0 deletions src/node-to-fsa/__tests__/NodeFileSystemFileHandle.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,19 @@ maybe('NodeFileSystemFileHandle', () => {
expect(contents).toBe('Hello, world!');
});
});

describe('.createWritable()', () => {
describe('.write(chunk)', () => {
test('can write to file', async () => {
const { dir, fs } = setup({
'file.txt': 'Hello, world!',
});
const entry = await dir.getFileHandle('file.txt');
const writable = await entry.createWritable();
await writable.write('...');
await writable.close();
expect(fs.readFileSync('/file.txt', 'utf8')).toBe('...');
});
});
});
});
2 changes: 1 addition & 1 deletion src/promises.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ export interface IFileHandle {
truncate(len?: number): Promise<void>;
utimes(atime: TTime, mtime: TTime): Promise<void>;
write(
buffer: Buffer | Uint8Array,
buffer: Buffer | ArrayBufferView | DataView,
offset?: number,
length?: number,
position?: number,
Expand Down
2 changes: 1 addition & 1 deletion tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"compilerOptions": {
"target": "es5",
"target": "es6",
"lib": ["ES2017", "dom"],
"module": "commonjs",
"removeComments": false,
Expand Down

0 comments on commit 2f9542c

Please sign in to comment.