diff --git a/src/tsconfig.strictNullChecks.json b/src/tsconfig.strictNullChecks.json index 0f5dbd180e3..0872131fade 100644 --- a/src/tsconfig.strictNullChecks.json +++ b/src/tsconfig.strictNullChecks.json @@ -67,6 +67,8 @@ "./vs/base/node/console.ts", "./vs/base/node/crypto.ts", "./vs/base/node/decoder.ts", + "./vs/base/node/encoding.ts", + "./vs/base/node/extfs.ts", "./vs/base/node/flow.ts", "./vs/base/node/id.ts", "./vs/base/node/paths.ts", @@ -75,6 +77,7 @@ "./vs/base/node/proxy.ts", "./vs/base/node/request.ts", "./vs/base/node/stats.ts", + "./vs/base/node/storage.ts", "./vs/base/node/stream.ts", "./vs/base/parts/contextmenu/common/contextmenu.ts", "./vs/base/parts/contextmenu/electron-browser/contextmenu.ts", diff --git a/src/vs/base/node/encoding.ts b/src/vs/base/node/encoding.ts index c1d248beb48..d06ada4b3de 100644 --- a/src/vs/base/node/encoding.ts +++ b/src/vs/base/node/encoding.ts @@ -78,7 +78,9 @@ export function toDecodeStream(readable: Readable, options: IDecodeStreamOptions this._decodeStreamConstruction = Promise.resolve(detectEncodingFromBuffer({ buffer: Buffer.concat(this._buffer), bytesRead: this._bytesBuffered }, options.guessEncoding)).then(detected => { - detected.encoding = options.overwriteEncoding(detected.encoding); + if (options.overwriteEncoding && detected.encoding) { + detected.encoding = options.overwriteEncoding(detected.encoding); + } this._decodeStream = decodeStream(detected.encoding); for (const buffer of this._buffer) { this._decodeStream.write(buffer); @@ -129,7 +131,7 @@ export function encodingExists(encoding: string): boolean { return iconv.encodingExists(toNodeEncoding(encoding)); } -export function decodeStream(encoding: string): NodeJS.ReadWriteStream { +export function decodeStream(encoding: string | null): NodeJS.ReadWriteStream { return iconv.decodeStream(toNodeEncoding(encoding)); } @@ -137,15 +139,15 @@ export function encodeStream(encoding: string, options?: { addBOM?: boolean }): return iconv.encodeStream(toNodeEncoding(encoding), options); } -function toNodeEncoding(enc: string): string { - if (enc === UTF8_with_bom) { +function toNodeEncoding(enc: string | null): string { + if (enc === UTF8_with_bom || enc === null) { return UTF8; // iconv does not distinguish UTF 8 with or without BOM, so we need to help it } return enc; } -export function detectEncodingByBOMFromBuffer(buffer: Buffer, bytesRead: number): string | null { +export function detectEncodingByBOMFromBuffer(buffer: Buffer | null, bytesRead: number): string | null { if (!buffer || bytesRead < 2) { return null; } @@ -181,7 +183,7 @@ export function detectEncodingByBOMFromBuffer(buffer: Buffer, bytesRead: number) * Detects the Byte Order Mark in a given file. * If no BOM is detected, null will be passed to callback. */ -export function detectEncodingByBOM(file: string): Promise { +export function detectEncodingByBOM(file: string): Promise { return stream.readExactlyByFile(file, 3).then(({ buffer, bytesRead }) => detectEncodingByBOMFromBuffer(buffer, bytesRead)); } @@ -191,7 +193,7 @@ const IGNORE_ENCODINGS = ['ascii', 'utf-8', 'utf-16', 'utf-32']; /** * Guesses the encoding from buffer. */ -export function guessEncodingByBuffer(buffer: Buffer): Promise { +export function guessEncodingByBuffer(buffer: Buffer): Promise { return import('jschardet').then(jschardet => { jschardet.Constants.MINIMUM_THRESHOLD = MINIMUM_THRESHOLD; @@ -266,7 +268,7 @@ const NO_GUESS_BUFFER_MAX_LEN = 512; // when not auto guessing the encoding, const AUTO_GUESS_BUFFER_MAX_LEN = 512 * 8; // with auto guessing we want a lot more content to be read for guessing export interface IDetectedEncodingResult { - encoding: string; + encoding: string | null; seemsBinary: boolean; } @@ -280,7 +282,7 @@ export function detectEncodingFromBuffer({ buffer, bytesRead }: stream.ReadResul // Detect 0 bytes to see if file is binary or UTF-16 LE/BE // unless we already know that this file has a UTF-16 encoding let seemsBinary = false; - if (encoding !== UTF16be && encoding !== UTF16le) { + if (encoding !== UTF16be && encoding !== UTF16le && buffer) { let couldBeUTF16LE = true; // e.g. 0xAA 0x00 let couldBeUTF16BE = true; // e.g. 0x00 0xAA let containsZeroByte = false; @@ -328,7 +330,7 @@ export function detectEncodingFromBuffer({ buffer, bytesRead }: stream.ReadResul } // Auto guess encoding if configured - if (autoGuessEncoding && !seemsBinary && !encoding) { + if (autoGuessEncoding && !seemsBinary && !encoding && buffer) { return guessEncodingByBuffer(buffer.slice(0, bytesRead)).then(guessedEncoding => { return { seemsBinary: false, diff --git a/src/vs/base/node/extfs.ts b/src/vs/base/node/extfs.ts index ab91d0dc26a..3cc6f7cbefc 100644 --- a/src/vs/base/node/extfs.ts +++ b/src/vs/base/node/extfs.ts @@ -28,13 +28,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 +49,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 +65,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,8 +85,8 @@ 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); }); }; @@ -130,7 +128,7 @@ function doCopyFile(source: string, target: string, mode: number, callback: (err } export function mkdirp(path: string, mode?: number, token?: CancellationToken): TPromise { - const mkdir = () => { + const mkdir = (): Promise => { return nfcall(fs.mkdir, path, mode).then(null, (mkdirErr: NodeJS.ErrnoException) => { // ENOENT: a parent folder does not exist yet @@ -180,7 +178,7 @@ export function mkdirp(path: string, mode?: number, token?: CancellationToken): // 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); @@ -198,7 +196,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 } @@ -221,7 +219,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!')); } @@ -297,12 +295,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); } @@ -481,7 +479,7 @@ function doWriteFileAndFlush(path: string, data: string | Buffer, options: IWrit } // 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); } @@ -520,7 +518,7 @@ export function writeFileAndFlushSync(path: string, data: string | Buffer, optio } // 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 { @@ -566,7 +564,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; @@ -616,7 +614,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); @@ -644,7 +642,7 @@ export function watch(path: string, onChange: (type: string, path?: string) => v const watcher = fs.watch(path); watcher.on('change', (type, raw) => { - let file: string | null = null; + let file: string | undefined; if (raw) { // https://github.com/Microsoft/vscode/issues/38191 file = raw.toString(); if (platform.isMacintosh) { diff --git a/src/vs/base/node/pfs.ts b/src/vs/base/node/pfs.ts index 8aa1f32b9ae..3222a44a14a 100644 --- a/src/vs/base/node/pfs.ts +++ b/src/vs/base/node/pfs.ts @@ -183,7 +183,7 @@ export function whenDeleted(path: string): TPromise { if (!exists) { clearInterval(interval); - c(null); + c(void 0); } }); } diff --git a/src/vs/base/node/storage.ts b/src/vs/base/node/storage.ts index 5012b19a65c..32525743f10 100644 --- a/src/vs/base/node/storage.ts +++ b/src/vs/base/node/storage.ts @@ -229,8 +229,8 @@ export class Storage extends Disposable implements IStorage { } export interface IUpdateRequest { - insert?: Map; - delete?: Set; + readonly insert?: Map; + readonly delete?: Set; } export class SQLiteStorageImpl { @@ -286,7 +286,7 @@ export class SQLiteStorageImpl { return this.transaction(db, () => { if (request.insert && request.insert.size > 0) { this.prepare(db, 'INSERT INTO ItemTable VALUES (?,?)', stmt => { - request.insert.forEach((value, key) => { + request.insert!.forEach((value, key) => { stmt.run([key, value]); }); }); @@ -294,7 +294,7 @@ export class SQLiteStorageImpl { if (request.delete && request.delete.size) { this.prepare(db, 'DELETE FROM ItemTable WHERE key=?', stmt => { - request.delete.forEach(key => { + request.delete!.forEach(key => { stmt.run(key); }); }); @@ -523,13 +523,13 @@ class SQLiteStorageLogger { } trace(msg: string): void { - if (this.logTrace) { + if (this.logTrace && this.options && this.options.logTrace) { this.options.logTrace(msg); } } error(error: string | Error): void { - if (this.logError) { + if (this.logError && this.options && this.options.logError) { this.options.logError(error); } }