191 строка
6.0 KiB
TypeScript
191 строка
6.0 KiB
TypeScript
/*---------------------------------------------------------------------------------------------
|
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
* Licensed under the MIT License. See License.txt in the project root for license information.
|
|
*--------------------------------------------------------------------------------------------*/
|
|
|
|
import { Exception, OutstandingTaskAwaiter, promisify } from '@azure-tools/tasks';
|
|
import * as fs from 'fs';
|
|
import * as path from 'path';
|
|
import { fileURLToPath, Url, URL } from 'url';
|
|
|
|
export class PathNotFoundException extends Exception {
|
|
constructor(path: string, public exitCode: number = 1) {
|
|
super(`File '${path}' not found.`, exitCode);
|
|
Object.setPrototypeOf(this, PathNotFoundException.prototype);
|
|
}
|
|
}
|
|
|
|
export class PathIsNotFileException extends Exception {
|
|
constructor(path: string, public exitCode: number = 1) {
|
|
super(`File '${path}' is not a file.`, exitCode);
|
|
Object.setPrototypeOf(this, PathIsNotFileException.prototype);
|
|
}
|
|
}
|
|
|
|
export class PathIsNotDirectoryException extends Exception {
|
|
constructor(path: string, public exitCode: number = 1) {
|
|
super(`File '${path}' is not a directory.`, exitCode);
|
|
Object.setPrototypeOf(this, PathIsNotFileException.prototype);
|
|
}
|
|
}
|
|
|
|
export class UnableToRemoveException extends Exception {
|
|
constructor(path: string, public exitCode: number = 1) {
|
|
super(`Unable to remove '${path}'.`, exitCode);
|
|
Object.setPrototypeOf(this, UnableToRemoveException.prototype);
|
|
}
|
|
}
|
|
|
|
export class UnableToMakeDirectoryException extends Exception {
|
|
constructor(path: string, public exitCode: number = 1) {
|
|
super(`Unable to create directory '${path}'.`, exitCode);
|
|
Object.setPrototypeOf(this, UnableToMakeDirectoryException.prototype);
|
|
}
|
|
}
|
|
export function filePath(path: string | Buffer | Url | URL): string {
|
|
path = path.toString();
|
|
return path.startsWith('file:///') ? fileURLToPath(path) : path;
|
|
}
|
|
export const exists = promisify(fs.exists);
|
|
export const readdir: (path: string | Buffer) => Promise<Array<string>> = promisify(fs.readdir);
|
|
export const close: (fd: number) => Promise<void> = promisify(fs.close);
|
|
|
|
export const writeFile: (filename: string, content: string | Buffer) => Promise<void> = promisify(fs.writeFile);
|
|
export const lstat: (path: string | Buffer) => Promise<fs.Stats> = promisify(fs.lstat);
|
|
|
|
const rmdirInternal: (path: string | Buffer) => Promise<void> = promisify(fs.rmdir);
|
|
const unlinkInternal: (path: string | Buffer) => Promise<void> = promisify(fs.unlink);
|
|
const mkdirInternal: (path: string | Buffer) => Promise<void> = promisify(fs.mkdir);
|
|
const openInternal: (path: string | Buffer, flags: string | number) => Promise<number> = promisify(fs.open);
|
|
const closeInternal: (fs: number) => Promise<void> = promisify(fs.close);
|
|
|
|
export async function isDirectory(dirPath: string): Promise<boolean> {
|
|
try {
|
|
if (await exists(dirPath)) {
|
|
return (await lstat(dirPath)).isDirectory();
|
|
}
|
|
} catch (e) {
|
|
// don't throw!
|
|
}
|
|
return false;
|
|
}
|
|
|
|
export async function mkdir(dirPath: string) {
|
|
if (!await isDirectory(dirPath)) {
|
|
const p = path.normalize(dirPath + '/');
|
|
const parent = path.dirname(dirPath);
|
|
if (! await isDirectory(parent)) {
|
|
if (p != parent) {
|
|
await mkdir(parent);
|
|
}
|
|
}
|
|
try {
|
|
await mkdirInternal(p);
|
|
} catch (e) {
|
|
if (!await isDirectory(p)) {
|
|
throw new UnableToMakeDirectoryException(p);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
const readFileInternal: (filename: string, encoding: string, ) => Promise<string> = promisify(fs.readFile);
|
|
|
|
export async function readFile(filename: string): Promise<string> {
|
|
return readFileInternal(filename, 'utf-8');
|
|
}
|
|
|
|
export async function readBinaryFile(filename: string): Promise<string> {
|
|
return readFileInternal(filename, 'base64');
|
|
}
|
|
|
|
export async function isFile(filePath: string): Promise<boolean> {
|
|
try {
|
|
return await exists(filePath) && !await isDirectory(filePath);
|
|
} catch (e) {
|
|
// don't throw!
|
|
}
|
|
return false;
|
|
}
|
|
|
|
export async function rmdir(dirPath: string, exceptions?: Set<string>) {
|
|
// if it's not there, do nothing.
|
|
if (!await exists(dirPath)) {
|
|
return;
|
|
}
|
|
exceptions = exceptions || new Set();
|
|
|
|
// if it's not a directory, that's bad.
|
|
if (!await isDirectory(dirPath)) {
|
|
throw new PathIsNotDirectoryException(dirPath);
|
|
}
|
|
|
|
// make sure this isn't the current directory.
|
|
if (process.cwd() === path.normalize(dirPath)) {
|
|
process.chdir(`${dirPath}/..`);
|
|
}
|
|
|
|
// make sure the folder is empty first.
|
|
const files = await readdir(dirPath);
|
|
if (files.length) {
|
|
const awaiter = new OutstandingTaskAwaiter();
|
|
try {
|
|
for (const file of files) {
|
|
try {
|
|
const p = path.join(dirPath, file);
|
|
if (exceptions.has(p.toLowerCase())) {
|
|
continue;
|
|
}
|
|
|
|
if (await isDirectory(p)) {
|
|
// folders are recursively rmdir'd
|
|
awaiter.Await(rmdir(p, exceptions));
|
|
} else {
|
|
// files and symlinks are unlink'd
|
|
awaiter.Await(unlinkInternal(p).catch(() => { }));
|
|
}
|
|
} catch (e) {
|
|
// uh... can't.. ok.
|
|
}
|
|
|
|
}
|
|
} finally {
|
|
// after all the entries are done
|
|
await awaiter.Wait();
|
|
}
|
|
}
|
|
try {
|
|
// if this fails for some reason, check if it's important.
|
|
await rmdirInternal(dirPath);
|
|
} catch (e) {
|
|
// is it gone? that's all we really care about.
|
|
if (await isDirectory(dirPath)) {
|
|
// directory did not delete
|
|
//throw new UnableToRemoveException(dirPath);
|
|
}
|
|
}
|
|
}
|
|
|
|
export async function rmFile(filePath: string) {
|
|
// not there? no problem
|
|
if (!exists(filePath)) {
|
|
return;
|
|
}
|
|
|
|
// not a file? that's not cool.
|
|
if (await isDirectory(filePath)) {
|
|
throw new PathIsNotFileException(filePath);
|
|
}
|
|
|
|
try {
|
|
// files and symlinks are unlink'd
|
|
await unlinkInternal(filePath);
|
|
} catch (e) {
|
|
// is it gone? that's all we really care about.
|
|
if (await exists(filePath)) {
|
|
// directory did not delete
|
|
throw new UnableToRemoveException(filePath);
|
|
}
|
|
}
|
|
}
|