Skip to content

Commit 5bd530a

Browse files
clydindylhunn
authored andcommitted
refactor(compiler-cli): add internal compiler option to control NgModule selector scope emit (angular#51007)
An internal compiler option named `supportJitMode` is now available for use by the Angular CLI. This option currently controls the emit of NgModule selector scope information. This emitted information is only needed in AOT mode when an application also uses JIT. However, AOT mode combined with JIT mode is not currently supported nor will work in the Angular CLI. With the Angular CLI, JIT mode is only supported if the entire application is built in JIT mode. Without this option, the CLI needs to manually perform a code transform to remove the information and also replicate TypeScript's import eliding. This is can be a complicated operation and must be continually kept up to date with any changes to both the Angular compiler and TypeScript. The introduction of this new option alleviates these concerns while also removing several build time actions that would otherwise need to be performed on every application build. PR Close angular#51007
1 parent 25153e9 commit 5bd530a

5 files changed

Lines changed: 87 additions & 14 deletions

File tree

packages/compiler-cli/src/ngtsc/annotations/ng_module/src/handler.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,8 @@ export class NgModuleDecoratorHandler implements
178178
private refEmitter: ReferenceEmitter, private annotateForClosureCompiler: boolean,
179179
private onlyPublishPublicTypings: boolean,
180180
private injectableRegistry: InjectableClassRegistry, private perf: PerfRecorder,
181-
private includeClassMetadata: boolean, private readonly compilationMode: CompilationMode) {}
181+
private includeClassMetadata: boolean, private includeSelectorScope: boolean,
182+
private readonly compilationMode: CompilationMode) {}
182183

183184
readonly precedence = HandlerPrecedence.PRIMARY;
184185
readonly name = 'NgModuleDecoratorHandler';
@@ -381,7 +382,8 @@ export class NgModuleDecoratorHandler implements
381382
id,
382383
// Use `ɵɵsetNgModuleScope` to patch selector scopes onto the generated definition in a
383384
// tree-shakeable way.
384-
selectorScopeMode: R3SelectorScopeMode.SideEffect,
385+
selectorScopeMode: this.includeSelectorScope ? R3SelectorScopeMode.SideEffect :
386+
R3SelectorScopeMode.Omit,
385387
// TODO: to be implemented as a part of FW-1004.
386388
schemas: [],
387389
};

packages/compiler-cli/src/ngtsc/annotations/ng_module/test/ng_module_spec.ts

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ function setup(program: ts.Program, compilationMode = CompilationMode.FULL) {
4242
exportedProviderStatusResolver, /* semanticDepGraphUpdater */ null,
4343
/* isCore */ false, refEmitter,
4444
/* annotateForClosureCompiler */ false,
45-
/* onlyPublishPublicTypings */ false, injectableRegistry, NOOP_PERF_RECORDER, true,
45+
/* onlyPublishPublicTypings */ false, injectableRegistry, NOOP_PERF_RECORDER, true, true,
4646
compilationMode);
4747

