##// END OF EJS Templates
text/template-compile ported to typescript
cin -
r56:224ffacdbef2 di-typescript
parent child
Show More
@@ -0,0 +1,49
1 import request = require("dojo/request");
2 import m = require("module");
3 import { TraceSource } from "../log/TraceSource";
4 import { TemplateCompiler } from "./TemplateCompiler";
5 import { TemplateParser } from "./TemplateParser";
6 import { isNullOrEmptyString } from "../safe";
7 import { MapOf } from "../interfaces";
8
9 type TemplateFn = (obj: object) => string;
10
11 const trace = TraceSource.get(m.id);
12
13 function compile(str: string) {
14 if (isNullOrEmptyString(str))
15 return () => "";
16
17 const parser = new TemplateParser(str);
18 const compiler = new TemplateCompiler();
19
20 return compiler.compile(parser);
21 }
22
23 const cache: MapOf<TemplateFn> = {};
24
25 interface OnLoadFn<T> {
26 (res: T): void;
27 error(e: any): void;
28 }
29
30 compile.load = (id: string, require: Require, callback: OnLoadFn<TemplateFn>) => {
31 const url = require.toUrl(id);
32 if (url in cache) {
33 trace.debug("{0} -> {1}: cached", id, url);
34 callback(cache[url]);
35 } else {
36 trace.debug("{0} -> {1}: load", id, url);
37 request(url).then(compile).then((tc: TemplateFn) => {
38 trace.debug("{0}: compiled", url);
39 callback(cache[url] = tc);
40 }, (err: any) => {
41 callback.error({
42 inner: err,
43 src: "@implab/core/text/template-compile"
44 });
45 });
46 }
47 };
48
49 export = compile;
@@ -0,0 +1,102
1 import { format } from "./StringFormat";
2 import { TraceSource, DebugLevel } from "../log/TraceSource";
3 import { ITemplateParser, TokenType } from "./TemplateParser";
4
5 const trace = TraceSource.get("@implab/text/TemplateCompiler");
6
7 type TemplateFn = (obj: object) => string;
8
9 export class TemplateCompiler {
10
11 _data: string[];
12 _code: string[];
13 _wrapWith = true;
14
15 constructor() {
16 this._code = [];
17 this._data = [];
18 }
19
20 compile(parser: ITemplateParser): TemplateFn {
21 this.preamble();
22 this.visitTemplate(parser);
23 this.postamble();
24
25 const text = this._code.join("\n");
26
27 try {
28 const compiled = new Function("obj, format, $data", text);
29 /**
30 * Функция форматирования по шаблону
31 *
32 * @type{Function}
33 * @param{Object} obj объект с параметрами для подстановки
34 */
35 return (obj: object) => compiled(obj || {}, format, this._data);
36 } catch (e) {
37 trace.traceEvent(DebugLevel, [e, text, this._data]);
38 throw e;
39 }
40 }
41
42 preamble() {
43 this._code.push(
44 "var $p = [];",
45 "var print = function(){",
46 " $p.push(format.apply(null,arguments));",
47 "};"
48 );
49
50 if (this._wrapWith)
51 this._code.push("with(obj){");
52 }
53
54 postamble() {
55 if (this._wrapWith)
56 this._code.push("}");
57
58 this._code.push("return $p.join('');");
59 }
60
61 visitTemplate(parser: ITemplateParser) {
62 while (parser.next()) {
63 switch (parser.token()) {
64 case TokenType.OpenBlock:
65 this.visitCode(parser);
66 break;
67 case TokenType.OpenInlineBlock:
68 this.visitInline(parser);
69 break;
70 default:
71 this.visitTextFragment(parser);
72 break;
73 }
74 }
75 }
76
77 visitInline(parser: ITemplateParser) {
78 const code = ["$p.push("];
79 while (parser.next()) {
80 if (parser.token() === TokenType.CloseBlock)
81 break;
82 code.push(parser.value());
83 }
84 code.push(");");
85 this._code.push(code.join(""));
86 }
87
88 visitCode(parser: ITemplateParser) {
89 const code = [];
90 while (parser.next()) {
91 if (parser.token() === TokenType.CloseBlock)
92 break;
93 code.push(parser.value());
94 }
95 this._code.push(code.join(""));
96 }
97
98 visitTextFragment(parser: ITemplateParser) {
99 const i = this._data.push(parser.value());
100 this._code.push("$p.push($data[" + i + "]);");
101 }
102 }
@@ -0,0 +1,64
1 import { argumentNotEmptyString } from "../safe";
2 import { MapOf } from "../interfaces";
3
4 const splitRx = /(<%=|\[%=|<%|\[%|%\]|%>)/;
5
6 export enum TokenType {
7 None,
8 Text,
9 OpenInlineBlock,
10 OpenBlock,
11 CloseBlock
12 }
13
14 const tokenMap: MapOf<TokenType> = {
15 "<%": TokenType.OpenBlock,
16 "[%": TokenType.OpenBlock,
17 "<%=": TokenType.OpenInlineBlock,
18 "[%=": TokenType.OpenInlineBlock,
19 "%>": TokenType.CloseBlock,
20 "%]": TokenType.CloseBlock
21 };
22
23 export interface ITemplateParser {
24 next(): boolean;
25 token(): TokenType;
26 value(): string;
27 }
28
29 export class TemplateParser implements ITemplateParser {
30
31 _tokens: string[];
32 _pos = -1;
33 _type: TokenType;
34 _value: string;
35
36 constructor(text: string) {
37 argumentNotEmptyString(text, "text");
38
39 this._tokens = text.split(splitRx);
40 this._type = TokenType.None;
41 }
42
43 next() {
44 this._pos++;
45 if (this._pos < this._tokens.length) {
46 this._value = this._tokens[this._pos];
47 this._type = tokenMap[this._value] || TokenType.Text;
48 return true;
49 } else {
50 this._type = TokenType.None;
51 this._value = undefined;
52 return false;
53 }
54 }
55
56 token() {
57 return this._type;
58 }
59
60 value() {
61 return this._value;
62 }
63
64 }
1 NO CONTENT: file was removed
General Comments 0
You need to be logged in to leave comments. Login now