Skip to content

Commit a00e56b

Browse files
committed
refactor(schematics): avoid typescript version conflicts
The Angular Material schematics parse TypeScript sources files and pass the AST to the `@schematics/angular` package which defined an explicit dependency on `typescript`. Since we currently just always require the flattened `typescript` dependency (`node_modules/typescript`), there could be either no TypeScript version or a different TypeScript version that causes the AST operations of the `@schematics/angular` utility functions to not work properly (e.g. different `SyntaxKind` ids) We should primarily try to load the same TypeScript version that has been shipped with the `@schematics/angular`. If that one couldn't be found, fall back to the top level `typescript` version.
1 parent dc6688d commit a00e56b

File tree

4 files changed

+49
-19
lines changed

4 files changed

+49
-19
lines changed

.circleci/config.yml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,11 @@ jobs:
4747
<<: *post_checkout
4848
- restore_cache:
4949
key: *cache_key
50-
- run: echo "Temporarily disabled until Bazel setup can be fixed"
50+
51+
- run: bazel run @nodejs//:npm install
52+
# TODO(jelbourn): Update this command to run all tests if the Bazel issues have been fixed.
53+
- run: bazel test src/lib/schematics:unit_tests
54+
5155
- save_cache:
5256
key: *cache_key
5357
paths:

src/lib/schematics/utils/ast.ts

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,11 @@ import {InsertChange} from '@schematics/angular/utility/change';
1212
import {getWorkspace, WorkspaceProject} from '@schematics/angular/utility/config';
1313
import {findModuleFromOptions as internalFindModule} from '@schematics/angular/utility/find-module';
1414
import {getAppModulePath} from '@schematics/angular/utility/ng-ast-utils';
15-
import * as ts from 'typescript';
15+
import {ts} from './version-agnostic-typescript';
1616

1717

1818
/** Reads file given path and returns TypeScript source file. */
19-
export function getSourceFile(host: Tree, path: string): ts.SourceFile {
19+
export function getSourceFile(host: Tree, path: string) {
2020
const buffer = host.read(path);
2121
if (!buffer) {
2222
throw new SchematicsException(`Could not find file for path: ${path}`);
@@ -48,9 +48,7 @@ export function addModuleImportToModule(
4848
throw new SchematicsException(`Module not found: ${modulePath}`);
4949
}
5050

51-
// TODO: cast to any, because the types for ts.SourceFile
52-
// aren't compatible with `strictFunctionTypes`.
53-
const changes = addImportToModule(moduleSource as any, modulePath, moduleName, src);
51+
const changes = addImportToModule(moduleSource, modulePath, moduleName, src);
5452
const recorder = host.beginUpdate(modulePath);
5553

5654
changes.forEach((change) => {

src/lib/schematics/utils/build-component.ts

Lines changed: 8 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -38,17 +38,16 @@ import {buildDefaultPath} from '@schematics/angular/utility/project';
3838
import {validateHtmlSelector, validateName} from '@schematics/angular/utility/validation';
3939
import {readFileSync} from 'fs';
4040
import {dirname, join, resolve} from 'path';
41-
import * as ts from 'typescript';
41+
import {ts} from './version-agnostic-typescript';
4242
import {getDefaultComponentOptions} from './schematic-options';
4343

44-
function readIntoSourceFile(host: Tree, modulePath: string): ts.SourceFile {
44+
function readIntoSourceFile(host: Tree, modulePath: string) {
4545
const text = host.read(modulePath);
4646
if (text === null) {
4747
throw new SchematicsException(`File ${modulePath} does not exist.`);
4848
}
49-
const sourceText = text.toString('utf-8');
5049

51-
return ts.createSourceFile(modulePath, sourceText, ts.ScriptTarget.Latest, true);
50+
return ts.createSourceFile(modulePath, text.toString('utf-8'), ts.ScriptTarget.Latest, true);
5251
}
5352

5453
function addDeclarationToNgModule(options: ComponentOptions): Rule {
@@ -67,9 +66,8 @@ function addDeclarationToNgModule(options: ComponentOptions): Rule {
6766
const relativePath = buildRelativePath(modulePath, componentPath);
6867
const classifiedName = strings.classify(`${options.name}Component`);
6968

70-
// TODO: cast to any, because the types for ts.SourceFile
71-
// aren't compatible with `strictFunctionTypes`.
72-
const declarationChanges = addDeclarationToModule(source as any,
69+
const declarationChanges = addDeclarationToModule(
70+
source,
7371
modulePath,
7472
classifiedName,
7573
relativePath);
@@ -87,9 +85,7 @@ function addDeclarationToNgModule(options: ComponentOptions): Rule {
8785
const source = readIntoSourceFile(host, modulePath);
8886
const exportRecorder = host.beginUpdate(modulePath);
8987

90-
// TODO: cast to any, because the types for ts.SourceFile
91-
// aren't compatible with `strictFunctionTypes`.
92-
const exportChanges = addExportToModule(source as any, modulePath,
88+
const exportChanges = addExportToModule(source, modulePath,
9389
strings.classify(`${options.name}Component`),
9490
relativePath);
9591

@@ -106,10 +102,9 @@ function addDeclarationToNgModule(options: ComponentOptions): Rule {
106102
const source = readIntoSourceFile(host, modulePath);
107103
const entryComponentRecorder = host.beginUpdate(modulePath);
108104

109-
// TODO: cast to any, because the types for ts.SourceFile
110-
// aren't compatible with `strictFunctionTypes`.
111105
const entryComponentChanges = addEntryComponentToModule(
112-
source as any, modulePath,
106+
source,
107+
modulePath,
113108
strings.classify(`${options.name}Component`),
114109
relativePath);
115110

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
/**
2+
* @license
3+
* Copyright Google LLC All Rights Reserved.
4+
*
5+
* Use of this source code is governed by an MIT-style license that can be
6+
* found in the LICENSE file at https://angular.io/license
7+
*/
8+
9+
/** This is just a type import and won't be generated in the release output. */
10+
import typescript = require('@schematics/angular/node_modules/typescript');
11+
12+
/**
13+
* This is an agnostic re-export of TypeScript. Depending on the context, this module file will
14+
* return the TypeScript version that is being shipped within the `@schematics/angular` package,
15+
* or fall back to the TypeScript version that has been flattened in the node modules.
16+
*
17+
* This is necessary because we parse TypeScript files and pass the resolved AST to the
18+
* `@schematics/angular` package which might have a different TypeScript version installed.
19+
*/
20+
let ts: typeof typescript;
21+
22+
try {
23+
ts = require('@schematics/angular/node_modules/typescript');
24+
} catch {
25+
try {
26+
ts = require('typescript');
27+
} catch {
28+
throw new Error('Error: Could not find TypeScript for the Angular Material schematics. ' +
29+
'Please report an issue on the Angular Material repository.');
30+
}
31+
}
32+
33+
export {ts};

0 commit comments

Comments
 (0)