diff --git a/src/cdk/schematics/ng-update/devkit-file-system.ts b/src/cdk/schematics/ng-update/devkit-file-system.ts index b11add75f4f3..dd7c5f03ceba 100644 --- a/src/cdk/schematics/ng-update/devkit-file-system.ts +++ b/src/cdk/schematics/ng-update/devkit-file-system.ts @@ -6,37 +6,26 @@ * found in the LICENSE file at https://angular.io/license */ -import {normalize, Path, relative} from '@angular-devkit/core'; +import {normalize, Path, PathIsDirectoryException} from '@angular-devkit/core'; import {Tree, UpdateRecorder} from '@angular-devkit/schematics'; +import {DirectoryEntry, FileSystem} from '../update-tool/file-system'; import * as path from 'path'; -import {FileSystem} from '../update-tool/file-system'; /** * File system that leverages the virtual tree from the CLI devkit. This file * system is commonly used by `ng update` migrations that run as part of the * Angular CLI. */ -export class DevkitFileSystem extends FileSystem { +export class DevkitFileSystem extends FileSystem { private _updateRecorderCache = new Map(); - private _workspaceFsPath: Path; - constructor(private _tree: Tree, workspaceFsPath: string) { + constructor(private _tree: Tree) { super(); - this._workspaceFsPath = normalize(workspaceFsPath); } resolve(...segments: string[]): Path { // Note: We use `posix.resolve` as the devkit paths are using posix separators. - const resolvedPath = normalize(path.posix.resolve(...segments.map(normalize))); - // If the resolved path points to the workspace root, then this is an absolute disk - // path and we need to compute a devkit tree relative path. - if (resolvedPath.startsWith(this._workspaceFsPath)) { - return relative(this._workspaceFsPath, resolvedPath); - } - // Otherwise we know that the path is absolute (due to the resolve), and that it - // refers to an absolute devkit tree path (like `/angular.json`). We keep those - // unmodified as they are already resolved workspace paths. - return resolvedPath; + return normalize(path.posix.resolve('/', ...segments.map(normalize))); } edit(filePath: Path) { @@ -53,8 +42,18 @@ export class DevkitFileSystem extends FileSystem { this._updateRecorderCache.clear(); } - exists(filePath: Path) { - return this._tree.exists(filePath); + exists(fileOrDirPath: Path) { + // The devkit tree does not expose an API for checking whether a given + // directory exists. It throws a specific error though if a directory + // is being read as a file. We use that to check if a directory exists. + try { + return this._tree.get(fileOrDirPath) !== null; + } catch (e) { + if (e instanceof PathIsDirectoryException) { + return true; + } + } + return false; } overwrite(filePath: Path, content: string) { @@ -73,4 +72,9 @@ export class DevkitFileSystem extends FileSystem { const buffer = this._tree.read(filePath); return buffer !== null ? buffer.toString() : null; } + + readDirectory(dirPath: Path): DirectoryEntry { + const {subdirs: directories, subfiles: files} = this._tree.getDir(dirPath); + return {directories, files}; + } } diff --git a/src/cdk/schematics/ng-update/devkit-migration-rule.ts b/src/cdk/schematics/ng-update/devkit-migration-rule.ts index 788f64be8a26..170553c0cce5 100644 --- a/src/cdk/schematics/ng-update/devkit-migration-rule.ts +++ b/src/cdk/schematics/ng-update/devkit-migration-rule.ts @@ -9,17 +9,16 @@ import {Rule, SchematicContext, Tree} from '@angular-devkit/schematics'; import {NodePackageInstallTask} from '@angular-devkit/schematics/tasks'; import {WorkspaceProject} from '@schematics/angular/utility/workspace-models'; -import {sync as globSync} from 'glob'; -import {join} from 'path'; import {UpdateProject} from '../update-tool'; +import {WorkspacePath} from '../update-tool/file-system'; import {MigrationCtor} from '../update-tool/migration'; import {TargetVersion} from '../update-tool/target-version'; -import {WorkspacePath} from '../update-tool/file-system'; import {getTargetTsconfigPath, getWorkspaceConfigGracefully} from '../utils/project-tsconfig-paths'; import {DevkitFileSystem} from './devkit-file-system'; import {DevkitContext, DevkitMigration, DevkitMigrationCtor} from './devkit-migration'; +import {findStylesheetFiles} from './find-stylesheets'; import {AttributeSelectorsMigration} from './migrations/attribute-selectors'; import {ClassInheritanceMigration} from './migrations/class-inheritance'; import {ClassNamesMigration} from './migrations/class-names'; @@ -74,9 +73,7 @@ export function createMigrationSchematicRule( // necessary because multiple TypeScript projects can contain the same source file and // we don't want to check these again, as this would result in duplicated failure messages. const analyzedFiles = new Set(); - // The CLI uses the working directory as the base directory for the virtual file system tree. - const workspaceFsPath = process.cwd(); - const fileSystem = new DevkitFileSystem(tree, workspaceFsPath); + const fileSystem = new DevkitFileSystem(tree); const projectNames = Object.keys(workspace.projects); const migrations: NullableDevkitMigration[] = [...cdkMigrations, ...extraMigrations]; let hasFailures = false; @@ -90,11 +87,18 @@ export function createMigrationSchematicRule( logger.warn(`Could not find TypeScript project for project: ${projectName}`); continue; } + + // In some applications, developers will have global stylesheets which are not + // specified in any Angular component. Therefore we glob up all CSS and SCSS files + // in the project and migrate them if needed. + // TODO: rework this to collect global stylesheets from the workspace config. COMP-280. + const additionalStylesheetPaths = findStylesheetFiles(tree, project.root); + if (buildTsconfigPath !== null) { - runMigrations(project, projectName, buildTsconfigPath, false); + runMigrations(project, projectName, buildTsconfigPath, additionalStylesheetPaths, false); } if (testTsconfigPath !== null) { - runMigrations(project, projectName, testTsconfigPath, true); + runMigrations(project, projectName, testTsconfigPath, additionalStylesheetPaths, true); } } @@ -123,12 +127,10 @@ export function createMigrationSchematicRule( /** Runs the migrations for the specified workspace project. */ function runMigrations(project: WorkspaceProject, projectName: string, - tsconfigPath: string, isTestTarget: boolean) { - const projectRootFsPath = join(workspaceFsPath, project.root); - const tsconfigFsPath = join(workspaceFsPath, tsconfigPath); - const program = UpdateProject.createProgramFromTsconfig(tsconfigFsPath, fileSystem); + tsconfigPath: WorkspacePath, additionalStylesheetPaths: string[], + isTestTarget: boolean) { + const program = UpdateProject.createProgramFromTsconfig(tsconfigPath, fileSystem); const updateContext: DevkitContext = { - workspaceFsPath, isTestTarget, projectName, project, @@ -143,17 +145,8 @@ export function createMigrationSchematicRule( context.logger, ); - // In some applications, developers will have global stylesheets which are not - // specified in any Angular component. Therefore we glob up all CSS and SCSS files - // outside of node_modules and dist. The files will be read by the individual - // stylesheet rules and checked. - // TODO: rework this to collect global stylesheets from the workspace config. COMP-280. - const additionalStylesheets = globSync( - '!(node_modules|dist)/**/*.+(css|scss)', - {absolute: true, cwd: projectRootFsPath, nodir: true}); - const result = - updateProject.migrate(migrations, targetVersion, upgradeData, additionalStylesheets); + updateProject.migrate(migrations, targetVersion, upgradeData, additionalStylesheetPaths); // Commit all recorded edits in the update recorder. We apply the edits after all // migrations ran because otherwise offsets in the TypeScript program would be diff --git a/src/cdk/schematics/ng-update/devkit-migration.ts b/src/cdk/schematics/ng-update/devkit-migration.ts index 9ce99c17ab7d..feae4bc66777 100644 --- a/src/cdk/schematics/ng-update/devkit-migration.ts +++ b/src/cdk/schematics/ng-update/devkit-migration.ts @@ -17,8 +17,6 @@ export type DevkitContext = { projectName: string; /** Workspace project the migrations run against. */ project: WorkspaceProject, - /** Absolute file system path to the workspace */ - workspaceFsPath: string, /** Whether the migrations run for a test target. */ isTestTarget: boolean, }; diff --git a/src/cdk/schematics/ng-update/find-stylesheets.ts b/src/cdk/schematics/ng-update/find-stylesheets.ts new file mode 100644 index 000000000000..52c7e840c7d7 --- /dev/null +++ b/src/cdk/schematics/ng-update/find-stylesheets.ts @@ -0,0 +1,30 @@ +/** + * @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.io/license + */ + +import {join} from '@angular-devkit/core'; +import {Tree} from '@angular-devkit/schematics'; + +/** Regular expression that matches stylesheet paths */ +const STYLESHEET_REGEX = /.*\.(css|scss)/; + +/** Finds stylesheets in the given directory from within the specified tree. */ +export function findStylesheetFiles(tree: Tree, baseDir: string): string[] { + const result: string[] = []; + const visitDir = dirPath => { + const {subfiles, subdirs} = tree.getDir(dirPath); + result.push(...subfiles.filter(f => STYLESHEET_REGEX.test(f))); + subdirs.forEach(fragment => { + // Do not visit directories or files inside node modules or `dist/` folders. + if (fragment !== 'node_modules' && fragment !== 'dist') { + visitDir(join(dirPath, fragment)); + } + }); + }; + visitDir(baseDir); + return result; +} diff --git a/src/cdk/schematics/ng-update/migrations/attribute-selectors.ts b/src/cdk/schematics/ng-update/migrations/attribute-selectors.ts index d9a88eb9f733..fd4bd03ea032 100644 --- a/src/cdk/schematics/ng-update/migrations/attribute-selectors.ts +++ b/src/cdk/schematics/ng-update/migrations/attribute-selectors.ts @@ -59,13 +59,12 @@ export class AttributeSelectorsMigration extends Migration { } const literalText = literal.getText(); - const filePath = literal.getSourceFile().fileName; + const filePath = this.fileSystem.resolve(literal.getSourceFile().fileName); this.data.forEach(selector => { findAllSubstringIndices(literalText, selector.replace) .map(offset => literal.getStart() + offset) - .forEach(start => this._replaceSelector( - this.fileSystem.resolve(filePath), start, selector)); + .forEach(start => this._replaceSelector(filePath, start, selector)); }); } diff --git a/src/cdk/schematics/ng-update/migrations/css-selectors.ts b/src/cdk/schematics/ng-update/migrations/css-selectors.ts index 959eefd7a15c..9b26c7249147 100644 --- a/src/cdk/schematics/ng-update/migrations/css-selectors.ts +++ b/src/cdk/schematics/ng-update/migrations/css-selectors.ts @@ -61,7 +61,7 @@ export class CssSelectorsMigration extends Migration { } const textContent = node.getText(); - const filePath = node.getSourceFile().fileName; + const filePath = this.fileSystem.resolve(node.getSourceFile().fileName); this.data.forEach(data => { if (data.whitelist && !data.whitelist.strings) { @@ -70,7 +70,7 @@ export class CssSelectorsMigration extends Migration { findAllSubstringIndices(textContent, data.replace) .map(offset => node.getStart() + offset) - .forEach(start => this._replaceSelector(this.fileSystem.resolve(filePath), start, data)); + .forEach(start => this._replaceSelector(filePath, start, data)); }); } diff --git a/src/cdk/schematics/ng-update/migrations/element-selectors.ts b/src/cdk/schematics/ng-update/migrations/element-selectors.ts index efdac857de65..163a6aa778cf 100644 --- a/src/cdk/schematics/ng-update/migrations/element-selectors.ts +++ b/src/cdk/schematics/ng-update/migrations/element-selectors.ts @@ -53,13 +53,12 @@ export class ElementSelectorsMigration extends Migration { } const textContent = node.getText(); - const filePath = node.getSourceFile().fileName; + const filePath = this.fileSystem.resolve(node.getSourceFile().fileName); this.data.forEach(selector => { findAllSubstringIndices(textContent, selector.replace) .map(offset => node.getStart() + offset) - .forEach(start => this._replaceSelector( - this.fileSystem.resolve(filePath), start, selector)); + .forEach(start => this._replaceSelector(filePath, start, selector)); }); } diff --git a/src/cdk/schematics/ng-update/test-cases/misc/external-resource-resolution.spec.ts b/src/cdk/schematics/ng-update/test-cases/misc/external-resource-resolution.spec.ts index 9124d57e299e..33e35fd414fd 100644 --- a/src/cdk/schematics/ng-update/test-cases/misc/external-resource-resolution.spec.ts +++ b/src/cdk/schematics/ng-update/test-cases/misc/external-resource-resolution.spec.ts @@ -5,7 +5,7 @@ import {createTestCaseSetup} from '../../../testing'; describe('ng-update external resource resolution', () => { it('should properly resolve referenced resources in components', async () => { - const {runFixers, writeFile, removeTempDir, appTree} = await createTestCaseSetup( + const {runFixers, writeFile, appTree} = await createTestCaseSetup( 'migration-v6', MIGRATION_PATH, [resolveBazelPath(__dirname, './external-resource-resolution_input.ts')]); @@ -24,7 +24,5 @@ describe('ng-update external resource resolution', () => { .toBe(expected, 'Expected relative paths with parent segments to work.'); expect(appTree.readContent('/projects/cdk-testing/src/test-cases/local.html')) .toBe(expected, 'Expected relative paths without explicit dot to work.'); - - removeTempDir(); }); }); diff --git a/src/cdk/schematics/ng-update/test-cases/misc/global-stylesheets.spec.ts b/src/cdk/schematics/ng-update/test-cases/misc/global-stylesheets.spec.ts index 7d1fbce6fc91..74591c51f424 100644 --- a/src/cdk/schematics/ng-update/test-cases/misc/global-stylesheets.spec.ts +++ b/src/cdk/schematics/ng-update/test-cases/misc/global-stylesheets.spec.ts @@ -6,7 +6,7 @@ import {createTestCaseSetup} from '../../../testing'; describe('global stylesheets migration', () => { it('should not check stylesheet twice if referenced in component', async () => { - const {runFixers, writeFile, removeTempDir, appTree} = await createTestCaseSetup( + const {runFixers, writeFile, appTree} = await createTestCaseSetup( 'migration-v6', MIGRATION_PATH, [resolveBazelPath(__dirname, './global-stylesheets_input.ts')]); @@ -25,12 +25,10 @@ describe('global stylesheets migration', () => { // the same replacements were recorded for the same source file. expect(appTree.readContent(testStylesheetPath)) .toBe(`[cdkPortalOutlet] {\n color: red;\n}\n`); - - removeTempDir(); }); it('should not check stylesheets outside of project target', async () => { - const {runFixers, writeFile, removeTempDir, appTree} = await createTestCaseSetup( + const {runFixers, writeFile, appTree} = await createTestCaseSetup( 'migration-v6', MIGRATION_PATH, []); const subProjectStylesheet = '[cdkPortalHost] {\n color: red;\n}\n'; @@ -43,7 +41,5 @@ describe('global stylesheets migration', () => { // if the external stylesheet that is not of a project target would have been checked // by accident, the stylesheet would differ from the original file content. expect(appTree.readContent('/sub_project/assets/test.css')).toBe(subProjectStylesheet); - - removeTempDir(); }); }); diff --git a/src/cdk/schematics/ng-update/test-cases/misc/method-call-checks.spec.ts b/src/cdk/schematics/ng-update/test-cases/misc/method-call-checks.spec.ts index dad003cfd346..4e571af88fde 100644 --- a/src/cdk/schematics/ng-update/test-cases/misc/method-call-checks.spec.ts +++ b/src/cdk/schematics/ng-update/test-cases/misc/method-call-checks.spec.ts @@ -4,7 +4,7 @@ import {createTestCaseSetup} from '../../../testing'; describe('v6 method call checks', () => { it('should properly report invalid method calls', async () => { - const {runFixers, removeTempDir} = await createTestCaseSetup( + const {runFixers} = await createTestCaseSetup( 'migration-v6', MIGRATION_PATH, [resolveBazelPath(__dirname, './method-call-checks_input.ts')]); @@ -14,7 +14,5 @@ describe('v6 method call checks', () => { /@15:5 - Found call to "FocusMonitor\.monitor".*renderer.*has been removed/); expect(logOutput).toMatch( /@16:5 - Found call to "FocusMonitor\.monitor".*renderer.*has been removed/); - - removeTempDir(); }); }); diff --git a/src/cdk/schematics/testing/test-case-setup.ts b/src/cdk/schematics/testing/test-case-setup.ts index 7d828ff1df7e..a268830c218e 100644 --- a/src/cdk/schematics/testing/test-case-setup.ts +++ b/src/cdk/schematics/testing/test-case-setup.ts @@ -5,12 +5,10 @@ * 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 {getSystemPath, JsonParseMode, normalize, parseJson, Path} from '@angular-devkit/core'; -import {TempScopedNodeJsSyncHost} from '@angular-devkit/core/node/testing'; -import * as virtualFs from '@angular-devkit/core/src/virtual-fs/host'; +import {getSystemPath, JsonParseMode, parseJson, Path} from '@angular-devkit/core'; import {HostTree, Tree} from '@angular-devkit/schematics'; import {SchematicTestRunner, UnitTestTree} from '@angular-devkit/schematics/testing'; -import {readFileSync, removeSync} from 'fs-extra'; +import {readFileSync} from 'fs-extra'; import {sync as globSync} from 'glob'; import {basename, extname, join, relative, sep} from 'path'; import {EMPTY} from 'rxjs'; @@ -38,10 +36,8 @@ export function readFileContent(filePath: string): string { * schematic tree. This would allow us to fully take advantage of the virtual file system. */ export async function createFileSystemTestApp(runner: SchematicTestRunner) { - const tempFileSystemHost = new TempScopedNodeJsSyncHost(); - const hostTree = new HostTree(tempFileSystemHost); + const hostTree = new HostTree(); const appTree: UnitTestTree = await createTestApp(runner, {name: 'cdk-testing'}, hostTree); - const tempPath = getSystemPath(tempFileSystemHost.root); // Since the TypeScript compiler API expects all files to be present on the real file system, we // map every file in the app tree to a temporary location on the file system. @@ -49,17 +45,13 @@ export async function createFileSystemTestApp(runner: SchematicTestRunner) { return { appTree, - tempFileSystemHost, - tempPath, writeFile, - removeTempDir: () => removeSync(tempPath), }; function writeFile(filePath: string, content: string) { // Update the temp file system host to reflect the changes in the real file system. // This is still necessary since we depend on the real file system for parsing the // TypeScript project. - tempFileSystemHost.sync.write(normalize(filePath), virtualFs.stringToFileBuffer(content)); if (hostTree.exists(filePath)) { hostTree.overwrite(filePath, content); } else { @@ -72,12 +64,11 @@ export async function createTestCaseSetup(migrationName: string, collectionPath: inputFiles: string[]) { const runner = new SchematicTestRunner('schematics', collectionPath); - const initialWorkingDir = process.cwd(); let logOutput = ''; runner.logger.subscribe(entry => logOutput += `${entry.message}\n`); - const {appTree, tempPath, writeFile, removeTempDir} = + const {appTree, writeFile} = await createFileSystemTestApp(runner); _patchTypeScriptDefaultLib(appTree); @@ -105,10 +96,6 @@ export async function createTestCaseSetup(migrationName: string, collectionPath: writeFile(testAppTsconfigPath, JSON.stringify(testAppTsconfig, null, 2)); const runFixers = async function() { - // Switch to the new temporary directory to simulate that "ng update" is ran - // from within the project. - process.chdir(tempPath); - // Patch "executePostTasks" to do nothing. This is necessary since // we cannot run the node install task in unit tests. Rather we just // assert that certain async post tasks are scheduled. @@ -117,13 +104,10 @@ export async function createTestCaseSetup(migrationName: string, collectionPath: await runner.runSchematicAsync(migrationName, {}, appTree).toPromise(); - // Switch back to the initial working directory. - process.chdir(initialWorkingDir); - return {logOutput}; }; - return {runner, appTree, writeFile, tempPath, removeTempDir, runFixers}; + return {runner, appTree, writeFile, runFixers}; } /** @@ -192,21 +176,17 @@ export function defineJasmineTestCases(versionName: string, collectionFile: stri let appTree: UnitTestTree; let testCasesOutputPath: string; - let cleanupTestApp: () => void; beforeAll(async () => { - const {appTree: _tree, runFixers, removeTempDir} = + const {appTree: _tree, runFixers} = await createTestCaseSetup(`migration-${versionName}`, collectionFile, inputFiles); await runFixers(); appTree = _tree; testCasesOutputPath = '/projects/cdk-testing/src/test-cases/'; - cleanupTestApp = removeTempDir; }); - afterAll(() => cleanupTestApp()); - // Iterates through every test case directory and generates a jasmine test block that will // verify that the update schematics properly updated the test input to the expected output. inputFiles.forEach(inputFile => { diff --git a/src/cdk/schematics/update-tool/file-system.ts b/src/cdk/schematics/update-tool/file-system.ts index bee5a86d4a0e..459487f6aca9 100644 --- a/src/cdk/schematics/update-tool/file-system.ts +++ b/src/cdk/schematics/update-tool/file-system.ts @@ -23,40 +23,46 @@ import {UpdateRecorder} from './update-recorder'; * * 1. The update-tool cannot have a dependency on the Angular devkit as that one * is not synced into g3. We want to be able to run migrations in g3 if needed. - * 2. `WorkspacePath` is stricter than `Path` because it does not intersect with `string`. - * That helps ensuring that workspace paths are not accidentally passed to native - * path manipulation functions (like `path.resolve`). This complicates the use of - * workspace paths, but it's a trade off for more predictable path manipulations. */ -export type WorkspacePath = { +export type WorkspacePath = string&{ // Brand signature matches the devkit paths so that existing path // utilities from the Angular devkit can be conveniently used. __PRIVATE_DEVKIT_PATH: void; }; +/** Interface that describes a directory. */ +export interface DirectoryEntry { + /** List of directories inside the directory. */ + directories: string[]; + /** List of files inside the directory. */ + files: string[]; +} + /** * Abstraction of the file system that migrations can use to record and apply * changes. This is necessary to support virtual file systems as used in the CLI devkit. */ -export abstract class FileSystem { - /** Checks whether a given file exists. */ - abstract exists(filePath: T): boolean; +export abstract class FileSystem { + /** Checks whether the given file or directory exists. */ + abstract exists(path: WorkspacePath): boolean; /** Gets the contents of the given file. */ - abstract read(filePath: T): string|null; + abstract read(filePath: WorkspacePath): string|null; + /** Reads the given directory to retrieve children. */ + abstract readDirectory(dirPath: WorkspacePath): DirectoryEntry; /** * Creates an update recorder for the given file. Edits can be recorded and * committed in batches. Changes are not applied automatically because otherwise * migrations would need to re-read files, or account for shifted file contents. */ - abstract edit(filePath: T): UpdateRecorder; + abstract edit(filePath: WorkspacePath): UpdateRecorder; /** Applies all changes which have been recorded in update recorders. */ abstract commitEdits(): void; /** Creates a new file with the given content. */ - abstract create(filePath: T, content: string): void; + abstract create(filePath: WorkspacePath, content: string): void; /** Overwrites an existing file with the given content. */ - abstract overwrite(filePath: T, content: string): void; + abstract overwrite(filePath: WorkspacePath, content: string): void; /** Deletes the given file. */ - abstract delete(filePath: T): void; + abstract delete(filePath: WorkspacePath): void; /** * Resolves given paths to a resolved path in the file system. For example, the devkit * tree considers the actual workspace directory as file system root. @@ -66,5 +72,5 @@ export abstract class FileSystem { * function will iterate from the target through other segments until it finds an * absolute path segment. */ - abstract resolve(...segments: string[]): T; + abstract resolve(...segments: string[]): WorkspacePath; } diff --git a/src/cdk/schematics/update-tool/index.ts b/src/cdk/schematics/update-tool/index.ts index 4b98555abb77..d8d71cb2a2b0 100644 --- a/src/cdk/schematics/update-tool/index.ts +++ b/src/cdk/schematics/update-tool/index.ts @@ -6,7 +6,6 @@ * found in the LICENSE file at https://angular.io/license */ -import {dirname} from 'path'; import * as ts from 'typescript'; import {ComponentResourceCollector} from './component-resource-collector'; @@ -15,6 +14,7 @@ import {defaultLogger, UpdateLogger} from './logger'; import {Migration, MigrationCtor, MigrationFailure} from './migration'; import {TargetVersion} from './target-version'; import {parseTsconfigFile} from './utils/parse-tsconfig'; +import {createFileSystemCompilerHost} from './utils/virtual-host'; /** * An update project that can be run against individual migrations. An update project @@ -26,10 +26,18 @@ import {parseTsconfigFile} from './utils/parse-tsconfig'; export class UpdateProject { private readonly _typeChecker: ts.TypeChecker = this._program.getTypeChecker(); - constructor(private _context: Context, + constructor(/** Context provided to all migrations. */ + private _context: Context, + /** TypeScript program using workspace paths. */ private _program: ts.Program, + /** File system used for reading, writing and editing files. */ private _fileSystem: FileSystem, + /** + * Set of analyzed files. Used for avoiding multiple migration runs if + * files overlap between targets. + */ private _analyzedFiles: Set = new Set(), + /** Logger used for printing messages. */ private _logger: UpdateLogger = defaultLogger) {} /** @@ -154,18 +162,11 @@ export class UpdateProject { /** * Creates a program form the specified tsconfig and patches the host - * to read files through the given file system. + * to read files and directories through the given file system. */ - static createProgramFromTsconfig(tsconfigFsPath: string, fs: FileSystem): ts.Program { - const parsed = parseTsconfigFile(tsconfigFsPath, dirname(tsconfigFsPath)); - const host = ts.createCompilerHost(parsed.options, true); - // Patch the host to read files through the specified file system. - host.readFile = fileName => { - const fileContent = fs.read(fs.resolve(fileName)); - // Strip BOM as otherwise TSC methods (e.g. "getWidth") will return an offset which - // which breaks the CLI UpdateRecorder. https://github.com/angular/angular/pull/30719 - return fileContent !== null ? fileContent.replace(/^\uFEFF/, '') : undefined; - }; + static createProgramFromTsconfig(tsconfigPath: WorkspacePath, fs: FileSystem): ts.Program { + const parsed = parseTsconfigFile(fs.resolve(tsconfigPath), fs); + const host = createFileSystemCompilerHost(parsed.options, fs); return ts.createProgram(parsed.fileNames, parsed.options, host); } } diff --git a/src/cdk/schematics/update-tool/utils/parse-tsconfig.ts b/src/cdk/schematics/update-tool/utils/parse-tsconfig.ts index 3c0839306b3e..eaf7ecca91c9 100644 --- a/src/cdk/schematics/update-tool/utils/parse-tsconfig.ts +++ b/src/cdk/schematics/update-tool/utils/parse-tsconfig.ts @@ -7,17 +7,14 @@ */ import * as ts from 'typescript'; +import {FileSystem, WorkspacePath} from '../file-system'; +import {FileSystemHost} from './virtual-host'; +import {dirname} from 'path'; -export function parseTsconfigFile(tsconfigPath: string, basePath: string): ts.ParsedCommandLine { - const {config} = ts.readConfigFile(tsconfigPath, ts.sys.readFile); - const parseConfigHost = { - useCaseSensitiveFileNames: ts.sys.useCaseSensitiveFileNames, - fileExists: ts.sys.fileExists, - readDirectory: ts.sys.readDirectory, - readFile: ts.sys.readFile, - }; - - - - return ts.parseJsonConfigFileContent(config, parseConfigHost, basePath, {}); +export function parseTsconfigFile(tsconfigPath: WorkspacePath, + fileSystem: FileSystem): ts.ParsedCommandLine { + const {config} = ts.readConfigFile(tsconfigPath, + p => fileSystem.read(fileSystem.resolve(p))!); + return ts.parseJsonConfigFileContent(config, new FileSystemHost(fileSystem), + dirname(tsconfigPath), {}); } diff --git a/src/cdk/schematics/update-tool/utils/virtual-host.ts b/src/cdk/schematics/update-tool/utils/virtual-host.ts new file mode 100644 index 000000000000..02598dc18114 --- /dev/null +++ b/src/cdk/schematics/update-tool/utils/virtual-host.ts @@ -0,0 +1,92 @@ +/** + * @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.io/license + */ + +import * as ts from 'typescript'; +import {FileSystem} from '../file-system'; + +// We use TypeScript's native `ts.matchFiles` utility for the virtual file system +// hosts, as that function implements complex logic for matching files with respect +// to root directory, extensions, excludes, includes etc. The function is currently +// internal but we can use it as the API most likely will not change any time soon, +// nor does it seem like this is being made public any time soon. +// Related issue for tracking: https://github.com/microsoft/TypeScript/issues/13793. +// https://github.com/microsoft/TypeScript/blob/b397d1fd4abd0edef85adf0afd91c030bb0b4955/src/compiler/utilities.ts#L6192 +declare module 'typescript' { + export interface FileSystemEntries { + readonly files: readonly string[]; + readonly directories: readonly string[]; + } + + export const matchFiles: undefined| + ((path: string, extensions: readonly string[]|undefined, + excludes: readonly string[]|undefined, includes: readonly string[]|undefined, + useCaseSensitiveFileNames: boolean, currentDirectory: string, depth: number|undefined, + getFileSystemEntries: (path: string) => FileSystemEntries, + realpath: (path: string) => string) => string[]); +} + +/** + * Implementation of a TypeScript parse config host that relies fully on + * a given virtual file system. + */ +export class FileSystemHost implements ts.ParseConfigHost { + useCaseSensitiveFileNames = ts.sys.useCaseSensitiveFileNames; + + constructor(private _fileSystem: FileSystem) {} + + fileExists(path: string): boolean { + return this._fileSystem.exists(this._fileSystem.resolve(path)); + } + + readFile(path: string): string|undefined { + const content = this._fileSystem.read(this._fileSystem.resolve(path)); + if (content === null) { + return undefined; + } + // Strip BOM as otherwise TSC methods (e.g. "getWidth") will return an offset which + // which breaks the CLI UpdateRecorder. https://github.com/angular/angular/pull/30719 + return content.replace(/^\uFEFF/, ''); + } + + readDirectory( + rootDir: string, extensions: string[], excludes: string[]|undefined, includes: string[], + depth?: number): string[] { + if (ts.matchFiles === undefined) { + throw Error( + 'Unable to read directory in virtual file system host. This means that ' + + 'TypeScript changed its file matching internals.\n\nPlease consider downgrading your ' + + 'TypeScript version, and report an issue in the Angular Components repository.'); + } + return ts.matchFiles( + rootDir, extensions, extensions, includes, this.useCaseSensitiveFileNames, '/', depth, + p => this._getFileSystemEntries(p), p => this._fileSystem.resolve(p)); + } + + private _getFileSystemEntries(path: string): ts.FileSystemEntries { + return this._fileSystem.readDirectory(this._fileSystem.resolve(path)); + } +} + +/** + * Creates a TypeScript compiler host that fully relies fully on the given + * virtual file system. i.e. no interactions with the working directory. + */ +export function createFileSystemCompilerHost( + options: ts.CompilerOptions, fileSystem: FileSystem): ts.CompilerHost { + const host = ts.createCompilerHost(options, true); + const virtualHost = new FileSystemHost(fileSystem); + + host.readFile = virtualHost.readFile.bind(virtualHost); + host.readDirectory = virtualHost.readDirectory.bind(virtualHost); + host.fileExists = virtualHost.fileExists.bind(virtualHost); + host.directoryExists = (dirPath) => fileSystem.exists(fileSystem.resolve(dirPath)); + host.getCurrentDirectory = () => '/'; + host.getCanonicalFileName = p => fileSystem.resolve(p); + + return host; +} diff --git a/src/cdk/schematics/utils/project-tsconfig-paths.spec.ts b/src/cdk/schematics/utils/project-tsconfig-paths.spec.ts index eb9664781a26..5083a9074a90 100644 --- a/src/cdk/schematics/utils/project-tsconfig-paths.spec.ts +++ b/src/cdk/schematics/utils/project-tsconfig-paths.spec.ts @@ -1,5 +1,6 @@ import {HostTree} from '@angular-devkit/schematics'; import {UnitTestTree} from '@angular-devkit/schematics/testing'; +import {WorkspacePath} from '../update-tool/file-system'; import {getTargetTsconfigPath, getWorkspaceConfigGracefully} from './project-tsconfig-paths'; describe('project tsconfig paths', () => { @@ -17,7 +18,7 @@ describe('project tsconfig paths', () => { const config = getWorkspaceConfigGracefully(testTree); expect(config).not.toBeNull(); expect(getTargetTsconfigPath(config!.projects['my_name'], 'build')) - .toEqual('my-custom-config.json'); + .toEqual('my-custom-config.json' as WorkspacePath); }); it('should be able to read workspace configuration which is using JSON5 features', () => { @@ -40,7 +41,7 @@ describe('project tsconfig paths', () => { const config = getWorkspaceConfigGracefully(testTree); expect(config).not.toBeNull(); expect(getTargetTsconfigPath(config!.projects['with_tests'], 'build')) - .toEqual('my-build-config.json'); + .toEqual('my-build-config.json' as WorkspacePath); }); it('should detect test tsconfig path inside of angular.json file', () => { @@ -52,7 +53,7 @@ describe('project tsconfig paths', () => { const config = getWorkspaceConfigGracefully(testTree); expect(config).not.toBeNull(); expect(getTargetTsconfigPath(config!.projects['my_name'], 'test')) - .toEqual('my-test-config.json'); + .toEqual('my-test-config.json' as WorkspacePath); }); it('should detect test tsconfig path inside of .angular.json file', () => { @@ -65,6 +66,6 @@ describe('project tsconfig paths', () => { const config = getWorkspaceConfigGracefully(testTree); expect(config).not.toBeNull(); expect(getTargetTsconfigPath(config!.projects['with_tests'], 'test')) - .toEqual('my-test-config.json'); + .toEqual('my-test-config.json' as WorkspacePath); }); }); diff --git a/src/cdk/schematics/utils/project-tsconfig-paths.ts b/src/cdk/schematics/utils/project-tsconfig-paths.ts index da867b37da04..8786113c5b59 100644 --- a/src/cdk/schematics/utils/project-tsconfig-paths.ts +++ b/src/cdk/schematics/utils/project-tsconfig-paths.ts @@ -9,12 +9,14 @@ import {JsonParseMode, normalize, parseJson} from '@angular-devkit/core'; import {Tree} from '@angular-devkit/schematics'; import {WorkspaceProject, WorkspaceSchema} from '@schematics/angular/utility/workspace-models'; +import {WorkspacePath} from '../update-tool/file-system'; /** Name of the default Angular CLI workspace configuration files. */ const defaultWorkspaceConfigPaths = ['/angular.json', '/.angular.json']; /** Gets the tsconfig path from the given target within the specified project. */ -export function getTargetTsconfigPath(project: WorkspaceProject, targetName: string): string|null { +export function getTargetTsconfigPath(project: WorkspaceProject, + targetName: string): WorkspacePath|null { if (project.targets && project.targets[targetName] && project.targets[targetName].options && project.targets[targetName].options.tsConfig) { return normalize(project.targets[targetName].options.tsConfig); diff --git a/src/material/schematics/ng-update/migrations/hammer-gestures-v9/hammer-gestures-migration.ts b/src/material/schematics/ng-update/migrations/hammer-gestures-v9/hammer-gestures-migration.ts index e39ddb2ef2c3..4cbf2bcb5649 100644 --- a/src/material/schematics/ng-update/migrations/hammer-gestures-v9/hammer-gestures-migration.ts +++ b/src/material/schematics/ng-update/migrations/hammer-gestures-v9/hammer-gestures-migration.ts @@ -7,10 +7,10 @@ */ import { - join as devkitJoin, - normalize as devkitNormalize, + join, Path, - Path as DevkitPath + relative, + dirname } from '@angular-devkit/core'; import {SchematicContext, Tree} from '@angular-devkit/schematics'; import { @@ -25,11 +25,10 @@ import { MigrationFailure, PostMigrationAction, ResolvedResource, - TargetVersion + TargetVersion, } from '@angular/cdk/schematics'; import {InsertChange} from '@schematics/angular/utility/change'; import {readFileSync} from 'fs'; -import {dirname, relative, resolve} from 'path'; import * as ts from 'typescript'; import {findHammerScriptImportElements} from './find-hammer-script-tags'; @@ -241,9 +240,9 @@ export class HammerGesturesMigration extends DevkitMigration { */ private _setupHammerWithCustomEvents() { const project = this.context.project; - const sourceRoot = devkitNormalize(project.sourceRoot || project.root); + const sourceRoot = this.fileSystem.resolve(project.sourceRoot || project.root); const newConfigPath = - devkitJoin(sourceRoot, this._getAvailableGestureConfigFileName(sourceRoot)); + join(sourceRoot, this._getAvailableGestureConfigFileName(sourceRoot)); // Copy gesture config template into the CLI project. this.fileSystem.create( @@ -251,10 +250,11 @@ export class HammerGesturesMigration extends DevkitMigration { // Replace all Material gesture config references to resolve to the // newly copied gesture config. - this._gestureConfigReferences.forEach( - i => this._replaceGestureConfigReference( - i, GESTURE_CONFIG_CLASS_NAME, - getModuleSpecifier(newConfigPath, i.node.getSourceFile().fileName))); + this._gestureConfigReferences.forEach(i => { + const filePath = this.fileSystem.resolve(i.node.getSourceFile().fileName); + return this._replaceGestureConfigReference(i, GESTURE_CONFIG_CLASS_NAME, + getModuleSpecifier(newConfigPath, filePath)); + }); // Setup the gesture config provider and the "HammerModule" in the root module // if not done already. The "HammerModule" is needed in v9 since it enables the @@ -478,14 +478,14 @@ export class HammerGesturesMigration extends DevkitMigration { * Determines an available file name for the gesture config which should * be stored in the specified file path. */ - private _getAvailableGestureConfigFileName(sourceRoot: DevkitPath) { - if (!this.fileSystem.exists(devkitJoin(sourceRoot, `${GESTURE_CONFIG_FILE_NAME}.ts`))) { + private _getAvailableGestureConfigFileName(sourceRoot: Path) { + if (!this.fileSystem.exists(join(sourceRoot, `${GESTURE_CONFIG_FILE_NAME}.ts`))) { return `${GESTURE_CONFIG_FILE_NAME}.ts`; } let possibleName = `${GESTURE_CONFIG_FILE_NAME}-`; let index = 1; - while (this.fileSystem.exists(devkitJoin(sourceRoot, `${possibleName}-${index}.ts`))) { + while (this.fileSystem.exists(join(sourceRoot, `${possibleName}-${index}.ts`))) { index++; } return `${possibleName + index}.ts`; @@ -639,8 +639,8 @@ export class HammerGesturesMigration extends DevkitMigration { } /** Sets up the Hammer gesture config in the root module if needed. */ - private _setupNewGestureConfigInRootModule(gestureConfigPath: string) { - const {workspaceFsPath, project} = this.context; + private _setupNewGestureConfigInRootModule(gestureConfigPath: Path) { + const {project} = this.context; const mainFilePath = getProjectMainFile(project); const rootModuleSymbol = this._getRootModuleSymbol(mainFilePath); @@ -654,7 +654,6 @@ export class HammerGesturesMigration extends DevkitMigration { } const sourceFile = rootModuleSymbol.valueDeclaration.getSourceFile(); - const relativePath = relative(workspaceFsPath, sourceFile.fileName); const metadata = getDecoratorMetadata(sourceFile, 'NgModule', '@angular/core') as ts.ObjectLiteralExpression[]; @@ -663,13 +662,14 @@ export class HammerGesturesMigration extends DevkitMigration { return; } - const recorder = this.fileSystem.edit(this.fileSystem.resolve(sourceFile.fileName)); + const filePath = this.fileSystem.resolve(sourceFile.fileName); + const recorder = this.fileSystem.edit(filePath); const providersField = getMetadataField(metadata[0], 'providers')[0]; const providerIdentifiers = providersField ? findMatchingChildNodes(providersField, ts.isIdentifier) : null; const gestureConfigExpr = this._importManager.addImportToSourceFile( sourceFile, GESTURE_CONFIG_CLASS_NAME, - getModuleSpecifier(gestureConfigPath, sourceFile.fileName), false, + getModuleSpecifier(gestureConfigPath, filePath), false, this._getGestureConfigIdentifiersOfFile(sourceFile)); const hammerConfigTokenExpr = this._importManager.addImportToSourceFile( sourceFile, HAMMER_CONFIG_TOKEN_NAME, HAMMER_CONFIG_TOKEN_MODULE); @@ -684,13 +684,13 @@ export class HammerGesturesMigration extends DevkitMigration { if (!providerIdentifiers || !(this._hammerConfigTokenReferences.some(r => providerIdentifiers.includes(r.node)) && this._gestureConfigReferences.some(r => providerIdentifiers.includes(r.node)))) { - addSymbolToNgModuleMetadata( - sourceFile, relativePath, 'providers', this._printNode(newProviderNode, sourceFile), null) - .forEach(change => { - if (change instanceof InsertChange) { - recorder.insertRight(change.pos, change.toAdd); - } - }); + const symbolName = this._printNode(newProviderNode, sourceFile); + addSymbolToNgModuleMetadata(sourceFile, sourceFile.fileName, 'providers', symbolName, null) + .forEach(change => { + if (change instanceof InsertChange) { + recorder.insertRight(change.pos, change.toAdd); + } + }); } } @@ -699,8 +699,7 @@ export class HammerGesturesMigration extends DevkitMigration { * bootstrap expression in the specified source file. */ private _getRootModuleSymbol(mainFilePath: Path): ts.Symbol|null { - const mainFile = this.program.getSourceFile(resolve( - this.context.workspaceFsPath, mainFilePath)); + const mainFile = this.program.getSourceFile(mainFilePath); if (!mainFile) { return null; } @@ -719,7 +718,7 @@ export class HammerGesturesMigration extends DevkitMigration { /** Sets up the "HammerModule" in the root module of the current project. */ private _setupHammerModuleInRootModule() { - const {workspaceFsPath, project} = this.context; + const {project} = this.context; const mainFilePath = getProjectMainFile(project); const rootModuleSymbol = this._getRootModuleSymbol(mainFilePath); @@ -733,7 +732,6 @@ export class HammerGesturesMigration extends DevkitMigration { } const sourceFile = rootModuleSymbol.valueDeclaration.getSourceFile(); - const relativePath = relative(workspaceFsPath, sourceFile.fileName); const metadata = getDecoratorMetadata(sourceFile, 'NgModule', '@angular/core') as ts.ObjectLiteralExpression[]; if (!metadata.length) { @@ -751,13 +749,13 @@ export class HammerGesturesMigration extends DevkitMigration { // by adding it to the "imports" field of the app module. if (!importIdentifiers || !this._hammerModuleReferences.some(r => importIdentifiers.includes(r.node))) { - addSymbolToNgModuleMetadata( - sourceFile, relativePath, 'imports', this._printNode(hammerModuleExpr, sourceFile), null) - .forEach(change => { - if (change instanceof InsertChange) { - recorder.insertRight(change.pos, change.toAdd); - } - }); + const symbolName = this._printNode(hammerModuleExpr, sourceFile); + addSymbolToNgModuleMetadata(sourceFile, sourceFile.fileName, 'imports', symbolName, null) + .forEach(change => { + if (change instanceof InsertChange) { + recorder.insertRight(change.pos, change.toAdd); + } + }); } } @@ -886,7 +884,7 @@ function unwrapExpression(node: ts.Node): ts.Node { * Converts the specified path to a valid TypeScript module specifier which is * relative to the given containing file. */ -function getModuleSpecifier(newPath: string, containingFile: string) { +function getModuleSpecifier(newPath: Path, containingFile: Path) { let result = relative(dirname(containingFile), newPath).replace(/\\/g, '/').replace(/\.ts$/, ''); if (!result.startsWith('.')) { result = `./${result}`; diff --git a/src/material/schematics/ng-update/migrations/misc-ripples-v7/ripple-speed-factor-migration.ts b/src/material/schematics/ng-update/migrations/misc-ripples-v7/ripple-speed-factor-migration.ts index d00cfc9fe155..a2c92c2b9355 100644 --- a/src/material/schematics/ng-update/migrations/misc-ripples-v7/ripple-speed-factor-migration.ts +++ b/src/material/schematics/ng-update/migrations/misc-ripples-v7/ripple-speed-factor-migration.ts @@ -6,7 +6,12 @@ * found in the LICENSE file at https://angular.io/license */ -import {Migration, ResolvedResource, TargetVersion, WorkspacePath} from '@angular/cdk/schematics'; +import { + Migration, + ResolvedResource, + TargetVersion, + WorkspacePath +} from '@angular/cdk/schematics'; import * as ts from 'typescript'; import { convertSpeedFactorToDuration, diff --git a/src/material/schematics/ng-update/test-cases/misc/class-inheritance.spec.ts b/src/material/schematics/ng-update/test-cases/misc/class-inheritance.spec.ts index 01cb98ab708f..1603ccdcd779 100644 --- a/src/material/schematics/ng-update/test-cases/misc/class-inheritance.spec.ts +++ b/src/material/schematics/ng-update/test-cases/misc/class-inheritance.spec.ts @@ -4,7 +4,7 @@ import {MIGRATION_PATH} from '../../../index.spec'; describe('class inheritance misc checks', () => { describe('v6 class which extends MatFormFieldControl', () => { it('should report if class does not declare "shouldLabelFloat"', async () => { - const {removeTempDir, runFixers} = await createTestCaseSetup( + const {runFixers} = await createTestCaseSetup( 'migration-v6', MIGRATION_PATH, [resolveBazelPath(__dirname, './class-inheritance_input.ts')]); @@ -14,8 +14,6 @@ describe('class inheritance misc checks', () => { /Found class "WithoutLabelProp".*extends "MatFormFieldControl.*must define "shouldLabelFloat"/); expect(logOutput).not.toMatch( /Found class "WithLabelProp".*extends "MatFormFieldControl".*must define "shouldLabelFloat"/); - - removeTempDir(); }); }); }); diff --git a/src/material/schematics/ng-update/test-cases/misc/constructor-checks.spec.ts b/src/material/schematics/ng-update/test-cases/misc/constructor-checks.spec.ts index 598ba96bd319..da0e8771d0ec 100644 --- a/src/material/schematics/ng-update/test-cases/misc/constructor-checks.spec.ts +++ b/src/material/schematics/ng-update/test-cases/misc/constructor-checks.spec.ts @@ -3,7 +3,7 @@ import {MIGRATION_PATH} from '../../../index.spec'; describe('constructor checks', () => { it('should properly report invalid constructor expression signatures', async () => { - const {removeTempDir, runFixers} = await createTestCaseSetup( + const {runFixers} = await createTestCaseSetup( 'migration-v6', MIGRATION_PATH, [resolveBazelPath(__dirname, './constructor-checks_input.ts')]); @@ -41,7 +41,5 @@ describe('constructor checks', () => { expect(logOutput).toMatch(/Found "ExtendedDateAdapter".*super.*: super\(string, Platform\)/); expect(logOutput).toMatch(/Found "ExtendedDateAdapter".*: new \w+\(string, Platform\)/); - - removeTempDir(); }); }); diff --git a/src/material/schematics/ng-update/test-cases/misc/import-checks.spec.ts b/src/material/schematics/ng-update/test-cases/misc/import-checks.spec.ts index b7710522bb85..3f4f7aaab497 100644 --- a/src/material/schematics/ng-update/test-cases/misc/import-checks.spec.ts +++ b/src/material/schematics/ng-update/test-cases/misc/import-checks.spec.ts @@ -3,14 +3,12 @@ import {MIGRATION_PATH} from '../../../index.spec'; describe('v6 import misc checks', () => { it('should report imports for deleted animation constants', async () => { - const {removeTempDir, runFixers} = await createTestCaseSetup( + const {runFixers} = await createTestCaseSetup( 'migration-v6', MIGRATION_PATH, [resolveBazelPath(__dirname, './import-checks_input.ts')]); const {logOutput} = await runFixers(); expect(logOutput).toMatch(/Found deprecated symbol "SHOW_ANIMATION"/); expect(logOutput).toMatch(/Found deprecated symbol "HIDE_ANIMATION"/); - - removeTempDir(); }); }); diff --git a/src/material/schematics/ng-update/test-cases/v8/misc/material-imports.spec.ts b/src/material/schematics/ng-update/test-cases/v8/misc/material-imports.spec.ts index 431230274752..baeed6d5429d 100644 --- a/src/material/schematics/ng-update/test-cases/v8/misc/material-imports.spec.ts +++ b/src/material/schematics/ng-update/test-cases/v8/misc/material-imports.spec.ts @@ -7,7 +7,7 @@ import {MIGRATION_PATH} from '../../../../index.spec'; describe('v8 material imports', () => { it('should re-map top-level material imports to the proper entry points', async () => { - const {runFixers, appTree, writeFile, removeTempDir} = await createTestCaseSetup( + const {runFixers, appTree, writeFile} = await createTestCaseSetup( 'migration-v8', MIGRATION_PATH, [resolveBazelPath(__dirname, './material-imports_input.ts')]); const materialPath = '/node_modules/@angular/material'; @@ -35,7 +35,5 @@ describe('v8 material imports', () => { expect(appTree.readContent('/projects/cdk-testing/src/test-cases/material-imports_input.ts')) .toBe(readFileContent( resolveBazelPath(__dirname, './material-imports_expected_output.ts'))); - - removeTempDir(); }); }); diff --git a/src/material/schematics/ng-update/test-cases/v9/misc/hammer-migration-v9.spec.ts b/src/material/schematics/ng-update/test-cases/v9/misc/hammer-migration-v9.spec.ts index 02cfecd95b8a..42a56882a117 100644 --- a/src/material/schematics/ng-update/test-cases/v9/misc/hammer-migration-v9.spec.ts +++ b/src/material/schematics/ng-update/test-cases/v9/misc/hammer-migration-v9.spec.ts @@ -13,7 +13,6 @@ describe('v9 HammerJS removal', () => { let tree: UnitTestTree; let writeFile: (filePath: string, text: string) => void; let runMigration: () => Promise<{logOutput: string}>; - let cleanupTest: () => void; beforeEach(async () => { const testSetup = await createTestCaseSetup('migration-v9', MIGRATION_PATH, []); @@ -22,11 +21,8 @@ describe('v9 HammerJS removal', () => { tree = testSetup.appTree; runMigration = testSetup.runFixers; writeFile = testSetup.writeFile; - cleanupTest = testSetup.removeTempDir; }); - afterEach(() => cleanupTest()); - function appendContent(filePath: string, text: string) { writeFile(filePath, text + tree.readContent(filePath)) } diff --git a/src/material/schematics/ng-update/test-cases/v9/misc/material-imports.spec.ts b/src/material/schematics/ng-update/test-cases/v9/misc/material-imports.spec.ts index f9881de3342f..4cac96d67697 100644 --- a/src/material/schematics/ng-update/test-cases/v9/misc/material-imports.spec.ts +++ b/src/material/schematics/ng-update/test-cases/v9/misc/material-imports.spec.ts @@ -4,7 +4,7 @@ import {MIGRATION_PATH} from '../../../../index.spec'; describe('v9 material imports', () => { it('should re-map top-level material imports to the proper entry points when top-level ' + '@angular/material package does not exist', async () => { - const {runFixers, appTree, removeTempDir} = await createTestCaseSetup( + const {runFixers, appTree} = await createTestCaseSetup( 'migration-v9', MIGRATION_PATH, [resolveBazelPath(__dirname, './material-imports_input.ts')]); @@ -15,7 +15,5 @@ describe('v9 material imports', () => { expect(appTree.readContent('/projects/cdk-testing/src/test-cases/material-imports_input.ts')) .toBe(readFileContent( resolveBazelPath(__dirname, './material-imports_expected_output.ts'))); - - removeTempDir(); }); });