From 227de982012a9b4ffae83e6a7000c8e162a206bc Mon Sep 17 00:00:00 2001 From: Kristiyan Kostadinov Date: Wed, 8 Nov 2023 17:29:06 +0100 Subject: [PATCH] refactor(compiler): add flag to disable block syntax in language service (#52683) Adds the private `_enableBlockSyntax` flag that can be used by the language service to disable blocks on apps that aren't on Angular v17. PR Close #52683 --- .../annotations/component/src/handler.ts | 6 +++++- .../annotations/component/src/resources.ts | 3 +++ .../component/test/component_spec.ts | 1 + .../src/ngtsc/core/api/src/options.ts | 8 ++++++++ .../src/ngtsc/core/src/compiler.ts | 4 +++- .../compiler-cli/test/ngtsc/ngtsc_spec.ts | 19 +++++++++++++++++++ 6 files changed, 39 insertions(+), 2 deletions(-) diff --git a/packages/compiler-cli/src/ngtsc/annotations/component/src/handler.ts b/packages/compiler-cli/src/ngtsc/annotations/component/src/handler.ts index a8c526eda95..49592bccb10 100644 --- a/packages/compiler-cli/src/ngtsc/annotations/component/src/handler.ts +++ b/packages/compiler-cli/src/ngtsc/annotations/component/src/handler.ts @@ -83,11 +83,13 @@ export class ComponentDecoratorHandler implements private hostDirectivesResolver: HostDirectivesResolver, private includeClassMetadata: boolean, private readonly compilationMode: CompilationMode, private readonly deferredSymbolTracker: DeferredSymbolTracker, - private readonly forbidOrphanRendering: boolean) { + private readonly forbidOrphanRendering: boolean, + private readonly enableBlockSyntax: boolean) { this.extractTemplateOptions = { enableI18nLegacyMessageIdFormat: this.enableI18nLegacyMessageIdFormat, i18nNormalizeLineEndingsInICUs: this.i18nNormalizeLineEndingsInICUs, usePoisonedData: this.usePoisonedData, + enableBlockSyntax: this.enableBlockSyntax, }; } @@ -106,6 +108,7 @@ export class ComponentDecoratorHandler implements enableI18nLegacyMessageIdFormat: boolean, i18nNormalizeLineEndingsInICUs: boolean, usePoisonedData: boolean, + enableBlockSyntax: boolean, }; readonly precedence = HandlerPrecedence.PRIMARY; @@ -360,6 +363,7 @@ export class ComponentDecoratorHandler implements enableI18nLegacyMessageIdFormat: this.enableI18nLegacyMessageIdFormat, i18nNormalizeLineEndingsInICUs: this.i18nNormalizeLineEndingsInICUs, usePoisonedData: this.usePoisonedData, + enableBlockSyntax: this.enableBlockSyntax, }, this.compilationMode); } diff --git a/packages/compiler-cli/src/ngtsc/annotations/component/src/resources.ts b/packages/compiler-cli/src/ngtsc/annotations/component/src/resources.ts index 51cfacdc6c9..1de8ce7d6b3 100644 --- a/packages/compiler-cli/src/ngtsc/annotations/component/src/resources.ts +++ b/packages/compiler-cli/src/ngtsc/annotations/component/src/resources.ts @@ -119,6 +119,7 @@ export interface ExtractTemplateOptions { usePoisonedData: boolean; enableI18nLegacyMessageIdFormat: boolean; i18nNormalizeLineEndingsInICUs: boolean; + enableBlockSyntax: boolean; } export function extractTemplate( @@ -237,6 +238,7 @@ function parseExtractedTemplate( enableI18nLegacyMessageIdFormat: options.enableI18nLegacyMessageIdFormat, i18nNormalizeLineEndingsInICUs, alwaysAttemptHtmlToR3AstConversion: options.usePoisonedData, + enableBlockSyntax: options.enableBlockSyntax, }); // Unfortunately, the primary parse of the template above may not contain accurate source map @@ -264,6 +266,7 @@ function parseExtractedTemplate( i18nNormalizeLineEndingsInICUs, leadingTriviaChars: [], alwaysAttemptHtmlToR3AstConversion: options.usePoisonedData, + enableBlockSyntax: options.enableBlockSyntax, }); return { diff --git a/packages/compiler-cli/src/ngtsc/annotations/component/test/component_spec.ts b/packages/compiler-cli/src/ngtsc/annotations/component/test/component_spec.ts index ada1a2f6cbd..8701bb2cb84 100644 --- a/packages/compiler-cli/src/ngtsc/annotations/component/test/component_spec.ts +++ b/packages/compiler-cli/src/ngtsc/annotations/component/test/component_spec.ts @@ -103,6 +103,7 @@ function setup( compilationMode, new DeferredSymbolTracker(checker), /* forbidOrphanRenderering */ false, + /* enableBlockSyntax */ true, ); return {reflectionHost, handler, resourceLoader, metaRegistry}; } diff --git a/packages/compiler-cli/src/ngtsc/core/api/src/options.ts b/packages/compiler-cli/src/ngtsc/core/api/src/options.ts index 7f76a11eafb..95a7e537ad6 100644 --- a/packages/compiler-cli/src/ngtsc/core/api/src/options.ts +++ b/packages/compiler-cli/src/ngtsc/core/api/src/options.ts @@ -66,6 +66,14 @@ export interface InternalOptions { * @internal */ supportJitMode?: boolean; + + /** + * Whether block syntax is enabled in the compiler. Defaults to true. + * Used in the language service to disable the new syntax for projects that aren't on v17. + * + * @internal + */ + _enableBlockSyntax?: boolean; } /** diff --git a/packages/compiler-cli/src/ngtsc/core/src/compiler.ts b/packages/compiler-cli/src/ngtsc/core/src/compiler.ts index 3aae70534f1..7e23fe457d0 100644 --- a/packages/compiler-cli/src/ngtsc/core/src/compiler.ts +++ b/packages/compiler-cli/src/ngtsc/core/src/compiler.ts @@ -258,6 +258,7 @@ export class NgCompiler { readonly ignoreForDiagnostics: Set; readonly ignoreForEmit: Set; readonly enableTemplateTypeChecker: boolean; + private readonly enableBlockSyntax: boolean; /** * `NgCompiler` can be reused for multiple compilations (for resource-only changes), and each @@ -322,6 +323,7 @@ export class NgCompiler { ) { this.enableTemplateTypeChecker = enableTemplateTypeChecker || (options['_enableTemplateTypeChecker'] ?? false); + this.enableBlockSyntax = options['_enableBlockSyntax'] ?? true; this.constructionDiagnostics.push( ...this.adapter.constructionDiagnostics, ...verifyCompatibleTypeCheckOptions(this.options)); @@ -1106,7 +1108,7 @@ export class NgCompiler { this.incrementalCompilation.depGraph, injectableRegistry, semanticDepGraphUpdater, this.closureCompilerEnabled, this.delegatingPerfRecorder, hostDirectivesResolver, supportTestBed, compilationMode, deferredSymbolsTracker, - !!this.options.forbidOrphanComponents), + !!this.options.forbidOrphanComponents, this.enableBlockSyntax), // TODO(alxhub): understand why the cast here is necessary (something to do with `null` // not being assignable to `unknown` when wrapped in `Readonly`). diff --git a/packages/compiler-cli/test/ngtsc/ngtsc_spec.ts b/packages/compiler-cli/test/ngtsc/ngtsc_spec.ts index 3da7a2d7d11..bcca44d7d63 100644 --- a/packages/compiler-cli/test/ngtsc/ngtsc_spec.ts +++ b/packages/compiler-cli/test/ngtsc/ngtsc_spec.ts @@ -8495,6 +8495,25 @@ function allTests(os: string) { expect(codes).toEqual([ngErrorCode(ErrorCode.NGMODULE_BOOTSTRAP_IS_STANDALONE)]); }); + it('should be able to turn off control flow using a compiler flag', () => { + env.tsconfig({_enableBlockSyntax: false}); + env.write('/test.ts', ` + import { Component } from '@angular/core'; + + @Component({ + standalone: true, + template: 'My email is foo@bar.com', + }) + export class TestCmp {} + `); + + env.driveMain(); + + // If blocks are enabled, this test will fail since `@bar.com` is an incomplete block. + const jsContents = env.getContents('test.js'); + expect(jsContents).toContain('text(0, "My email is foo@bar.com")'); + }); + describe('InjectorDef emit optimizations for standalone', () => { it('should not filter components out of NgModule.imports', () => { env.write('test.ts', `