Skip to content

Commit 00d3b60

Browse files
committed
fix(compiler): support css stylesheets in offline compiler
1 parent c386fc8 commit 00d3b60

38 files changed

+436
-388
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ tmp
2323

2424
# Files created by the template compiler
2525
**/*.ngfactory.ts
26+
**/*.css.ts
27+
**/*.css.shim.ts
2628

2729
# Or type definitions we mirror from github
2830
# (NB: these lines are removed in publish-build-artifacts.sh)
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,16 @@
11
import * as selector from './src/selector';
2+
import * as pathUtil from './src/output/path_util';
23

34
export namespace __compiler_private__ {
45
export type SelectorMatcher = selector.SelectorMatcher;
56
export var SelectorMatcher = selector.SelectorMatcher;
67

78
export type CssSelector = selector.CssSelector;
89
export var CssSelector = selector.CssSelector;
10+
11+
export type AssetUrl = pathUtil.AssetUrl;
12+
export var AssetUrl = pathUtil.AssetUrl;
13+
14+
export type ImportGenerator = pathUtil.ImportGenerator;
15+
export var ImportGenerator = pathUtil.ImportGenerator;
916
}

modules/@angular/compiler/src/compile_metadata.ts

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -466,24 +466,20 @@ export class CompileTemplateMetadata {
466466
styles: string[];
467467
styleUrls: string[];
468468
ngContentSelectors: string[];
469-
baseUrl: string;
470-
constructor({encapsulation, template, templateUrl, styles, styleUrls, ngContentSelectors,
471-
baseUrl}: {
469+
constructor({encapsulation, template, templateUrl, styles, styleUrls, ngContentSelectors}: {
472470
encapsulation?: ViewEncapsulation,
473471
template?: string,
474472
templateUrl?: string,
475473
styles?: string[],
476474
styleUrls?: string[],
477-
ngContentSelectors?: string[],
478-
baseUrl?: string
475+
ngContentSelectors?: string[]
479476
} = {}) {
480477
this.encapsulation = isPresent(encapsulation) ? encapsulation : ViewEncapsulation.Emulated;
481478
this.template = template;
482479
this.templateUrl = templateUrl;
483480
this.styles = isPresent(styles) ? styles : [];
484481
this.styleUrls = isPresent(styleUrls) ? styleUrls : [];
485482
this.ngContentSelectors = isPresent(ngContentSelectors) ? ngContentSelectors : [];
486-
this.baseUrl = baseUrl;
487483
}
488484

489485
static fromJson(data: {[key: string]: any}): CompileTemplateMetadata {
@@ -495,8 +491,7 @@ export class CompileTemplateMetadata {
495491
templateUrl: data['templateUrl'],
496492
styles: data['styles'],
497493
styleUrls: data['styleUrls'],
498-
ngContentSelectors: data['ngContentSelectors'],
499-
baseUrl: data['baseUrl']
494+
ngContentSelectors: data['ngContentSelectors']
500495
});
501496
}
502497

@@ -508,8 +503,7 @@ export class CompileTemplateMetadata {
508503
'templateUrl': this.templateUrl,
509504
'styles': this.styles,
510505
'styleUrls': this.styleUrls,
511-
'ngContentSelectors': this.ngContentSelectors,
512-
'baseUrl': this.baseUrl
506+
'ngContentSelectors': this.ngContentSelectors
513507
};
514508
}
515509
}

modules/@angular/compiler/src/directive_normalizer.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -63,9 +63,9 @@ export class DirectiveNormalizer {
6363
template: CompileTemplateMetadata): Promise<CompileTemplateMetadata> {
6464
if (isPresent(template.template)) {
6565
return PromiseWrapper.resolve(this.normalizeLoadedTemplate(
66-
directiveType, template, template.template, template.baseUrl));
66+
directiveType, template, template.template, directiveType.moduleUrl));
6767
} else if (isPresent(template.templateUrl)) {
68-
var sourceAbsUrl = this._urlResolver.resolve(template.baseUrl, template.templateUrl);
68+
var sourceAbsUrl = this._urlResolver.resolve(directiveType.moduleUrl, template.templateUrl);
6969
return this._xhr.get(sourceAbsUrl)
7070
.then(templateContent => this.normalizeLoadedTemplate(directiveType, template,
7171
templateContent, sourceAbsUrl));
@@ -90,7 +90,7 @@ export class DirectiveNormalizer {
9090
visitor.styleUrls.filter(isStyleUrlResolvable)
9191
.map(url => this._urlResolver.resolve(templateAbsUrl, url))
9292
.concat(templateMeta.styleUrls.filter(isStyleUrlResolvable)
93-
.map(url => this._urlResolver.resolve(templateMeta.baseUrl, url)));
93+
.map(url => this._urlResolver.resolve(directiveType.moduleUrl, url)));
9494

9595
var allResolvedStyles = allStyles.map(style => {
9696
var styleWithImports = extractStyleUrls(this._urlResolver, templateAbsUrl, style);

modules/@angular/compiler/src/metadata_resolver.ts

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -95,8 +95,7 @@ export class CompileMetadataResolver {
9595
template: viewMeta.template,
9696
templateUrl: viewMeta.templateUrl,
9797
styles: viewMeta.styles,
98-
styleUrls: viewMeta.styleUrls,
99-
baseUrl: calcTemplateBaseUrl(this._reflector, directiveType, cmpMeta)
98+
styleUrls: viewMeta.styleUrls
10099
});
101100
changeDetectionStrategy = cmpMeta.changeDetection;
102101
if (isPresent(dirMeta.viewProviders)) {
@@ -118,7 +117,10 @@ export class CompileMetadataResolver {
118117
selector: dirMeta.selector,
119118
exportAs: dirMeta.exportAs,
120119
isComponent: isPresent(templateMeta),
121-
type: this.getTypeMetadata(directiveType, staticTypeModuleUrl(directiveType)),
120+
type: this.getTypeMetadata(directiveType,
121+
isPresent(cmpMeta) ?
122+
componentModuleUrl(this._reflector, directiveType, cmpMeta) :
123+
staticTypeModuleUrl(dirMeta)),
122124
template: templateMeta,
123125
changeDetection: changeDetectionStrategy,
124126
inputs: dirMeta.inputs,
@@ -392,21 +394,21 @@ function flattenArray(tree: any[], out: Array<Type | any[]>): void {
392394
}
393395

394396
function isStaticType(value: any): boolean {
395-
return isStringMap(value) && isPresent(value['name']) && isPresent(value['moduleId']);
397+
return isStringMap(value) && isPresent(value['name']) && isPresent(value['filePath']);
396398
}
397399

398400
function isValidType(value: any): boolean {
399401
return isStaticType(value) || (value instanceof Type);
400402
}
401403

402404
function staticTypeModuleUrl(value: any): string {
403-
return isStaticType(value) ? value['moduleId'] : null;
405+
return isStaticType(value) ? value['filePath'] : null;
404406
}
405407

406-
function calcTemplateBaseUrl(reflector: ReflectorReader, type: any,
407-
cmpMetadata: ComponentMetadata): string {
408+
function componentModuleUrl(reflector: ReflectorReader, type: any,
409+
cmpMetadata: ComponentMetadata): string {
408410
if (isStaticType(type)) {
409-
return type['filePath'];
411+
return staticTypeModuleUrl(type);
410412
}
411413

412414
if (isPresent(cmpMetadata.moduleId)) {

modules/@angular/compiler/src/offline_compiler.ts

Lines changed: 46 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import {TemplateParser} from './template_parser';
1515
import {DirectiveNormalizer} from './directive_normalizer';
1616
import {OutputEmitter} from './output/abstract_emitter';
1717
import * as o from './output/output_ast';
18+
import {XHR} from './xhr';
1819

1920
import {
2021
MODULE_SUFFIX,
@@ -31,6 +32,10 @@ export class SourceModule {
3132
constructor(public moduleUrl: string, public source: string) {}
3233
}
3334

35+
export class StyleSheetSourceWithImports {
36+
constructor(public source: SourceModule, public importedUrls: string[]) {}
37+
}
38+
3439
export class NormalizedComponentWithViewDirectives {
3540
constructor(public component: CompileDirectiveMetadata,
3641
public directives: CompileDirectiveMetadata[], public pipes: CompilePipeMetadata[]) {}
@@ -39,7 +44,8 @@ export class NormalizedComponentWithViewDirectives {
3944
export class OfflineCompiler {
4045
constructor(private _directiveNormalizer: DirectiveNormalizer,
4146
private _templateParser: TemplateParser, private _styleCompiler: StyleCompiler,
42-
private _viewCompiler: ViewCompiler, private _outputEmitter: OutputEmitter) {}
47+
private _viewCompiler: ViewCompiler, private _outputEmitter: OutputEmitter,
48+
private _xhr: XHR) {}
4349

4450
normalizeDirectiveMetadata(directive: CompileDirectiveMetadata):
4551
Promise<CompileDirectiveMetadata> {
@@ -80,15 +86,19 @@ export class OfflineCompiler {
8086
return this._codegenSourceModule(moduleUrl, statements, exportedVars);
8187
}
8288

83-
compileStylesheet(stylesheetUrl: string, cssText: string): SourceModule[] {
84-
var plainStyles = this._styleCompiler.compileStylesheet(stylesheetUrl, cssText, false);
85-
var shimStyles = this._styleCompiler.compileStylesheet(stylesheetUrl, cssText, true);
86-
return [
87-
this._codegenSourceModule(_stylesModuleUrl(stylesheetUrl, false),
88-
_resolveStyleStatements(plainStyles), [plainStyles.stylesVar]),
89-
this._codegenSourceModule(_stylesModuleUrl(stylesheetUrl, true),
90-
_resolveStyleStatements(shimStyles), [shimStyles.stylesVar])
91-
];
89+
loadAndCompileStylesheet(stylesheetUrl: string, shim: boolean,
90+
suffix: string): Promise<StyleSheetSourceWithImports> {
91+
return this._xhr.get(stylesheetUrl)
92+
.then((cssText) => {
93+
var compileResult = this._styleCompiler.compileStylesheet(stylesheetUrl, cssText, shim);
94+
var importedUrls = [];
95+
compileResult.dependencies.forEach((dep) => {
96+
importedUrls.push(dep.moduleUrl);
97+
dep.valuePlaceholder.moduleUrl = _stylesModuleUrl(dep.moduleUrl, dep.isShimmed, suffix);
98+
});
99+
return new StyleSheetSourceWithImports(
100+
this._codgenStyles(stylesheetUrl, shim, suffix, compileResult), importedUrls);
101+
});
92102
}
93103

94104
private _compileComponent(compMeta: CompileDirectiveMetadata,
@@ -99,11 +109,18 @@ export class OfflineCompiler {
99109
directives, pipes, compMeta.type.name);
100110
var viewResult = this._viewCompiler.compileComponent(compMeta, parsedTemplate,
101111
o.variable(styleResult.stylesVar), pipes);
102-
ListWrapper.addAll(targetStatements, _resolveStyleStatements(styleResult));
112+
ListWrapper.addAll(targetStatements,
113+
_resolveStyleStatements(compMeta.type.moduleUrl, styleResult));
103114
ListWrapper.addAll(targetStatements, _resolveViewStatements(viewResult));
104115
return viewResult.viewFactoryVar;
105116
}
106117

118+
private _codgenStyles(inputUrl: string, shim: boolean, suffix: string,
119+
stylesCompileResult: StylesCompileResult): SourceModule {
120+
return this._codegenSourceModule(_stylesModuleUrl(inputUrl, shim, suffix),
121+
stylesCompileResult.statements,
122+
[stylesCompileResult.stylesVar]);
123+
}
107124

108125
private _codegenSourceModule(moduleUrl: string, statements: o.Statement[],
109126
exportedVars: string[]): SourceModule {
@@ -119,25 +136,36 @@ function _resolveViewStatements(compileResult: ViewCompileResult): o.Statement[]
119136
}
120137

121138

122-
function _resolveStyleStatements(compileResult: StylesCompileResult): o.Statement[] {
139+
function _resolveStyleStatements(containingModuleUrl: string,
140+
compileResult: StylesCompileResult): o.Statement[] {
141+
var containingSuffix = _splitSuffix(containingModuleUrl)[1];
123142
compileResult.dependencies.forEach((dep) => {
124-
dep.valuePlaceholder.moduleUrl = _stylesModuleUrl(dep.sourceUrl, dep.isShimmed);
143+
dep.valuePlaceholder.moduleUrl =
144+
_stylesModuleUrl(dep.moduleUrl, dep.isShimmed, containingSuffix);
125145
});
126146
return compileResult.statements;
127147
}
128148

129149
function _templateModuleUrl(comp: CompileDirectiveMetadata): string {
130-
var moduleUrl = comp.type.moduleUrl;
131-
var urlWithoutSuffix = moduleUrl.substring(0, moduleUrl.length - MODULE_SUFFIX.length);
132-
return `${urlWithoutSuffix}.ngfactory${MODULE_SUFFIX}`;
150+
var urlWithSuffix = _splitSuffix(comp.type.moduleUrl);
151+
return `${urlWithSuffix[0]}.ngfactory${urlWithSuffix[1]}`;
133152
}
134153

135-
function _stylesModuleUrl(stylesheetUrl: string, shim: boolean): string {
136-
return shim ? `${stylesheetUrl}.shim${MODULE_SUFFIX}` : `${stylesheetUrl}${MODULE_SUFFIX}`;
154+
function _stylesModuleUrl(stylesheetUrl: string, shim: boolean, suffix: string): string {
155+
return shim ? `${stylesheetUrl}.shim${suffix}` : `${stylesheetUrl}${suffix}`;
137156
}
138157

139158
function _assertComponent(meta: CompileDirectiveMetadata) {
140159
if (!meta.isComponent) {
141160
throw new BaseException(`Could not compile '${meta.type.name}' because it is not a component.`);
142161
}
143162
}
163+
164+
function _splitSuffix(path: string): string[] {
165+
let lastDot = path.lastIndexOf('.');
166+
if (lastDot !== -1) {
167+
return [path.substring(0, lastDot), path.substring(lastDot)];
168+
} else {
169+
return [path, ''];
170+
}
171+
}

modules/@angular/compiler/src/output/dart_emitter.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import {
99
CATCH_ERROR_VAR,
1010
CATCH_STACK_VAR,
1111
} from './abstract_emitter';
12-
import {getImportModulePath, ImportEnv} from './path_util';
12+
import {ImportGenerator} from './path_util';
1313

1414
var _debugModuleUrl = 'asset://debug/lib';
1515

@@ -37,7 +37,7 @@ export function debugOutputAstAsDart(ast: o.Statement | o.Expression | o.Type |
3737
}
3838

3939
export class DartEmitter implements OutputEmitter {
40-
constructor() {}
40+
constructor(private _importGenerator: ImportGenerator) {}
4141
emitStatements(moduleUrl: string, stmts: o.Statement[], exportedVars: string[]): string {
4242
var srcParts = [];
4343
// Note: We are not creating a library here as Dart does not need it.
@@ -49,7 +49,7 @@ export class DartEmitter implements OutputEmitter {
4949

5050
converter.importsWithPrefixes.forEach((prefix, importedModuleUrl) => {
5151
srcParts.push(
52-
`import '${getImportModulePath(moduleUrl, importedModuleUrl, ImportEnv.Dart)}' as ${prefix};`);
52+
`import '${this._importGenerator.getImportPath(moduleUrl, importedModuleUrl)}' as ${prefix};`);
5353
});
5454
srcParts.push(ctx.toSource());
5555
return srcParts.join('\n');
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import {BaseException} from '../../src/facade/exceptions';
2+
import {isPresent, isBlank, RegExpWrapper, Math} from '../../src/facade/lang';
3+
import {Injectable} from '@angular/core';
4+
5+
import {AssetUrl, ImportGenerator} from './path_util';
6+
7+
var _PATH_SEP = '/';
8+
var _PATH_SEP_RE = /\//g;
9+
10+
@Injectable()
11+
export class DartImportGenerator implements ImportGenerator {
12+
getImportPath(moduleUrlStr: string, importedUrlStr: string): string {
13+
var moduleUrl = AssetUrl.parse(moduleUrlStr, false);
14+
var importedUrl = AssetUrl.parse(importedUrlStr, true);
15+
if (isBlank(importedUrl)) {
16+
return importedUrlStr;
17+
}
18+
// Try to create a relative path first
19+
if (moduleUrl.firstLevelDir == importedUrl.firstLevelDir &&
20+
moduleUrl.packageName == importedUrl.packageName) {
21+
return getRelativePath(moduleUrl.modulePath, importedUrl.modulePath);
22+
} else if (importedUrl.firstLevelDir == 'lib') {
23+
return `package:${importedUrl.packageName}/${importedUrl.modulePath}`;
24+
}
25+
throw new BaseException(`Can't import url ${importedUrlStr} from ${moduleUrlStr}`);
26+
}
27+
}
28+
29+
export function getRelativePath(modulePath: string, importedPath: string): string {
30+
var moduleParts = modulePath.split(_PATH_SEP_RE);
31+
var importedParts = importedPath.split(_PATH_SEP_RE);
32+
var longestPrefix = getLongestPathSegmentPrefix(moduleParts, importedParts);
33+
34+
var resultParts = [];
35+
var goParentCount = moduleParts.length - 1 - longestPrefix;
36+
for (var i = 0; i < goParentCount; i++) {
37+
resultParts.push('..');
38+
}
39+
for (var i = longestPrefix; i < importedParts.length; i++) {
40+
resultParts.push(importedParts[i]);
41+
}
42+
return resultParts.join(_PATH_SEP);
43+
}
44+
45+
export function getLongestPathSegmentPrefix(arr1: string[], arr2: string[]): number {
46+
var prefixSize = 0;
47+
var minLen = Math.min(arr1.length, arr2.length);
48+
while (prefixSize < minLen && arr1[prefixSize] == arr2[prefixSize]) {
49+
prefixSize++;
50+
}
51+
return prefixSize;
52+
}

modules/@angular/compiler/src/output/js_emitter.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,19 +10,20 @@ import {
1010
import {BaseException} from '@angular/core';
1111
import {OutputEmitter, EmitterVisitorContext} from './abstract_emitter';
1212
import {AbstractJsEmitterVisitor} from './abstract_js_emitter';
13-
import {getImportModulePath, ImportEnv} from './path_util';
13+
import {ImportGenerator} from './path_util';
1414

1515
export class JavaScriptEmitter implements OutputEmitter {
16-
constructor() {}
16+
constructor(private _importGenerator: ImportGenerator) {}
1717
emitStatements(moduleUrl: string, stmts: o.Statement[], exportedVars: string[]): string {
1818
var converter = new JsEmitterVisitor(moduleUrl);
1919
var ctx = EmitterVisitorContext.createRoot(exportedVars);
2020
converter.visitAllStatements(stmts, ctx);
2121
var srcParts = [];
2222
converter.importsWithPrefixes.forEach((prefix, importedModuleUrl) => {
2323
// Note: can't write the real word for import as it screws up system.js auto detection...
24-
srcParts.push(`var ${prefix} = req` +
25-
`uire('${getImportModulePath(moduleUrl, importedModuleUrl, ImportEnv.JS)}');`);
24+
srcParts.push(
25+
`var ${prefix} = req` +
26+
`uire('${this._importGenerator.getImportPath(moduleUrl, importedModuleUrl)}');`);
2627
});
2728
srcParts.push(ctx.toSource());
2829
return srcParts.join('\n');

0 commit comments

Comments
 (0)