Skip to content

Commit

Permalink
feat: initial rewrite for version 3
Browse files Browse the repository at this point in the history
  • Loading branch information
nebrelbug committed May 27, 2023
1 parent e05fa5f commit f722c32
Show file tree
Hide file tree
Showing 39 changed files with 742 additions and 2,205 deletions.
14 changes: 3 additions & 11 deletions deno_dist/browser.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,3 @@
export { default as compileToString } from "./compile-string.ts";
export { default as compile } from "./compile.ts";
export { default as parse } from "./parse.ts";
export { default as render, renderAsync } from "./render.ts";
export { templates } from "./containers.ts";
export {
config,
config as defaultConfig,
configure,
getConfig,
} from "./config.ts";
import { Eta as EtaCore } from "./core.ts";

export class Eta extends EtaCore {}
96 changes: 51 additions & 45 deletions deno_dist/compile-string.ts
Original file line number Diff line number Diff line change
@@ -1,46 +1,51 @@
import Parse from "./parse.ts";

/* TYPES */

import type { EtaConfig } from "./config.ts";
import type { Options } from "./config.ts";
import type { AstObject } from "./parse.ts";
import type { Eta } from "./core.ts";

/* END TYPES */

/**
* Compiles a template string to a function string. Most often users just use `compile()`, which calls `compileToString` and creates a new function using the result
*
* **Example**
*
* ```js
* compileToString("Hi <%= it.user %>", eta.config)
* // "var tR='',include=E.include.bind(E),includeFile=E.includeFile.bind(E);tR+='Hi ';tR+=E.e(it.user);if(cb){cb(null,tR)} return tR"
* ```
*/

