Skip to content

Commit

Permalink
Create parent directories in Switch.file() write mode
Browse files Browse the repository at this point in the history
  • Loading branch information
TooTallNate committed Oct 31, 2024
1 parent e58e432 commit c1640a8
Show file tree
Hide file tree
Showing 3 changed files with 57 additions and 3 deletions.
5 changes: 5 additions & 0 deletions .changeset/sweet-ghosts-destroy.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@nx.js/runtime": patch
---

Create parent directories in `Switch.file()` write mode
34 changes: 34 additions & 0 deletions apps/tests/src/switch.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,19 @@ test('`Switch.file()` read stream', async () => {
assert.equal(chunks, ['thi', 's i', 's a', ' te', 'xt ', 'fil', 'e\n']);
});

test('`Switch.file()` read stream throws on ENOENT', async () => {
let err: Error | undefined;
const file = Switch.file('romfs:/does_not_exist');
try {
for await (const _ of file.stream()) {
}
} catch (_err) {
err = _err as Error;
}
assert.ok(err);
assert.equal(err.message, 'No such file or directory');
});

test('`Switch.file()` write stream', async () => {
const path = 'sdmc:/nxjs-test-file.txt';
try {
Expand All @@ -182,4 +195,25 @@ test('`Switch.file()` write stream', async () => {
}
});

test('`Switch.file()` creates parent directories', async () => {
const dir = 'sdmc:/__nxjs_test__/';

// Ensure directory does not exist
assert.equal(Switch.statSync(dir), null);

const filePath = new URL('test/file.txt', dir);
try {
const file = Switch.file(filePath);

const writer = file.writable.getWriter();
writer.write('a');
await writer.close();

// Ensure directory exists
assert.not.equal(Switch.statSync(dir), null);
} finally {
await Switch.remove(dir);
}
});

test.run();
21 changes: 18 additions & 3 deletions source/fs.c
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,20 @@ JSValue nx_fclose(JSContext *ctx, JSValueConst this_val, int argc,

void nx_fopen_do(nx_work_t *req) {
nx_fs_fopen_async_t *data = (nx_fs_fopen_async_t *)req->data;

// Create any parent directories (only for write mode)
if (data->mode[0] == 'w') {
char *dir = dirname(data->path);
if (dir) {
if (createDirectoryRecursively(dir, 0777) == -1) {
data->err = errno;
free(dir);
return;
}
free(dir);
}
}

data->file = fopen(data->path, data->mode);
if (!data->file) {
data->err = errno;
Expand All @@ -182,15 +196,16 @@ JSValue nx_fopen_cb(JSContext *ctx, nx_work_t *req) {
JS_FreeCString(ctx, data->path);
JS_FreeCString(ctx, data->mode);

if (data->err == ENOENT) {
return JS_NULL;
} else if (data->err) {
// Throw even on parent directory ENOENT, since the parent
// dirs were supposed to be created by the worker thread
if (data->err) {
JSValue err = JS_NewError(ctx);
JS_DefinePropertyValueStr(ctx, err, "message",
JS_NewString(ctx, strerror(data->err)),
JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
return JS_Throw(ctx, err);
}

JSValue f = JS_NewObjectClass(ctx, nx_file_class_id);
nx_file_t *file = js_mallocz(ctx, sizeof(nx_file_t));
file->file = data->file;
Expand Down

0 comments on commit c1640a8

Please sign in to comment.