Skip to content

Commit c765836

Browse files
author
Kartik Raj
committed
Implement pathExists using fs-extra
1 parent 283d9f0 commit c765836

File tree

5 files changed

+37
-59
lines changed

5 files changed

+37
-59
lines changed

src/client/common/platform/fileSystem.ts

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ interface IRawFSExtra {
8888
readFileSync(path: string, encoding: string): string;
8989
createReadStream(filename: string): ReadStream;
9090
createWriteStream(filename: string): WriteStream;
91+
pathExists(filename: string): Promise<boolean>;
9192
}
9293

9394
interface IRawPath {
@@ -125,6 +126,10 @@ export class RawFileSystem implements IRawFileSystem {
125126
);
126127
}
127128

129+
public async pathExists(filename: string): Promise<boolean> {
130+
return this.fsExtra.pathExists(filename);
131+
}
132+
128133
public async stat(filename: string): Promise<FileStat> {
129134
// Note that, prior to the November release of VS Code,
130135
// stat.ctime was always 0.
@@ -355,6 +360,10 @@ export class FileSystemUtils implements IFileSystemUtils {
355360
// matches; otherwise a mismatch results in a "false" value
356361
fileType?: FileType,
357362
): Promise<boolean> {
363+
if (fileType === undefined) {
364+
// Do not need to run stat if not asking for file type.
365+
return this.raw.pathExists(filename);
366+
}
358367
let stat: FileStat;
359368
try {
360369
// Note that we are using stat() rather than lstat(). This
@@ -368,9 +377,6 @@ export class FileSystemUtils implements IFileSystemUtils {
368377
return false;
369378
}
370379

371-
if (fileType === undefined) {
372-
return true;
373-
}
374380
if (fileType === FileType.Unknown) {
375381
// FileType.Unknown == 0, hence do not use bitwise operations.
376382
return stat.type === FileType.Unknown;
@@ -563,6 +569,10 @@ export class FileSystem implements IFileSystem {
563569
return this.utils.fileExists(filename);
564570
}
565571

572+
public pathExists(filename: string): Promise<boolean> {
573+
return this.utils.pathExists(filename);
574+
}
575+
566576
public fileExistsSync(filename: string): boolean {
567577
return this.utils.fileExistsSync(filename);
568578
}

src/client/common/platform/types.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ export type WriteStream = fs.WriteStream;
9595

9696
// The low-level filesystem operations on which the extension depends.
9797
export interface IRawFileSystem {
98+
pathExists(filename: string): Promise<boolean>;
9899
// Get information about a file (resolve symlinks).
99100
stat(filename: string): Promise<FileStat>;
100101
// Get information about a file (do not resolve synlinks).
@@ -216,6 +217,7 @@ export interface IFileSystem {
216217
createWriteStream(path: string): fs.WriteStream;
217218

218219
// utils
220+
pathExists(path: string): Promise<boolean>;
219221
fileExists(path: string): Promise<boolean>;
220222
fileExistsSync(path: string): boolean;
221223
directoryExists(path: string): Promise<boolean>;

src/client/common/variables/environment.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { inject, injectable } from 'inversify';
55
import * as path from 'path';
66
import { sendTelemetryEvent } from '../../telemetry';
77
import { EventName } from '../../telemetry/constants';
8+
import { traceError } from '../logger';
89
import { IFileSystem } from '../platform/types';
910
import { IPathUtils } from '../types';
1011
import { EnvironmentVariables, IEnvironmentVariablesService } from './types';
@@ -22,7 +23,14 @@ export class EnvironmentVariablesService implements IEnvironmentVariablesService
2223
filePath?: string,
2324
baseVars?: EnvironmentVariables,
2425
): Promise<EnvironmentVariables | undefined> {
25-
if (!filePath || !(await this.fs.fileExists(filePath))) {
26+
if (!filePath || !(await this.fs.pathExists(filePath))) {
27+
return;
28+
}
29+
const contents = await this.fs.readFile(filePath).catch((ex) => {
30+
traceError('Custom .env is likely not pointing to a valid file', ex);
31+
return undefined;
32+
});
33+
if (!contents) {
2634
return;
2735
}
2836
return parseEnvFile(await this.fs.readFile(filePath), baseVars);

src/test/common/platform/filesystem.unit.test.ts

Lines changed: 10 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ interface IRawFS extends IPaths {
5656
writeFile(uri: vscode.Uri, content: Uint8Array): Thenable<void>;
5757

5858
// "fs-extra"
59+
pathExists(filename: string): Promise<boolean>;
5960
lstat(filename: string): Promise<fs.Stats>;
6061
chmod(filePath: string, mode: string | number): Promise<void>;
6162
appendFile(filename: string, data: {}): Promise<void>;
@@ -926,51 +927,26 @@ suite('FileSystemUtils', () => {
926927
suite('pathExists', () => {
927928
test('exists (without type)', async () => {
928929
const filename = 'x/y/z/spam.py';
929-
const stat = createMockStat();
930-
deps.setup((d) => d.stat(filename)) // The "file" exists.
931-
.returns(() => Promise.resolve(stat.object));
930+
deps.setup((d) => d.pathExists(filename)) // The "file" exists.
931+
.returns(() => Promise.resolve(true));
932932

933933
const exists = await utils.pathExists(filename);
934934

935935
expect(exists).to.equal(true);
936936
verifyAll();
937937
});
938938

939-
test('does not exist', async () => {
939+
test('does not exist (without type)', async () => {
940940
const filename = 'x/y/z/spam.py';
941-
const err = vscode.FileSystemError.FileNotFound(filename);
942-
deps.setup((d) => d.stat(filename)) // The file does not exist.
943-
.throws(err);
941+
deps.setup((d) => d.pathExists(filename)) // The "file" exists.
942+
.returns(() => Promise.resolve(false));
944943

945944
const exists = await utils.pathExists(filename);
946945

947946
expect(exists).to.equal(false);
948947
verifyAll();
949948
});
950949

951-
test('ignores errors from stat()', async () => {
952-
const filename = 'x/y/z/spam.py';
953-
deps.setup((d) => d.stat(filename)) // It's broken.
954-
.returns(() => Promise.reject(new Error('oops!')));
955-
956-
const exists = await utils.pathExists(filename);
957-
958-
expect(exists).to.equal(false);
959-
verifyAll();
960-
});
961-
962-
test('matches (type: undefined)', async () => {
963-
const filename = 'x/y/z/spam.py';
964-
const stat = createMockStat();
965-
deps.setup((d) => d.stat(filename)) // The "file" exists.
966-
.returns(() => Promise.resolve(stat.object));
967-
968-
const exists = await utils.pathExists(filename);
969-
970-
expect(exists).to.equal(true);
971-
verifyAll();
972-
});
973-
974950
test('matches (type: file)', async () => {
975951
const filename = 'x/y/z/spam.py';
976952
const stat = createMockStat();
@@ -1231,8 +1207,8 @@ suite('FileSystemUtils', () => {
12311207
const err = vscode.FileSystemError.FileNotFound(dirname);
12321208
deps.setup((d) => d.listdir(dirname)) // The "file" does not exist.
12331209
.returns(() => Promise.reject(err));
1234-
deps.setup((d) => d.stat(dirname)) // The "file" does not exist.
1235-
.returns(() => Promise.reject(err));
1210+
deps.setup((d) => d.pathExists(dirname)) // The "file" does not exist.
1211+
.returns(() => Promise.resolve(false));
12361212

12371213
const entries = await utils.listdir(dirname);
12381214

@@ -1245,9 +1221,7 @@ suite('FileSystemUtils', () => {
12451221
const err = vscode.FileSystemError.FileNotADirectory(dirname);
12461222
deps.setup((d) => d.listdir(dirname)) // Fail (async) with not-a-directory.
12471223
.returns(() => Promise.reject(err));
1248-
const stat = createMockStat();
1249-
deps.setup((d) => d.stat(dirname)) // The "file" exists.
1250-
.returns(() => Promise.resolve(stat.object));
1224+
deps.setup((d) => d.pathExists(dirname)).returns(() => Promise.resolve(true)); // The "file" exists.
12511225

12521226
const promise = utils.listdir(dirname);
12531227

@@ -1260,29 +1234,13 @@ suite('FileSystemUtils', () => {
12601234
const err = new Error('oops!');
12611235
deps.setup((d) => d.listdir(dirname)) // Fail (async) with an arbitrary error.
12621236
.returns(() => Promise.reject(err));
1263-
deps.setup((d) => d.stat(dirname)) // Fail with file-not-found.
1264-
.throws(vscode.FileSystemError.FileNotFound(dirname));
1237+
deps.setup((d) => d.pathExists(dirname)).returns(() => Promise.resolve(false));
12651238

12661239
const entries = await utils.listdir(dirname);
12671240

12681241
expect(entries).to.deep.equal([]);
12691242
verifyAll();
12701243
});
1271-
1272-
test('fails if the raw call fails', async () => {
1273-
const dirname = 'x/y/z/spam';
1274-
const err = new Error('oops!');
1275-
deps.setup((d) => d.listdir(dirname)) // Fail with an arbirary error.
1276-
.throws(err);
1277-
const stat = createMockStat();
1278-
deps.setup((d) => d.stat(dirname)) // The "file" exists.
1279-
.returns(() => Promise.resolve(stat.object));
1280-
1281-
const promise = utils.listdir(dirname);
1282-
1283-
await expect(promise).to.eventually.be.rejected;
1284-
verifyAll();
1285-
});
12861244
});
12871245

12881246
suite('getSubDirectories', () => {

src/test/common/variables/envVarsService.unit.test.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ suite('Environment Variables Service', () => {
3838
fs.verifyAll();
3939
}
4040
function setFile(fileName: string, text: string) {
41-
fs.setup((f) => f.fileExists(fileName)) // Handle the specific file.
41+
fs.setup((f) => f.pathExists(fileName)) // Handle the specific file.
4242
.returns(() => Promise.resolve(true)); // The file exists.
4343
fs.setup((f) => f.readFile(fileName)) // Handle the specific file.
4444
.returns(() => Promise.resolve(text)); // Pretend to read from the file.
@@ -53,7 +53,7 @@ suite('Environment Variables Service', () => {
5353
});
5454

5555
test('Custom variables should be undefined with non-existent files', async () => {
56-
fs.setup((f) => f.fileExists(filename)) // Handle the specific file.
56+
fs.setup((f) => f.pathExists(filename)) // Handle the specific file.
5757
.returns(() => Promise.resolve(false)); // The file is missing.
5858

5959
const vars = await variablesService.parseFile(filename);
@@ -64,7 +64,7 @@ suite('Environment Variables Service', () => {
6464

6565
test('Custom variables should be undefined when folder name is passed instead of a file name', async () => {
6666
const dirname = 'x/y/z';
67-
fs.setup((f) => f.fileExists(dirname)) // Handle the specific "file".
67+
fs.setup((f) => f.pathExists(dirname)) // Handle the specific "file".
6868
.returns(() => Promise.resolve(false)); // It isn't a "regular" file.
6969

7070
const vars = await variablesService.parseFile(dirname);

0 commit comments

Comments
 (0)