export default function compileToString(
export function compileToString(
this: Eta,
str: string,
config: EtaConfig,
options?: Partial<Options>,
): string {
const buffer: Array<AstObject> = Parse(str, config);

let res = "var tR='',__l,__lP" +
(config.include ? ",include=E.include.bind(E)" : "") +
(config.includeFile ? ",includeFile=E.includeFile.bind(E)" : "") +
"\nfunction layout(p,d){__l=p;__lP=d}\n" +
(config.useWith ? "with(" + config.varName + "||{}){" : "") +
compileScope(buffer, config) +
(config.includeFile
? "if(__l)tR=" +
(config.async ? "await " : "") +
`includeFile(__l,Object.assign(${config.varName},{body:tR},__lP))\n`
: config.include
? "if(__l)tR=" +
(config.async ? "await " : "") +
`include(__l,Object.assign(${config.varName},{body:tR},__lP))\n`
: "") +
"if(cb){cb(null,tR)} return tR" +
(config.useWith ? "}" : "");
const config = this.config;
const isAsync = options && options.async;

const buffer: Array<AstObject> = this.parse.call(this, str);

// note: when the include function passes through options, the only parameter that matters is the filepath parameter
let res = `
let include = (template, data) => this.render(template, data, options);
let includeAsync = (template, data) => this.renderAsync(template, data, options);
let __eta = {res: "", e: this.escapeFunction, f: this.filterFunction};
function layout(path, data) {
__eta.layout = path;
__eta.layoutData = data;
}
${config.useWith ? "with(" + config.varName + "||{}){" : ""}
${compileBody.call(this, buffer)}
if (__eta.layout) {
__eta.res = ${
isAsync ? "await includeAsync" : "include"
} (__eta.layout, {body: __eta.res, ...__eta.layoutData});
}
${config.useWith ? "}" : ""}
return __eta.res;
`;

if (config.plugins) {
for (let i = 0; i < config.plugins.length; i++) {
Expand All @@ -60,14 +65,15 @@ export default function compileToString(
* **Example**
*
* ```js
* // AST version of 'Hi <%= it.user %>'
* let templateAST = ['Hi ', { val: 'it.user', t: 'i' }]
* compileScope(templateAST, eta.config)
* // "tR+='Hi ';tR+=E.e(it.user);"
* let templateAST = ['Hi ', { val: 'it.name', t: 'i' }]
* compileBody.call(Eta, templateAST)
* // => "__eta.res+='Hi '\n__eta.res+=__eta.e(it.name)\n"
* ```
*/

function compileScope(buff: Array<AstObject>, config: EtaConfig) {
function compileBody(this: Eta, buff: Array<AstObject>) {
const config = this.config;

let i = 0;
const buffLength = buff.length;
let returnStr = "";
Expand All @@ -78,34 +84,34 @@ function compileScope(buff: Array<AstObject>, config: EtaConfig) {
const str = currentBlock;

// we know string exists
returnStr += "tR+='" + str + "'\n";
returnStr += "__eta.res+='" + str + "'\n";
} else {
const type = currentBlock.t; // ~, s, !, ?, r
const type = currentBlock.t; // "r", "e", or "i"
let content = currentBlock.val || "";

if (type === "r") {
// raw

if (config.filter) {
content = "E.filter(" + content + ")";
content = "__eta.f(" + content + ")";
}

returnStr += "tR+=" + content + "\n";
returnStr += "__eta.res+=" + content + "\n";
} else if (type === "i") {
// interpolate

if (config.filter) {
content = "E.filter(" + content + ")";
content = "__eta.f(" + content + ")";
}

if (config.autoEscape) {
content = "E.e(" + content + ")";
content = "__eta.e(" + content + ")";
}
returnStr += "tR+=" + content + "\n";
// reference

returnStr += "__eta.res+=" + content + "\n";
} else if (type === "e") {
// execute
returnStr += content + "\n"; // you need a \n in case you have <% } %>
returnStr += content + "\n";
}
}
}
Expand Down
56 changes: 23 additions & 33 deletions deno_dist/compile.ts
Original file line number Diff line number Diff line change
@@ -1,65 +1,55 @@
import compileToString from "./compile-string.ts";
import { getConfig } from "./config.ts";
import EtaErr from "./err.ts";
import { EtaError } from "./err.ts";

/* TYPES */
import type { Eta } from "./core.ts";
import type { EtaConfig, Options } from "./config.ts";

import type { EtaConfig, PartialConfig } from "./config.ts";
import type { CallbackFn } from "./file-handlers.ts";
import { getAsyncFunctionConstructor } from "./polyfills.ts";
export type TemplateFunction = (
data: object,
config: EtaConfig,
cb?: CallbackFn,
this: Eta,
data?: object,
options?: Partial<Options>,
) => string;

/* END TYPES */

const AsyncFunction = async function () {}.constructor; // eslint-disable-line @typescript-eslint/no-empty-function

/**
* Takes a template string and returns a template function that can be called with (data, config, [cb])
* Takes a template string and returns a template function that can be called with (data, config)
*
* @param str - The template string
* @param config - A custom configuration object (optional)
*
* **Example**
*
* ```js
* let compiledFn = eta.compile("Hi <%= it.user %>")
* // function anonymous()
* let compiledFnStr = compiledFn.toString()
* // "function anonymous(it,E,cb\n) {\nvar tR='',include=E.include.bind(E),includeFile=E.includeFile.bind(E);tR+='Hi ';tR+=E.e(it.user);if(cb){cb(null,tR)} return tR\n}"
* ```
*/

export default function compile(
export function compile(
this: Eta,
str: string,
config?: PartialConfig,
options?: Partial<Options>,
): TemplateFunction {
const options: EtaConfig = getConfig(config || {});
const Eta = this;

const config: EtaConfig = this.config;

/* ASYNC HANDLING */
// The below code is modified from mde/ejs. All credit should go to them.
const ctor = options.async
? (getAsyncFunctionConstructor() as FunctionConstructor)
/* ASYNC HANDLING (modified from mde/ejs) */
const ctor = options && options.async
? (AsyncFunction as FunctionConstructor)
: Function;
/* END ASYNC HANDLING */

try {
return new ctor(
options.varName,
"E", // EtaConfig
"cb", // optional callback
compileToString(str, options),
config.varName,
"options",
this.compileToString.call(Eta, str, options),
) as TemplateFunction; // eslint-disable-line no-new-func
} catch (e) {
if (e instanceof SyntaxError) {
throw EtaErr(
throw new EtaError(
"Bad template syntax\n\n" +
e.message +
"\n" +
Array(e.message.length + 1).join("=") +
"\n" +
compileToString(str, options) +
this.compileToString.call(Eta, str, options) +
"\n", // This will put an extra newline before the callstack for extra readability
);
} else {
Expand Down
Loading

0 comments on commit f722c32

Please sign in to comment.