4848
return {handler, reflectionHost};
@@ -127,9 +127,9 @@ runInEachFileSystem(() => {
127127
contents: `
128128
import {NgModule} from '@angular/core';
129129
import {SomeModule} from './some_where';
130-
130+
131131
@NgModule({
132-
imports: [SomeModule],
132+
imports: [SomeModule],
133133
}) class TestModule {}
134134
`
135135
}
@@ -157,9 +157,9 @@ runInEachFileSystem(() => {
157157
contents: `
158158
import {NgModule} from '@angular/core';
159159
import {SomeModule} from './some_where';
160-
160+
161161
@NgModule({
162-
exports: [SomeModule],
162+
exports: [SomeModule],
163163
}) class TestModule {}
164164
`
165165
}
@@ -187,9 +187,9 @@ runInEachFileSystem(() => {
187187
contents: `
188188
import {NgModule} from '@angular/core';
189189
import {SomeComponent} from './some_where';
190-
190+
191191
@NgModule({
192-
declarations: [SomeComponent],
192+
declarations: [SomeComponent],
193193
}) class TestModule {}
194194
`
195195
}
@@ -217,9 +217,9 @@ runInEachFileSystem(() => {
217217
contents: `
218218
import {NgModule} from '@angular/core';
219219
import {SomeComponent} from './some_where';
220-
220+
221221
@NgModule({
222-
bootstrap: [SomeComponent],
222+
bootstrap: [SomeComponent],
223223
}) class TestModule {}
224224
`
225225
}
@@ -247,9 +247,9 @@ runInEachFileSystem(() => {
247247
contents: `
248248
import {NgModule, CUSTOM_ELEMENTS_SCHEMA} from '@angular/core';
249249
import {SomeComponent} from './some_where';
250-
250+
251251
@NgModule({
252-
schemas: [CUSTOM_ELEMENTS_SCHEMA],
252+
schemas: [CUSTOM_ELEMENTS_SCHEMA],
253253
}) class TestModule {}
254254
`
255255
}

packages/compiler-cli/src/ngtsc/core/api/src/options.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,17 @@ export interface InternalOptions {
5555
* @internal
5656
*/
5757
supportTestBed?: boolean;
58+
59+
/**
60+
* Enables the usage of the JIT compiler in combination with AOT compiled code by emitting
61+
* selector scope information for NgModules.
62+
*
63+
* This is only intended to be used by the Angular CLI.
64+
* Defaults to true if not specified.
65+
*
66+
* @internal
67+
*/
68+
supportJitMode?: boolean;
5869
}
5970

6071
/**

packages/compiler-cli/src/ngtsc/core/src/compiler.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1041,6 +1041,7 @@ export class NgCompiler {
10411041
CycleHandlingStrategy.Error;
10421042

10431043
const strictCtorDeps = this.options.strictInjectionParameters || false;
1044+
const supportJitMode = this.options.supportJitMode ?? true;
10441045
const supportTestBed = this.options.supportTestBed ?? true;
10451046

10461047
// Libraries compiled in partial mode could potentially be used with TestBed within an
@@ -1050,6 +1051,10 @@ export class NgCompiler {
10501051
throw new Error(
10511052
'TestBed support ("supportTestBed" option) cannot be disabled in partial compilation mode.');
10521053
}
1054+
if (supportJitMode === false && compilationMode === CompilationMode.PARTIAL) {
1055+
throw new Error(
1056+
'JIT mode support ("supportJitMode" option) cannot be disabled in partial compilation mode.');
1057+
}
10531058

10541059
// Set up the IvyCompilation, which manages state for the Ivy transformer.
10551060
const handlers: DecoratorHandler<unknown, unknown, SemanticSymbol|null, unknown>[] = [
@@ -1088,7 +1093,8 @@ export class NgCompiler {
10881093
reflector, evaluator, metaReader, metaRegistry, ngModuleScopeRegistry, referencesRegistry,
10891094
exportedProviderStatusResolver, semanticDepGraphUpdater, isCore, refEmitter,
10901095
this.closureCompilerEnabled, this.options.onlyPublishPublicTypingsForNgModules ?? false,
1091-
injectableRegistry, this.delegatingPerfRecorder, supportTestBed, compilationMode),
1096+
injectableRegistry, this.delegatingPerfRecorder, supportTestBed, supportJitMode,
1097+
compilationMode),
10921098
];
10931099

10941100
const traitCompiler = new TraitCompiler(

packages/compiler-cli/test/ngtsc/ngtsc_spec.ts

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4909,6 +4909,60 @@ function allTests(os: string) {
49094909
trim('MyModule.ɵmod = /*@__PURE__*/ i0.ɵɵdefineNgModule({ type: MyModule });'));
49104910
});
49114911

4912+
it('should emit setNgModuleScope calls for NgModules by default', () => {
4913+
env.write('test.ts', `
4914+
import {Component, Directive, Injectable, NgModule, Pipe} from '@angular/core';
4915+
4916+
@Component({selector: 'cmp', template: 'I am a component!'}) class TestComponent {}
4917+
@Directive({selector: 'dir'}) class TestDirective {}
4918+
@Injectable() class TestInjectable {}
4919+
@NgModule({declarations: [TestComponent, TestDirective]}) class TestNgModule {}
4920+
@Pipe({name: 'pipe'}) class TestPipe {}
4921+
`);
4922+
4923+
env.driveMain();
4924+
const jsContents = env.getContents('test.js');
4925+
expect(jsContents).toContain('\u0275setNgModuleScope(TestNgModule, ');
4926+
});
4927+
4928+
it('should emit setNgModuleScope calls for NgModules when supportJitMode is true', () => {
4929+
env.tsconfig({
4930+
'supportJitMode': true,
4931+
});
4932+
env.write('test.ts', `
4933+
import {Component, Directive, Injectable, NgModule, Pipe} from '@angular/core';
4934+
4935+
@Component({selector: 'cmp', template: 'I am a component!'}) class TestComponent {}
4936+
@Directive({selector: 'dir'}) class TestDirective {}
4937+
@Injectable() class TestInjectable {}
4938+
@NgModule({declarations: [TestComponent, TestDirective]}) class TestNgModule {}
4939+
@Pipe({name: 'pipe'}) class TestPipe {}
4940+
`);
4941+
4942+
env.driveMain();
4943+
const jsContents = env.getContents('test.js');
4944+
expect(jsContents).toContain('\u0275setNgModuleScope(TestNgModule, ');
4945+
});
4946+
4947+
it('should not emit setNgModuleScope calls for NgModules when supportJitMode is false', () => {
4948+
env.tsconfig({
4949+
'supportJitMode': false,
4950+
});
4951+
env.write('test.ts', `
4952+
import {Component, Directive, Injectable, NgModule, Pipe} from '@angular/core';
4953+
4954+
@Component({selector: 'cmp', template: 'I am a component!'}) class TestComponent {}
4955+
@Directive({selector: 'dir'}) class TestDirective {}
4956+
@Injectable() class TestInjectable {}
4957+
@NgModule({declarations: [TestComponent, TestDirective]}) class TestNgModule {}
4958+
@Pipe({name: 'pipe'}) class TestPipe {}
4959+
`);
4960+
4961+
env.driveMain();
4962+
const jsContents = env.getContents('test.js');
4963+
expect(jsContents).not.toContain('\u0275setNgModuleScope(');
4964+
});
4965+
49124966
it('should emit setClassMetadata calls for all types by default', () => {
49134967
env.write('test.ts', `
49144968
import {Component, Directive, Injectable, NgModule, Pipe} from '@angular/core';

0 commit comments

Comments
 (0)