1
+ import { DirectiveMetadata , SourceModule , ViewEncapsulation } from './api' ;
2
+ import { XHR } from 'angular2/src/core/render/xhr' ;
3
+ import { StringWrapper , isJsObject , isBlank } from 'angular2/src/core/facade/lang' ;
4
+ import { PromiseWrapper , Promise } from 'angular2/src/core/facade/async' ;
5
+ import { ShadowCss } from 'angular2/src/core/render/dom/compiler/shadow_css' ;
6
+ import { UrlResolver } from 'angular2/src/core/services/url_resolver' ;
7
+ import { resolveStyleUrls } from './style_url_resolver' ;
8
+
9
+ const COMPONENT_VARIABLE = '%COMP%' ;
10
+ var COMPONENT_REGEX = / % C O M P % / g;
11
+ const HOST_ATTR = `_nghost-${ COMPONENT_VARIABLE } ` ;
12
+ const CONTENT_ATTR = `_ngcontent-${ COMPONENT_VARIABLE } ` ;
13
+ var ESCAPE_STRING_RE = / ' | \\ | \n / g;
14
+ var IS_DART = ! isJsObject ( { } ) ;
15
+
16
+ export class StyleCompiler {
17
+ private _styleCache : Map < string , Promise < string [ ] > > = new Map < string , Promise < string [ ] > > ( ) ;
18
+ private _shadowCss : ShadowCss = new ShadowCss ( ) ;
19
+
20
+ constructor ( private _xhr : XHR , private _urlResolver : UrlResolver ) { }
21
+
22
+ compileComponentRuntime ( component : DirectiveMetadata ) : Promise < string [ ] > {
23
+ var styles = component . template . styles ;
24
+ var styleAbsUrls = component . template . styleAbsUrls ;
25
+ return this . _loadStyles ( styles , styleAbsUrls ,
26
+ component . template . encapsulation === ViewEncapsulation . Emulated )
27
+ . then ( styles => styles . map ( style => StringWrapper . replaceAll ( style , COMPONENT_REGEX ,
28
+ `${ component . type . id } ` ) ) ) ;
29
+ }
30
+
31
+ compileComponentCodeGen ( component : DirectiveMetadata ) : SourceModule {
32
+ var shim = component . template . encapsulation === ViewEncapsulation . Emulated ;
33
+ var suffix ;
34
+ if ( shim ) {
35
+ var componentId = `${ component . type . id } ` ;
36
+ suffix =
37
+ codeGenMapArray ( [ 'style' ] , `style${ codeGenReplaceAll ( COMPONENT_VARIABLE , componentId ) } ` ) ;
38
+ } else {
39
+ suffix = '' ;
40
+ }
41
+ return this . _styleCodeGen ( `$component.type.typeUrl}.styles` , component . template . styles ,
42
+ component . template . styleAbsUrls , shim , suffix ) ;
43
+ }
44
+
45
+ compileStylesheetCodeGen ( moduleName : string , cssText : string ) : SourceModule [ ] {
46
+ var styleWithImports = resolveStyleUrls ( this . _urlResolver , moduleName , cssText ) ;
47
+ return [
48
+ this . _styleCodeGen ( moduleName , [ styleWithImports . style ] , styleWithImports . styleUrls , false ,
49
+ '' ) ,
50
+ this . _styleCodeGen ( moduleName , [ styleWithImports . style ] , styleWithImports . styleUrls , true , '' )
51
+ ] ;
52
+ }
53
+
54
+ private _loadStyles ( plainStyles : string [ ] , absUrls : string [ ] ,
55
+ encapsulate : boolean ) : Promise < string [ ] > {
56
+ var promises = absUrls . map ( ( absUrl ) => {
57
+ var cacheKey = `${ absUrl } ${ encapsulate ? '.shim' : '' } ` ;
58
+ var result = this . _styleCache . get ( cacheKey ) ;
59
+ if ( isBlank ( result ) ) {
60
+ result = this . _xhr . get ( absUrl ) . then ( ( style ) => {
61
+ var styleWithImports = resolveStyleUrls ( this . _urlResolver , absUrl , style ) ;
62
+ return this . _loadStyles ( [ styleWithImports . style ] , styleWithImports . styleUrls ,
63
+ encapsulate ) ;
64
+ } ) ;
65
+ this . _styleCache . set ( cacheKey , result ) ;
66
+ }
67
+ return result ;
68
+ } ) ;
69
+ return PromiseWrapper . all ( promises ) . then ( ( nestedStyles : string [ ] [ ] ) => {
70
+ var result = plainStyles . map ( plainStyle => this . _shimIfNeeded ( plainStyle , encapsulate ) ) ;
71
+ nestedStyles . forEach ( styles => styles . forEach ( style => result . push ( style ) ) ) ;
72
+ return result ;
73
+ } ) ;
74
+ }
75
+
76
+ private _styleCodeGen ( moduleName : string , plainStyles : string [ ] , absUrls : string [ ] , shim : boolean ,
77
+ suffix : string ) : SourceModule {
78
+ var imports : string [ ] [ ] = [ ] ;
79
+ var moduleSource = `${ codeGenExportVar ( 'STYLES' ) } (` ;
80
+ moduleSource +=
81
+ `[${ plainStyles . map ( plainStyle => escapeString ( this . _shimIfNeeded ( plainStyle , shim ) ) ) . join ( ',' ) } ]` ;
82
+ for ( var i = 0 ; i < absUrls . length ; i ++ ) {
83
+ var url = absUrls [ i ] ;
84
+ var moduleAlias = `import${ i } ` ;
85
+ imports . push ( [ this . _shimModuleName ( url , shim ) , moduleAlias ] ) ;
86
+ moduleSource += `${ codeGenConcatArray ( moduleAlias + '.STYLES' ) } ` ;
87
+ }
88
+ moduleSource += `)${ suffix } ;` ;
89
+ return new SourceModule ( this . _shimModuleName ( moduleName , shim ) , moduleSource , imports ) ;
90
+ }
91
+
92
+ private _shimIfNeeded ( style : string , shim : boolean ) : string {
93
+ return shim ? this . _shadowCss . shimCssText ( style , CONTENT_ATTR , HOST_ATTR ) : style ;
94
+ }
95
+
96
+ private _shimModuleName ( originalUrl : string , shim : boolean ) : string {
97
+ return shim ? `${ originalUrl } .shim` : originalUrl ;
98
+ }
99
+ }
100
+
101
+ function escapeString ( input : string ) : string {
102
+ var escapedInput = StringWrapper . replaceAllMapped ( input , ESCAPE_STRING_RE , ( match ) => {
103
+ if ( match [ 0 ] == "'" || match [ 0 ] == '\\' ) {
104
+ return `\\${ match [ 0 ] } ` ;
105
+ } else {
106
+ return '\\n' ;
107
+ }
108
+ } ) ;
109
+ return `'${ escapedInput } '` ;
110
+ }
111
+
112
+ function codeGenExportVar ( name : string ) : string {
113
+ if ( IS_DART ) {
114
+ return `var ${ name } =` ;
115
+ } else {
116
+ return `var ${ name } = exports.${ name } =` ;
117
+ }
118
+ }
119
+
120
+ function codeGenConcatArray ( expression : string ) : string {
121
+ return `${ IS_DART ? '..addAll' : '.concat' } (${ expression } )` ;
122
+ }
123
+
124
+ function codeGenMapArray ( argNames : string [ ] , callback : string ) : string {
125
+ if ( IS_DART ) {
126
+ return `.map( (${ argNames . join ( ',' ) } ) => ${ callback } ).toList()` ;
127
+ } else {
128
+ return `.map(function(${ argNames . join ( ',' ) } ) { return ${ callback } ; })` ;
129
+ }
130
+ }
131
+
132
+ function codeGenReplaceAll ( pattern : string , value : string ) : string {
133
+ if ( IS_DART ) {
134
+ return `.replaceAll('${ pattern } ', '${ value } ')` ;
135
+ } else {
136
+ return `.replace(/${ pattern } /g, '${ value } ')` ;
137
+ }
138
+ }
0 commit comments