Skip to content

Commit

Permalink
fix(schematics): secondary entry point migration not working against …
Browse files Browse the repository at this point in the history
…v9 (#17452)

Currently the secondary entry point migration uses the type checker to figure out which Material module a symbol belongs to. This won't work in v9, because we removed the top-level `@angular/material`. These changes fall back to looking up the name of the symbol in a list of known symbols and their module names.

Fixes #17433.
  • Loading branch information
crisbeto authored and jelbourn committed Oct 21, 2019
1 parent 1ab4b77 commit 5e10833
Show file tree
Hide file tree
Showing 9 changed files with 634 additions and 39 deletions.
53 changes: 53 additions & 0 deletions scripts/generate-schematic-imports-map.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import {sync as glob} from 'glob';
import {readFileSync, writeFileSync} from 'fs';
import {join, basename} from 'path';
import * as ts from 'typescript';

// Script that generates mappings from our publicly-exported symbols to their entry points. The
// mappings are intended to be used by the secondary entry points schematic and should be committed
// next to the relevant schematic file.
// Can be run using `ts-node --project scripts scripts/generate-schematic-imports-map.ts`.
const mappings: {[symbolName: string]: string} = {};
const outputPath = join(__dirname, '../temp-entry-points-mapping.json');

glob('**/*.d.ts', {
absolute: true,
cwd: join(__dirname, '../tools/public_api_guard/material')
}).forEach(fileName => {
const content = readFileSync(fileName, 'utf8');
const sourceFile = ts.createSourceFile(fileName, content, ts.ScriptTarget.ES5);
const moduleName = basename(fileName, '.d.ts');

// We only care about the top-level symbols.
sourceFile.forEachChild((node: ts.Node & {name?: ts.Identifier}) => {
// Most of the exports are named nodes (e.g. classes, types, interfaces) so we can use the
// `name` property to extract the name. The one exception are variable declarations for
// which we need to loop through the list of declarations.
if (node.name) {
addMapping(moduleName, node.name.text);
} else if (ts.isVariableStatement(node)) {
node.declarationList.declarations.forEach(declaration => {
if (ts.isIdentifier(declaration.name)) {
addMapping(moduleName, declaration.name.text);
} else {
throw Error('Unsupported variable kind.');
}
});
} else if (node.kind !== ts.SyntaxKind.EndOfFileToken) {
throw Error(`Unhandled node kind ${node.kind} in ${fileName}.`);
}
});
});

/** Adds a symbol to the mappings. */
function addMapping(moduleName: string, symbolName: string) {
if (mappings[symbolName] && mappings[symbolName] !== moduleName) {
throw Error(`Duplicate symbol name ${symbolName}.`);
}

mappings[symbolName] = moduleName;
}

writeFileSync(outputPath, JSON.stringify(mappings, null, 2));
console.log(`Generated mappings to ${outputPath}. You should move the file to the ` +
`proper place yourself.`);
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import {createTestCaseSetup, readFileContent} from '@angular/cdk/schematics/test
import {migrationCollection} from '../index.spec';

describe('v8 material imports', () => {
it('should report imports for deleted animation constants', async () => {
it('should re-map top-level material imports to the proper entry points', async () => {
const {runFixers, appTree, writeFile, removeTempDir} = await createTestCaseSetup(
'migration-v8', migrationCollection, [require.resolve('./material-imports_input.ts')]);
const materialPath = '/node_modules/@angular/material';
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import {createTestCaseSetup, readFileContent} from '@angular/cdk/schematics/testing';
import {migrationCollection} 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(
'migration-v9', migrationCollection, [require.resolve('./material-imports_input.ts')]);

// Note: don't create a fake @angular/material package here, because
// we're testing what would happen if it doesn't exist anymore.
await runFixers();

expect(appTree.readContent('/projects/cdk-testing/src/test-cases/material-imports_input.ts'))
.toBe(readFileContent(require.resolve('./material-imports_expected_output.ts')));

removeTempDir();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { MatDialogContainer as DialogContainer } from '@angular/material/dialog';
import { MatAccordion, MatExpansionPanel, MatExpansionPanelHeader } from '@angular/material/expansion';
import { MAT_PROGRESS_SPINNER_DEFAULT_OPTIONS as SPINNER_DEFAULTS } from '@angular/material/progress-spinner';

// unsorted
import { MatTreeNodeToggle, MatTreeNodeDef, MatTree } from '@angular/material/tree';

import { /* comment */ MatTooltip as Tooltip } from '@angular/material/tooltip';

// primary entry-point export
import { VERSION } from '@angular/material/core';

// type import
import { MatMenuPanel } from "@angular/material/menu";
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import {MatDialogContainer as DialogContainer} from '@angular/material';
import {MatAccordion, MatExpansionPanel, MatExpansionPanelHeader} from '@angular/material';
import {MAT_PROGRESS_SPINNER_DEFAULT_OPTIONS as SPINNER_DEFAULTS} from '@angular/material';

// unsorted
import {MatTreeNodeToggle, MatTreeNodeDef, MatTree} from '@angular/material';

import {/* comment */ MatTooltip as Tooltip} from '@angular/material';

// primary entry-point export
import {VERSION} from '@angular/material';

// type import
import {MatMenuPanel} from "@angular/material";
Loading

0 comments on commit 5e10833

Please sign in to comment.