@tsed/engines 8.0.1 → 8.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +4 -3
- package/src/components/AtplEngine.ts +5 -0
- package/src/components/BracketEngine.ts +11 -0
- package/src/components/DotEngine.ts +14 -0
- package/src/components/DustEngine.ts +55 -0
- package/src/components/EctEngine.ts +24 -0
- package/src/components/EjsEngine.ts +5 -0
- package/src/components/Engine.ts +87 -0
- package/src/components/HamlCoffeeEngine.ts +5 -0
- package/src/components/HamlEngine.ts +12 -0
- package/src/components/HamletEngine.ts +12 -0
- package/src/components/HandlebarsEngine.ts +19 -0
- package/src/components/HoganEngine.spec.ts +79 -0
- package/src/components/HoganEngine.ts +14 -0
- package/src/components/HtmlingEngine.ts +11 -0
- package/src/components/JazzEngine.ts +10 -0
- package/src/components/JqtplEngine.ts +10 -0
- package/src/components/JustEngine.ts +24 -0
- package/src/components/LiquorEngine.ts +5 -0
- package/src/components/LodashEngine.ts +14 -0
- package/src/components/MoteEngine.ts +5 -0
- package/src/components/MustacheEngine.ts +9 -0
- package/src/components/NunjucksEngine.ts +58 -0
- package/src/components/PlatesEngine.ts +12 -0
- package/src/components/PugEngine.spec.ts +50 -0
- package/src/components/PugEngine.ts +15 -0
- package/src/components/RactiveEngine.spec.ts +36 -0
- package/src/components/RactiveEngine.ts +34 -0
- package/src/components/ReactEngine.ts +163 -0
- package/src/components/SlmEngine.ts +5 -0
- package/src/components/SquirrellyEngine.spec.ts +98 -0
- package/src/components/SquirrellyEngine.ts +18 -0
- package/src/components/SwigEngine.ts +14 -0
- package/src/components/TemplayedEngine.ts +9 -0
- package/src/components/TwigEngine.spec.ts +44 -0
- package/src/components/TwigEngine.ts +24 -0
- package/src/components/TwingEngine.spec.ts +36 -0
- package/src/components/TwingEngine.ts +31 -0
- package/src/components/UnderscoreEngine.ts +26 -0
- package/src/components/VashEngine.ts +33 -0
- package/src/components/VelocityEngine.spec.ts +50 -0
- package/src/components/VelocityEngine.ts +11 -0
- package/src/components/WalrusEngine.ts +11 -0
- package/src/decorators/viewEngine.ts +8 -0
- package/src/index.ts +42 -0
- package/src/registries/EnginesContainer.ts +37 -0
- package/src/utils/cache.spec.ts +13 -0
- package/src/utils/cache.ts +154 -0
- package/src/utils/getEngines.spec.ts +133 -0
- package/src/utils/getEngines.ts +58 -0
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import {Engine, ViewEngineOptions} from "../components/Engine.js";
|
|
2
|
+
|
|
3
|
+
export interface EngineProvider {
|
|
4
|
+
name: string;
|
|
5
|
+
useClass: typeof Engine;
|
|
6
|
+
instance?: Engine;
|
|
7
|
+
options: ViewEngineOptions;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export class EnginesContainer {
|
|
11
|
+
#providers: Map<string | typeof Engine, EngineProvider> = new Map();
|
|
12
|
+
|
|
13
|
+
has(key: string | typeof Engine) {
|
|
14
|
+
return this.#providers.has(key);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
get(key: string | typeof Engine) {
|
|
18
|
+
const provider = this.#providers.get(key);
|
|
19
|
+
|
|
20
|
+
if (provider && !provider.instance) {
|
|
21
|
+
provider.instance = new provider.useClass(provider.name, provider.options);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
return provider?.instance;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
set(key: typeof Engine, provider: EngineProvider) {
|
|
28
|
+
this.#providers.set(key, provider);
|
|
29
|
+
this.#providers.set(provider.name, provider);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
getSupportedEngines(): string[] {
|
|
33
|
+
return [...this.#providers.keys()].filter((key) => typeof key === "string") as string[];
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export const engines = new EnginesContainer();
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import {clearCache, getFromCache, setToCache} from "./cache.js";
|
|
2
|
+
|
|
3
|
+
describe("cache", () => {
|
|
4
|
+
describe("setToCache()", () => {
|
|
5
|
+
it("should set result to cache", () => {
|
|
6
|
+
setToCache("key", "value");
|
|
7
|
+
expect(getFromCache("key")).toEqual("value");
|
|
8
|
+
|
|
9
|
+
clearCache();
|
|
10
|
+
expect(getFromCache("key")).toEqual(undefined);
|
|
11
|
+
});
|
|
12
|
+
});
|
|
13
|
+
});
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import {readFile} from "node:fs/promises";
|
|
2
|
+
import {dirname, extname, isAbsolute, join} from "node:path";
|
|
3
|
+
|
|
4
|
+
import fs from "fs";
|
|
5
|
+
|
|
6
|
+
const readCache: Map<string, string> = new Map();
|
|
7
|
+
const cacheStore: Map<string, any> = new Map();
|
|
8
|
+
|
|
9
|
+
export const requires: Map<string, any> = new Map();
|
|
10
|
+
|
|
11
|
+
export function getCachedEngine(name: string, toName: string = name) {
|
|
12
|
+
const mdl = requires.get(toName) || {};
|
|
13
|
+
|
|
14
|
+
return mdl.default || mdl;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export async function importEngine(name: string, toName: string = name) {
|
|
18
|
+
if (!requires.has(toName)) {
|
|
19
|
+
await setEngine(name, toName);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
return getCachedEngine(toName);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export async function setEngine(name: string, toName: string = name) {
|
|
26
|
+
const mdl = await import(name);
|
|
27
|
+
|
|
28
|
+
requires.set(toName, mdl);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function setToCache(key: string, value: any) {
|
|
32
|
+
readCache.set(key, value);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function getFromCache(key: string) {
|
|
36
|
+
return readCache.get(key);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Clear the cache.
|
|
41
|
+
*
|
|
42
|
+
* @api public
|
|
43
|
+
*/
|
|
44
|
+
|
|
45
|
+
export const clearCache = () => {
|
|
46
|
+
readCache.clear();
|
|
47
|
+
cacheStore.clear();
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Conditionally cache `compiled` template based
|
|
52
|
+
* on the `options` filename and `.cache` boolean.
|
|
53
|
+
*
|
|
54
|
+
* @param {Object} options
|
|
55
|
+
* @param {Function} compiled
|
|
56
|
+
* @return {Function}
|
|
57
|
+
*/
|
|
58
|
+
export function cache(options: any, compiled?: any) {
|
|
59
|
+
// cachable
|
|
60
|
+
if (compiled && options.filename && options.cache) {
|
|
61
|
+
readCache.delete(options.filename);
|
|
62
|
+
cacheStore.set(options.filename, compiled);
|
|
63
|
+
return compiled;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// check cache
|
|
67
|
+
if (options.filename && options.cache) {
|
|
68
|
+
return cacheStore.get(options.filename);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return compiled;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Read `path` with `options` with
|
|
76
|
+
* callback `(err, str)`. When `options.cache`
|
|
77
|
+
* is true the template string will be cached.
|
|
78
|
+
*
|
|
79
|
+
* @param path
|
|
80
|
+
* @param {String} options
|
|
81
|
+
*/
|
|
82
|
+
export async function read(path: string, options: any): Promise<string> {
|
|
83
|
+
let str = readCache.get(path);
|
|
84
|
+
|
|
85
|
+
// cached (only if cached is a string and not a compiled template function)
|
|
86
|
+
if (options.cache && str && typeof str === "string") {
|
|
87
|
+
return str;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// read
|
|
91
|
+
str = await readFile(path, "utf8");
|
|
92
|
+
|
|
93
|
+
// remove extraneous utf8 BOM marker
|
|
94
|
+
str = str.replace(/^\uFEFF/, "");
|
|
95
|
+
|
|
96
|
+
if (options.cache) {
|
|
97
|
+
readCache.set(path, str);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return str;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Read `path` with `options` with
|
|
105
|
+
* callback `(err, str)`. When `options.cache`
|
|
106
|
+
* is true the partial string will be cached.
|
|
107
|
+
*
|
|
108
|
+
* @param path
|
|
109
|
+
* @param {String} options
|
|
110
|
+
* @api private
|
|
111
|
+
*/
|
|
112
|
+
export function readPartials(path: string, options: any): Promise<Record<string, string> | void> {
|
|
113
|
+
if (!options.partials) {
|
|
114
|
+
return Promise.resolve();
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const keys = Object.keys(options.partials);
|
|
118
|
+
const partials: Record<string, string> = {};
|
|
119
|
+
|
|
120
|
+
return new Promise((resolve, reject) => {
|
|
121
|
+
async function next(index: number): Promise<any> {
|
|
122
|
+
if (index === keys.length) {
|
|
123
|
+
return resolve(partials);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const key = keys[index];
|
|
127
|
+
const partialPath = options.partials[key];
|
|
128
|
+
|
|
129
|
+
if (partialPath === undefined || partialPath === null || partialPath === false) {
|
|
130
|
+
return next(++index);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
let file;
|
|
134
|
+
if (isAbsolute(partialPath)) {
|
|
135
|
+
if (extname(partialPath) !== "") {
|
|
136
|
+
file = partialPath;
|
|
137
|
+
} else {
|
|
138
|
+
file = join(partialPath + extname(path));
|
|
139
|
+
}
|
|
140
|
+
} else {
|
|
141
|
+
file = join(dirname(path), partialPath + extname(path));
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
try {
|
|
145
|
+
partials[key] = await read(file, options);
|
|
146
|
+
next(++index);
|
|
147
|
+
} catch (err) {
|
|
148
|
+
reject(err);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
next(0);
|
|
153
|
+
});
|
|
154
|
+
}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import "../index.js";
|
|
2
|
+
|
|
3
|
+
import {join} from "path";
|
|
4
|
+
|
|
5
|
+
import {getEngine, getEngines} from "./getEngines.js";
|
|
6
|
+
|
|
7
|
+
const rootDir = import.meta.dirname; // automatically replaced by import.meta.dirname on build
|
|
8
|
+
|
|
9
|
+
describe("getEngines", () => {
|
|
10
|
+
describe("getEngine()", () => {
|
|
11
|
+
describe("render file", () => {
|
|
12
|
+
it("should return a render function", async () => {
|
|
13
|
+
const render = getEngine("ejs");
|
|
14
|
+
|
|
15
|
+
const result = await new Promise((resolve, reject) => {
|
|
16
|
+
render(
|
|
17
|
+
join(rootDir, "../../test/fixtures/ejs/user.ejs"),
|
|
18
|
+
{
|
|
19
|
+
user: {
|
|
20
|
+
name: "Tobi"
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
(err, html) => {
|
|
24
|
+
err ? reject(err) : resolve(html);
|
|
25
|
+
}
|
|
26
|
+
);
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
expect(result).toEqual("<p>Tobi</p>");
|
|
30
|
+
});
|
|
31
|
+
it("should catch error", async () => {
|
|
32
|
+
const render = getEngine("ejs");
|
|
33
|
+
|
|
34
|
+
const error: any = await new Promise((resolve) => {
|
|
35
|
+
render(
|
|
36
|
+
join(rootDir, "../../test/fixtures/ejs/user2.ejs"),
|
|
37
|
+
{
|
|
38
|
+
user: {
|
|
39
|
+
name: "Tobi"
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
(err) => {
|
|
43
|
+
resolve(err);
|
|
44
|
+
}
|
|
45
|
+
);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
expect(error.message).toContain("ENOENT: no such file or directory");
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
describe("render template", () => {
|
|
52
|
+
it("should return a render function", async () => {
|
|
53
|
+
const {render} = getEngine("ejs");
|
|
54
|
+
|
|
55
|
+
const result = await new Promise((resolve, reject) => {
|
|
56
|
+
render(
|
|
57
|
+
"<p><%= user.name %></p>",
|
|
58
|
+
{
|
|
59
|
+
user: {
|
|
60
|
+
name: "Tobi"
|
|
61
|
+
}
|
|
62
|
+
},
|
|
63
|
+
(err, html) => {
|
|
64
|
+
err ? reject(err) : resolve(html);
|
|
65
|
+
}
|
|
66
|
+
);
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
expect(result).toEqual("<p>Tobi</p>");
|
|
70
|
+
});
|
|
71
|
+
it("should catch error", async () => {
|
|
72
|
+
const {render} = getEngine("ejs");
|
|
73
|
+
|
|
74
|
+
const error: any = await new Promise((resolve) => {
|
|
75
|
+
render(
|
|
76
|
+
"<p><%= user.name></p>",
|
|
77
|
+
{
|
|
78
|
+
user: {
|
|
79
|
+
name: "Tobi"
|
|
80
|
+
}
|
|
81
|
+
},
|
|
82
|
+
(err) => {
|
|
83
|
+
resolve(err);
|
|
84
|
+
}
|
|
85
|
+
);
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
expect(error.message).toContain('Could not find matching close tag for "<%=".');
|
|
89
|
+
});
|
|
90
|
+
});
|
|
91
|
+
});
|
|
92
|
+
describe("getEngines()", () => {
|
|
93
|
+
it("should return engines", () => {
|
|
94
|
+
const engines = getEngines();
|
|
95
|
+
expect(Object.keys(engines)).toEqual([
|
|
96
|
+
"atpl",
|
|
97
|
+
"bracket",
|
|
98
|
+
"dot",
|
|
99
|
+
"dust",
|
|
100
|
+
"ect",
|
|
101
|
+
"ejs",
|
|
102
|
+
"haml-coffee",
|
|
103
|
+
"haml",
|
|
104
|
+
"hamlet",
|
|
105
|
+
"handlebars",
|
|
106
|
+
"hogan",
|
|
107
|
+
"htmling",
|
|
108
|
+
"jazz",
|
|
109
|
+
"jqtpl",
|
|
110
|
+
"just",
|
|
111
|
+
"liquor",
|
|
112
|
+
"lodash",
|
|
113
|
+
"mote",
|
|
114
|
+
"mustache",
|
|
115
|
+
"nunjucks",
|
|
116
|
+
"plates",
|
|
117
|
+
"pug",
|
|
118
|
+
"ractive",
|
|
119
|
+
"react",
|
|
120
|
+
"slm",
|
|
121
|
+
"squirrelly",
|
|
122
|
+
"swig",
|
|
123
|
+
"templayed",
|
|
124
|
+
"twig",
|
|
125
|
+
"twing",
|
|
126
|
+
"underscore",
|
|
127
|
+
"vash",
|
|
128
|
+
"velocityjs",
|
|
129
|
+
"walrus"
|
|
130
|
+
]);
|
|
131
|
+
});
|
|
132
|
+
});
|
|
133
|
+
});
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import {EngineOptions} from "../components/Engine.js";
|
|
2
|
+
import {engines} from "../registries/EnginesContainer.js";
|
|
3
|
+
|
|
4
|
+
export type RenderCallback = (err: Error | null, str?: string | any) => any;
|
|
5
|
+
export interface EngineFunction {
|
|
6
|
+
(path: string, options: any, cb: RenderCallback): void;
|
|
7
|
+
(path: string, options: any): Promise<string>;
|
|
8
|
+
|
|
9
|
+
render(template: string, options: any, cb: RenderCallback): void;
|
|
10
|
+
render(template: string, options: any): Promise<string>;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const callbackify = async (fn: any, cb?: RenderCallback) => {
|
|
14
|
+
try {
|
|
15
|
+
const html = await fn();
|
|
16
|
+
cb && cb(null, html);
|
|
17
|
+
return html;
|
|
18
|
+
} catch (er) {
|
|
19
|
+
if (cb) {
|
|
20
|
+
cb(er);
|
|
21
|
+
} else {
|
|
22
|
+
throw er;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
export function getEngine(name: string): EngineFunction {
|
|
28
|
+
const cb = async (path: string, options: any, cb?: RenderCallback) => {
|
|
29
|
+
const engine = engines.get(name)!;
|
|
30
|
+
await engine.$onInit();
|
|
31
|
+
|
|
32
|
+
return callbackify(() => engine.renderFile(path, options), cb);
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
cb.render = async (template: string, options: EngineOptions, cb: RenderCallback) => {
|
|
36
|
+
const engine = engines.get(name)!;
|
|
37
|
+
await engine.$onInit();
|
|
38
|
+
|
|
39
|
+
return callbackify(() => engine.render(template, options), cb);
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
return cb as any;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
let localEngines: any;
|
|
46
|
+
|
|
47
|
+
export function getEngines(): Record<string, EngineFunction> {
|
|
48
|
+
localEngines =
|
|
49
|
+
localEngines ||
|
|
50
|
+
engines.getSupportedEngines().reduce((acc, key) => {
|
|
51
|
+
return {
|
|
52
|
+
...acc,
|
|
53
|
+
[key]: getEngine(key)
|
|
54
|
+
};
|
|
55
|
+
}, {});
|
|
56
|
+
|
|
57
|
+
return localEngines;
|
|
58
|
+
}
|