vtpl
Version:
a tiny template engine
245 lines (240 loc) • 6.62 kB
JavaScript
// src/filter.js
var _filters = {};
var registerFilter = (name, fn) => {
if (typeof fn === "function") {
_filters[name] = fn;
}
};
var applyFilter = (name, str, ...args) => {
const f = _filters[name];
if (!f) {
return str;
}
return f(str, ...args);
};
var filter_default = applyFilter;
// src/escape.js
var escapeMap = {
"<": "<",
">": ">",
'"': """,
"'": "'",
"&": "&"
};
var escapeHtml = (content) => {
content += "";
return content.replace(/&(?![\w#]+;)|[<>"']/g, (v) => escapeMap[v]);
};
var escape_default = escapeHtml;
// src/each.js
var hasOwn = Object.prototype.hasOwnProperty;
var each_default = (items, fn) => {
if (Array.isArray(items)) {
for (let i = 0, len = items.length; i < len; i++) {
fn(items[i], i, items);
}
} else if (typeof items === "object") {
for (const i in items) {
if (hasOwn.call(items, i)) {
fn(items[i], i, items);
}
}
}
};
// src/config.js
var defaultConfig = {
openTag: "{{",
closeTag: "}}",
escape: true
};
var keys = Object.keys(defaultConfig);
var config = Object.assign({}, defaultConfig);
var config_default = (k, v) => {
if (typeof v === "undefined") {
return config[k];
} else {
if (!~keys.indexOf(k)) {
return;
}
config = Object.assign(config, {
[k]: v
});
return config;
}
};
// src/compile.js
var util = { escape: escape_default, filter: filter_default, each: each_default };
var compile_default = (template, userConfig) => {
const OPEN_TAG = (userConfig == null ? void 0 : userConfig.openTag) ?? config_default("openTag");
const CLOSE_TAG = (userConfig == null ? void 0 : userConfig.closeTag) ?? config_default("closeTag");
const ESCAPE = (userConfig == null ? void 0 : userConfig.escape) ?? config_default("escape");
const pieces = template.split(new RegExp(`(${OPEN_TAG}|${CLOSE_TAG})`, "g"));
const code = [];
const all = [];
let counter = 1;
let logic = false;
pieces.forEach((v, i) => {
let tmp;
switch (v) {
case OPEN_TAG:
logic = true;
break;
case CLOSE_TAG:
logic = false;
break;
default:
tmp = {
content: v.trim(),
position: i
};
all.push({
content: v,
position: i,
type: logic ? "code" : "html",
refer: tmp
});
if (logic) {
code.push(tmp);
}
}
});
code.forEach((v, i) => {
const parts = v.content.split(" ");
const first = parts[0];
if (first === "if" || first === "each") {
for (let j = code.length - 1; j > i; j--) {
if (!code[j].binded && new RegExp("^/" + first).test(code[j].content)) {
code[j].binded = true;
v.bind = code[j].position;
break;
}
}
}
});
code.forEach((v, i) => {
const parts = v.content.split(" ");
const first = parts[0];
if (first === "else") {
for (let j = i - 1; j >= 0; j--) {
if (code[j].bind && code[j].bind > v.position && /^if\s+/.test(code[j].content)) {
v.belong = code[j].position;
break;
}
}
}
});
const makeExprWithData = function(expr, suffix) {
return `
${suffix ? "var $e" + suffix + ";" : ""}
with( $data ) {
$e${suffix || ""} = ${expr};
}
`;
};
const parseEach = (expr) => {
const parts = expr.split(" as ");
if (parts.length === 2) {
let tmp = parts[1].split(",");
let i = tmp[1] || "$i";
let v = tmp[0] || "$v";
return {
items: parts[0],
v,
i
};
}
};
const joinEachExpr = (items, i) => {
return `var ${i} = 0, len = ${items}.length; ${i} < len; ${i}++`;
};
let output = [];
output.push(`
var $o = "";
var $util = this;
var $escape = $util.escape;
var $filter = $util.filter;
var $each = $util.each;
$data = $data || {};
`);
all.forEach((v) => {
if (v.type === "html") {
output.push("$o += " + JSON.stringify(v.content) + ";");
} else if (v.type === "code") {
const content = v.content.trim();
const parts = content.split(" ");
const first = parts[0];
const rest = parts.slice(1);
if (first === "if") {
v.declare = [];
v.declare.push(makeExprWithData(rest.join(" "), counter));
output.push(v.declare);
output.push(`if( $e${counter} ) {`);
counter++;
} else if (first === "else") {
let matched;
if (rest[0] === "if") {
all.forEach((v2) => {
if (v2.position === v.refer.belong) {
matched = v2;
return false;
}
});
matched.declare.push(makeExprWithData(rest.slice(1).join(" "), counter));
output.push(`} else if( $e${counter} ) {`);
counter++;
} else {
output.push("} else {");
}
} else if (first === "each") {
let parsed = parseEach(rest.join(" "));
output.push(makeExprWithData(parsed.items, counter));
output.push(`$each($e${counter}, function(${parsed.v}, ${parsed.i}) {
`);
counter++;
} else if (first === "/if") {
output.push("}");
} else if (first === "/each") {
output.push(`});`);
} else {
let parts2 = content.split("|");
let expr = parts2[0];
let filters = parts2.slice(1);
if (expr[0] !== "=") {
output.push(makeExprWithData(expr, counter));
expr = `$e${counter}`;
for (let i = 0, len = filters.length; i < len; i++) {
let nameAndArgs = filters[i].split(":");
let name = nameAndArgs[0].trim();
let args = nameAndArgs.slice(1).join("");
expr = `$filter( ${JSON.stringify(name)}, ${expr} ${args ? "," + args : ""} )`;
}
if (ESCAPE) {
output.push(`$o += $escape(${expr});`);
} else {
output.push(`$o += ${expr};`);
}
counter++;
} else {
output.push(makeExprWithData(expr.slice(1), counter));
output.push(`$o += $e${counter};`);
counter++;
}
}
}
});
output.push("return $o;");
output = output.map(function(v) {
if (!Array.isArray(v)) {
return v;
} else {
return v.join("");
}
});
return new Function("$data", output.join("\n")).bind(util);
};
// src/index.js
var src_default = { config: config_default, compile: compile_default, registerFilter };
export {
src_default as default
};
//# sourceMappingURL=index.js.map