-
Notifications
You must be signed in to change notification settings - Fork 235
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #163 from mgechev/external-templates
feat: external templates and styles
- Loading branch information
Showing
27 changed files
with
959 additions
and
76 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) { | ||
|
||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 ''; | ||
} | ||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
export abstract class FileResolver { | ||
abstract resolve(path: string): string; | ||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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(); | ||
} | ||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} | ||
} | ||
|
Oops, something went wrong.