diff --git a/packages/compiler-cli/src/ngtsc/annotations/common/src/evaluation.ts b/packages/compiler-cli/src/ngtsc/annotations/common/src/evaluation.ts
index 8d63aa03207..81978b08a84 100644
--- a/packages/compiler-cli/src/ngtsc/annotations/common/src/evaluation.ts
+++ b/packages/compiler-cli/src/ngtsc/annotations/common/src/evaluation.ts
@@ -6,6 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/
+import {ViewEncapsulation} from '@angular/compiler';
import ts from 'typescript';
import {ErrorCode, FatalDiagnosticError} from '../../../diagnostics';
@@ -34,6 +35,38 @@ export function resolveEnumValue(
return resolved;
}
+/**
+ * Resolves a EncapsulationEnum expression locally on best effort without having to calculate the
+ * reference. This suites local compilation mode where each file is compiled individually.
+ *
+ * The static analysis is still needed in local compilation mode since the value of this enum will
+ * be used later to decide the generated code for styles.
+ */
+export function resolveEncapsulationEnumValueLocally(expr?: ts.Expression): number|null {
+ if (!expr) {
+ return null;
+ }
+
+ const exprText = expr.getText().trim();
+
+ for (const key in ViewEncapsulation) {
+ if (!Number.isNaN(Number(key))) {
+ continue;
+ }
+
+ const suffix = `ViewEncapsulation.${key}`;
+
+ // Check whether the enum is imported by name or used by import namespace (e.g.,
+ // core.ViewEncapsulation.None)
+ if (exprText === suffix || exprText.endsWith(`.${suffix}`)) {
+ const ans = Number(ViewEncapsulation[key]);
+ return ans;
+ }
+ }
+
+ return null;
+}
+
/** Determines if the result of an evaluation is a string array. */
export function isStringArray(resolvedValue: ResolvedValue): resolvedValue is string[] {
return Array.isArray(resolvedValue) && resolvedValue.every(elem => typeof elem === 'string');
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 c875cfa7dca..ac0d5a977b6 100644
--- a/packages/compiler-cli/src/ngtsc/annotations/component/src/handler.ts
+++ b/packages/compiler-cli/src/ngtsc/annotations/component/src/handler.ts
@@ -26,7 +26,7 @@ import {TypeCheckableDirectiveMeta, TypeCheckContext} from '../../../typecheck/a
import {ExtendedTemplateChecker} from '../../../typecheck/extended/api';
import {getSourceFile} from '../../../util/src/typescript';
import {Xi18nContext} from '../../../xi18n';
-import {combineResolvers, compileDeclareFactory, compileInputTransformFields, compileNgFactoryDefField, compileResults, extractClassMetadata, extractSchemas, findAngularDecorator, forwardRefResolver, getDirectiveDiagnostics, getProviderDiagnostics, InjectableClassRegistry, isExpressionForwardReference, readBaseClass, ReferencesRegistry, removeIdentifierReferences, resolveEnumValue, resolveImportedFile, resolveLiteral, resolveProvidersRequiringFactory, ResourceLoader, toFactoryMetadata, validateHostDirectives, wrapFunctionExpressionsInParens,} from '../../common';
+import {combineResolvers, compileDeclareFactory, compileInputTransformFields, compileNgFactoryDefField, compileResults, extractClassMetadata, extractSchemas, findAngularDecorator, forwardRefResolver, getDirectiveDiagnostics, getProviderDiagnostics, InjectableClassRegistry, isExpressionForwardReference, readBaseClass, ReferencesRegistry, removeIdentifierReferences, resolveEncapsulationEnumValueLocally, resolveEnumValue, resolveImportedFile, resolveLiteral, resolveProvidersRequiringFactory, ResourceLoader, toFactoryMetadata, validateHostDirectives, wrapFunctionExpressionsInParens,} from '../../common';
import {extractDirectiveMetadata, parseDirectiveStyles} from '../../directive';
import {createModuleWithProvidersResolver, NgModuleSymbol} from '../../ng_module';
@@ -233,7 +233,9 @@ export class ComponentDecoratorHandler implements
const {decorator: component, metadata, inputs, outputs, hostDirectives, rawHostDirectives} =
directiveResult;
const encapsulation: number =
- resolveEnumValue(this.evaluator, component, 'encapsulation', 'ViewEncapsulation') ??
+ (this.compilationMode !== CompilationMode.LOCAL ?
+ resolveEnumValue(this.evaluator, component, 'encapsulation', 'ViewEncapsulation') :
+ resolveEncapsulationEnumValueLocally(component.get('encapsulation'))) ??
ViewEncapsulation.Emulated;
let changeDetection: number|Expression|null = null;
diff --git a/packages/compiler-cli/test/ngtsc/local_compilation_spec.ts b/packages/compiler-cli/test/ngtsc/local_compilation_spec.ts
index 7e234ec7ad0..cd40769f1a9 100644
--- a/packages/compiler-cli/test/ngtsc/local_compilation_spec.ts
+++ b/packages/compiler-cli/test/ngtsc/local_compilation_spec.ts
@@ -364,6 +364,106 @@ runInEachFileSystem(() => {
expect(jsContents).toContain('changeDetection: SomeWeirdThing');
});
+
+ it('should place the correct value of encapsulation into the component def - case of ViewEncapsulation.Emulated with no styles',
+ () => {
+ env.write('test.ts', `
+ import {Component, ViewEncapsulation} from '@angular/core';
+
+ @Component({
+ encapsulation: ViewEncapsulation.Emulated,
+ template: 'Hello world!',
+ })
+ export class MainComponent {
+ }
+ `);
+
+ env.driveMain();
+ const jsContents = env.getContents('test.js');
+
+ // If there is no style, don't generate css selectors on elements by setting
+ // encapsulation to none (=2)
+ expect(jsContents).toContain('encapsulation: 2');
+ });
+
+ it('should place the correct value of encapsulation into the component def - case of ViewEncapsulation.Emulated with styles',
+ () => {
+ env.write('test.ts', `
+ import {Component, ViewEncapsulation} from '@angular/core';
+
+ @Component({
+ encapsulation: ViewEncapsulation.Emulated,
+ styles: ['color: blue'],
+ template: 'Hello world!',
+ })
+ export class MainComponent {
+ }
+ `);
+
+ env.driveMain();
+ const jsContents = env.getContents('test.js');
+
+ // encapsulation is set only for non-default value
+ expect(jsContents).not.toContain('encapsulation: 0');
+ expect(jsContents).toContain('styles: ["color: blue"]');
+ });
+
+ it('should place the correct value of encapsulation into the component def - case of ViewEncapsulation.ShadowDom',
+ () => {
+ env.write('test.ts', `
+ import {Component, ViewEncapsulation} from '@angular/core';
+
+ @Component({
+ encapsulation: ViewEncapsulation.ShadowDom,
+ template: 'Hello world!',
+ })
+ export class MainComponent {
+ }
+ `);
+
+ env.driveMain();
+ const jsContents = env.getContents('test.js');
+
+ expect(jsContents).toContain('encapsulation: 3');
+ });
+
+ it('should place the correct value of encapsulation into the component def - case of ViewEncapsulation.None',
+ () => {
+ env.write('test.ts', `
+ import {Component, ViewEncapsulation} from '@angular/core';
+
+ @Component({
+ encapsulation: ViewEncapsulation.None,
+ template: 'Hello world!',
+ })
+ export class MainComponent {
+ }
+ `);
+
+ env.driveMain();
+ const jsContents = env.getContents('test.js');
+
+ expect(jsContents).toContain('encapsulation: 2');
+ });
+
+ it('should default encapsulation to Emulated', () => {
+ env.write('test.ts', `
+ import {Component, ViewEncapsulation} from '@angular/core';
+
+ @Component({
+ template: 'Hello world!',
+ })
+ export class MainComponent {
+ }
+ `);
+
+ env.driveMain();
+ const jsContents = env.getContents('test.js');
+
+ // If there is no style, don't generate css selectors on elements by setting encapsulation
+ // to none (=2)
+ expect(jsContents).toContain('encapsulation: 2');
+ });
});
describe('constructor injection', () => {