From 57192fe98112970fcda201fcf240c57da071cdae Mon Sep 17 00:00:00 2001 From: Vadim Dalecky Date: Sun, 18 Jun 2023 23:02:12 +0200 Subject: [PATCH] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20add=20appendFileSync()?= =?UTF-8?q?=20method?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- demo/fsa/main.ts | 4 ++++ src/fsa-to-node/FsaNodeFs.ts | 12 +++++++++++- src/fsa-to-node/__tests__/FsaNodeFs.test.ts | 7 +++++++ src/fsa-to-node/types.ts | 1 + src/fsa-to-node/worker/FsaNodeSyncWorker.ts | 3 +++ src/fsa-to-node/worker/SyncMessenger.ts | 21 +++++++++++---------- 6 files changed, 37 insertions(+), 11 deletions(-) diff --git a/demo/fsa/main.ts b/demo/fsa/main.ts index a8fe3baaf..7ab5e6b4b 100644 --- a/demo/fsa/main.ts +++ b/demo/fsa/main.ts @@ -33,6 +33,10 @@ const demo = async (dir: fsa.IFileSystemDirectoryHandle) => { console.log('writeFileSync() - can write text to a new file'); fs.writeFileSync('/cool.txt', 'worlds'); strictEqual(fs.readFileSync('/cool.txt', 'utf8'), 'worlds'); + + console.log('appendFileSync() - can append to an existing file'); + fs.appendFileSync('/cool.txt', '!'); + strictEqual(fs.readFileSync('/cool.txt', 'utf8'), 'worlds!'); }; const main = async () => { diff --git a/src/fsa-to-node/FsaNodeFs.ts b/src/fsa-to-node/FsaNodeFs.ts index 9b7140812..658781916 100644 --- a/src/fsa-to-node/FsaNodeFs.ts +++ b/src/fsa-to-node/FsaNodeFs.ts @@ -14,6 +14,7 @@ import { getWriteFileOptions, getOptions, getStatOptions, + getAppendFileOpts, } from '../node/options'; import { bufToUint8, @@ -29,6 +30,7 @@ import { pathToFilename, validateCallback, validateFd, + isFd, } from '../node/util'; import { pathToLocation, testDirectoryIsWritable } from './util'; import { ERRSTR, MODE } from '../node/constants'; @@ -815,6 +817,15 @@ export class FsaNodeFs extends FsaNodeCore implements FsCallbackApi, FsSynchrono adapter.call('writeFile', { filename, data: bufToUint8(buf), opts }); }; + public readonly appendFileSync: FsSynchronousApi['appendFileSync'] = (id: misc.TFileId, data: misc.TData, options?: opts.IAppendFileOptions | string) => { + const opts = getAppendFileOpts(options); + if (!opts.flag || isFd(id)) opts.flag = 'a'; + const filename = this.getFileName(id); + const buf = dataToBuffer(data, opts.encoding); + const adapter = this.getSyncAdapter(); + adapter.call('appendFile', { filename, data: bufToUint8(buf), opts }); + }; + public readonly closeSync: FsSynchronousApi['closeSync'] = (fd: number) => { validateFd(fd); const file = this.getFileByFd(fd, 'close'); @@ -832,7 +843,6 @@ export class FsaNodeFs extends FsaNodeCore implements FsCallbackApi, FsSynchrono } }; - public readonly appendFileSync: FsSynchronousApi['appendFileSync'] = notSupported; public readonly copyFileSync: FsSynchronousApi['copyFileSync'] = notSupported; public readonly ftruncateSync: FsSynchronousApi['ftruncateSync'] = notSupported; public readonly linkSync: FsSynchronousApi['linkSync'] = notSupported; diff --git a/src/fsa-to-node/__tests__/FsaNodeFs.test.ts b/src/fsa-to-node/__tests__/FsaNodeFs.test.ts index 3fba306e3..a930a7c2a 100644 --- a/src/fsa-to-node/__tests__/FsaNodeFs.test.ts +++ b/src/fsa-to-node/__tests__/FsaNodeFs.test.ts @@ -310,6 +310,13 @@ onlyOnNode20('FsaNodeFs', () => { await fs.promises.appendFile('file', 'x'); expect(mfs.readFileSync('/mountpoint/file', 'utf8')).toBe('123x'); }); + + test('can append to a file - 2', async () => { + const { fs, mfs } = setup({ file: '123' }); + await fs.promises.writeFile('cool.txt', 'worlds'); + await fs.promises.appendFile('cool.txt', '!'); + expect(mfs.readFileSync('/mountpoint/cool.txt', 'utf8')).toBe('worlds!'); + }); }); describe('.write()', () => { diff --git a/src/fsa-to-node/types.ts b/src/fsa-to-node/types.ts index 1edc2e611..fd0e62130 100644 --- a/src/fsa-to-node/types.ts +++ b/src/fsa-to-node/types.ts @@ -10,6 +10,7 @@ export interface FsaNodeSyncAdapterApi { access(req: { filename: string; mode: number }): void; readFile(req: { filename: string; opts?: opts.IReadFileOptions }): Uint8Array; writeFile(req: { filename: string; data: Uint8Array; opts?: opts.IWriteFileOptions }): void; + appendFile(req: { filename: string; data: Uint8Array; opts?: opts.IAppendFileOptions }): void; } export interface FsaNodeSyncAdapter { diff --git a/src/fsa-to-node/worker/FsaNodeSyncWorker.ts b/src/fsa-to-node/worker/FsaNodeSyncWorker.ts index e2be65daa..bc7b24575 100644 --- a/src/fsa-to-node/worker/FsaNodeSyncWorker.ts +++ b/src/fsa-to-node/worker/FsaNodeSyncWorker.ts @@ -132,5 +132,8 @@ export class FsaNodeSyncWorker { writeFile: async ({ filename, data, opts }): Promise => { await this.fs.promises.writeFile(filename, data, { ...opts, encoding: 'buffer' }); }, + appendFile: async ({ filename, data, opts }): Promise => { + await this.fs.promises.appendFile(filename, data, { ...opts, encoding: 'buffer' }); + }, }; } diff --git a/src/fsa-to-node/worker/SyncMessenger.ts b/src/fsa-to-node/worker/SyncMessenger.ts index fadcc561c..3f9d832fa 100644 --- a/src/fsa-to-node/worker/SyncMessenger.ts +++ b/src/fsa-to-node/worker/SyncMessenger.ts @@ -1,8 +1,7 @@ export type AsyncCallback = (request: Uint8Array) => Promise; const microSleepSync = () => { - /** @todo Replace this by synchronous XHR call. */ - Math.random(); + // Math.random(); }; const sleepUntilSync = (condition: () => boolean) => { @@ -38,12 +37,13 @@ export class SyncMessenger { public callSync(data: Uint8Array): Uint8Array { const requestLength = data.length; const headerSize = this.headerSize; - this.int32[1] = 0; - this.int32[2] = requestLength; + const int32 = this.int32; + int32[1] = 0; + int32[2] = requestLength; this.uint8.set(data, headerSize); - Atomics.notify(this.int32, 0); - sleepUntilSync(() => this.int32[1] === 1); - const responseLength = this.int32[2]; + Atomics.notify(int32, 0); + sleepUntilSync(() => int32[1] === 1); + const responseLength = int32[2]; const response = this.uint8.slice(headerSize, headerSize + responseLength); return response; } @@ -52,15 +52,16 @@ export class SyncMessenger { const headerSize = this.headerSize; (async () => { try { - const res = Atomics.wait(this.int32, 0, 0); + const int32 = this.int32; + const res = Atomics.wait(int32, 0, 0); if (res !== 'ok') throw new Error(`Unexpected Atomics.wait result: ${res}`); const requestLength = this.int32[2]; const request = this.uint8.slice(headerSize, headerSize + requestLength); const response = await callback(request); const responseLength = response.length; - this.int32[2] = responseLength; + int32[2] = responseLength; this.uint8.set(response, headerSize); - this.int32[1] = 1; + int32[1] = 1; } catch {} this.serveAsync(callback); })().catch(() => {});