-
-
Notifications
You must be signed in to change notification settings - Fork 75
/
liquid.ts
145 lines (121 loc) Β· 3.78 KB
/
liquid.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
import { Liquid } from "../deps/liquid.ts";
import { join } from "../deps/path.ts";
import loader from "../core/loaders/text.ts";
import { merge } from "../core/utils.ts";
import type { Data, Engine, Helper, HelperOptions, Site } from "../core.ts";
import type { LiquidOptions } from "../deps/liquid.ts";
export interface Options {
/** The list of extensions this plugin applies to */
extensions: string[] | {
pages: string[];
components: string[];
};
/** Custom includes path */
includes: string;
/** Options passed to Liquidjs library */
options: Partial<LiquidOptions>;
}
// Default options
export const defaults: Options = {
extensions: [".liquid"],
includes: "",
options: {},
};
/** Template engine to render Liquid files */
export class LiquidEngine implements Engine {
// deno-lint-ignore no-explicit-any
liquid: any;
cache = new Map<string, unknown>();
basePath: string;
// deno-lint-ignore no-explicit-any
constructor(liquid: any, basePath: string) {
this.liquid = liquid;
this.basePath = basePath;
}
deleteCache(file: string): void {
this.cache.delete(file);
}
async render(content: string, data?: Data, filename?: string) {
if (!filename) {
return this.liquid.parseAndRender(content, data);
}
const template = this.getTemplate(content, filename);
return await this.liquid.render(template, data);
}
renderSync(content: string, data?: Data, filename?: string) {
if (!filename) {
return this.liquid.parseAndRenderSync(content, data);
}
const template = this.getTemplate(content, filename);
return this.liquid.renderSync(template, data);
}
getTemplate(content: string, filename: string) {
if (!this.cache.has(filename)) {
this.cache.set(
filename,
this.liquid.parse(content, join(this.basePath, filename)),
);
}
return this.cache.get(filename)!;
}
addHelper(name: string, fn: Helper, options: HelperOptions) {
switch (options.type) {
case "filter":
this.liquid.registerFilter(name, fn);
break;
case "tag":
// Tag with body not supported yet
if (!options.body) {
this.liquid.registerTag(name, createCustomTag(fn));
}
break;
}
}
}
/** Register the plugin to add support for Liquid files */
export default function (userOptions?: Partial<Options>) {
return function (site: Site) {
const options = merge(
{ ...defaults, includes: site.options.includes },
userOptions,
);
const extensions = Array.isArray(options.extensions)
? {
pages: options.extensions,
includes: options.extensions,
components: options.extensions,
}
: options.extensions;
const liquidOptions: LiquidOptions = {
root: site.src(options.includes),
...options.options,
};
const engine = new LiquidEngine(new Liquid(liquidOptions), site.src());
site.loadPages(extensions.pages, loader, engine);
site.includes(extensions.pages, options.includes);
site.includes(extensions.components, options.includes);
site.loadComponents(extensions.components, loader, engine);
// Register the liquid filter
site.filter("liquid", filter as Helper, true);
function filter(string: string, data?: Data) {
return engine.render(string, { ...site.globalData, ...data });
}
};
}
/**
* Create a custom tag
* https://liquidjs.com/tutorials/register-filters-tags.html#Register-Tags
*/
function createCustomTag(fn: Helper) {
return {
parse(tagToken: unknown) {
// @ts-ignore: No types for Liquid
this.str = tagToken.args;
},
async render(ctx: unknown): Promise<unknown> {
// @ts-ignore: No types for Liquid
const str = await this.liquid.evalValue(this.str, ctx);
return fn(str);
},
};
}