Skip to content

Commit 71cbb49

Browse files
committed
refactor(compiler): allow to serialize and deserialize DirectiveMetadata
1 parent 67c79ba commit 71cbb49

File tree

10 files changed

+439
-153
lines changed

10 files changed

+439
-153
lines changed

modules/angular2/src/compiler/api.ts

Lines changed: 116 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,36 @@
1-
import {isPresent, normalizeBool} from 'angular2/src/core/facade/lang';
2-
import {HtmlAst} from './html_ast';
3-
import {ChangeDetectionStrategy} from 'angular2/src/core/change_detection/change_detection';
1+
import {isPresent, normalizeBool, serializeEnum, Type} from 'angular2/src/core/facade/lang';
2+
import {
3+
ChangeDetectionStrategy,
4+
changeDetectionStrategyFromJson
5+
} from 'angular2/src/core/change_detection/change_detection';
6+
import {ViewEncapsulation, viewEncapsulationFromJson} from 'angular2/src/core/render/api';
47

58
export class TypeMetadata {
69
id: number;
7-
type: any;
10+
type: Type;
811
typeName: string;
912
typeUrl: string;
1013
constructor({id, type, typeName, typeUrl}:
11-
{id?: number, type?: string, typeName?: string, typeUrl?: string} = {}) {
14+
{id?: number, type?: Type, typeName?: string, typeUrl?: string} = {}) {
1215
this.id = id;
1316
this.type = type;
1417
this.typeName = typeName;
1518
this.typeUrl = typeUrl;
1619
}
20+
21+
static fromJson(data: StringMap<string, any>): TypeMetadata {
22+
return new TypeMetadata(
23+
{id: data['id'], type: data['type'], typeName: data['typeName'], typeUrl: data['typeUrl']});
24+
}
25+
26+
toJson(): StringMap<string, any> {
27+
return {
28+
// Note: Runtime type can't be serialized...
29+
'id': this.id,
30+
'typeName': this.typeName,
31+
'typeUrl': this.typeUrl
32+
};
33+
}
1734
}
1835

1936
export class ChangeDetectionMetadata {
@@ -44,7 +61,7 @@ export class ChangeDetectionMetadata {
4461
callOnChanges?: boolean,
4562
callDoCheck?: boolean,
4663
callOnInit?: boolean
47-
}) {
64+
} = {}) {
4865
this.changeDetection = changeDetection;
4966
this.properties = properties;
5067
this.events = events;
@@ -58,60 +75,102 @@ export class ChangeDetectionMetadata {
5875
this.callDoCheck = callDoCheck;
5976
this.callOnInit = callOnInit;
6077
}
78+
79+
static fromJson(data: StringMap<string, any>): ChangeDetectionMetadata {
80+
return new ChangeDetectionMetadata({
81+
changeDetection: isPresent(data['changeDetection']) ?
82+
changeDetectionStrategyFromJson(data['changeDetection']) :
83+
data['changeDetection'],
84+
properties: data['properties'],
85+
events: data['events'],
86+
hostListeners: data['hostListeners'],
87+
hostProperties: data['hostProperties'],
88+
callAfterContentInit: data['callAfterContentInit'],
89+
callAfterContentChecked: data['callAfterContentChecked'],
90+
callAfterViewInit: data['callAfterViewInit'],
91+
callAfterViewChecked: data['callAfterViewChecked'],
92+
callOnChanges: data['callOnChanges'],
93+
callDoCheck: data['callDoCheck'],
94+
callOnInit: data['callOnInit']
95+
});
96+
}
97+
98+
toJson(): StringMap<string, any> {
99+
return {
100+
'changeDetection': isPresent(this.changeDetection) ? serializeEnum(this.changeDetection) :
101+
this.changeDetection,
102+
'properties': this.properties,
103+
'events': this.events,
104+
'hostListeners': this.hostListeners,
105+
'hostProperties': this.hostProperties,
106+
'callAfterContentInit': this.callAfterContentInit,
107+
'callAfterContentChecked': this.callAfterContentChecked,
108+
'callAfterViewInit': this.callAfterViewInit,
109+
'callAfterViewChecked': this.callAfterViewChecked,
110+
'callOnChanges': this.callOnChanges,
111+
'callDoCheck': this.callDoCheck,
112+
'callOnInit': this.callOnInit
113+
};
114+
}
61115
}
62116

