mirror of
https://github.com/angular/angular-cli.git
synced 2025-05-17 02:54:21 +08:00
Updates for all angular.io links to the new angular.dev domain. Additionally, adjustment to new resources where the equivalent does not exist on the new site (e.g. Tour of Heroes tutorial)
166 lines
4.8 KiB
TypeScript
166 lines
4.8 KiB
TypeScript
/**
|
|
* @license
|
|
* Copyright Google LLC 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.dev/license
|
|
*/
|
|
|
|
import {
|
|
Path,
|
|
PathFragment,
|
|
basename,
|
|
dirname,
|
|
join,
|
|
normalize,
|
|
relative,
|
|
virtualFs,
|
|
} from '@angular-devkit/core';
|
|
import { NodeJsSyncHost } from '@angular-devkit/core/node';
|
|
import { Stats } from 'fs';
|
|
import {
|
|
EMPTY,
|
|
Observable,
|
|
concatMap,
|
|
delay,
|
|
finalize,
|
|
from,
|
|
map,
|
|
mergeMap,
|
|
of,
|
|
retry,
|
|
tap,
|
|
} from 'rxjs';
|
|
|
|
/**
|
|
* @deprecated
|
|
*/
|
|
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 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): PathFragment | undefined {
|
|
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))),
|
|
);
|
|
}
|
|
}
|