@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.
Files changed (50) hide show
  1. package/package.json +4 -3
  2. package/src/components/AtplEngine.ts +5 -0
  3. package/src/components/BracketEngine.ts +11 -0
  4. package/src/components/DotEngine.ts +14 -0
  5. package/src/components/DustEngine.ts +55 -0
  6. package/src/components/EctEngine.ts +24 -0
  7. package/src/components/EjsEngine.ts +5 -0
  8. package/src/components/Engine.ts +87 -0
  9. package/src/components/HamlCoffeeEngine.ts +5 -0
  10. package/src/components/HamlEngine.ts +12 -0
  11. package/src/components/HamletEngine.ts +12 -0
  12. package/src/components/HandlebarsEngine.ts +19 -0
  13. package/src/components/HoganEngine.spec.ts +79 -0
  14. package/src/components/HoganEngine.ts +14 -0
  15. package/src/components/HtmlingEngine.ts +11 -0
  16. package/src/components/JazzEngine.ts +10 -0
  17. package/src/components/JqtplEngine.ts +10 -0
  18. package/src/components/JustEngine.ts +24 -0
  19. package/src/components/LiquorEngine.ts +5 -0
  20. package/src/components/LodashEngine.ts +14 -0
  21. package/src/components/MoteEngine.ts +5 -0
  22. package/src/components/MustacheEngine.ts +9 -0
  23. package/src/components/NunjucksEngine.ts +58 -0
  24. package/src/components/PlatesEngine.ts +12 -0
  25. package/src/components/PugEngine.spec.ts +50 -0
  26. package/src/components/PugEngine.ts +15 -0
  27. package/src/components/RactiveEngine.spec.ts +36 -0
  28. package/src/components/RactiveEngine.ts +34 -0
  29. package/src/components/ReactEngine.ts +163 -0
  30. package/src/components/SlmEngine.ts +5 -0
  31. package/src/components/SquirrellyEngine.spec.ts +98 -0
  32. package/src/components/SquirrellyEngine.ts +18 -0
  33. package/src/components/SwigEngine.ts +14 -0
  34. package/src/components/TemplayedEngine.ts +9 -0
  35. package/src/components/TwigEngine.spec.ts +44 -0
  36. package/src/components/TwigEngine.ts +24 -0
  37. package/src/components/TwingEngine.spec.ts +36 -0
  38. package/src/components/TwingEngine.ts +31 -0
  39. package/src/components/UnderscoreEngine.ts +26 -0
  40. package/src/components/VashEngine.ts +33 -0
  41. package/src/components/VelocityEngine.spec.ts +50 -0
  42. package/src/components/VelocityEngine.ts +11 -0
  43. package/src/components/WalrusEngine.ts +11 -0
  44. package/src/decorators/viewEngine.ts +8 -0
  45. package/src/index.ts +42 -0
  46. package/src/registries/EnginesContainer.ts +37 -0
  47. package/src/utils/cache.spec.ts +13 -0
  48. package/src/utils/cache.ts +154 -0
  49. package/src/utils/getEngines.spec.ts +133 -0
  50. 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
+ }