Skip to content

Commit c2f5b60

Browse files
committed
Fixed incorrect path resolution with context PWD (fixes #263)
Fixed `bindContext` copying from the created child context instead of assigning to it Fixed `resolveMount` not resolving paths using context PWD `normalizePath` now passed through `this` to `path.resolve` Anti-regression tests now have the issue number in the test name
1 parent 185e08f commit c2f5b60

File tree

10 files changed

+18
-16
lines changed

10 files changed

+18
-16
lines changed

src/context.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,7 @@ export function bindContext(this: V_Context, init: ContextInit = {}): BoundConte
2828

2929
const ctx = createChildContext($, init);
3030

31-
const bound = {
32-
...ctx,
31+
const bound = Object.assign(ctx, {
3332
fs: {
3433
...bindFunctions(fs, ctx),
3534
promises: bindFunctions(fs.promises, ctx),
@@ -41,7 +40,7 @@ export function bindContext(this: V_Context, init: ContextInit = {}): BoundConte
4140
ctx.children.push(child);
4241
return child;
4342
},
44-
} satisfies BoundContext;
43+
}) satisfies BoundContext;
4544

4645
boundContexts.set(ctx.id, bound);
4746

src/node/sync.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -540,7 +540,7 @@ export function realpathSync(this: V_Context, path: fs.PathLike, options: fs.Buf
540540
export function realpathSync(this: V_Context, path: fs.PathLike, options?: fs.EncodingOption): string;
541541
export function realpathSync(this: V_Context, path: fs.PathLike, options?: fs.EncodingOption | fs.BufferEncodingOption): string | Buffer {
542542
const encoding = typeof options == 'string' ? options : (options?.encoding ?? 'utf8');
543-
path = normalizePath(path);
543+
path = normalizePath(path, true);
544544

545545
const { fullPath } = _sync.resolve(this, path);
546546
if (encoding == 'utf8' || encoding == 'utf-8') return fullPath;

src/path.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -347,7 +347,8 @@ export function format(pathObject: ParsedPath): string {
347347
}
348348

349349
export function parse(path: string): ParsedPath {
350-
const isAbsolute = path.startsWith('/');
350+
const isAbsolute = path[0] === '/';
351+
351352
const ret = { root: isAbsolute ? '/' : '', dir: '', base: '', ext: '', name: '' };
352353
if (path.length === 0) return ret;
353354

src/utils.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import type * as fs from 'node:fs';
44
import type { Worker as NodeWorker } from 'node:worker_threads';
55
import { decodeUTF8, encodeUTF8, type OptionalTuple } from 'utilium';
66
import { resolve } from './path.js';
7+
import type { V_Context } from './internal/contexts.js';
78

89
declare global {
910
function atob(data: string): string;
@@ -67,8 +68,9 @@ export function normalizeTime(time: fs.TimeLike): number {
6768
/**
6869
* Normalizes a path
6970
* @internal
71+
* @todo clean this up and make it so `path.resolve` is only called when an explicit context is passed (i.e. `normalizePath(..., $)` to use `path.resolve`)
7072
*/
71-
export function normalizePath(p: fs.PathLike, noResolve: boolean = false): string {
73+
export function normalizePath(this: V_Context, p: fs.PathLike, noResolve: boolean = false): string {
7274
if (p instanceof URL) {
7375
if (p.protocol != 'file:') throw withErrno('EINVAL', 'URLs must use the file: protocol');
7476
p = p.pathname;
@@ -80,7 +82,7 @@ export function normalizePath(p: fs.PathLike, noResolve: boolean = false): strin
8082
p = p.replaceAll(/[/\\]+/g, '/');
8183

8284
// Note: PWD is not resolved here, it is resolved later.
83-
return noResolve ? p : resolve(p);
85+
return noResolve ? p : resolve.call(this, p);
8486
}
8587

8688
/**

src/vfs/async.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ import { emitChange } from './watchers.js';
2323
* @internal @hidden
2424
*/
2525
export async function resolve($: V_Context, path: string, preserveSymlinks?: boolean, extra?: ExceptionExtra): Promise<ResolvedPath> {
26+
path = resolvePath.call($, path);
27+
2628
if (preserveSymlinks) {
2729
const resolved = resolveMount(path, $, extra);
2830
const stats = await resolved.fs.stat(resolved.path).catch(() => undefined);

src/vfs/shared.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,8 @@ export interface ResolvedPath extends ResolvedMount {
9292
export function resolveMount(path: string, ctx: V_Context, extra?: ExceptionExtra): ResolvedMount {
9393
const { root } = contextOf(ctx);
9494
const _exceptionContext = { path, ...extra };
95-
path = normalizePath(join(root, path));
95+
path = normalizePath(join(root, path), true);
96+
path = resolve.call(ctx, path);
9697
const sortedMounts = [...mounts].sort((a, b) => (a[0].length > b[0].length ? -1 : 1)); // descending order of the string length
9798
for (const [mountPoint, fs] of sortedMounts) {
9899
// We know path is normalized, so it would be a substring of the mount point.

src/vfs/sync.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import { emitChange } from './watchers.js';
2525
* @internal @hidden
2626
*/
2727
export function resolve($: V_Context, path: string, preserveSymlinks?: boolean, extra?: ExceptionExtra): ResolvedPath {
28+
path = resolvePath.call($, path);
2829
/* Try to resolve it directly. If this works,
2930
that means we don't need to perform any resolution for parent directories. */
3031
try {

tests/common/context.test.ts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -60,8 +60,7 @@ suite('Context', () => {
6060
await promise;
6161
});
6262

63-
test('Path resolution of / with context root and mount point being the same', async () => {
64-
// @zenfs/core#226
63+
test('Path resolution of / with context root and mount point being the same #226', async () => {
6564
await configure({
6665
mounts: { '/bananas': InMemory },
6766
});
@@ -73,8 +72,7 @@ suite('Context', () => {
7372
assert.deepEqual(bananas.fs.readdirSync('/'), ['yellow']);
7473
});
7574

76-
test('Different working directory', { todo: true }, () => {
77-
// @zenfs/core#263
75+
test('Different working directory #263', () => {
7876
ctx.mkdirSync('/test');
7977
context.pwd = '/test';
8078

tests/fs/links.test.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,7 @@ suite('Links', () => {
1919
assert(stats.isSymbolicLink());
2020
});
2121

22-
test('lstat file inside symlinked directory', async () => {
23-
// @zenfs/core#241
22+
test('lstat file inside symlinked directory #241', async () => {
2423
await fs.promises.mkdir('/a');
2524
await fs.promises.writeFile('/a/hello.txt', 'hello world');
2625
await fs.promises.symlink('/a', '/b');

tests/fs/read.test.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,8 +70,7 @@ suite('read', () => {
7070
assert.equal(bytesRead, expected.length);
7171
});
7272

73-
test('read using callback API', async () => {
74-
// @zenfs/core#239
73+
test('read using callback API #239', async () => {
7574
const path = '/text.txt';
7675

7776
fs.writeFileSync(path, 'hello world');

0 commit comments

Comments
 (0)