Skip to content

Commit

Permalink
Merge pull request #163 from mgechev/external-templates
Browse files Browse the repository at this point in the history
feat: external templates and styles
  • Loading branch information
mgechev authored Nov 26, 2016
2 parents c8618f4 + b18b718 commit 67d5a07
Show file tree
Hide file tree
Showing 27 changed files with 959 additions and 76 deletions.
7 changes: 4 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@
"copy:common": "cp README.md dist",
"build:links": "ts-node build/links.ts --src ./dist",
"prepare:package": "cat package.json | ts-node build/package.ts > dist/package.json",
"test": "rimraf dist && tsc && mocha dist/test/*.js dist/test/**/*.js",
"test:watch": "rimraf dist && tsc && mocha dist/test/** dist/test/** --watch",
"test": "rimraf dist && tsc && cp -r test/fixtures dist/test && mocha dist/test/** dist/test/** --recursive",
"test:watch": "rimraf dist && tsc && cp -r test/fixtures dist/test && mocha dist/test/** dist/test/** --watch --recursive",
"tscv": "tsc --version",
"tsc": "tsc",
"tsc:watch": "tsc --w"
Expand Down Expand Up @@ -61,11 +61,12 @@
"zone.js": "^0.6.21"
},
"peerDependencies": {
"tslint": "^3.9.0",
"tslint": "~4.0.0",
"@angular/compiler": "~2.2.0",
"@angular/core": "~2.2.0"
},
"dependencies": {
"app-root-path": "^2.0.1",
"css-selector-tokenizer": "^0.7.0",
"cssauron": "^1.4.0",
"sprintf-js": "^1.0.3"
Expand Down
40 changes: 39 additions & 1 deletion src/angular/config.ts
Original file line number Diff line number Diff line change
@@ -1 +1,39 @@
export const INTERPOLATION: [string, string] = ['{{', '}}'];
import * as ts from 'typescript';

const root = require('app-root-path');
const join = require('path').join;

export interface UrlResolver {
(url: string, d: ts.Decorator): string;
}

export interface Config {
interpolation: [string, string];
resolveUrl: UrlResolver;
predefinedDirectives: DirectiveDeclaration[];
basePath: string;
}

export interface DirectiveDeclaration {
selector: string;
exportAs: string;
}

export let Config: Config = {
interpolation: ['{{', '}}'],
resolveUrl(url: string, d: ts.Decorator) {
return url;
},
predefinedDirectives: [
{ selector: 'form', exportAs: 'ngForm' }
],
basePath: ''
};

try {
let newConfig = require(join(root.path, '.codelyzer'));
Object.assign(Config, newConfig);
} catch (e) {

}

8 changes: 8 additions & 0 deletions src/angular/fileResolver/dummyFileResolver.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import {FileResolver} from './fileResolver';

export class DummyFileResolver extends FileResolver {
resolve(path: string) {
return '';
}
}

4 changes: 4 additions & 0 deletions src/angular/fileResolver/fileResolver.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export abstract class FileResolver {
abstract resolve(path: string): string;
}

10 changes: 10 additions & 0 deletions src/angular/fileResolver/fsFileResolver.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import {readFileSync} from 'fs';

import {FileResolver} from './fileResolver';

export class FsFileResolver extends FileResolver {
resolve(path: string) {
return readFileSync(path).toString();
}
}

31 changes: 31 additions & 0 deletions src/angular/metadata.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import * as ts from 'typescript';

export interface TemplateMetadata {
template: string;
node: ts.Node;
source: string;
}

export interface StyleMetadata {
style: string;
node: ts.Node;
source: string;
}

export interface StylesMetadata {
[index: number]: StyleMetadata;
length: number;
push(e: StyleMetadata): number;
}

export class DirectiveMetadata {
selector: string;
controller: ts.ClassDeclaration;
decorator: ts.Decorator;
}

export class ComponentMetadata extends DirectiveMetadata {
template: TemplateMetadata;
styles: StylesMetadata;
}

134 changes: 134 additions & 0 deletions src/angular/metadataReader.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
import * as ts from 'typescript';
import {current} from '../util/syntaxKind';
import {isSimpleTemplateString, getDecoratorPropertyInitializer} from '../util/utils';

const kinds = current();

import {FileResolver} from './fileResolver/fileResolver';
import {AbstractResolver} from './urlResolvers/abstractResolver';
import {UrlResolver} from './urlResolvers/urlResolver';

import {DirectiveMetadata, ComponentMetadata, StylesMetadata} from './metadata';

export class MetadataReader {
constructor(private _fileResolver: FileResolver, private _urlResolver?: AbstractResolver) {
this._urlResolver = this._urlResolver || new UrlResolver();
}

read(d: ts.ClassDeclaration): DirectiveMetadata {
let directiveDecorator: ts.Decorator = null;
let componentDecorator: ts.Decorator = null;
(d.decorators || ([] as ts.Decorator[])).forEach((dec: ts.Decorator) => {
let expr = dec.expression;
if (expr && expr.kind === kinds.CallExpression && (<ts.CallExpression>expr).expression) {
expr = (<ts.CallExpression>expr).expression;
}
const identifier = (<ts.Identifier>expr);
if (expr && expr.kind === kinds.Identifier && identifier.text) {
if (identifier.text === 'Component') {
componentDecorator = dec;
} else if (identifier.text === 'Directive') {
directiveDecorator = dec;
}
}
});
if (directiveDecorator) {
return this.readDirectiveMetadata(d, directiveDecorator);
}
if (componentDecorator) {
return this.readComponentMetadata(d, componentDecorator);
}
return null;
}

readDirectiveMetadata(d: ts.ClassDeclaration, dec: ts.Decorator) {
const expr = this.getDecoratorArgument(dec);
const metadata = new DirectiveMetadata();
metadata.controller = d;
metadata.decorator = dec;
if (!expr) {
return metadata;
}
expr.properties.forEach((p: any) => {
if (p.kind !== kinds.PropertyAssignment) {
return;
}
const prop = <ts.PropertyAssignment>p;
if ((<any>prop).name.text === 'selector' && isSimpleTemplateString(prop.initializer)) {
metadata.selector = (<any>prop).initializer.text;
}
});
return metadata;
}

readComponentMetadata(d: ts.ClassDeclaration, dec: ts.Decorator) {
const expr = this.getDecoratorArgument(dec);
const metadata = this.readDirectiveMetadata(d, dec);
const result = new ComponentMetadata();
if (!expr) {
return result;
}
result.selector = metadata.selector;
result.controller = metadata.controller;
const inlineTemplate = getDecoratorPropertyInitializer(dec, 'template');
const external = this._urlResolver.resolve(dec);
if (inlineTemplate && isSimpleTemplateString(inlineTemplate)) {
result.template = {
template: inlineTemplate.text,
source: null,
node: inlineTemplate
};
}
const inlineStyles = getDecoratorPropertyInitializer(dec, 'styles');
if (inlineStyles && inlineStyles.kind === kinds.ArrayLiteralExpression) {
inlineStyles.elements.forEach((inlineStyle: any) => {
if (isSimpleTemplateString(inlineStyle)) {
result.styles = result.styles || [];
result.styles.push({
style: inlineStyle.text,
source: null,
node: inlineStyle
});
}
});
}
if (!result.template && external.templateUrl) {
try {
result.template = {
template: this._fileResolver.resolve(external.templateUrl),
source: external.templateUrl,
node: null
};
} catch (e) {
console.log(e);
console.log('Cannot read the external template ' + external.templateUrl);
}
}
if (!result.styles || !result.styles.length) {
try {
result.styles = <any>external.styleUrls.map((url: string) => {
return {
style: this._fileResolver.resolve(url),
source: url,
node: null
};
});
} catch (e) {
console.log('Unable to read external style. ' + e.toString());
}
}
return result;
}

protected getDecoratorArgument(decorator: ts.Decorator): ts.ObjectLiteralExpression {
const expr = <ts.CallExpression>decorator.expression;
if (expr && expr.arguments && expr.arguments.length) {
const arg = <ts.ObjectLiteralExpression>expr.arguments[0];
if (arg.kind === kinds.ObjectLiteralExpression && arg.properties) {
return arg;
}
}
return null;
}
}

Loading

0 comments on commit 67d5a07

Please sign in to comment.