Skip to content

Commit

Permalink
fix(core): renderer-to-renderer2 migration not migrating methods (#33571
Browse files Browse the repository at this point in the history
)

The `renderer-to-renderer2` migration currently does not work
properly in v9 because the migration relies on the type checker
for detecting references to `Renderer` from `@angular/core`.

This is contradictory since the `Renderer` is no longer
exported in v9 `@angular/core`. In order to make sure that
the migration still works in v9, and that we can rely on the
type checker for the best possible detection, we take advantage
of module augmentation and in-memory add the `Renderer` export
to the `@angular/core` module.

PR Close #33571
  • Loading branch information
devversion authored and atscott committed Nov 5, 2019
1 parent 974005b commit d751ca7
Show file tree
Hide file tree
Showing 3 changed files with 24 additions and 8 deletions.
24 changes: 19 additions & 5 deletions packages/core/schematics/migrations/renderer-to-renderer2/index.ts
Expand Up @@ -18,18 +18,17 @@ import {HelperFunction, getHelper} from './helpers';
import {migrateExpression, replaceImport} from './migration';
import {findCoreImport, findRendererReferences} from './util';


const MODULE_AUGMENTATION_FILENAME = 'ɵɵRENDERER_MIGRATION_CORE_AUGMENTATION.d.ts';

/**
* Migration that switches from `Renderer` to `Renderer2`. More information on how it works:
* https://hackmd.angular.io/UTzUZTnPRA-cSa_4mHyfYw
*/
export default function(): Rule {
return (tree: Tree, context: SchematicContext) => {
return (tree: Tree) => {
const {buildPaths, testPaths} = getProjectTsConfigPaths(tree);
const basePath = process.cwd();
const allPaths = [...buildPaths, ...testPaths];
const logger = context.logger;

if (!allPaths.length) {
throw new SchematicsException(
Expand All @@ -44,8 +43,23 @@ export default function(): Rule {

function runRendererToRenderer2Migration(tree: Tree, tsconfigPath: string, basePath: string) {
const parsed = parseTsconfigFile(tsconfigPath, dirname(tsconfigPath));
const host = createMigrationCompilerHost(tree, parsed.options, basePath);
const program = ts.createProgram(parsed.fileNames, parsed.options, host);
const host = createMigrationCompilerHost(tree, parsed.options, basePath, fileName => {
// In case the module augmentation file has been requested, we return a source file that
// augments "@angular/core" to include a named export called "Renderer". This ensures that
// we can rely on the type checker for this migration in v9 where "Renderer" has been removed.
if (fileName === MODULE_AUGMENTATION_FILENAME) {
return `
import '@angular/core';
declare module "@angular/core" {
class Renderer {}
}
`;
}
return null;
});

const program =
ts.createProgram(parsed.fileNames.concat(MODULE_AUGMENTATION_FILENAME), parsed.options, host);
const typeChecker = program.getTypeChecker();
const printer = ts.createPrinter();
const sourceFiles = program.getSourceFiles().filter(
Expand Down
Expand Up @@ -35,7 +35,6 @@ describe('Renderer to Renderer2 migration', () => {
}));
// We need to declare the Angular symbols we're testing for, otherwise type checking won't work.
writeFile('/node_modules/@angular/core/index.d.ts', `
export declare abstract class Renderer {}
export declare function forwardRef(fn: () => any): any {}
`);

Expand Down
7 changes: 5 additions & 2 deletions packages/core/schematics/utils/typescript/compiler_host.ts
Expand Up @@ -10,15 +10,18 @@ import {relative} from 'path';
import * as ts from 'typescript';

export function createMigrationCompilerHost(
tree: Tree, options: ts.CompilerOptions, basePath: string): ts.CompilerHost {
tree: Tree, options: ts.CompilerOptions, basePath: string,
fakeRead?: (fileName: string) => string | null): ts.CompilerHost {
const host = ts.createCompilerHost(options, true);

// We need to overwrite the host "readFile" method, as we want the TypeScript
// program to be based on the file contents in the virtual file tree. Otherwise
// if we run multiple migrations we might have intersecting changes and
// source files.
host.readFile = fileName => {
const buffer = tree.read(relative(basePath, fileName));
const treeRelativePath = relative(basePath, fileName);
const fakeOutput = fakeRead ? fakeRead(treeRelativePath) : null;
const buffer = fakeOutput === null ? tree.read(treeRelativePath) : fakeOutput;
// Strip BOM as otherwise TSC methods (Ex: getWidth) will return an offset which
// which breaks the CLI UpdateRecorder.
// See: https://github.com/angular/angular/pull/30719
Expand Down

0 comments on commit d751ca7

Please sign in to comment.