Merge from master

This commit is contained in:
Raj Musuku
2019-02-21 17:56:04 -08:00
parent 5a146e34fa
commit 666ae11639
11482 changed files with 119352 additions and 255574 deletions

View File

@@ -3,8 +3,6 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import * as fs from 'fs';
import * as paths from 'path';
import { nfcall } from 'vs/base/common/async';
@@ -12,9 +10,10 @@ import { normalizeNFC } from 'vs/base/common/normalization';
import * as platform from 'vs/base/common/platform';
import * as strings from 'vs/base/common/strings';
import * as uuid from 'vs/base/common/uuid';
import { TPromise } from 'vs/base/common/winjs.base';
import { encode, encodeStream } from 'vs/base/node/encoding';
import * as flow from 'vs/base/node/flow';
import { CancellationToken } from 'vs/base/common/cancellation';
import { IDisposable, toDisposable, Disposable } from 'vs/base/common/lifecycle';
const loop = flow.loop;
@@ -28,13 +27,13 @@ export function readdirSync(path: string): string[] {
return fs.readdirSync(path);
}
export function readdir(path: string, callback: (error: Error, files: string[]) => void): void {
export function readdir(path: string, callback: (error: Error | null, files: string[]) => void): void {
// Mac: uses NFD unicode form on disk, but we want NFC
// See also https://github.com/nodejs/node/issues/2165
if (platform.isMacintosh) {
return fs.readdir(path, (error, children) => {
if (error) {
return callback(error, null);
return callback(error, []);
}
return callback(null, children.map(c => normalizeNFC(c)));
@@ -49,7 +48,7 @@ export interface IStatAndLink {
isSymbolicLink: boolean;
}
export function statLink(path: string, callback: (error: Error, statAndIsLink: IStatAndLink) => void): void {
export function statLink(path: string, callback: (error: Error | null, statAndIsLink: IStatAndLink | null) => void): void {
fs.lstat(path, (error, lstat) => {
if (error || lstat.isSymbolicLink()) {
fs.stat(path, (error, stat) => {
@@ -65,10 +64,8 @@ export function statLink(path: string, callback: (error: Error, statAndIsLink: I
});
}
export function copy(source: string, target: string, callback: (error: Error) => void, copiedSources?: { [path: string]: boolean }): void {
if (!copiedSources) {
copiedSources = Object.create(null);
}
export function copy(source: string, target: string, callback: (error: Error | null) => void, copiedSourcesIn?: { [path: string]: boolean }): void {
const copiedSources = copiedSourcesIn ? copiedSourcesIn : Object.create(null);
fs.stat(source, (error, stat) => {
if (error) {
@@ -87,13 +84,13 @@ export function copy(source: string, target: string, callback: (error: Error) =>
const proceed = function () {
readdir(source, (err, files) => {
loop(files, (file: string, clb: (error: Error, result: string[]) => void) => {
copy(paths.join(source, file), paths.join(target, file), (error: Error) => clb(error, void 0), copiedSources);
loop(files, (file: string, clb: (error: Error | null, result: string[]) => void) => {
copy(paths.join(source, file), paths.join(target, file), (error: Error) => clb(error, []), copiedSources);
}, callback);
});
};
mkdirp(target, stat.mode & 511).done(proceed, proceed);
mkdirp(target, stat.mode & 511).then(proceed, proceed);
});
}
@@ -129,37 +126,42 @@ function doCopyFile(source: string, target: string, mode: number, callback: (err
reader.pipe(writer);
}
export function mkdirp(path: string, mode?: number): TPromise<boolean> {
const mkdir = () => {
export function mkdirp(path: string, mode?: number, token?: CancellationToken): Promise<boolean> {
const mkdir = (): Promise<null> => {
return nfcall(fs.mkdir, path, mode).then(null, (mkdirErr: NodeJS.ErrnoException) => {
// ENOENT: a parent folder does not exist yet
if (mkdirErr.code === 'ENOENT') {
return TPromise.wrapError(mkdirErr);
return Promise.reject(mkdirErr);
}
// Any other error: check if folder exists and
// return normally in that case if its a folder
return nfcall(fs.stat, path).then((stat: fs.Stats) => {
if (!stat.isDirectory()) {
return TPromise.wrapError(new Error(`'${path}' exists and is not a directory.`));
return Promise.reject(new Error(`'${path}' exists and is not a directory.`));
}
return null;
}, statErr => {
return TPromise.wrapError(mkdirErr); // bubble up original mkdir error
return Promise.reject(mkdirErr); // bubble up original mkdir error
});
});
};
// stop at root
if (path === paths.dirname(path)) {
return TPromise.as(true);
return Promise.resolve(true);
}
// recursively mkdir
return mkdir().then(null, (err: NodeJS.ErrnoException) => {
// Respect cancellation
if (token && token.isCancellationRequested) {
return Promise.resolve(false);
}
// ENOENT: a parent folder does not exist yet, continue
// to create the parent folder and then try again.
if (err.code === 'ENOENT') {
@@ -167,7 +169,7 @@ export function mkdirp(path: string, mode?: number): TPromise<boolean> {
}
// Any other error
return TPromise.wrapError<boolean>(err);
return Promise.reject(err);
});
}
@@ -175,7 +177,7 @@ export function mkdirp(path: string, mode?: number): TPromise<boolean> {
// after the rename, the contents are out of the workspace although not yet deleted. The greater benefit however is that this operation
// will fail in case any file is used by another process. fs.unlink() in node will not bail if a file unlinked is used by another process.
// However, the consequences are bad as outlined in all the related bugs from https://github.com/joyent/node/issues/7164
export function del(path: string, tmpFolder: string, callback: (error: Error) => void, done?: (error: Error) => void): void {
export function del(path: string, tmpFolder: string, callback: (error: Error | null) => void, done?: (error: Error | null) => void): void {
fs.exists(path, exists => {
if (!exists) {
return callback(null);
@@ -193,7 +195,7 @@ export function del(path: string, tmpFolder: string, callback: (error: Error) =>
}
const pathInTemp = paths.join(tmpFolder, uuid.generateUuid());
fs.rename(path, pathInTemp, (error: Error) => {
fs.rename(path, pathInTemp, (error: Error | null) => {
if (error) {
return rmRecursive(path, callback); // if rename fails, delete without tmp dir
}
@@ -216,7 +218,7 @@ export function del(path: string, tmpFolder: string, callback: (error: Error) =>
});
}
function rmRecursive(path: string, callback: (error: Error) => void): void {
function rmRecursive(path: string, callback: (error: Error | null) => void): void {
if (path === '\\' || path === '/') {
return callback(new Error('Will not delete root!'));
}
@@ -248,7 +250,7 @@ function rmRecursive(path: string, callback: (error: Error) => void): void {
} else if (children.length === 0) {
fs.rmdir(path, callback);
} else {
let firstError: Error = null;
let firstError: Error | null = null;
let childrenLeft = children.length;
children.forEach(child => {
rmRecursive(paths.join(path, child), (err: Error) => {
@@ -292,12 +294,12 @@ export function delSync(path: string): void {
}
}
export function mv(source: string, target: string, callback: (error: Error) => void): void {
export function mv(source: string, target: string, callback: (error: Error | null) => void): void {
if (source === target) {
return callback(null);
}
function updateMtime(err: Error): void {
function updateMtime(err: Error | null): void {
if (err) {
return callback(err);
}
@@ -365,7 +367,7 @@ export interface IWriteFileOptions {
}
let canFlush = true;
export function writeFileAndFlush(path: string, data: string | NodeBuffer | NodeJS.ReadableStream, options: IWriteFileOptions, callback: (error?: Error) => void): void {
export function writeFileAndFlush(path: string, data: string | Buffer | NodeJS.ReadableStream, options: IWriteFileOptions, callback: (error?: Error) => void): void {
options = ensureOptions(options);
if (typeof data === 'string' || Buffer.isBuffer(data)) {
@@ -466,7 +468,7 @@ function doWriteFileStreamAndFlush(path: string, reader: NodeJS.ReadableStream,
// not in some cache.
//
// See https://github.com/nodejs/node/blob/v5.10.0/lib/fs.js#L1194
function doWriteFileAndFlush(path: string, data: string | NodeBuffer, options: IWriteFileOptions, callback: (error?: Error) => void): void {
function doWriteFileAndFlush(path: string, data: string | Buffer, options: IWriteFileOptions, callback: (error?: Error) => void): void {
if (options.encoding) {
data = encode(data, options.encoding.charset, { addBOM: options.encoding.addBOM });
}
@@ -476,7 +478,7 @@ function doWriteFileAndFlush(path: string, data: string | NodeBuffer, options: I
}
// Open the file with same flags and mode as fs.writeFile()
fs.open(path, options.flag, options.mode, (openError, fd) => {
fs.open(path, typeof options.flag === 'string' ? options.flag : 'r', options.mode, (openError, fd) => {
if (openError) {
return callback(openError);
}
@@ -503,7 +505,7 @@ function doWriteFileAndFlush(path: string, data: string | NodeBuffer, options: I
});
}
export function writeFileAndFlushSync(path: string, data: string | NodeBuffer, options?: IWriteFileOptions): void {
export function writeFileAndFlushSync(path: string, data: string | Buffer, options?: IWriteFileOptions): void {
options = ensureOptions(options);
if (options.encoding) {
@@ -515,7 +517,7 @@ export function writeFileAndFlushSync(path: string, data: string | NodeBuffer, o
}
// Open the file with same flags and mode as fs.writeFile()
const fd = fs.openSync(path, options.flag, options.mode);
const fd = fs.openSync(path, typeof options.flag === 'string' ? options.flag : 'r', options.mode);
try {
@@ -561,7 +563,7 @@ function ensureOptions(options?: IWriteFileOptions): IWriteFileOptions {
* In case of errors, null is returned. But you cannot use this function to verify that a path exists.
* realcaseSync does not handle '..' or '.' path segments and it does not take the locale into account.
*/
export function realcaseSync(path: string): string {
export function realcaseSync(path: string): string | null {
const dir = paths.dirname(path);
if (path === dir) { // end recursion
return path;
@@ -611,7 +613,7 @@ export function realpathSync(path: string): string {
}
}
export function realpath(path: string, callback: (error: Error, realpath: string) => void): void {
export function realpath(path: string, callback: (error: Error | null, realpath: string) => void): void {
return fs.realpath(path, (error, realpath) => {
if (!error) {
return callback(null, realpath);
@@ -634,12 +636,12 @@ function normalizePath(path: string): string {
return strings.rtrim(paths.normalize(path), paths.sep);
}
export function watch(path: string, onChange: (type: string, path?: string) => void, onError: (error: string) => void): fs.FSWatcher {
export function watch(path: string, onChange: (type: string, path?: string) => void, onError: (error: string) => void): IDisposable {
try {
const watcher = fs.watch(path);
watcher.on('change', (type, raw) => {
let file: string = null;
let file: string | undefined;
if (raw) { // https://github.com/Microsoft/vscode/issues/38191
file = raw.toString();
if (platform.isMacintosh) {
@@ -654,7 +656,10 @@ export function watch(path: string, onChange: (type: string, path?: string) => v
watcher.on('error', (code: number, signal: string) => onError(`Failed to watch ${path} for changes (${code}, ${signal})`));
return watcher;
return toDisposable(() => {
watcher.removeAllListeners();
watcher.close();
});
} catch (error) {
fs.exists(path, exists => {
if (exists) {
@@ -663,5 +668,41 @@ export function watch(path: string, onChange: (type: string, path?: string) => v
});
}
return void 0;
return Disposable.None;
}
export function sanitizeFilePath(candidate: string, cwd: string): string {
// Special case: allow to open a drive letter without trailing backslash
if (platform.isWindows && strings.endsWith(candidate, ':')) {
candidate += paths.sep;
}
// Ensure absolute
if (!paths.isAbsolute(candidate)) {
candidate = paths.join(cwd, candidate);
}
// Ensure normalized
candidate = paths.normalize(candidate);
// Ensure no trailing slash/backslash
if (platform.isWindows) {
candidate = strings.rtrim(candidate, paths.sep);
// Special case: allow to open drive root ('C:\')
if (strings.endsWith(candidate, ':')) {
candidate += paths.sep;
}
} else {
candidate = strings.rtrim(candidate, paths.sep);
// Special case: allow to open root ('/')
if (!candidate) {
candidate = paths.sep;
}
}
return candidate;
}