feat(ivy): support styleUrls in ngtsc (#27357)
This commit adds support for resolution of styleUrls to ngtsc. Previously this field was never read, and so components with styleUrls would appear unstyled after compilation. PR Close #27357
This commit is contained in:
parent
b5ed403bc4
commit
cfb67edd85
|
@ -57,6 +57,7 @@ export class ComponentDecoratorHandler implements
|
||||||
preanalyze(node: ts.ClassDeclaration, decorator: Decorator): Promise<void>|undefined {
|
preanalyze(node: ts.ClassDeclaration, decorator: Decorator): Promise<void>|undefined {
|
||||||
const meta = this._resolveLiteral(decorator);
|
const meta = this._resolveLiteral(decorator);
|
||||||
const component = reflectObjectLiteral(meta);
|
const component = reflectObjectLiteral(meta);
|
||||||
|
const promises: Promise<void>[] = [];
|
||||||
|
|
||||||
if (this.resourceLoader.preload !== undefined && component.has('templateUrl')) {
|
if (this.resourceLoader.preload !== undefined && component.has('templateUrl')) {
|
||||||
const templateUrlExpr = component.get('templateUrl') !;
|
const templateUrlExpr = component.get('templateUrl') !;
|
||||||
|
@ -66,9 +67,27 @@ export class ComponentDecoratorHandler implements
|
||||||
ErrorCode.VALUE_HAS_WRONG_TYPE, templateUrlExpr, 'templateUrl must be a string');
|
ErrorCode.VALUE_HAS_WRONG_TYPE, templateUrlExpr, 'templateUrl must be a string');
|
||||||
}
|
}
|
||||||
const url = path.posix.resolve(path.dirname(node.getSourceFile().fileName), templateUrl);
|
const url = path.posix.resolve(path.dirname(node.getSourceFile().fileName), templateUrl);
|
||||||
return this.resourceLoader.preload(url);
|
const promise = this.resourceLoader.preload(url);
|
||||||
|
if (promise !== undefined) {
|
||||||
|
promises.push(promise);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const styleUrls = this._extractStyleUrls(component);
|
||||||
|
if (this.resourceLoader.preload !== undefined && styleUrls !== null) {
|
||||||
|
for (const styleUrl of styleUrls) {
|
||||||
|
const url = path.posix.resolve(path.dirname(node.getSourceFile().fileName), styleUrl);
|
||||||
|
const promise = this.resourceLoader.preload(url);
|
||||||
|
if (promise !== undefined) {
|
||||||
|
promises.push(promise);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (promises.length !== 0) {
|
||||||
|
return Promise.all(promises).then(() => undefined);
|
||||||
|
} else {
|
||||||
|
return undefined;
|
||||||
}
|
}
|
||||||
return undefined;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
analyze(node: ts.ClassDeclaration, decorator: Decorator): AnalysisOutput<ComponentHandlerData> {
|
analyze(node: ts.ClassDeclaration, decorator: Decorator): AnalysisOutput<ComponentHandlerData> {
|
||||||
|
@ -186,6 +205,14 @@ export class ComponentDecoratorHandler implements
|
||||||
styles = parseFieldArrayValue(component, 'styles', this.reflector, this.checker);
|
styles = parseFieldArrayValue(component, 'styles', this.reflector, this.checker);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let styleUrls = this._extractStyleUrls(component);
|
||||||
|
if (styleUrls !== null) {
|
||||||
|
if (styles === null) {
|
||||||
|
styles = [];
|
||||||
|
}
|
||||||
|
styles.push(...styleUrls.map(styleUrl => this.resourceLoader.load(styleUrl)));
|
||||||
|
}
|
||||||
|
|
||||||
let encapsulation: number = 0;
|
let encapsulation: number = 0;
|
||||||
if (component.has('encapsulation')) {
|
if (component.has('encapsulation')) {
|
||||||
encapsulation = parseInt(staticallyResolve(
|
encapsulation = parseInt(staticallyResolve(
|
||||||
|
@ -282,4 +309,18 @@ export class ComponentDecoratorHandler implements
|
||||||
this.literalCache.set(decorator, meta);
|
this.literalCache.set(decorator, meta);
|
||||||
return meta;
|
return meta;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _extractStyleUrls(component: Map<string, ts.Expression>): string[]|null {
|
||||||
|
if (!component.has('styleUrls')) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const styleUrlsExpr = component.get('styleUrls') !;
|
||||||
|
const styleUrls = staticallyResolve(styleUrlsExpr, this.reflector, this.checker);
|
||||||
|
if (!Array.isArray(styleUrls) || !styleUrls.every(url => typeof url === 'string')) {
|
||||||
|
throw new FatalDiagnosticError(
|
||||||
|
ErrorCode.VALUE_HAS_WRONG_TYPE, styleUrlsExpr, 'styleUrls must be an array of strings');
|
||||||
|
}
|
||||||
|
return styleUrls as string[];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -89,6 +89,26 @@ describe('ngtsc behavioral tests', () => {
|
||||||
expect(jsContents).toContain('Hello World');
|
expect(jsContents).toContain('Hello World');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should compile components with styleUrls', () => {
|
||||||
|
env.tsconfig();
|
||||||
|
env.write('test.ts', `
|
||||||
|
import {Component} from '@angular/core';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'test-cmp',
|
||||||
|
styleUrls: ['./dir/style.css'],
|
||||||
|
template: '',
|
||||||
|
})
|
||||||
|
export class TestCmp {}
|
||||||
|
`);
|
||||||
|
env.write('dir/style.css', ':host { background-color: blue; }');
|
||||||
|
|
||||||
|
env.driveMain();
|
||||||
|
|
||||||
|
const jsContents = env.getContents('test.js');
|
||||||
|
expect(jsContents).toContain('background-color: blue');
|
||||||
|
});
|
||||||
|
|
||||||
it('should compile NgModules without errors', () => {
|
it('should compile NgModules without errors', () => {
|
||||||
env.tsconfig();
|
env.tsconfig();
|
||||||
env.write('test.ts', `
|
env.write('test.ts', `
|
||||||
|
|
Loading…
Reference in New Issue