63117
export class TemplateMetadata {
64118
encapsulation: ViewEncapsulation;
65-
nodes: HtmlAst[];
119+
template: string;
66120
styles: string[];
67121
styleAbsUrls: string[];
68122
ngContentSelectors: string[];
69-
constructor({encapsulation, nodes, styles, styleAbsUrls, ngContentSelectors}: {
123+
constructor({encapsulation, template, styles, styleAbsUrls, ngContentSelectors}: {
70124
encapsulation?: ViewEncapsulation,
71-
nodes?: HtmlAst[],
125+
template?: string,
72126
styles?: string[],
73127
styleAbsUrls?: string[],
74128
ngContentSelectors?: string[]
75-
}) {
129+
} = {}) {
76130
this.encapsulation = encapsulation;
77-
this.nodes = nodes;
131+
this.template = template;
78132
this.styles = styles;
79133
this.styleAbsUrls = styleAbsUrls;
80134
this.ngContentSelectors = ngContentSelectors;
81135
}
82-
}
83136

84-
/**
85-
* How the template and styles of a view should be encapsulated.
86-
*/
87-
export enum ViewEncapsulation {
88-
/**
89-
* Emulate scoping of styles by preprocessing the style rules
90-
* and adding additional attributes to elements. This is the default.
91-
*/
92-
Emulated,
93-
/**
94-
* Uses the native mechanism of the renderer. For the DOM this means creating a ShadowRoot.
95-
*/
96-
Native,
97-
/**
98-
* Don't scope the template nor the styles.
99-
*/
100-
None
137+
static fromJson(data: StringMap<string, any>):TemplateMetadata {
138+
return new TemplateMetadata({
139+
encapsulation: isPresent(data['encapsulation']) ?
140+
viewEncapsulationFromJson(data['encapsulation']) :
141+
data['encapsulation'],
142+
template: data['template'],
143+
styles: data['styles'],
144+
styleAbsUrls: data['styleAbsUrls'],
145+
ngContentSelectors: data['ngContentSelectors'],
146+
});
147+
}
148+
149+
toJson(): StringMap<string, any> {
150+
return {
151+
'encapsulation':
152+
isPresent(this.encapsulation) ? serializeEnum(this.encapsulation) : this.encapsulation,
153+
'template': this.template,
154+
'styles': this.styles,
155+
'styleAbsUrls': this.styleAbsUrls,
156+
'ngContentSelectors': this.ngContentSelectors,
157+
};
158+
}
101159
}
102160

161+
103162
export class DirectiveMetadata {
104163
type: TypeMetadata;
105164
isComponent: boolean;
106165
selector: string;
107-
hostAttributes: Map<string, string>;
166+
hostAttributes: StringMap<string, string>;
108167
changeDetection: ChangeDetectionMetadata;
109168
template: TemplateMetadata;
110169
constructor({type, isComponent, selector, hostAttributes, changeDetection, template}: {
111170
type?: TypeMetadata,
112171
isComponent?: boolean,
113172
selector?: string,
114-
hostAttributes?: Map<string, string>,
173+
hostAttributes?: StringMap<string, string>,
115174
changeDetection?: ChangeDetectionMetadata,
116175
template?: TemplateMetadata
117176
} = {}) {
@@ -122,6 +181,32 @@ export class DirectiveMetadata {
122181
this.changeDetection = changeDetection;
123182
this.template = template;
124183
}
184+
185+
static fromJson(data: StringMap<string, any>): DirectiveMetadata {
186+
return new DirectiveMetadata({
187+
type: isPresent(data['type']) ? TypeMetadata.fromJson(data['type']) : data['type'],
188+
isComponent: data['isComponent'],
189+
selector: data['selector'],
190+
hostAttributes: data['hostAttributes'],
191+
changeDetection: isPresent(data['changeDetection']) ?
192+
ChangeDetectionMetadata.fromJson(data['changeDetection']) :
193+
data['changeDetection'],
194+
template: isPresent(data['template']) ? TemplateMetadata.fromJson(data['template']) :
195+
data['template']
196+
});
197+
}
198+
199+
toJson(): StringMap<string, any> {
200+
return {
201+
'type': isPresent(this.type) ? this.type.toJson() : this.type,
202+
'isComponent': this.isComponent,
203+
'selector': this.selector,
204+
'hostAttributes': this.hostAttributes,
205+
'changeDetection':
206+
isPresent(this.changeDetection) ? this.changeDetection.toJson() : this.changeDetection,
207+
'template': isPresent(this.template) ? this.template.toJson() : this.template
208+
};
209+
}
125210
}
126211

127212
export class SourceModule {

modules/angular2/src/compiler/html_ast.ts

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,35 +2,35 @@ import {isPresent} from 'angular2/src/core/facade/lang';
22

33
export interface HtmlAst {
44
sourceInfo: string;
5-
visit(visitor: HtmlAstVisitor): any;
5+
visit(visitor: HtmlAstVisitor, context: any): any;
66
}
77

88
export class HtmlTextAst implements HtmlAst {
99
constructor(public value: string, public sourceInfo: string) {}
10-
visit(visitor: HtmlAstVisitor): any { return visitor.visitText(this); }
10+
visit(visitor: HtmlAstVisitor, context: any): any { return visitor.visitText(this, context); }
1111
}
1212

1313
export class HtmlAttrAst implements HtmlAst {
1414
constructor(public name: string, public value: string, public sourceInfo: string) {}
15-
visit(visitor: HtmlAstVisitor): any { return visitor.visitAttr(this); }
15+
visit(visitor: HtmlAstVisitor, context: any): any { return visitor.visitAttr(this, context); }
1616
}
1717

1818
export class HtmlElementAst implements HtmlAst {
1919
constructor(public name: string, public attrs: HtmlAttrAst[], public children: HtmlAst[],
2020
public sourceInfo: string) {}
21-
visit(visitor: HtmlAstVisitor): any { return visitor.visitElement(this); }
21+
visit(visitor: HtmlAstVisitor, context: any): any { return visitor.visitElement(this, context); }
2222
}
2323

2424
export interface HtmlAstVisitor {
25-
visitElement(ast: HtmlElementAst): any;
26-
visitAttr(ast: HtmlAttrAst): any;
27-
visitText(ast: HtmlTextAst): any;
25+
visitElement(ast: HtmlElementAst, context: any): any;
26+
visitAttr(ast: HtmlAttrAst, context: any): any;
27+
visitText(ast: HtmlTextAst, context: any): any;
2828
}
2929

30-
export function htmlVisitAll(visitor: HtmlAstVisitor, asts: HtmlAst[]): any[] {
30+
export function htmlVisitAll(visitor: HtmlAstVisitor, asts: HtmlAst[], context: any = null): any[] {
3131
var result = [];
3232
asts.forEach(ast => {
33-
var astResult = ast.visit(visitor);
33+
var astResult = ast.visit(visitor, context);
3434
if (isPresent(astResult)) {
3535
result.push(astResult);
3636
}

modules/angular2/src/compiler/html_parser.ts

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,16 @@ import {
77
} from 'angular2/src/core/facade/lang';
88
import {DOM} from 'angular2/src/core/dom/dom_adapter';
99

10-
import {HtmlAst, HtmlAttrAst, HtmlTextAst, HtmlElementAst} from './html_ast';
10+
import {
11+
HtmlAst,
12+
HtmlAttrAst,
13+
HtmlTextAst,
14+
HtmlElementAst,
15+
HtmlAstVisitor,
16+
htmlVisitAll
17+
} from './html_ast';
18+
19+
import {escapeDoubleQuoteString} from './util';
1120

1221
const NG_NON_BINDABLE = 'ng-non-bindable';
1322

@@ -16,6 +25,12 @@ export class HtmlParser {
1625
var root = DOM.createTemplate(template);
1726
return parseChildNodes(root, sourceInfo);
1827
}
28+
unparse(nodes: HtmlAst[]): string {
29+
var visitor = new UnparseVisitor();
30+
var parts = [];
31+
htmlVisitAll(visitor, nodes, parts);
32+
return parts.join('');
33+
}
1934
}
2035

2136
function parseText(text: Text, indexInParent: number, parentSourceInfo: string): HtmlTextAst {
@@ -92,3 +107,27 @@ function ignoreChildren(attrs: HtmlAttrAst[]): boolean {
92107
}
93108
return false;
94109
}
110+
111+
class UnparseVisitor implements HtmlAstVisitor {
112+
visitElement(ast: HtmlElementAst, parts: string[]): any {
113+
parts.push(`<${ast.name}`);
114+
var attrs = [];
115+
htmlVisitAll(this, ast.attrs, attrs);
116+
if (ast.attrs.length > 0) {
117+
parts.push(' ');
118+
parts.push(attrs.join(' '));
119+
}
120+
parts.push(`>`);
121+
htmlVisitAll(this, ast.children, parts);
122+
parts.push(`</${ast.name}>`);
123+
return null;
124+
}
125+
visitAttr(ast: HtmlAttrAst, parts: string[]): any {
126+
parts.push(`${ast.name}=${escapeDoubleQuoteString(ast.value)}`);
127+
return null;
128+
}
129+
visitText(ast: HtmlTextAst, parts: string[]): any {
130+
parts.push(ast.value);
131+
return null;
132+
}
133+
}

modules/angular2/src/compiler/template_loader.ts

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1-
import {TypeMetadata, TemplateMetadata, ViewEncapsulation} from './api';
2-
import {isPresent} from 'angular2/src/core/facade/lang';
1+
import {TypeMetadata, TemplateMetadata} from './api';
2+
import {ViewEncapsulation} from 'angular2/src/core/render/api';
3+
import {isPresent, isBlank} from 'angular2/src/core/facade/lang';
34
import {Promise, PromiseWrapper} from 'angular2/src/core/facade/async';
45

56
import {XHR} from 'angular2/src/core/render/xhr';
@@ -61,7 +62,7 @@ export class TemplateLoader {
6162
allStyleUrls.map(styleUrl => this._urlResolver.resolve(templateSourceUrl, styleUrl));
6263
return new TemplateMetadata({
6364
encapsulation: encapsulation,
64-
nodes: remainingNodes,
65+
template: this._domParser.unparse(remainingNodes),
6566
styles: allResolvedStyles,
6667
styleAbsUrls: allStyleAbsUrls,
6768
ngContentSelectors: visitor.ngContentSelectors
@@ -74,7 +75,7 @@ class TemplatePreparseVisitor implements HtmlAstVisitor {
7475
styles: string[] = [];
7576
styleUrls: string[] = [];
7677

77-
visitElement(ast: HtmlElementAst): HtmlElementAst {
78+
visitElement(ast: HtmlElementAst, context: any): HtmlElementAst {
7879
var selectAttr = null;
7980
var hrefAttr = null;
8081
var relAttr = null;
@@ -90,7 +91,7 @@ class TemplatePreparseVisitor implements HtmlAstVisitor {
9091
var nodeName = ast.name;
9192
var keepElement = true;
9293
if (nodeName == NG_CONTENT_ELEMENT) {
93-
this.ngContentSelectors.push(selectAttr);
94+
this.ngContentSelectors.push(normalizeNgContentSelect(selectAttr));
9495
} else if (nodeName == STYLE_ELEMENT) {
9596
keepElement = false;
9697
var textContent = '';
@@ -111,6 +112,13 @@ class TemplatePreparseVisitor implements HtmlAstVisitor {
111112
return null;
112113
}
113114
}
114-
visitAttr(ast: HtmlAttrAst): HtmlAttrAst { return ast; }
115-
visitText(ast: HtmlTextAst): HtmlTextAst { return ast; }
115+
visitAttr(ast: HtmlAttrAst, context: any): HtmlAttrAst { return ast; }
116+
visitText(ast: HtmlTextAst, context: any): HtmlTextAst { return ast; }
116117
}
118+
119+
function normalizeNgContentSelect(selectAttr: string): string {
120+
if (isBlank(selectAttr) || selectAttr.length === 0) {
121+
return '*';
122+
}
123+
return selectAttr;
124+
}

modules/angular2/src/core/change_detection/change_detection.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ export {
3434
DebugContext,
3535
ChangeDetectorGenConfig
3636
} from './interfaces';
37-
export {ChangeDetectionStrategy} from './constants';
37+
export {ChangeDetectionStrategy, changeDetectionStrategyFromJson} from './constants';
3838
export {DynamicProtoChangeDetector} from './proto_change_detector';
3939
export {BindingRecord, BindingTarget} from './binding_record';
4040
export {DirectiveIndex, DirectiveRecord} from './directive_record';

0 commit comments

Comments
 (0)