mirror of https://github.com/status-im/metro.git
metro-memory-fs: add support for win32
Reviewed By: rafeca Differential Revision: D7545983 fbshipit-source-id: 1fa8ae6c773e933a73fa525c191599b35d85c604
This commit is contained in:
parent
0349db8654
commit
13e0844dcf
|
@ -1,25 +1,27 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`throws when finding a symlink loop 1`] = `"ELOOP: \`/foo.txt\`: too many levels of symbolic links"`;
|
||||
exports[`posix support throws when finding a symlink loop 1`] = `"ELOOP: \`/foo.txt\`: too many levels of symbolic links"`;
|
||||
|
||||
exports[`throws when trying to create symlink over existing file 1`] = `"EEXIST: \`/foo.txt\`: directory or file already exists"`;
|
||||
exports[`posix support throws when trying to create symlink over existing file 1`] = `"EEXIST: \`/foo.txt\`: directory or file already exists"`;
|
||||
|
||||
exports[`throws when trying to open too many files 1`] = `"EMFILE: \`/foo.txt\`: too many open files"`;
|
||||
exports[`posix support throws when trying to open too many files 1`] = `"EMFILE: \`/foo.txt\`: too many open files"`;
|
||||
|
||||
exports[`throws when trying to read a directory entry 1`] = `"EISDIR: \`/glo\`: cannot read/write to a directory"`;
|
||||
exports[`posix support throws when trying to read a directory entry 1`] = `"EISDIR: \`/glo\`: cannot read/write to a directory"`;
|
||||
|
||||
exports[`throws when trying to read directory as file 1`] = `"EISDIR: \`/glo\`: cannot read/write to a directory"`;
|
||||
exports[`posix support throws when trying to read directory as file 1`] = `"EISDIR: \`/glo\`: cannot read/write to a directory"`;
|
||||
|
||||
exports[`throws when trying to read file via inexistent directory 1`] = `"ENOENT: \`/glo/../foo.txt\`: no such file or directory"`;
|
||||
exports[`posix support throws when trying to read file via inexistent directory 1`] = `"ENOENT: \`/glo/../foo.txt\`: no such file or directory"`;
|
||||
|
||||
exports[`throws when trying to read file with trailing slash 1`] = `"ENOTDIR: \`/foo.txt/\`: not a directory"`;
|
||||
exports[`posix support throws when trying to read file with trailing slash 1`] = `"ENOTDIR: \`/foo.txt/\`: not a directory"`;
|
||||
|
||||
exports[`throws when trying to read inexistent file (async) 1`] = `"ENOENT: \`/foo.txt\`: no such file or directory"`;
|
||||
exports[`posix support throws when trying to read inexistent file (async) 1`] = `"ENOENT: \`/foo.txt\`: no such file or directory"`;
|
||||
|
||||
exports[`throws when trying to read inexistent file 1`] = `"ENOENT: \`/foo.txt\`: no such file or directory"`;
|
||||
exports[`posix support throws when trying to read inexistent file 1`] = `"ENOENT: \`/foo.txt\`: no such file or directory"`;
|
||||
|
||||
exports[`throws when trying to write a directory entry 1`] = `"EISDIR: \`/glo\`: cannot read/write to a directory"`;
|
||||
exports[`posix support throws when trying to write a directory entry 1`] = `"EISDIR: \`/glo\`: cannot read/write to a directory"`;
|
||||
|
||||
exports[`throws when trying to write to a read-only file descriptor 1`] = `"EBADF: file descriptor cannot be written to"`;
|
||||
exports[`posix support throws when trying to write to a read-only file descriptor 1`] = `"EBADF: file descriptor cannot be written to"`;
|
||||
|
||||
exports[`throws when trying to write to an inexistent file descriptor 1`] = `"EBADF: file descriptor is not open"`;
|
||||
exports[`posix support throws when trying to write to a win32-style path 1`] = `"ENOENT: \`C:\\\\foo.txt\`: no such file or directory"`;
|
||||
|
||||
exports[`posix support throws when trying to write to an inexistent file descriptor 1`] = `"EBADF: file descriptor is not open"`;
|
||||
|
|
|
@ -17,27 +17,28 @@ const MemoryFs = require('../index');
|
|||
|
||||
let fs;
|
||||
|
||||
beforeEach(() => {
|
||||
describe('posix support', () => {
|
||||
beforeEach(() => {
|
||||
fs = new MemoryFs();
|
||||
});
|
||||
});
|
||||
|
||||
it('can write then read a file', () => {
|
||||
it('can write then read a file', () => {
|
||||
fs.writeFileSync('/foo.txt', 'test');
|
||||
expect(fs.readFileSync('/foo.txt', 'utf8')).toEqual('test');
|
||||
});
|
||||
});
|
||||
|
||||
it('can write then read a file with options object', () => {
|
||||
it('can write then read a file with options object', () => {
|
||||
fs.writeFileSync('/foo.txt', 'test');
|
||||
expect(fs.readFileSync('/foo.txt', {encoding: 'utf8'})).toEqual('test');
|
||||
});
|
||||
});
|
||||
|
||||
it('works without binding functions', () => {
|
||||
it('works without binding functions', () => {
|
||||
const {writeFileSync, readFileSync} = fs;
|
||||
writeFileSync('/foo.txt', 'test');
|
||||
expect(readFileSync('/foo.txt', 'utf8')).toEqual('test');
|
||||
});
|
||||
});
|
||||
|
||||
it('can write then read a file (async)', done => {
|
||||
it('can write then read a file (async)', done => {
|
||||
fs.writeFile('/foo.txt', 'test', wrError => {
|
||||
if (wrError) {
|
||||
done(wrError);
|
||||
|
@ -52,14 +53,14 @@ it('can write then read a file (async)', done => {
|
|||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('can write then read a file as buffer', () => {
|
||||
it('can write then read a file as buffer', () => {
|
||||
fs.writeFileSync('/foo.txt', new Buffer([1, 2, 3, 4]));
|
||||
expect(fs.readFileSync('/foo.txt')).toEqual(new Buffer([1, 2, 3, 4]));
|
||||
});
|
||||
});
|
||||
|
||||
describe('createWriteStream', () => {
|
||||
describe('createWriteStream', () => {
|
||||
it('writes a file', done => {
|
||||
const st = fs.createWriteStream('/foo.txt');
|
||||
let opened = false;
|
||||
|
@ -123,9 +124,9 @@ describe('createWriteStream', () => {
|
|||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('createReadStream', () => {
|
||||
describe('createReadStream', () => {
|
||||
const REF_STR = 'foo bar baz glo beep boop';
|
||||
|
||||
beforeEach(() => {
|
||||
|
@ -247,77 +248,77 @@ describe('createReadStream', () => {
|
|||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('truncates a file that already exist', () => {
|
||||
it('truncates a file that already exist', () => {
|
||||
fs.writeFileSync('/foo.txt', 'test');
|
||||
fs.writeFileSync('/foo.txt', 'hop');
|
||||
expect(fs.readFileSync('/foo.txt', 'utf8')).toEqual('hop');
|
||||
});
|
||||
});
|
||||
|
||||
it('can write to an arbitrary position in a file', () => {
|
||||
it('can write to an arbitrary position in a file', () => {
|
||||
const fd = fs.openSync('/foo.txt', 'w');
|
||||
fs.writeSync(fd, 'test');
|
||||
fs.writeSync(fd, 'a', 1);
|
||||
fs.writeSync(fd, 'e', 4);
|
||||
fs.closeSync(fd);
|
||||
expect(fs.readFileSync('/foo.txt', 'utf8')).toEqual('taste');
|
||||
});
|
||||
});
|
||||
|
||||
it('can check a file exist', () => {
|
||||
it('can check a file exist', () => {
|
||||
fs.writeFileSync('/foo.txt', 'test');
|
||||
expect(fs.existsSync('/foo.txt')).toBe(true);
|
||||
expect(fs.existsSync('/bar.txt')).toBe(false);
|
||||
expect(fs.existsSync('/glo/bar.txt')).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
it('can write then read a file in a subdirectory', () => {
|
||||
it('can write then read a file in a subdirectory', () => {
|
||||
fs.mkdirSync('/glo');
|
||||
fs.writeFileSync('/glo/foo.txt', 'test');
|
||||
expect(fs.readFileSync('/glo/foo.txt', 'utf8')).toEqual('test');
|
||||
});
|
||||
});
|
||||
|
||||
it('can write then read via a symlinked file', () => {
|
||||
it('can write then read via a symlinked file', () => {
|
||||
fs.symlinkSync('foo.txt', '/bar.txt');
|
||||
fs.writeFileSync('/bar.txt', 'test');
|
||||
expect(fs.readFileSync('/bar.txt', 'utf8')).toEqual('test');
|
||||
expect(fs.readFileSync('/foo.txt', 'utf8')).toEqual('test');
|
||||
});
|
||||
});
|
||||
|
||||
it('can write then read via a symlinked file (absolute path)', () => {
|
||||
it('can write then read via a symlinked file (absolute path)', () => {
|
||||
fs.symlinkSync('/foo.txt', '/bar.txt');
|
||||
fs.writeFileSync('/bar.txt', 'test');
|
||||
expect(fs.readFileSync('/bar.txt', 'utf8')).toEqual('test');
|
||||
expect(fs.readFileSync('/foo.txt', 'utf8')).toEqual('test');
|
||||
});
|
||||
});
|
||||
|
||||
it('can write then read a file in a symlinked directory', () => {
|
||||
it('can write then read a file in a symlinked directory', () => {
|
||||
fs.mkdirSync('/glo');
|
||||
fs.symlinkSync('glo', '/baz');
|
||||
fs.writeFileSync('/baz/foo.txt', 'test');
|
||||
expect(fs.readFileSync('/baz/foo.txt', 'utf8')).toEqual('test');
|
||||
expect(fs.readFileSync('/glo/foo.txt', 'utf8')).toEqual('test');
|
||||
});
|
||||
});
|
||||
|
||||
it('gives the real path for a symbolic link to a non-existent file', () => {
|
||||
it('gives the real path for a symbolic link to a non-existent file', () => {
|
||||
fs.symlinkSync('foo.txt', '/bar.txt');
|
||||
// This *is* expected to work even if the file doesn't actually exist.
|
||||
expect(fs.realpathSync('/bar.txt')).toEqual('/foo.txt');
|
||||
});
|
||||
});
|
||||
|
||||
it('gives the real path for a symbolic link to a file', () => {
|
||||
it('gives the real path for a symbolic link to a file', () => {
|
||||
fs.writeFileSync('/foo.txt', 'test');
|
||||
fs.symlinkSync('foo.txt', '/bar.txt');
|
||||
expect(fs.realpathSync('/bar.txt')).toEqual('/foo.txt');
|
||||
});
|
||||
});
|
||||
|
||||
it('gives the real path via a symlinked directory', () => {
|
||||
it('gives the real path via a symlinked directory', () => {
|
||||
fs.mkdirSync('/glo');
|
||||
fs.symlinkSync('glo', '/baz');
|
||||
expect(fs.realpathSync('/baz/foo.txt')).toEqual('/glo/foo.txt');
|
||||
});
|
||||
});
|
||||
|
||||
describe('stat', () => {
|
||||
describe('stat', () => {
|
||||
it('works for a regular file', () => {
|
||||
fs.writeFileSync('/foo.txt', 'test');
|
||||
const st = fs.statSync('/foo.txt');
|
||||
|
@ -336,9 +337,9 @@ describe('stat', () => {
|
|||
expect(st.isSymbolicLink()).toBe(false);
|
||||
expect(st.size).toBe(4);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('lstat', () => {
|
||||
describe('lstat', () => {
|
||||
it('works for a regular file', () => {
|
||||
fs.writeFileSync('/foo.txt', 'test');
|
||||
const st = fs.lstatSync('/foo.txt');
|
||||
|
@ -357,17 +358,17 @@ describe('lstat', () => {
|
|||
expect(st.isSymbolicLink()).toBe(true);
|
||||
expect(st.size).toBe(linkStr.length);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('able to list files of a directory', () => {
|
||||
it('able to list files of a directory', () => {
|
||||
fs.mkdirSync('/baz');
|
||||
fs.writeFileSync('/baz/foo.txt', 'test');
|
||||
fs.writeFileSync('/baz/bar.txt', 'boop');
|
||||
fs.symlinkSync('glo', '/baz/glo.txt');
|
||||
expect(fs.readdirSync('/baz')).toEqual(['foo.txt', 'bar.txt', 'glo.txt']);
|
||||
});
|
||||
});
|
||||
|
||||
describe('watch', () => {
|
||||
describe('watch', () => {
|
||||
it('reports changed files', () => {
|
||||
const changedPaths = [];
|
||||
fs.writeFileSync('/foo.txt', '');
|
||||
|
@ -443,9 +444,9 @@ describe('watch', () => {
|
|||
events.push([eventName, filePath]);
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('unlink', () => {
|
||||
describe('unlink', () => {
|
||||
it('removes a file', () => {
|
||||
fs.writeFileSync('/foo.txt', 'test');
|
||||
expect(fs.readdirSync('/')).toEqual(['foo.txt']);
|
||||
|
@ -491,35 +492,35 @@ describe('unlink', () => {
|
|||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('throws when trying to read inexistent file', () => {
|
||||
it('throws when trying to read inexistent file', () => {
|
||||
expectFsError('ENOENT', () => fs.readFileSync('/foo.txt'));
|
||||
});
|
||||
});
|
||||
|
||||
it('throws when trying to read file via inexistent directory', () => {
|
||||
it('throws when trying to read file via inexistent directory', () => {
|
||||
fs.writeFileSync('/foo.txt', 'test');
|
||||
// It is *not* expected to simplify the path before resolution. Because
|
||||
// `glo` does not exist along the way, it is expected to fail.
|
||||
expectFsError('ENOENT', () => fs.readFileSync('/glo/../foo.txt'));
|
||||
});
|
||||
});
|
||||
|
||||
it('throws when trying to create symlink over existing file', () => {
|
||||
it('throws when trying to create symlink over existing file', () => {
|
||||
fs.writeFileSync('/foo.txt', 'test');
|
||||
expectFsError('EEXIST', () => fs.symlinkSync('bar', '/foo.txt'));
|
||||
});
|
||||
});
|
||||
|
||||
it('throws when trying to write a directory entry', () => {
|
||||
it('throws when trying to write a directory entry', () => {
|
||||
fs.mkdirSync('/glo');
|
||||
expectFsError('EISDIR', () => fs.writeFileSync('/glo', 'test'));
|
||||
});
|
||||
});
|
||||
|
||||
it('throws when trying to read a directory entry', () => {
|
||||
it('throws when trying to read a directory entry', () => {
|
||||
fs.mkdirSync('/glo');
|
||||
expectFsError('EISDIR', () => fs.readFileSync('/glo'));
|
||||
});
|
||||
});
|
||||
|
||||
it('throws when trying to read inexistent file (async)', done => {
|
||||
it('throws when trying to read inexistent file (async)', done => {
|
||||
fs.readFile('/foo.txt', error => {
|
||||
if (error.code !== 'ENOENT') {
|
||||
done(error);
|
||||
|
@ -528,42 +529,77 @@ it('throws when trying to read inexistent file (async)', done => {
|
|||
expect(error.message).toMatchSnapshot();
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('throws when trying to read directory as file', () => {
|
||||
it('throws when trying to read directory as file', () => {
|
||||
fs.mkdirSync('/glo');
|
||||
expectFsError('EISDIR', () => fs.readFileSync('/glo'));
|
||||
});
|
||||
});
|
||||
|
||||
it('throws when trying to read file with trailing slash', () => {
|
||||
it('throws when trying to write to a win32-style path', () => {
|
||||
expectFsError('ENOENT', () => fs.writeFileSync('C:\\foo.txt', ''));
|
||||
});
|
||||
|
||||
it('throws when trying to read file with trailing slash', () => {
|
||||
fs.writeFileSync('/foo.txt', 'test');
|
||||
expectFsError('ENOTDIR', () => fs.readFileSync('/foo.txt/'));
|
||||
});
|
||||
});
|
||||
|
||||
it('throws when finding a symlink loop', () => {
|
||||
it('throws when finding a symlink loop', () => {
|
||||
fs.symlinkSync('foo.txt', '/bar.txt');
|
||||
fs.symlinkSync('bar.txt', '/glo.txt');
|
||||
fs.symlinkSync('glo.txt', '/foo.txt');
|
||||
expectFsError('ELOOP', () => fs.readFileSync('/foo.txt'));
|
||||
});
|
||||
});
|
||||
|
||||
it('throws when trying to write to an inexistent file descriptor', () => {
|
||||
it('throws when trying to write to an inexistent file descriptor', () => {
|
||||
expectFsError('EBADF', () => fs.writeSync(42, new Buffer([1])));
|
||||
});
|
||||
});
|
||||
|
||||
it('throws when trying to write to a read-only file descriptor', () => {
|
||||
it('throws when trying to write to a read-only file descriptor', () => {
|
||||
fs.writeFileSync('/foo.txt', 'test');
|
||||
const fd = fs.openSync('/foo.txt', 'r');
|
||||
expectFsError('EBADF', () => fs.writeSync(fd, new Buffer([1])));
|
||||
});
|
||||
});
|
||||
|
||||
it('throws when trying to open too many files', () => {
|
||||
it('throws when trying to open too many files', () => {
|
||||
fs.writeFileSync('/foo.txt', 'test');
|
||||
expectFsError('EMFILE', () => {
|
||||
for (let i = 0; i < 1000; ++i) {
|
||||
fs.openSync('/foo.txt', 'r');
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('win32 support', () => {
|
||||
beforeEach(() => {
|
||||
fs = new MemoryFs('win32');
|
||||
});
|
||||
|
||||
it('can write then read a file', () => {
|
||||
fs.writeFileSync('C:\\foo.txt', 'test');
|
||||
expect(fs.readFileSync('C:\\foo.txt', 'utf8')).toEqual('test');
|
||||
});
|
||||
|
||||
it('gives the real path for a file', () => {
|
||||
fs.writeFileSync('C:\\foo.txt', 'test');
|
||||
expect(fs.realpathSync('c:/foo.txt')).toEqual('c:\\foo.txt');
|
||||
});
|
||||
|
||||
it('can write then read via a symlinked file', () => {
|
||||
fs.symlinkSync('foo.txt', 'c:\\bar.txt');
|
||||
fs.writeFileSync('c:\\bar.txt', 'test');
|
||||
expect(fs.readFileSync('c:\\bar.txt', 'utf8')).toEqual('test');
|
||||
expect(fs.readFileSync('c:\\foo.txt', 'utf8')).toEqual('test');
|
||||
});
|
||||
|
||||
it('can write then read via an absolutely symlinked file', () => {
|
||||
fs.symlinkSync('c:\\foo.txt', 'c:\\bar.txt');
|
||||
fs.writeFileSync('c:\\bar.txt', 'test');
|
||||
expect(fs.readFileSync('c:\\bar.txt', 'utf8')).toEqual('test');
|
||||
expect(fs.readFileSync('c:\\foo.txt', 'utf8')).toEqual('test');
|
||||
});
|
||||
});
|
||||
|
||||
function expectFsError(code, handler) {
|
||||
|
|
|
@ -12,7 +12,6 @@
|
|||
|
||||
// $FlowFixMe: not defined by Flow
|
||||
const constants = require('constants');
|
||||
const path = require('path');
|
||||
const stream = require('stream');
|
||||
|
||||
const {EventEmitter} = require('events');
|
||||
|
@ -61,6 +60,7 @@ type Resolution = {|
|
|||
+basename: string,
|
||||
+dirNode: DirectoryNode,
|
||||
+dirPath: Array<[string, EntityNode]>,
|
||||
+drive: string,
|
||||
+node: ?EntityNode,
|
||||
+realpath: string,
|
||||
|};
|
||||
|
@ -113,9 +113,11 @@ const ASYNC_FUNC_NAMES = [
|
|||
* closely the behavior of file path resolution and file accesses.
|
||||
*/
|
||||
class MemoryFs {
|
||||
_root: DirectoryNode;
|
||||
_roots: Map<string, DirectoryNode>;
|
||||
_fds: Map<number, Descriptor>;
|
||||
_nextId: number;
|
||||
_platform: 'win32' | 'posix';
|
||||
_pathSep: string;
|
||||
|
||||
close: (fd: number, callback: (error: ?Error) => mixed) => void;
|
||||
open: (
|
||||
|
@ -169,7 +171,9 @@ class MemoryFs {
|
|||
callback?: (?Error) => mixed,
|
||||
) => void;
|
||||
|
||||
constructor() {
|
||||
constructor(platform: 'win32' | 'posix' = 'posix') {
|
||||
this._platform = platform;
|
||||
this._pathSep = platform === 'win32' ? '\\' : '/';
|
||||
this.reset();
|
||||
ASYNC_FUNC_NAMES.forEach(funcName => {
|
||||
const func = (this: $FlowFixMe)[`${funcName}Sync`];
|
||||
|
@ -191,7 +195,12 @@ class MemoryFs {
|
|||
|
||||
reset() {
|
||||
this._nextId = 1;
|
||||
this._root = this._makeDir();
|
||||
this._roots = new Map();
|
||||
if (this._platform === 'posix') {
|
||||
this._roots.set('', this._makeDir());
|
||||
} else if (this._platform === 'win32') {
|
||||
this._roots.set('C:', this._makeDir());
|
||||
}
|
||||
this._fds = new Map();
|
||||
}
|
||||
|
||||
|
@ -640,48 +649,78 @@ class MemoryFs {
|
|||
});
|
||||
}
|
||||
|
||||
_parsePath(
|
||||
filePath: string,
|
||||
): {|
|
||||
+drive: ?string,
|
||||
+entNames: Array<string>,
|
||||
|} {
|
||||
let drive;
|
||||
const sep = this._platform === 'win32' ? /[\\/]/ : /\//;
|
||||
if (this._platform === 'win32' && filePath.match(/^[a-zA-Z]:[\\/]/)) {
|
||||
drive = filePath.substring(0, 2);
|
||||
filePath = filePath.substring(3);
|
||||
}
|
||||
if (sep.test(filePath[0])) {
|
||||
if (this._platform === 'posix') {
|
||||
drive = '';
|
||||
filePath = filePath.substring(1);
|
||||
} else {
|
||||
throw makeError(
|
||||
'EINVAL',
|
||||
filePath,
|
||||
'path is invalid because it cannot start with a separator',
|
||||
);
|
||||
}
|
||||
}
|
||||
return {entNames: filePath.split(sep), drive};
|
||||
}
|
||||
|
||||
/**
|
||||
* Implemented according with
|
||||
* http://man7.org/linux/man-pages/man7/path_resolution.7.html
|
||||
*/
|
||||
_resolve(
|
||||
originalFilePath: string,
|
||||
filePath: string,
|
||||
options?: {keepFinalSymlink: boolean},
|
||||
): Resolution {
|
||||
let keepFinalSymlink = false;
|
||||
if (options != null) {
|
||||
({keepFinalSymlink} = options);
|
||||
}
|
||||
let filePath = originalFilePath;
|
||||
let drive = '';
|
||||
if (path === path.win32 && filePath.match(/^[a-zA-Z]:\\/)) {
|
||||
drive = filePath.substring(0, 2);
|
||||
filePath = filePath.substring(2);
|
||||
}
|
||||
if (filePath === '') {
|
||||
throw makeError('ENOENT', originalFilePath, 'no such file or directory');
|
||||
throw makeError('ENOENT', filePath, 'no such file or directory');
|
||||
}
|
||||
if (filePath[0] === '/') {
|
||||
filePath = filePath.substring(1);
|
||||
} else {
|
||||
filePath = path.join(process.cwd().substring(1), filePath);
|
||||
let {drive, entNames} = this._parsePath(filePath);
|
||||
if (drive == null) {
|
||||
const cwPath = this._parsePath(process.cwd());
|
||||
drive = cwPath.drive;
|
||||
if (drive == null) {
|
||||
throw new Error(
|
||||
'On a win32 FS, `process.cwd()` must return a valid win32 absolute ' +
|
||||
`path. This happened while trying to resolve: \`${filePath}\``,
|
||||
);
|
||||
}
|
||||
const entNames = filePath.split(path.sep);
|
||||
checkPathLength(entNames, originalFilePath);
|
||||
entNames = cwPath.entNames.concat(entNames);
|
||||
}
|
||||
checkPathLength(entNames, filePath);
|
||||
const root = this._getRoot(drive, filePath);
|
||||
const context = {
|
||||
node: this._root,
|
||||
nodePath: [['', this._root]],
|
||||
drive,
|
||||
node: root,
|
||||
nodePath: [['', root]],
|
||||
entNames,
|
||||
symlinkCount: 0,
|
||||
keepFinalSymlink,
|
||||
};
|
||||
while (context.entNames.length > 0) {
|
||||
const entName = context.entNames.shift();
|
||||
this._resolveEnt(context, originalFilePath, entName);
|
||||
this._resolveEnt(context, filePath, entName);
|
||||
}
|
||||
const {nodePath} = context;
|
||||
return {
|
||||
realpath: drive + nodePath.map(x => x[0]).join(path.sep),
|
||||
drive: context.drive,
|
||||
realpath: context.drive + nodePath.map(x => x[0]).join(this._pathSep),
|
||||
dirNode: (() => {
|
||||
const dirNode =
|
||||
nodePath.length >= 2
|
||||
|
@ -733,17 +772,25 @@ class MemoryFs {
|
|||
if (context.symlinkCount >= 10) {
|
||||
throw makeError('ELOOP', filePath, 'too many levels of symbolic links');
|
||||
}
|
||||
let {target} = childNode;
|
||||
if (target[0] === '/') {
|
||||
target = target.substring(1);
|
||||
context.node = this._root;
|
||||
const {entNames, drive} = this._parsePath(childNode.target);
|
||||
if (drive != null) {
|
||||
context.drive = drive;
|
||||
context.node = this._getRoot(drive, filePath);
|
||||
context.nodePath = [['', context.node]];
|
||||
}
|
||||
context.entNames = target.split(path.sep).concat(context.entNames);
|
||||
context.entNames = entNames.concat(context.entNames);
|
||||
checkPathLength(context.entNames, filePath);
|
||||
++context.symlinkCount;
|
||||
}
|
||||
|
||||
_getRoot(drive: string, filePath: string): DirectoryNode {
|
||||
const root = this._roots.get(drive.toUpperCase());
|
||||
if (root == null) {
|
||||
throw makeError('ENOENT', filePath, `no such drive: \`${drive}\``);
|
||||
}
|
||||
return root;
|
||||
}
|
||||
|
||||
_write(
|
||||
fd: number,
|
||||
buffer: Buffer,
|
||||
|
@ -809,7 +856,7 @@ class MemoryFs {
|
|||
}
|
||||
watcher.listener(options.eventType, filePath);
|
||||
}
|
||||
filePath = path.join(dirNode[0], filePath);
|
||||
filePath = dirNode[0] + this._pathSep + filePath;
|
||||
recursive = true;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue