Skip to content

Commit

Permalink
fix(compiler): support css stylesheets in offline compiler
Browse files Browse the repository at this point in the history
  • Loading branch information
tbosch committed May 2, 2016
1 parent c386fc8 commit 00d3b60
Show file tree
Hide file tree
Showing 38 changed files with 436 additions and 388 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ tmp

# Files created by the template compiler
**/*.ngfactory.ts
**/*.css.ts
**/*.css.shim.ts

# Or type definitions we mirror from github
# (NB: these lines are removed in publish-build-artifacts.sh)
Expand Down
7 changes: 7 additions & 0 deletions modules/@angular/compiler/private_export.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,16 @@
import * as selector from './src/selector';
import * as pathUtil from './src/output/path_util';

export namespace __compiler_private__ {
export type SelectorMatcher = selector.SelectorMatcher;
export var SelectorMatcher = selector.SelectorMatcher;

export type CssSelector = selector.CssSelector;
export var CssSelector = selector.CssSelector;

export type AssetUrl = pathUtil.AssetUrl;
export var AssetUrl = pathUtil.AssetUrl;

export type ImportGenerator = pathUtil.ImportGenerator;
export var ImportGenerator = pathUtil.ImportGenerator;
}
14 changes: 4 additions & 10 deletions modules/@angular/compiler/src/compile_metadata.ts
Original file line number Diff line number Diff line change
Expand Up @@ -466,24 +466,20 @@ export class CompileTemplateMetadata {
styles: string[];
styleUrls: string[];
ngContentSelectors: string[];
baseUrl: string;
constructor({encapsulation, template, templateUrl, styles, styleUrls, ngContentSelectors,
baseUrl}: {
constructor({encapsulation, template, templateUrl, styles, styleUrls, ngContentSelectors}: {
encapsulation?: ViewEncapsulation,
template?: string,
templateUrl?: string,
styles?: string[],
styleUrls?: string[],
ngContentSelectors?: string[],
baseUrl?: string
ngContentSelectors?: string[]
} = {}) {
this.encapsulation = isPresent(encapsulation) ? encapsulation : ViewEncapsulation.Emulated;
this.template = template;
this.templateUrl = templateUrl;
this.styles = isPresent(styles) ? styles : [];
this.styleUrls = isPresent(styleUrls) ? styleUrls : [];
this.ngContentSelectors = isPresent(ngContentSelectors) ? ngContentSelectors : [];
this.baseUrl = baseUrl;
}

static fromJson(data: {[key: string]: any}): CompileTemplateMetadata {
Expand All @@ -495,8 +491,7 @@ export class CompileTemplateMetadata {
templateUrl: data['templateUrl'],
styles: data['styles'],
styleUrls: data['styleUrls'],
ngContentSelectors: data['ngContentSelectors'],
baseUrl: data['baseUrl']
ngContentSelectors: data['ngContentSelectors']
});
}

Expand All @@ -508,8 +503,7 @@ export class CompileTemplateMetadata {
'templateUrl': this.templateUrl,
'styles': this.styles,
'styleUrls': this.styleUrls,
'ngContentSelectors': this.ngContentSelectors,
'baseUrl': this.baseUrl
'ngContentSelectors': this.ngContentSelectors
};
}
}
Expand Down
6 changes: 3 additions & 3 deletions modules/@angular/compiler/src/directive_normalizer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,9 +63,9 @@ export class DirectiveNormalizer {
template: CompileTemplateMetadata): Promise<CompileTemplateMetadata> {
if (isPresent(template.template)) {
return PromiseWrapper.resolve(this.normalizeLoadedTemplate(
directiveType, template, template.template, template.baseUrl));
directiveType, template, template.template, directiveType.moduleUrl));
} else if (isPresent(template.templateUrl)) {
var sourceAbsUrl = this._urlResolver.resolve(template.baseUrl, template.templateUrl);
var sourceAbsUrl = this._urlResolver.resolve(directiveType.moduleUrl, template.templateUrl);
return this._xhr.get(sourceAbsUrl)
.then(templateContent => this.normalizeLoadedTemplate(directiveType, template,
templateContent, sourceAbsUrl));
Expand All @@ -90,7 +90,7 @@ export class DirectiveNormalizer {
visitor.styleUrls.filter(isStyleUrlResolvable)
.map(url => this._urlResolver.resolve(templateAbsUrl, url))
.concat(templateMeta.styleUrls.filter(isStyleUrlResolvable)
.map(url => this._urlResolver.resolve(templateMeta.baseUrl, url)));
.map(url => this._urlResolver.resolve(directiveType.moduleUrl, url)));

var allResolvedStyles = allStyles.map(style => {
var styleWithImports = extractStyleUrls(this._urlResolver, templateAbsUrl, style);
Expand Down
18 changes: 10 additions & 8 deletions modules/@angular/compiler/src/metadata_resolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,8 +95,7 @@ export class CompileMetadataResolver {
template: viewMeta.template,
templateUrl: viewMeta.templateUrl,
styles: viewMeta.styles,
styleUrls: viewMeta.styleUrls,
baseUrl: calcTemplateBaseUrl(this._reflector, directiveType, cmpMeta)
styleUrls: viewMeta.styleUrls
});
changeDetectionStrategy = cmpMeta.changeDetection;
if (isPresent(dirMeta.viewProviders)) {
Expand All @@ -118,7 +117,10 @@ export class CompileMetadataResolver {
selector: dirMeta.selector,
exportAs: dirMeta.exportAs,
isComponent: isPresent(templateMeta),
type: this.getTypeMetadata(directiveType, staticTypeModuleUrl(directiveType)),
type: this.getTypeMetadata(directiveType,
isPresent(cmpMeta) ?
componentModuleUrl(this._reflector, directiveType, cmpMeta) :
staticTypeModuleUrl(dirMeta)),
template: templateMeta,
changeDetection: changeDetectionStrategy,
inputs: dirMeta.inputs,
Expand Down Expand Up @@ -392,21 +394,21 @@ function flattenArray(tree: any[], out: Array<Type | any[]>): void {
}

function isStaticType(value: any): boolean {
return isStringMap(value) && isPresent(value['name']) && isPresent(value['moduleId']);
return isStringMap(value) && isPresent(value['name']) && isPresent(value['filePath']);
}

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

function staticTypeModuleUrl(value: any): string {
return isStaticType(value) ? value['moduleId'] : null;
return isStaticType(value) ? value['filePath'] : null;
}

function calcTemplateBaseUrl(reflector: ReflectorReader, type: any,
cmpMetadata: ComponentMetadata): string {
function componentModuleUrl(reflector: ReflectorReader, type: any,
cmpMetadata: ComponentMetadata): string {
if (isStaticType(type)) {
return type['filePath'];
return staticTypeModuleUrl(type);
}

if (isPresent(cmpMetadata.moduleId)) {
Expand Down
64 changes: 46 additions & 18 deletions modules/@angular/compiler/src/offline_compiler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {TemplateParser} from './template_parser';
import {DirectiveNormalizer} from './directive_normalizer';
import {OutputEmitter} from './output/abstract_emitter';
import * as o from './output/output_ast';
import {XHR} from './xhr';

import {
MODULE_SUFFIX,
Expand All @@ -31,6 +32,10 @@ export class SourceModule {
constructor(public moduleUrl: string, public source: string) {}
}

export class StyleSheetSourceWithImports {
constructor(public source: SourceModule, public importedUrls: string[]) {}
}

export class NormalizedComponentWithViewDirectives {
constructor(public component: CompileDirectiveMetadata,
public directives: CompileDirectiveMetadata[], public pipes: CompilePipeMetadata[]) {}
Expand All @@ -39,7 +44,8 @@ export class NormalizedComponentWithViewDirectives {
export class OfflineCompiler {
constructor(private _directiveNormalizer: DirectiveNormalizer,
private _templateParser: TemplateParser, private _styleCompiler: StyleCompiler,
private _viewCompiler: ViewCompiler, private _outputEmitter: OutputEmitter) {}
private _viewCompiler: ViewCompiler, private _outputEmitter: OutputEmitter,
private _xhr: XHR) {}

normalizeDirectiveMetadata(directive: CompileDirectiveMetadata):
Promise<CompileDirectiveMetadata> {
Expand Down Expand Up @@ -80,15 +86,19 @@ export class OfflineCompiler {
return this._codegenSourceModule(moduleUrl, statements, exportedVars);
}

compileStylesheet(stylesheetUrl: string, cssText: string): SourceModule[] {
var plainStyles = this._styleCompiler.compileStylesheet(stylesheetUrl, cssText, false);
var shimStyles = this._styleCompiler.compileStylesheet(stylesheetUrl, cssText, true);
return [
this._codegenSourceModule(_stylesModuleUrl(stylesheetUrl, false),
_resolveStyleStatements(plainStyles), [plainStyles.stylesVar]),
this._codegenSourceModule(_stylesModuleUrl(stylesheetUrl, true),
_resolveStyleStatements(shimStyles), [shimStyles.stylesVar])
];
loadAndCompileStylesheet(stylesheetUrl: string, shim: boolean,
suffix: string): Promise<StyleSheetSourceWithImports> {
return this._xhr.get(stylesheetUrl)
.then((cssText) => {
var compileResult = this._styleCompiler.compileStylesheet(stylesheetUrl, cssText, shim);
var importedUrls = [];
compileResult.dependencies.forEach((dep) => {
importedUrls.push(dep.moduleUrl);
dep.valuePlaceholder.moduleUrl = _stylesModuleUrl(dep.moduleUrl, dep.isShimmed, suffix);
});
return new StyleSheetSourceWithImports(
this._codgenStyles(stylesheetUrl, shim, suffix, compileResult), importedUrls);
});
}

private _compileComponent(compMeta: CompileDirectiveMetadata,
Expand All @@ -99,11 +109,18 @@ export class OfflineCompiler {
directives, pipes, compMeta.type.name);
var viewResult = this._viewCompiler.compileComponent(compMeta, parsedTemplate,
o.variable(styleResult.stylesVar), pipes);
ListWrapper.addAll(targetStatements, _resolveStyleStatements(styleResult));
ListWrapper.addAll(targetStatements,
_resolveStyleStatements(compMeta.type.moduleUrl, styleResult));
ListWrapper.addAll(targetStatements, _resolveViewStatements(viewResult));
return viewResult.viewFactoryVar;
}

private _codgenStyles(inputUrl: string, shim: boolean, suffix: string,
stylesCompileResult: StylesCompileResult): SourceModule {
return this._codegenSourceModule(_stylesModuleUrl(inputUrl, shim, suffix),
stylesCompileResult.statements,
[stylesCompileResult.stylesVar]);
}

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


function _resolveStyleStatements(compileResult: StylesCompileResult): o.Statement[] {
function _resolveStyleStatements(containingModuleUrl: string,
compileResult: StylesCompileResult): o.Statement[] {
var containingSuffix = _splitSuffix(containingModuleUrl)[1];
compileResult.dependencies.forEach((dep) => {
dep.valuePlaceholder.moduleUrl = _stylesModuleUrl(dep.sourceUrl, dep.isShimmed);
dep.valuePlaceholder.moduleUrl =
_stylesModuleUrl(dep.moduleUrl, dep.isShimmed, containingSuffix);
});
return compileResult.statements;
}

function _templateModuleUrl(comp: CompileDirectiveMetadata): string {
var moduleUrl = comp.type.moduleUrl;
var urlWithoutSuffix = moduleUrl.substring(0, moduleUrl.length - MODULE_SUFFIX.length);
return `${urlWithoutSuffix}.ngfactory${MODULE_SUFFIX}`;
var urlWithSuffix = _splitSuffix(comp.type.moduleUrl);
return `${urlWithSuffix[0]}.ngfactory${urlWithSuffix[1]}`;
}

function _stylesModuleUrl(stylesheetUrl: string, shim: boolean): string {
return shim ? `${stylesheetUrl}.shim${MODULE_SUFFIX}` : `${stylesheetUrl}${MODULE_SUFFIX}`;
function _stylesModuleUrl(stylesheetUrl: string, shim: boolean, suffix: string): string {
return shim ? `${stylesheetUrl}.shim${suffix}` : `${stylesheetUrl}${suffix}`;
}

function _assertComponent(meta: CompileDirectiveMetadata) {
if (!meta.isComponent) {
throw new BaseException(`Could not compile '${meta.type.name}' because it is not a component.`);
}
}

function _splitSuffix(path: string): string[] {
let lastDot = path.lastIndexOf('.');
if (lastDot !== -1) {
return [path.substring(0, lastDot), path.substring(lastDot)];
} else {
return [path, ''];
}
}
6 changes: 3 additions & 3 deletions modules/@angular/compiler/src/output/dart_emitter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {
CATCH_ERROR_VAR,
CATCH_STACK_VAR,
} from './abstract_emitter';
import {getImportModulePath, ImportEnv} from './path_util';
import {ImportGenerator} from './path_util';

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

Expand Down Expand Up @@ -37,7 +37,7 @@ export function debugOutputAstAsDart(ast: o.Statement | o.Expression | o.Type |
}

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

converter.importsWithPrefixes.forEach((prefix, importedModuleUrl) => {
srcParts.push(
`import '${getImportModulePath(moduleUrl, importedModuleUrl, ImportEnv.Dart)}' as ${prefix};`);
`import '${this._importGenerator.getImportPath(moduleUrl, importedModuleUrl)}' as ${prefix};`);
});
srcParts.push(ctx.toSource());
return srcParts.join('\n');
Expand Down
52 changes: 52 additions & 0 deletions modules/@angular/compiler/src/output/dart_imports.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import {BaseException} from '../../src/facade/exceptions';
import {isPresent, isBlank, RegExpWrapper, Math} from '../../src/facade/lang';
import {Injectable} from '@angular/core';

import {AssetUrl, ImportGenerator} from './path_util';

var _PATH_SEP = '/';
var _PATH_SEP_RE = /\//g;

@Injectable()
export class DartImportGenerator implements ImportGenerator {
getImportPath(moduleUrlStr: string, importedUrlStr: string): string {
var moduleUrl = AssetUrl.parse(moduleUrlStr, false);
var importedUrl = AssetUrl.parse(importedUrlStr, true);
if (isBlank(importedUrl)) {
return importedUrlStr;
}
// Try to create a relative path first
if (moduleUrl.firstLevelDir == importedUrl.firstLevelDir &&
moduleUrl.packageName == importedUrl.packageName) {
return getRelativePath(moduleUrl.modulePath, importedUrl.modulePath);
} else if (importedUrl.firstLevelDir == 'lib') {
return `package:${importedUrl.packageName}/${importedUrl.modulePath}`;
}
throw new BaseException(`Can't import url ${importedUrlStr} from ${moduleUrlStr}`);
}
}

export function getRelativePath(modulePath: string, importedPath: string): string {
var moduleParts = modulePath.split(_PATH_SEP_RE);
var importedParts = importedPath.split(_PATH_SEP_RE);
var longestPrefix = getLongestPathSegmentPrefix(moduleParts, importedParts);

var resultParts = [];
var goParentCount = moduleParts.length - 1 - longestPrefix;
for (var i = 0; i < goParentCount; i++) {
resultParts.push('..');
}
for (var i = longestPrefix; i < importedParts.length; i++) {
resultParts.push(importedParts[i]);
}
return resultParts.join(_PATH_SEP);
}

export function getLongestPathSegmentPrefix(arr1: string[], arr2: string[]): number {
var prefixSize = 0;
var minLen = Math.min(arr1.length, arr2.length);
while (prefixSize < minLen && arr1[prefixSize] == arr2[prefixSize]) {
prefixSize++;
}
return prefixSize;
}
9 changes: 5 additions & 4 deletions modules/@angular/compiler/src/output/js_emitter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,20 @@ import {
import {BaseException} from '@angular/core';
import {OutputEmitter, EmitterVisitorContext} from './abstract_emitter';
import {AbstractJsEmitterVisitor} from './abstract_js_emitter';
import {getImportModulePath, ImportEnv} from './path_util';
import {ImportGenerator} from './path_util';

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

0 comments on commit 00d3b60

Please sign in to comment.