diff --git a/packages/compiler-cli/ngcc/src/analysis/decoration_analyzer.ts b/packages/compiler-cli/ngcc/src/analysis/decoration_analyzer.ts index 1fc2a48d9f..b8bd0b9634 100644 --- a/packages/compiler-cli/ngcc/src/analysis/decoration_analyzer.ts +++ b/packages/compiler-cli/ngcc/src/analysis/decoration_analyzer.ts @@ -137,6 +137,7 @@ export class DecorationAnalyzer { analyzeProgram(): DecorationAnalyses { const decorationAnalyses = new DecorationAnalyses(); const analyzedFiles = this.program.getSourceFiles() + .filter(sourceFile => !sourceFile.isDeclarationFile) .filter(sourceFile => isWithinPackage(this.packagePath, sourceFile)) .map(sourceFile => this.analyzeFile(sourceFile)) .filter(isDefined); diff --git a/packages/compiler-cli/ngcc/test/analysis/decoration_analyzer_spec.ts b/packages/compiler-cli/ngcc/test/analysis/decoration_analyzer_spec.ts index 0ecba3079d..4f057ec35b 100644 --- a/packages/compiler-cli/ngcc/test/analysis/decoration_analyzer_spec.ts +++ b/packages/compiler-cli/ngcc/test/analysis/decoration_analyzer_spec.ts @@ -11,7 +11,7 @@ import {FatalDiagnosticError, makeDiagnostic} from '../../../src/ngtsc/diagnosti import {absoluteFrom, getFileSystem, getSourceFileOrError} from '../../../src/ngtsc/file_system'; import {TestFile, runInEachFileSystem} from '../../../src/ngtsc/file_system/testing'; import {ClassDeclaration, Decorator} from '../../../src/ngtsc/reflection'; -import {DecoratorHandler, DetectResult} from '../../../src/ngtsc/transform'; +import {AnalysisOutput, CompileResult, DecoratorHandler, DetectResult, HandlerPrecedence} from '../../../src/ngtsc/transform'; import {loadFakeCore, loadTestFiles} from '../../../test/helpers'; import {DecorationAnalyzer} from '../../src/analysis/decoration_analyzer'; import {NgccReferencesRegistry} from '../../src/analysis/ngcc_references_registry'; @@ -392,6 +392,29 @@ runInEachFileSystem(() => { expect(diagnosticLogs[1]).toEqual(jasmine.objectContaining({code: -999998})); }); }); + + describe('declaration files', () => { + it('should not run decorator handlers against declaration files', () => { + class FakeDecoratorHandler implements DecoratorHandler<{}|null, unknown, unknown> { + name = 'FakeDecoratorHandler'; + precedence = HandlerPrecedence.PRIMARY; + + detect(): undefined { throw new Error('detect should not have been called'); } + analyze(): AnalysisOutput { + throw new Error('analyze should not have been called'); + } + compile(): CompileResult { throw new Error('compile should not have been called'); } + } + + const analyzer = setUpAnalyzer([{ + name: _('/node_modules/test-package/index.d.ts'), + contents: 'export declare class SomeDirective {}', + }]); + analyzer.handlers = [new FakeDecoratorHandler()]; + result = analyzer.analyzeProgram(); + expect(result.size).toBe(0); + }); + }); }); }); }); diff --git a/packages/compiler-cli/src/ngtsc/annotations/src/directive.ts b/packages/compiler-cli/src/ngtsc/annotations/src/directive.ts index c9d51d2d6e..4439c12ea8 100644 --- a/packages/compiler-cli/src/ngtsc/annotations/src/directive.ts +++ b/packages/compiler-cli/src/ngtsc/annotations/src/directive.ts @@ -55,10 +55,6 @@ export class DirectiveDecoratorHandler implements detect(node: ClassDeclaration, decorators: Decorator[]|null): DetectResult|undefined { - // Compiling declaration files is invalid. - if (node.getSourceFile().isDeclarationFile) { - return undefined; - } // If the class is undecorated, check if any of the fields have Angular decorators or lifecycle // hooks, and if they do, label the class as an abstract directive. if (!decorators) { diff --git a/packages/compiler-cli/src/ngtsc/incremental/index.ts b/packages/compiler-cli/src/ngtsc/incremental/index.ts index e4a3ff8580..506dd2bbfe 100644 --- a/packages/compiler-cli/src/ngtsc/incremental/index.ts +++ b/packages/compiler-cli/src/ngtsc/incremental/index.ts @@ -6,4 +6,5 @@ * found in the LICENSE file at https://angular.io/license */ +export {NOOP_INCREMENTAL_BUILD} from './src/noop'; export {IncrementalDriver} from './src/state'; diff --git a/packages/compiler-cli/src/ngtsc/incremental/src/noop.ts b/packages/compiler-cli/src/ngtsc/incremental/src/noop.ts new file mode 100644 index 0000000000..3ad486d2c4 --- /dev/null +++ b/packages/compiler-cli/src/ngtsc/incremental/src/noop.ts @@ -0,0 +1,13 @@ +/** + * @license + * Copyright Google Inc. 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 {IncrementalBuild} from '../api'; + +export const NOOP_INCREMENTAL_BUILD: IncrementalBuild = { + priorWorkFor: () => null +}; diff --git a/packages/compiler-cli/src/ngtsc/transform/src/compilation.ts b/packages/compiler-cli/src/ngtsc/transform/src/compilation.ts index 84d32fc1d5..a47b59a0df 100644 --- a/packages/compiler-cli/src/ngtsc/transform/src/compilation.ts +++ b/packages/compiler-cli/src/ngtsc/transform/src/compilation.ts @@ -103,6 +103,11 @@ export class TraitCompiler { private analyze(sf: ts.SourceFile, preanalyze: false): void; private analyze(sf: ts.SourceFile, preanalyze: true): Promise|undefined; private analyze(sf: ts.SourceFile, preanalyze: boolean): Promise|undefined { + // We shouldn't analyze declaration files. + if (sf.isDeclarationFile) { + return undefined; + } + // analyze() really wants to return `Promise|void`, but TypeScript cannot narrow a return // type of 'void', so `undefined` is used instead. const promises: Promise[] = []; diff --git a/packages/compiler-cli/src/ngtsc/transform/test/BUILD.bazel b/packages/compiler-cli/src/ngtsc/transform/test/BUILD.bazel new file mode 100644 index 0000000000..9346799eff --- /dev/null +++ b/packages/compiler-cli/src/ngtsc/transform/test/BUILD.bazel @@ -0,0 +1,32 @@ +load("//tools:defaults.bzl", "jasmine_node_test", "ts_library") + +package(default_visibility = ["//visibility:public"]) + +ts_library( + name = "test_lib", + testonly = True, + srcs = glob([ + "**/*.ts", + ]), + deps = [ + "//packages:types", + "//packages/compiler", + "//packages/compiler-cli/src/ngtsc/file_system", + "//packages/compiler-cli/src/ngtsc/file_system/testing", + "//packages/compiler-cli/src/ngtsc/incremental", + "//packages/compiler-cli/src/ngtsc/perf", + "//packages/compiler-cli/src/ngtsc/reflection", + "//packages/compiler-cli/src/ngtsc/testing", + "//packages/compiler-cli/src/ngtsc/transform", + "@npm//typescript", + ], +) + +jasmine_node_test( + name = "test", + bootstrap = ["angular/tools/testing/init_node_no_angular_spec.js"], + deps = [ + ":test_lib", + "//tools/testing:node_no_angular", + ], +) diff --git a/packages/compiler-cli/src/ngtsc/transform/test/compilation_spec.ts b/packages/compiler-cli/src/ngtsc/transform/test/compilation_spec.ts new file mode 100644 index 0000000000..8608e4364f --- /dev/null +++ b/packages/compiler-cli/src/ngtsc/transform/test/compilation_spec.ts @@ -0,0 +1,50 @@ +/** + * @license + * Copyright Google Inc. 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 {absoluteFrom} from '../../file_system'; +import {runInEachFileSystem} from '../../file_system/testing'; +import {NOOP_INCREMENTAL_BUILD} from '../../incremental'; +import {NOOP_PERF_RECORDER} from '../../perf'; +import {ClassDeclaration, TypeScriptReflectionHost} from '../../reflection'; +import {makeProgram} from '../../testing'; +import {DtsTransformRegistry, TraitCompiler} from '../../transform'; +import {AnalysisOutput, CompileResult, DecoratorHandler, HandlerPrecedence} from '../src/api'; + +runInEachFileSystem(() => { + describe('TraitCompiler', () => { + let _: typeof absoluteFrom; + beforeEach(() => _ = absoluteFrom); + + it('should not run decoration handlers against declaration files', () => { + class FakeDecoratorHandler implements DecoratorHandler<{}|null, unknown, unknown> { + name = 'FakeDecoratorHandler'; + precedence = HandlerPrecedence.PRIMARY; + + detect(): undefined { throw new Error('detect should not have been called'); } + analyze(): AnalysisOutput { + throw new Error('analyze should not have been called'); + } + compile(): CompileResult { throw new Error('compile should not have been called'); } + } + + const {program} = makeProgram([{ + name: _('/lib.d.ts'), + contents: `export declare class SomeDirective {}`, + }]); + const checker = program.getTypeChecker(); + const reflectionHost = new TypeScriptReflectionHost(checker); + const compiler = new TraitCompiler( + [new FakeDecoratorHandler()], reflectionHost, NOOP_PERF_RECORDER, NOOP_INCREMENTAL_BUILD, + true, new DtsTransformRegistry()); + const sourceFile = program.getSourceFile('lib.d.ts') !; + const analysis = compiler.analyzeSync(sourceFile); + + expect(sourceFile.isDeclarationFile).toBe(true); + expect(analysis).toBeFalsy(); + }); + }); +});