Hans Larsen 483cbe2665 refactor: add tslint-sonarts and a lot of tslint rules
This should clean up the code a bit.

Note: at first I added the no-useless-cast rule, but after getting frustrated
with it (as it has many false positive), I decided to remove the rule but some
useless casts were removed so I let those in the PR.
2018-07-10 15:07:36 -07:00

153 lines
4.8 KiB
TypeScript

/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {
Path,
basename,
dirname,
join,
normalize,
relative,
virtualFs,
} from '@angular-devkit/core';
import { NodeJsSyncHost } from '@angular-devkit/core/node';
import { Stats } from 'fs';
import { EMPTY, Observable, from, of } from 'rxjs';
import { concatMap, delay, finalize, map, mergeMap, retry, tap } from 'rxjs/operators';
export class TestProjectHost extends NodeJsSyncHost {
private _currentRoot: Path | null = null;
private _scopedSyncHost: virtualFs.SyncDelegateHost<Stats> | null = null;
constructor(protected _templateRoot: Path) {
super();
}
root(): Path {
if (this._currentRoot === null) {
throw new Error('TestProjectHost must be initialized before being used.');
}
return this._currentRoot;
}
scopedSync(): virtualFs.SyncDelegateHost<Stats> {
if (this._currentRoot === null || this._scopedSyncHost === null) {
throw new Error('TestProjectHost must be initialized before being used.');
}
return this._scopedSyncHost;
}
initialize(): Observable<void> {
const recursiveList = (path: Path): Observable<Path> => this.list(path).pipe(
// Emit each fragment individually.
concatMap(fragments => from(fragments)),
// Join the path with fragment.
map(fragment => join(path, fragment)),
// Emit directory content paths instead of the directory path.
mergeMap(path => this.isDirectory(path).pipe(
concatMap(isDir => isDir ? recursiveList(path) : of(path)),
)),
);
// Find a unique folder that we can write to to use as current root.
return this.findUniqueFolderPath().pipe(
// Save the path and create a scoped host for it.
tap(newFolderPath => {
this._currentRoot = newFolderPath;
this._scopedSyncHost = new virtualFs.SyncDelegateHost(
new virtualFs.ScopedHost(this, this.root()));
}),
// List all files in root.
concatMap(() => recursiveList(this._templateRoot)),
// Copy them over to the current root.
concatMap(from => {
const to = join(this.root(), relative(this._templateRoot, from));
return this.read(from).pipe(
concatMap(buffer => this.write(to, buffer)),
);
}),
map(() => { }),
);
}
restore(): Observable<void> {
if (this._currentRoot === null) {
return EMPTY;
}
// Delete the current root and clear the variables.
// Wait 50ms and retry up to 10 times, to give time for file locks to clear.
return this.exists(this.root()).pipe(
delay(50),
concatMap(exists => exists ? this.delete(this.root()) : EMPTY),
retry(10),
finalize(() => {
this._currentRoot = null;
this._scopedSyncHost = null;
}),
);
}
writeMultipleFiles(files: { [path: string]: string | ArrayBufferLike | Buffer }): void {
Object.keys(files).forEach(fileName => {
let content = files[fileName];
if (typeof content == 'string') {
content = virtualFs.stringToFileBuffer(content);
} else if (content instanceof Buffer) {
content = content.buffer.slice(
content.byteOffset,
content.byteOffset + content.byteLength,
);
}
this.scopedSync().write(
normalize(fileName),
content,
);
});
}
replaceInFile(path: string, match: RegExp | string, replacement: string) {
const content = virtualFs.fileBufferToString(this.scopedSync().read(normalize(path)));
this.scopedSync().write(normalize(path),
virtualFs.stringToFileBuffer(content.replace(match, replacement)));
}
appendToFile(path: string, str: string) {
const content = virtualFs.fileBufferToString(this.scopedSync().read(normalize(path)));
this.scopedSync().write(normalize(path),
virtualFs.stringToFileBuffer(content.concat(str)));
}
fileMatchExists(dir: string, regex: RegExp) {
const [fileName] = this.scopedSync().list(normalize(dir)).filter(name => name.match(regex));
return fileName || undefined;
}
copyFile(from: string, to: string) {
const content = this.scopedSync().read(normalize(from));
this.scopedSync().write(normalize(to), content);
}
private findUniqueFolderPath(): Observable<Path> {
// 11 character alphanumeric string.
const randomString = Math.random().toString(36).slice(2);
const newFolderName = `test-project-host-${basename(this._templateRoot)}-${randomString}`;
const newFolderPath = join(dirname(this._templateRoot), newFolderName);
return this.exists(newFolderPath).pipe(
concatMap(exists => exists ? this.findUniqueFolderPath() : of(newFolderPath)),
);
}
}