mirror of
https://github.com/angular/angular.git
synced 2026-09-14 13:54:52 +08:00
fix(compiler-cli): resolve component encapsulation enum in local compilation mode (#51848)
Currently the field encapsulation undergoes some static analysis to check if it is `ViewEncapsulation` enum. Such static check fails in local compilation mode in g3 as the symbol cannot be resolved. On the other hand this field has to be resolved statically as its value determined the generated code. So in local compilation mode we add a lighter resolving logic which relies only on local information. PR Close #51848
This commit is contained in:
committed by
Dylan Hunn
parent
377a7abfda
commit
f91f222b55
@@ -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');
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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: '<span>Hello world!</span>',
|
||||
})
|
||||
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: '<span>Hello world!</span>',
|
||||
})
|
||||
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: '<span>Hello world!</span>',
|
||||
})
|
||||
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: '<span>Hello world!</span>',
|
||||
})
|
||||
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: '<span>Hello world!</span>',
|
||||
})
|
||||
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', () => {
|
||||
|
||||
Reference in New Issue
Block a user