From cc4fb04b33f95843a1c4d387b58127fae7672e90 Mon Sep 17 00:00:00 2001 From: Paul Gschwendtner Date: Fri, 10 Jul 2020 11:35:18 +0200 Subject: [PATCH] fix(ng-update): properly handle update from different working directory In some situations, developers will run `ng update` from a sub directory of the project. This currently results in a noop migration as file system paths were computed incorrectly. This is because ng-update always assumed that the CWD is the workspace root in the real file system. This is not the case and therefore threw off path resolution. We fix this by fully relying on the virtual file system now. Previously we mixed virtual and real file system as TypeScript relied on the real file system for parsing configuration, matching and reading files. This commit adds a new compiler host for config parsing and program creation that works fully with a virtual file system. This is something we had planned a long time ago as schematics should never deal with the real file system. We can re-use this logic in other repositories too (such as Angular framework, potentially the CLI, and universal schematics). Ideally we will share this as part of better tooling for migrations. Fixes #19779. --- .../ng-update/devkit-file-system.ts | 40 ++++---- .../ng-update/devkit-migration-rule.ts | 24 ++--- .../schematics/ng-update/devkit-migration.ts | 2 - .../schematics/ng-update/find-stylesheets.ts | 24 +++++ .../migrations/attribute-selectors.ts | 5 +- .../ng-update/migrations/css-selectors.ts | 4 +- .../ng-update/migrations/element-selectors.ts | 5 +- .../misc/external-resource-resolution.spec.ts | 4 +- .../misc/global-stylesheets.spec.ts | 8 +- .../misc/method-call-checks.spec.ts | 4 +- src/cdk/schematics/testing/test-case-setup.ts | 30 ++---- src/cdk/schematics/update-tool/file-system.ts | 34 ++++--- src/cdk/schematics/update-tool/index.ts | 28 +++--- .../update-tool/utils/parse-tsconfig.ts | 21 ++--- .../update-tool/utils/virtual-host.ts | 91 +++++++++++++++++++ .../utils/project-tsconfig-paths.spec.ts | 9 +- .../utils/project-tsconfig-paths.ts | 4 +- src/cdk/schematics/utils/workspace-fs-path.ts | 30 ++++++ .../hammer-gestures-migration.ts | 74 ++++++++------- .../ripple-speed-factor-migration.ts | 7 +- .../test-cases/misc/class-inheritance.spec.ts | 4 +- .../misc/constructor-checks.spec.ts | 4 +- .../test-cases/misc/import-checks.spec.ts | 4 +- .../v8/misc/material-imports.spec.ts | 4 +- .../v9/misc/hammer-migration-v9.spec.ts | 6 +- .../v9/misc/material-imports.spec.ts | 4 +- 26 files changed, 293 insertions(+), 181 deletions(-) create mode 100644 src/cdk/schematics/ng-update/find-stylesheets.ts create mode 100644 src/cdk/schematics/update-tool/utils/virtual-host.ts create mode 100644 src/cdk/schematics/utils/workspace-fs-path.ts diff --git a/src/cdk/schematics/ng-update/devkit-file-system.ts b/src/cdk/schematics/ng-update/devkit-file-system.ts index b11add75f4f3..6f6fdcde2ec6 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) { @@ -73,4 +62,23 @@ export class DevkitFileSystem extends FileSystem { const buffer = this._tree.read(filePath); return buffer !== null ? buffer.toString() : null; } + + existsDirectory(dirPath: 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 { + this._tree.get(dirPath); + } catch (e) { + if (e instanceof PathIsDirectoryException) { + return true; + } + } + return false; + } + + readDirectory(dirPath: Path): DirectoryEntry { + const {subdirs: directories, subfiles: files} = this._tree.getDir(dirPath); + return {directories: 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..5e48f15a79d9 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; @@ -123,12 +120,9 @@ 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, isTestTarget: boolean) { + const program = UpdateProject.createProgramFromTsconfig(tsconfigPath, fileSystem); const updateContext: DevkitContext = { - workspaceFsPath, isTestTarget, projectName, project, @@ -145,13 +139,9 @@ export function createMigrationSchematicRule( // 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. + // in the project and migrate them if needed. // 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 additionalStylesheets = findStylesheetFiles(tree, project.root); const result = updateProject.migrate(migrations, targetVersion, upgradeData, additionalStylesheets); 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..fa027098b015 --- /dev/null +++ b/src/cdk/schematics/ng-update/find-stylesheets.ts @@ -0,0 +1,24 @@ +/** + * @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 {Path} 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[] = []; + tree.getDir(baseDir).visit((filePath: Path) => { + if (STYLESHEET_REGEX.test(filePath)) { + result.push(filePath); + } + }); + 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..6885d6ad904c 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 { @@ -77,7 +69,7 @@ export async function createTestCaseSetup(migrationName: string, collectionPath: 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 +97,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 +105,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}; } /** @@ -195,18 +180,15 @@ export function defineJasmineTestCases(versionName: string, collectionFile: stri 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..df627790d2e8 100644 --- a/src/cdk/schematics/update-tool/file-system.ts +++ b/src/cdk/schematics/update-tool/file-system.ts @@ -23,40 +23,48 @@ 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 { +export abstract class FileSystem { /** Checks whether a given file exists. */ - abstract exists(filePath: T): boolean; + abstract exists(filePath: WorkspacePath): boolean; + /** Gets whether the given directory exists. */ + abstract existsDirectory(dirPath: 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 +74,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..6a6cded53cda 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) {} /** @@ -90,6 +98,7 @@ export class UpdateProject { // migration. Note that this can only happen after source files have been // visited because we find stylesheets through the TypeScript source files. resourceCollector.resolvedStylesheets.forEach(stylesheet => { + console.error(stylesheet.filePath) // Do not visit the stylesheet if it has been checked before. Inline // stylesheets cannot be referenced multiple times. if (stylesheet.inline || !this._analyzedFiles.has(stylesheet.filePath)) { @@ -154,18 +163,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..4217c8d0350d --- /dev/null +++ b/src/cdk/schematics/update-tool/utils/virtual-host.ts @@ -0,0 +1,91 @@ +/** + * @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 relies fully on the given virtual file system. + */ +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.existsDirectory(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/cdk/schematics/utils/workspace-fs-path.ts b/src/cdk/schematics/utils/workspace-fs-path.ts new file mode 100644 index 000000000000..9352ad797342 --- /dev/null +++ b/src/cdk/schematics/utils/workspace-fs-path.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 {getSystemPath} from '@angular-devkit/core'; +import {HostDirEntry, HostTree, Tree} from '@angular-devkit/schematics'; + +/** + * Gets the workspace file system path from the given tree. Returns + * `null` if the path could not be determined. + */ +// TODO: Remove this once we have a fully virtual TypeScript compiler host: COMP-387 +export function getWorkspaceFileSystemPath(tree: Tree): string|null { + if (!(tree.root instanceof HostDirEntry)) { + return null; + } + const hostTree = tree.root['_tree']; + if (!(hostTree instanceof HostTree) || hostTree['_backend'] === undefined) { + return null; + } + const backend = hostTree['_backend']; + if (backend['_root'] !== undefined) { + return getSystemPath(backend['_root']); + } + return null; +} 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..9169d9e84ea0 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 @@ -5,7 +5,7 @@ import {createTestCaseSetup, resolveBazelPath} from '@angular/cdk/schematics/tes import {readFileSync} from 'fs'; import {MIGRATION_PATH} from '../../../../index.spec'; -describe('v9 HammerJS removal', () => { +fdescribe('v9 HammerJS removal', () => { const GESTURE_CONFIG_TEMPLATE_PATH = resolveBazelPath(__dirname, '../../../migrations/hammer-gestures-v9/gesture-config.template'); @@ -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(); }); });