Skip to content

Commit 67d5a07

Browse files
authored
Merge pull request #163 from mgechev/external-templates
feat: external templates and styles
2 parents c8618f4 + b18b718 commit 67d5a07

27 files changed

+959
-76
lines changed

package.json

+4-3
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@
99
"copy:common": "cp README.md dist",
1010
"build:links": "ts-node build/links.ts --src ./dist",
1111
"prepare:package": "cat package.json | ts-node build/package.ts > dist/package.json",
12-
"test": "rimraf dist && tsc && mocha dist/test/*.js dist/test/**/*.js",
13-
"test:watch": "rimraf dist && tsc && mocha dist/test/** dist/test/** --watch",
12+
"test": "rimraf dist && tsc && cp -r test/fixtures dist/test && mocha dist/test/** dist/test/** --recursive",
13+
"test:watch": "rimraf dist && tsc && cp -r test/fixtures dist/test && mocha dist/test/** dist/test/** --watch --recursive",
1414
"tscv": "tsc --version",
1515
"tsc": "tsc",
1616
"tsc:watch": "tsc --w"
@@ -61,11 +61,12 @@
6161
"zone.js": "^0.6.21"
6262
},
6363
"peerDependencies": {
64-
"tslint": "^3.9.0",
64+
"tslint": "~4.0.0",
6565
"@angular/compiler": "~2.2.0",
6666
"@angular/core": "~2.2.0"
6767
},
6868
"dependencies": {
69+
"app-root-path": "^2.0.1",
6970
"css-selector-tokenizer": "^0.7.0",
7071
"cssauron": "^1.4.0",
7172
"sprintf-js": "^1.0.3"

src/angular/config.ts

+39-1
Original file line numberDiff line numberDiff line change
@@ -1 +1,39 @@
1-
export const INTERPOLATION: [string, string] = ['{{', '}}'];
1+
import * as ts from 'typescript';
2+
3+
const root = require('app-root-path');
4+
const join = require('path').join;
5+
6+
export interface UrlResolver {
7+
(url: string, d: ts.Decorator): string;
8+
}
9+
10+
export interface Config {
11+
interpolation: [string, string];
12+
resolveUrl: UrlResolver;
13+
predefinedDirectives: DirectiveDeclaration[];
14+
basePath: string;
15+
}
16+
17+
export interface DirectiveDeclaration {
18+
selector: string;
19+
exportAs: string;
20+
}
21+
22+
export let Config: Config = {
23+
interpolation: ['{{', '}}'],
24+
resolveUrl(url: string, d: ts.Decorator) {
25+
return url;
26+
},
27+
predefinedDirectives: [
28+
{ selector: 'form', exportAs: 'ngForm' }
29+
],
30+
basePath: ''
31+
};
32+
33+
try {
34+
let newConfig = require(join(root.path, '.codelyzer'));
35+
Object.assign(Config, newConfig);
36+
} catch (e) {
37+
38+
}
39+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import {FileResolver} from './fileResolver';
2+
3+
export class DummyFileResolver extends FileResolver {
4+
resolve(path: string) {
5+
return '';
6+
}
7+
}
8+
+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
export abstract class FileResolver {
2+
abstract resolve(path: string): string;
3+
}
4+
+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import {readFileSync} from 'fs';
2+
3+
import {FileResolver} from './fileResolver';
4+
5+
export class FsFileResolver extends FileResolver {
6+
resolve(path: string) {
7+
return readFileSync(path).toString();
8+
}
9+
}
10+

src/angular/metadata.ts

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import * as ts from 'typescript';
2+
3+
export interface TemplateMetadata {
4+
template: string;
5+
node: ts.Node;
6+
source: string;
7+
}
8+
9+
export interface StyleMetadata {
10+
style: string;
11+
node: ts.Node;
12+
source: string;
13+
}
14+
15+
export interface StylesMetadata {
16+
[index: number]: StyleMetadata;
17+
length: number;
18+
push(e: StyleMetadata): number;
19+
}
20+
21+
export class DirectiveMetadata {
22+
selector: string;
23+
controller: ts.ClassDeclaration;
24+
decorator: ts.Decorator;
25+
}
26+
27+
export class ComponentMetadata extends DirectiveMetadata {
28+
template: TemplateMetadata;
29+
styles: StylesMetadata;
30+
}
31+

src/angular/metadataReader.ts

+134
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
import * as ts from 'typescript';
2+
import {current} from '../util/syntaxKind';
3+
import {isSimpleTemplateString, getDecoratorPropertyInitializer} from '../util/utils';
4+
5+
const kinds = current();
6+
7+
import {FileResolver} from './fileResolver/fileResolver';
8+
import {AbstractResolver} from './urlResolvers/abstractResolver';
9+
import {UrlResolver} from './urlResolvers/urlResolver';
10+
11+
import {DirectiveMetadata, ComponentMetadata, StylesMetadata} from './metadata';
12+
13+
export class MetadataReader {
14+
constructor(private _fileResolver: FileResolver, private _urlResolver?: AbstractResolver) {
15+
this._urlResolver = this._urlResolver || new UrlResolver();
16+
}
17+
18+
read(d: ts.ClassDeclaration): DirectiveMetadata {
19+
let directiveDecorator: ts.Decorator = null;
20+
let componentDecorator: ts.Decorator = null;
21+
(d.decorators || ([] as ts.Decorator[])).forEach((dec: ts.Decorator) => {
22+
let expr = dec.expression;
23+
if (expr && expr.kind === kinds.CallExpression && (<ts.CallExpression>expr).expression) {
24+
expr = (<ts.CallExpression>expr).expression;
25+
}
26+
const identifier = (<ts.Identifier>expr);
27+
if (expr && expr.kind === kinds.Identifier && identifier.text) {
28+
if (identifier.text === 'Component') {
29+
componentDecorator = dec;
30+
} else if (identifier.text === 'Directive') {
31+
directiveDecorator = dec;
32+
}
33+
}
34+
});
35+
if (directiveDecorator) {
36+
return this.readDirectiveMetadata(d, directiveDecorator);
37+
}
38+
if (componentDecorator) {
39+
return this.readComponentMetadata(d, componentDecorator);
40+
}
41+
return null;
42+
}
43+
44+
readDirectiveMetadata(d: ts.ClassDeclaration, dec: ts.Decorator) {
45+
const expr = this.getDecoratorArgument(dec);
46+
const metadata = new DirectiveMetadata();
47+
metadata.controller = d;
48+
metadata.decorator = dec;
49+
if (!expr) {
50+
return metadata;
51+
}
52+
expr.properties.forEach((p: any) => {
53+
if (p.kind !== kinds.PropertyAssignment) {
54+
return;
55+
}
56+
const prop = <ts.PropertyAssignment>p;
57+
if ((<any>prop).name.text === 'selector' && isSimpleTemplateString(prop.initializer)) {
58+
metadata.selector = (<any>prop).initializer.text;
59+
}
60+
});
61+
return metadata;
62+
}
63+
64+
readComponentMetadata(d: ts.ClassDeclaration, dec: ts.Decorator) {
65+
const expr = this.getDecoratorArgument(dec);
66+
const metadata = this.readDirectiveMetadata(d, dec);
67+
const result = new ComponentMetadata();
68+
if (!expr) {
69+
return result;
70+
}
71+
result.selector = metadata.selector;
72+
result.controller = metadata.controller;
73+
const inlineTemplate = getDecoratorPropertyInitializer(dec, 'template');
74+
const external = this._urlResolver.resolve(dec);
75+
if (inlineTemplate && isSimpleTemplateString(inlineTemplate)) {
76+
result.template = {
77+
template: inlineTemplate.text,
78+
source: null,
79+
node: inlineTemplate
80+
};
81+
}
82+
const inlineStyles = getDecoratorPropertyInitializer(dec, 'styles');
83+
if (inlineStyles && inlineStyles.kind === kinds.ArrayLiteralExpression) {
84+
inlineStyles.elements.forEach((inlineStyle: any) => {
85+
if (isSimpleTemplateString(inlineStyle)) {
86+
result.styles = result.styles || [];
87+
result.styles.push({
88+
style: inlineStyle.text,
89+
source: null,
90+
node: inlineStyle
91+
});
92+
}
93+
});
94+
}
95+
if (!result.template && external.templateUrl) {
96+
try {
97+
result.template = {
98+
template: this._fileResolver.resolve(external.templateUrl),
99+
source: external.templateUrl,
100+
node: null
101+
};
102+
} catch (e) {
103+
console.log(e);
104+
console.log('Cannot read the external template ' + external.templateUrl);
105+
}
106+
}
107+
if (!result.styles || !result.styles.length) {
108+
try {
109+
result.styles = <any>external.styleUrls.map((url: string) => {
110+
return {
111+
style: this._fileResolver.resolve(url),
112+
source: url,
113+
node: null
114+
};
115+
});
116+
} catch (e) {
117+
console.log('Unable to read external style. ' + e.toString());
118+
}
119+
}
120+
return result;
121+
}
122+
123+
protected getDecoratorArgument(decorator: ts.Decorator): ts.ObjectLiteralExpression {
124+
const expr = <ts.CallExpression>decorator.expression;
125+
if (expr && expr.arguments && expr.arguments.length) {
126+
const arg = <ts.ObjectLiteralExpression>expr.arguments[0];
127+
if (arg.kind === kinds.ObjectLiteralExpression && arg.properties) {
128+
return arg;
129+
}
130+
}
131+
return null;
132+
}
133+
}
134+

0 commit comments

Comments
 (0)