Skip to content

Commit

Permalink
feat: 🎸 implement initial version of writeFile() method
Browse files Browse the repository at this point in the history
  • Loading branch information
streamich committed Jun 20, 2023
1 parent de2bb0a commit 80e8499
Show file tree
Hide file tree
Showing 4 changed files with 58 additions and 40 deletions.
46 changes: 26 additions & 20 deletions src/fsa-to-node/FsaNodeFs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import {
getAppendFileOptsAndCb,
getStatOptsAndCb,
getRealpathOptsAndCb,
writeFileDefaults,
getWriteFileOptions,
} from '../node/options';
import {
createError,
Expand Down Expand Up @@ -125,11 +127,11 @@ export class FsaNodeFs implements FsCallbackApi, FsCommonObjects {
return file;
}

private async getFileById(id: misc.TFileId, funcName?: string): Promise<fsa.IFileSystemFileHandle> {
private async getFileById(id: misc.TFileId, funcName?: string, create?: boolean): Promise<fsa.IFileSystemFileHandle> {
if (typeof id === 'number') return (await this.getFileByFd(id, funcName)).file;
const filename = pathToFilename(id);
const [folder, name] = pathToLocation(filename);
return await this.getFile(folder, name, funcName);
return await this.getFile(folder, name, funcName, create);
}

private async getFileByIdOrCreate(id: misc.TFileId, funcName?: string): Promise<fsa.IFileSystemFileHandle> {
Expand Down Expand Up @@ -235,20 +237,30 @@ export class FsaNodeFs implements FsCallbackApi, FsCommonObjects {
);
};

writeFile(id: misc.TFileId, data: misc.TData, callback: misc.TCallback<void>);
writeFile(
id: misc.TFileId,
data: misc.TData,
options: opts.IWriteFileOptions | string,
callback: misc.TCallback<void>,
);
writeFile(
public readonly writeFile: FsCallbackApi['writeFile'] = (
id: misc.TFileId,
data: misc.TData,
a: misc.TCallback<void> | opts.IWriteFileOptions | string,
b?: misc.TCallback<void>,
) {
throw new Error('Not implemented');
): void => {
let options: opts.IWriteFileOptions | string = a as opts.IWriteFileOptions;
let callback: misc.TCallback<void> | undefined = b;
if (typeof a === 'function') {
options = writeFileDefaults;
callback = a;
}
const cb = validateCallback(callback);
const opts = getWriteFileOptions(options);
const flagsNum = flagsToNumber(opts.flag);
const modeNum = modeToNumber(opts.mode);
const buf = dataToBuffer(data, opts.encoding);
(async () => {
const createIfMissing = !!(flagsNum & FLAG.O_CREAT);
const file = await this.getFileById(id, 'writeFile', createIfMissing);
const writable = await file.createWritable({ keepExistingData: false });
await writable.write(buf);
await writable.close();
})().then(() => cb(null), error => cb(error));
}

public readonly copyFile: FsCallbackApi['copyFile'] = (src: misc.PathLike, dest: misc.PathLike, a, b?): void => {
Expand Down Expand Up @@ -316,14 +328,6 @@ export class FsaNodeFs implements FsCallbackApi, FsCommonObjects {
callback(null, strToEncoding(pathFilename, opts.encoding));
};

public readonly lstat: FsCallbackApi['lstat'] = (
path: misc.PathLike,
a: misc.TCallback<misc.IStats> | opts.IStatOptions,
b?: misc.TCallback<misc.IStats>,
): void => {
this.stat(path, <any>a, <any>b);
};

public readonly stat: FsCallbackApi['stat'] = (
path: misc.PathLike,
a: misc.TCallback<misc.IStats> | opts.IStatOptions,
Expand All @@ -341,6 +345,8 @@ export class FsaNodeFs implements FsCallbackApi, FsCommonObjects {
);
};

public readonly lstat: FsCallbackApi['lstat'] = this.stat;

public readonly fstat: FsCallbackApi['fstat'] = (
fd: number,
a: misc.TCallback<misc.IStats> | opts.IStatOptions,
Expand Down
19 changes: 19 additions & 0 deletions src/fsa-to-node/__tests__/FsaNodeFs.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -560,3 +560,22 @@ describe('.copyFile()', () => {
});
});
});

describe('.writeFile()', () => {
test('can create a new file', async () => {
const { fs, mfs } = setup({ folder: { file: 'test' }, 'empty-folder': null, 'f.html': 'test' });
const res = await new Promise<void>((resolve, reject) => {
fs.writeFile('/folder/foo', 'bar', (error) => {
if (error) reject(error);
else resolve();
});
});
expect(res).toBe(undefined);
expect(mfs.__vol.toJSON()).toStrictEqual({
'/mountpoint/folder/file': 'test',
'/mountpoint/folder/foo': 'bar',
'/mountpoint/empty-folder': null,
'/mountpoint/f.html': 'test',
});
});
});
7 changes: 7 additions & 0 deletions src/node/options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,3 +102,10 @@ export const getStatOptsAndCb: (
const realpathDefaults: opts.IReadFileOptions = optsDefaults;
export const getRealpathOptions = optsGenerator<opts.IRealpathOptions>(realpathDefaults);
export const getRealpathOptsAndCb = optsAndCbGenerator<opts.IRealpathOptions, misc.TDataOut>(getRealpathOptions);

export const writeFileDefaults: opts.IWriteFileOptions = {
encoding: 'utf8',
mode: MODE.DEFAULT,
flag: FLAGS[FLAGS.w],
};
export const getWriteFileOptions = optsGenerator<opts.IWriteFileOptions>(writeFileDefaults);
26 changes: 6 additions & 20 deletions src/volume.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,14 @@ import {
getRmOptsAndCb,
getRmdirOptions,
optsAndCbGenerator,
optsDefaults,
optsGenerator,
getAppendFileOptsAndCb,
getAppendFileOpts,
getStatOptsAndCb,
getStatOptions,
getRealpathOptsAndCb,
getRealpathOptions,
getWriteFileOptions,
writeFileDefaults,
} from './node/options';
import {
validateCallback,
Expand Down Expand Up @@ -116,17 +116,6 @@ export type TFlagsCopy =

// ---------------------------------------- Options

// General options with optional `encoding` property that most commands accept.

// Options for `fs.writeFile` and `fs.writeFileSync`
export interface IWriteFileOptions extends opts.IFileOptions {}
const writeFileDefaults: IWriteFileOptions = {
encoding: 'utf8',
mode: MODE.DEFAULT,
flag: FLAGS[FLAGS.w],
};
const getWriteFileOptions = optsGenerator<IWriteFileOptions>(writeFileDefaults);

// Options for `fs.appendFile` and `fs.appendFileSync`
export interface IAppendFileOptions extends opts.IFileOptions {}

Expand Down Expand Up @@ -1017,7 +1006,7 @@ export class Volume {
}
}

writeFileSync(id: TFileId, data: TData, options?: IWriteFileOptions) {
writeFileSync(id: TFileId, data: TData, options?: opts.IWriteFileOptions) {
const opts = getWriteFileOptions(options);
const flagsNum = flagsToNumber(opts.flag);
const modeNum = modeToNumber(opts.mode);
Expand All @@ -1026,18 +1015,15 @@ export class Volume {
}

writeFile(id: TFileId, data: TData, callback: TCallback<void>);
writeFile(id: TFileId, data: TData, options: IWriteFileOptions | string, callback: TCallback<void>);
writeFile(id: TFileId, data: TData, a: TCallback<void> | IWriteFileOptions | string, b?: TCallback<void>) {
let options: IWriteFileOptions | string = a as IWriteFileOptions;
writeFile(id: TFileId, data: TData, options: opts.IWriteFileOptions | string, callback: TCallback<void>);
writeFile(id: TFileId, data: TData, a: TCallback<void> | opts.IWriteFileOptions | string, b?: TCallback<void>) {
let options: opts.IWriteFileOptions | string = a as opts.IWriteFileOptions;
let callback: TCallback<void> | undefined = b;

if (typeof a === 'function') {
options = writeFileDefaults;
callback = a;
}

const cb = validateCallback(callback);

const opts = getWriteFileOptions(options);
const flagsNum = flagsToNumber(opts.flag);
const modeNum = modeToNumber(opts.mode);
Expand Down

0 comments on commit 80e8499

Please sign in to comment.