From 6f56f01bd334cd0c0921f98eb859c5c019bec966 Mon Sep 17 00:00:00 2001 From: Paul Gschwendtner Date: Wed, 5 Sep 2018 21:10:48 +0200 Subject: [PATCH 1/2] 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. --- .circleci/config.yml | 6 +++- src/lib/schematics/utils/ast.ts | 9 ++--- src/lib/schematics/utils/build-component.ts | 21 +++++------- .../utils/version-agnostic-typescript.ts | 33 +++++++++++++++++++ 4 files changed, 49 insertions(+), 20 deletions(-) create mode 100644 src/lib/schematics/utils/version-agnostic-typescript.ts diff --git a/.circleci/config.yml b/.circleci/config.yml index ffa93b531e19..408788728750 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -47,7 +47,11 @@ jobs: <<: *post_checkout - restore_cache: key: *cache_key - - run: echo "Temporarily disabled until Bazel setup can be fixed" + + - run: bazel run @nodejs//:npm install + # TODO(jelbourn): Update this command to run all tests if the Bazel issues have been fixed. + - run: bazel test src/lib/schematics:unit_tests + - save_cache: key: *cache_key paths: diff --git a/src/lib/schematics/utils/ast.ts b/src/lib/schematics/utils/ast.ts index 5f8d010deda0..ccb7f3b44f73 100644 --- a/src/lib/schematics/utils/ast.ts +++ b/src/lib/schematics/utils/ast.ts @@ -13,12 +13,11 @@ import {InsertChange} from '@schematics/angular/utility/change'; import {getWorkspace, WorkspaceProject} from '@schematics/angular/utility/config'; import {findModuleFromOptions as internalFindModule} from '@schematics/angular/utility/find-module'; import {getAppModulePath} from '@schematics/angular/utility/ng-ast-utils'; -import * as ts from 'typescript'; import {getProjectMainFile} from './project-main-file'; - +import {ts} from './version-agnostic-typescript'; /** Reads file given path and returns TypeScript source file. */ -export function getSourceFile(host: Tree, path: string): ts.SourceFile { +export function getSourceFile(host: Tree, path: string) { const buffer = host.read(path); if (!buffer) { throw new SchematicsException(`Could not find file for path: ${path}`); @@ -50,9 +49,7 @@ export function addModuleImportToModule(host: Tree, modulePath: string, moduleNa throw new SchematicsException(`Module not found: ${modulePath}`); } - // TODO: cast to any, because the types for ts.SourceFile - // aren't compatible with `strictFunctionTypes`. - const changes = addImportToModule(moduleSource as any, modulePath, moduleName, src); + const changes = addImportToModule(moduleSource, modulePath, moduleName, src); const recorder = host.beginUpdate(modulePath); changes.forEach((change) => { diff --git a/src/lib/schematics/utils/build-component.ts b/src/lib/schematics/utils/build-component.ts index 6e2c6f64504f..60084ea16dba 100644 --- a/src/lib/schematics/utils/build-component.ts +++ b/src/lib/schematics/utils/build-component.ts @@ -38,18 +38,17 @@ import {buildDefaultPath} from '@schematics/angular/utility/project'; import {validateHtmlSelector, validateName} from '@schematics/angular/utility/validation'; import {readFileSync} from 'fs'; import {dirname, join, resolve} from 'path'; -import * as ts from 'typescript'; import {getProjectFromWorkspace} from './get-project'; import {getDefaultComponentOptions} from './schematic-options'; +import {ts} from './version-agnostic-typescript'; -function readIntoSourceFile(host: Tree, modulePath: string): ts.SourceFile { +function readIntoSourceFile(host: Tree, modulePath: string) { const text = host.read(modulePath); if (text === null) { throw new SchematicsException(`File ${modulePath} does not exist.`); } - const sourceText = text.toString('utf-8'); - return ts.createSourceFile(modulePath, sourceText, ts.ScriptTarget.Latest, true); + return ts.createSourceFile(modulePath, text.toString('utf-8'), ts.ScriptTarget.Latest, true); } function addDeclarationToNgModule(options: ComponentOptions): Rule { @@ -68,9 +67,8 @@ function addDeclarationToNgModule(options: ComponentOptions): Rule { const relativePath = buildRelativePath(modulePath, componentPath); const classifiedName = strings.classify(`${options.name}Component`); - // TODO: cast to any, because the types for ts.SourceFile - // aren't compatible with `strictFunctionTypes`. - const declarationChanges = addDeclarationToModule(source as any, + const declarationChanges = addDeclarationToModule( + source, modulePath, classifiedName, relativePath); @@ -88,9 +86,7 @@ function addDeclarationToNgModule(options: ComponentOptions): Rule { const source = readIntoSourceFile(host, modulePath); const exportRecorder = host.beginUpdate(modulePath); - // TODO: cast to any, because the types for ts.SourceFile - // aren't compatible with `strictFunctionTypes`. - const exportChanges = addExportToModule(source as any, modulePath, + const exportChanges = addExportToModule(source, modulePath, strings.classify(`${options.name}Component`), relativePath); @@ -107,10 +103,9 @@ function addDeclarationToNgModule(options: ComponentOptions): Rule { const source = readIntoSourceFile(host, modulePath); const entryComponentRecorder = host.beginUpdate(modulePath); - // TODO: cast to any, because the types for ts.SourceFile - // aren't compatible with `strictFunctionTypes`. const entryComponentChanges = addEntryComponentToModule( - source as any, modulePath, + source, + modulePath, strings.classify(`${options.name}Component`), relativePath); diff --git a/src/lib/schematics/utils/version-agnostic-typescript.ts b/src/lib/schematics/utils/version-agnostic-typescript.ts new file mode 100644 index 000000000000..d52e90f35997 --- /dev/null +++ b/src/lib/schematics/utils/version-agnostic-typescript.ts @@ -0,0 +1,33 @@ +/** + * @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 + */ + +/** This is just a type import and won't be generated in the release output. */ +import typescript = require('@schematics/angular/node_modules/typescript'); + +/** + * This is an agnostic re-export of TypeScript. Depending on the context, this module file will + * return the TypeScript version that is being shipped within the `@schematics/angular` package, + * or fall back to the TypeScript version that has been flattened in the node modules. + * + * This is necessary because we parse TypeScript files and pass the resolved AST to the + * `@schematics/angular` package which might have a different TypeScript version installed. + */ +let ts: typeof typescript; + +try { + ts = require('@schematics/angular/node_modules/typescript'); +} catch { + try { + ts = require('typescript'); + } catch { + throw new Error('Error: Could not find TypeScript for the Angular Material schematics. ' + + 'Please report an issue on the Angular Material repository.'); + } +} + +export {ts}; From 33cb34cd0738f505b0ec682f5164fdeab2685fd7 Mon Sep 17 00:00:00 2001 From: Paul Gschwendtner Date: Thu, 6 Sep 2018 01:26:43 +0200 Subject: [PATCH 2/2] Add workaround for Bazel ts module resolution --- src/lib/schematics/utils/ast.ts | 5 ++++- src/lib/schematics/utils/build-component.ts | 17 ++++++++++++++--- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/src/lib/schematics/utils/ast.ts b/src/lib/schematics/utils/ast.ts index ccb7f3b44f73..06cb9b14cf28 100644 --- a/src/lib/schematics/utils/ast.ts +++ b/src/lib/schematics/utils/ast.ts @@ -49,7 +49,10 @@ export function addModuleImportToModule(host: Tree, modulePath: string, moduleNa throw new SchematicsException(`Module not found: ${modulePath}`); } - const changes = addImportToModule(moduleSource, modulePath, moduleName, src); + // TODO(devversion): Cast to any because the Bazel typescript rules seem to incorrectly resolve + // the the required TypeScript version for the @schematics/angular utility functions. Meaning + // that is a type signature mismatch at compilation which is not valid. + const changes = addImportToModule(moduleSource as any, modulePath, moduleName, src); const recorder = host.beginUpdate(modulePath); changes.forEach((change) => { diff --git a/src/lib/schematics/utils/build-component.ts b/src/lib/schematics/utils/build-component.ts index 60084ea16dba..0c2b0927ea68 100644 --- a/src/lib/schematics/utils/build-component.ts +++ b/src/lib/schematics/utils/build-component.ts @@ -67,8 +67,11 @@ function addDeclarationToNgModule(options: ComponentOptions): Rule { const relativePath = buildRelativePath(modulePath, componentPath); const classifiedName = strings.classify(`${options.name}Component`); + // TODO(devversion): Cast to any because the Bazel typescript rules seem to incorrectly resolve + // the the required TypeScript version for the @schematics/angular utility functions. Meaning + // that is a type signature mismatch at compilation which is not valid. const declarationChanges = addDeclarationToModule( - source, + source as any, modulePath, classifiedName, relativePath); @@ -86,7 +89,12 @@ function addDeclarationToNgModule(options: ComponentOptions): Rule { const source = readIntoSourceFile(host, modulePath); const exportRecorder = host.beginUpdate(modulePath); - const exportChanges = addExportToModule(source, modulePath, + // TODO(devversion): Cast to any because the Bazel typescript rules seem to incorrectly resolve + // the the required TypeScript version for the @schematics/angular utility functions. Meaning + // that is a type signature mismatch at compilation which is not valid. + const exportChanges = addExportToModule( + source as any, + modulePath, strings.classify(`${options.name}Component`), relativePath); @@ -103,8 +111,11 @@ function addDeclarationToNgModule(options: ComponentOptions): Rule { const source = readIntoSourceFile(host, modulePath); const entryComponentRecorder = host.beginUpdate(modulePath); + // TODO(devversion): Cast to any because the Bazel typescript rules seem to incorrectly resolve + // the the required TypeScript version for the @schematics/angular utility functions. Meaning + // that is a type signature mismatch at compilation which is not valid. const entryComponentChanges = addEntryComponentToModule( - source, + source as any, modulePath, strings.classify(`${options.name}Component`), relativePath);