refactor(ivy): ngcc - implement abstract FileSystem (#29643)

This commit introduces a new interface, which abstracts access
to the underlying `FileSystem`. There is initially one concrete
implementation, `NodeJsFileSystem`, which is simply wrapping the
`fs` library of NodeJs.

Going forward, we can provide a `MockFileSystem` for test, which
should allow us to stop using `mock-fs` for most of the unit tests.
We could also implement a `CachedFileSystem` that may improve the
performance of ngcc.

PR Close #29643
This commit is contained in:
Pete Bacon Darwin
2019-04-28 20:47:57 +01:00
committed by Andrew Kushnir
parent 1fd2cc6340
commit 16d7dde2ad
36 changed files with 590 additions and 353 deletions

View File

@ -6,15 +6,11 @@
* 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 {dirname} from 'canonical-path';
import {existsSync, writeFileSync} from 'fs';
import {mkdir, mv} from 'shelljs';
import {AbsoluteFsPath} from '../../../src/ngtsc/path';
import {FileSystem} from '../file_system/file_system';
import {EntryPoint} from '../packages/entry_point';
import {EntryPointBundle} from '../packages/entry_point_bundle';
import {FileInfo} from '../rendering/renderer';
import {FileWriter} from './file_writer';
/**
@ -22,19 +18,22 @@ import {FileWriter} from './file_writer';
* a back-up of the original file with an extra `.bak` extension.
*/
export class InPlaceFileWriter implements FileWriter {
constructor(protected fs: FileSystem) {}
writeBundle(_entryPoint: EntryPoint, _bundle: EntryPointBundle, transformedFiles: FileInfo[]) {
transformedFiles.forEach(file => this.writeFileAndBackup(file));
}
protected writeFileAndBackup(file: FileInfo): void {
mkdir('-p', dirname(file.path));
const backPath = file.path + '.__ivy_ngcc_bak';
if (existsSync(backPath)) {
this.fs.ensureDir(AbsoluteFsPath.dirname(file.path));
const backPath = AbsoluteFsPath.fromUnchecked(`${file.path}.__ivy_ngcc_bak`);
if (this.fs.exists(backPath)) {
throw new Error(
`Tried to overwrite ${backPath} with an ngcc back up file, which is disallowed.`);
}
if (existsSync(file.path)) {
mv(file.path, backPath);
if (this.fs.exists(file.path)) {
this.fs.moveFile(file.path, backPath);
}
writeFileSync(file.path, file.contents, 'utf8');
this.fs.writeFile(file.path, file.contents);
}
}

View File

@ -6,12 +6,7 @@
* 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 {dirname, join, relative} from 'canonical-path';
import {writeFileSync} from 'fs';
import {cp, mkdir} from 'shelljs';
import {AbsoluteFsPath} from '../../../src/ngtsc/path';
import {AbsoluteFsPath, PathSegment} from '../../../src/ngtsc/path';
import {isDtsPath} from '../../../src/ngtsc/util/src/typescript';
import {EntryPoint, EntryPointJsonProperty} from '../packages/entry_point';
import {EntryPointBundle} from '../packages/entry_point_bundle';
@ -32,7 +27,7 @@ const NGCC_DIRECTORY = '__ivy_ngcc__';
export class NewEntryPointFileWriter extends InPlaceFileWriter {
writeBundle(entryPoint: EntryPoint, bundle: EntryPointBundle, transformedFiles: FileInfo[]) {
// The new folder is at the root of the overall package
const ngccFolder = AbsoluteFsPath.fromUnchecked(join(entryPoint.package, NGCC_DIRECTORY));
const ngccFolder = AbsoluteFsPath.join(entryPoint.package, NGCC_DIRECTORY);
this.copyBundle(bundle, entryPoint.package, ngccFolder);
transformedFiles.forEach(file => this.writeFile(file, entryPoint.package, ngccFolder));
this.updatePackageJson(entryPoint, bundle.formatProperty, ngccFolder);
@ -41,12 +36,13 @@ export class NewEntryPointFileWriter extends InPlaceFileWriter {
protected copyBundle(
bundle: EntryPointBundle, packagePath: AbsoluteFsPath, ngccFolder: AbsoluteFsPath) {
bundle.src.program.getSourceFiles().forEach(sourceFile => {
const relativePath = relative(packagePath, sourceFile.fileName);
const relativePath =
PathSegment.relative(packagePath, AbsoluteFsPath.fromSourceFile(sourceFile));
const isOutsidePackage = relativePath.startsWith('..');
if (!sourceFile.isDeclarationFile && !isOutsidePackage) {
const newFilePath = join(ngccFolder, relativePath);
mkdir('-p', dirname(newFilePath));
cp(sourceFile.fileName, newFilePath);
const newFilePath = AbsoluteFsPath.join(ngccFolder, relativePath);
this.fs.ensureDir(AbsoluteFsPath.dirname(newFilePath));
this.fs.copyFile(AbsoluteFsPath.fromSourceFile(sourceFile), newFilePath);
}
});
}
@ -57,19 +53,24 @@ export class NewEntryPointFileWriter extends InPlaceFileWriter {
// This is either `.d.ts` or `.d.ts.map` file
super.writeFileAndBackup(file);
} else {
const relativePath = relative(packagePath, file.path);
const newFilePath = join(ngccFolder, relativePath);
mkdir('-p', dirname(newFilePath));
writeFileSync(newFilePath, file.contents, 'utf8');
const relativePath = PathSegment.relative(packagePath, file.path);
const newFilePath = AbsoluteFsPath.join(ngccFolder, relativePath);
this.fs.ensureDir(AbsoluteFsPath.dirname(newFilePath));
this.fs.writeFile(newFilePath, file.contents);
}
}
protected updatePackageJson(
entryPoint: EntryPoint, formatProperty: EntryPointJsonProperty, ngccFolder: AbsoluteFsPath) {
const formatPath = join(entryPoint.path, entryPoint.packageJson[formatProperty] !);
const newFormatPath = join(ngccFolder, relative(entryPoint.package, formatPath));
const formatPath =
AbsoluteFsPath.join(entryPoint.path, entryPoint.packageJson[formatProperty] !);
const newFormatPath =
AbsoluteFsPath.join(ngccFolder, PathSegment.relative(entryPoint.package, formatPath));
const newFormatProperty = formatProperty + '_ivy_ngcc';
(entryPoint.packageJson as any)[newFormatProperty] = relative(entryPoint.path, newFormatPath);
writeFileSync(join(entryPoint.path, 'package.json'), JSON.stringify(entryPoint.packageJson));
(entryPoint.packageJson as any)[newFormatProperty] =
PathSegment.relative(entryPoint.path, newFormatPath);
this.fs.writeFile(
AbsoluteFsPath.join(entryPoint.path, 'package.json'),
JSON.stringify(entryPoint.packageJson));
}
}