-
-
Notifications
You must be signed in to change notification settings - Fork 31
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(Template): get first version of template renderer ready
- Loading branch information
1 parent
cc3aa63
commit e4935eb
Showing
13 changed files
with
1,184 additions
and
286 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
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 |
---|---|---|
@@ -0,0 +1,50 @@ | ||
/* | ||
* edge | ||
* | ||
* (c) Harminder Virk <virk@adonisjs.com> | ||
* | ||
* For the full copyright and license information, please view the LICENSE | ||
* file that was distributed with this source code. | ||
*/ | ||
|
||
import { ILoader } from '../Contracts' | ||
import { Parser } from 'edge-parser' | ||
import { ITag } from 'edge-parser/build/src/Contracts' | ||
|
||
export class Compiler { | ||
private cache: Map<string, string> = new Map() | ||
|
||
constructor (private loader: ILoader, private tags: { [key: string]: ITag }, private shouldCache: boolean = true) { | ||
} | ||
|
||
/** | ||
* Compiles a given template by loading it using the loader | ||
*/ | ||
private _compile (templatePath: string, diskName: string): string { | ||
const template = this.loader.resolve(templatePath, diskName) | ||
const parser = new Parser(this.tags) | ||
return parser.parseTemplate(template) | ||
} | ||
|
||
/** | ||
* Compiles a given template by loading it using the loader, also caches | ||
* the template and returns from the cache (if exists). | ||
*/ | ||
public compile (templatePath: string, diskName: string = 'default'): string { | ||
templatePath = this.loader.makePath(templatePath, diskName) | ||
|
||
/** | ||
* Compile right away when cache is disabled | ||
*/ | ||
if (!this.shouldCache) { | ||
return this._compile(templatePath, diskName) | ||
} | ||
|
||
/* istanbul ignore else */ | ||
if (!this.cache.get(templatePath)) { | ||
this.cache.set(templatePath, this._compile(templatePath, diskName)) | ||
} | ||
|
||
return this.cache.get(templatePath)! | ||
} | ||
} |
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,140 @@ | ||
/* | ||
* edge.js | ||
* | ||
* (c) Harminder Virk <virk@adonisjs.com> | ||
* | ||
* For the full copyright and license information, please view the LICENSE | ||
* file that was distributed with this source code. | ||
*/ | ||
|
||
import * as Macroable from 'macroable' | ||
import { IPresenter } from '../Contracts' | ||
import * as he from 'he' | ||
|
||
export class Context extends Macroable { | ||
/* tslint:disable-next-line */ | ||
private static _macros: object = {} | ||
|
||
/* tslint:disable-next-line */ | ||
private static _getters: object = {} | ||
|
||
/** | ||
* Remove all macros and getters | ||
*/ | ||
public static hydrate () { | ||
super.hydrate() | ||
} | ||
|
||
/** | ||
* Define macro on the context | ||
*/ | ||
public static macro (name: string, callback: Function) { | ||
super.macro(name, callback) | ||
} | ||
|
||
/** | ||
* Define getter on the context | ||
*/ | ||
public static getter (name: string, callback: Function, singleton: boolean = false) { | ||
super.getter(name, callback, singleton) | ||
} | ||
|
||
/** | ||
* Frames are used to define a inner scope in which values will | ||
* be resolved. The resolve function starts with the deepest | ||
* frame and then resolve the value up until the first | ||
* frame. | ||
*/ | ||
private frames: object[] = [] | ||
|
||
constructor (public presenter: IPresenter, public sharedState: object) { | ||
super() | ||
} | ||
|
||
/** | ||
* Creates a new frame object. | ||
*/ | ||
public newFrame (): void { | ||
this.frames.unshift({}) | ||
} | ||
|
||
/** | ||
* Set key/value pair on the frame object | ||
*/ | ||
public setOnFrame (key: string, value: any): void { | ||
const recentFrame = this.frames[0] | ||
|
||
if (!recentFrame) { | ||
throw new Error('Make sure to call {newFrame} before calling {setOnFrame}') | ||
} | ||
|
||
recentFrame[key] = value | ||
} | ||
|
||
/** | ||
* Removes the most recent frame | ||
*/ | ||
public removeFrame (): void { | ||
this.frames.shift() | ||
} | ||
|
||
/** | ||
* Escapes the value to be HTML safe | ||
*/ | ||
public escape (input: string): string { | ||
return he.escape(input) | ||
} | ||
|
||
/** | ||
* Resolves value for a given key. It will look for the value in different | ||
* stores and continues till the end if `undefined` is returned. | ||
* | ||
* 1. Check for value inside frames | ||
* 2. Then on the presenter instance | ||
* 3. Then the presenter `state` object | ||
* 4. Finally fallback to the sharedState | ||
*/ | ||
public resolve (key: string): any { | ||
let value | ||
|
||
/** | ||
* Pull from one of the nested frames | ||
*/ | ||
value = this.getFromFrame(key) | ||
if (value !== undefined) { | ||
return typeof (value) === 'function' ? value.bind(this) : value | ||
} | ||
|
||
/** | ||
* Check for value as a property on the presenter | ||
* itself. | ||
*/ | ||
value = this.presenter[key] | ||
if (value !== undefined) { | ||
return typeof (value) === 'function' ? value.bind(this.presenter) : value | ||
} | ||
|
||
/** | ||
* Otherwise look into presenter state | ||
*/ | ||
value = this.presenter.state[key] | ||
if (value !== undefined) { | ||
return typeof (value) === 'function' ? value.bind(this.presenter.state) : value | ||
} | ||
|
||
/** | ||
* Finally fallback to defined globals | ||
*/ | ||
value = this.sharedState[key] | ||
return typeof (value) === 'function' ? value.bind(this.sharedState) : value | ||
} | ||
|
||
/** | ||
* Returns value for a key inside frames. Stops looking for it, | ||
* when value is found inside the first frame | ||
*/ | ||
private getFromFrame (key: string): any { | ||
const frameWithVal = this.frames.find((frame) => frame[key] !== undefined) | ||
return frameWithVal ? frameWithVal[key] : undefined | ||
} | ||
} |
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,11 @@ | ||
export interface ILoader { | ||
mounted: object | ||
mount (diskName: string, dirPath: string): void | ||
unmount (diskName: string): void | ||
resolve (templatePath: string, diskName: string): string | ||
makePath (templatePath: string, diskName: string): string | ||
} | ||
|
||
export interface IPresenter { | ||
state: any | ||
} |
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 |
---|---|---|
@@ -0,0 +1,15 @@ | ||
/* | ||
* edge | ||
* | ||
* (c) Harminder Virk <virk@adonisjs.com> | ||
* | ||
* For the full copyright and license information, please view the LICENSE | ||
* file that was distributed with this source code. | ||
*/ | ||
|
||
import { IPresenter } from '../Contracts' | ||
|
||
export class Presenter implements IPresenter { | ||
constructor (public state: any) { | ||
} | ||
} |
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,23 @@ | ||
/* | ||
* edge | ||
* | ||
* (c) Harminder Virk <virk@adonisjs.com> | ||
* | ||
* For the full copyright and license information, please view the LICENSE | ||
* file that was distributed with this source code. | ||
*/ | ||
|
||
import { Context } from '../Context' | ||
import { IPresenter } from '../Contracts' | ||
import { Compiler } from '../Compiler' | ||
|
||
export class Template { | ||
constructor (private compiler: Compiler, private sharedState: any) { | ||
} | ||
|
||
public render (template: string, presenter: IPresenter, diskName?: string): string { | ||
const compiledTemplate = this.compiler.compile(template, diskName) | ||
const ctx = new Context(presenter, this.sharedState) | ||
return new Function('template', 'ctx', `return ${compiledTemplate}`)(this, ctx) | ||
} | ||
} |
Oops, something went wrong.