1 | const
|
2 | fs = require('fs'),
|
3 | path = require('path'),
|
4 | extend = require('extend'),
|
5 | objectHash = require('object-hash'),
|
6 | sgUtil = require('./util');
|
7 |
|
8 | class TemplateEngine {
|
9 | constructor({conf}) {
|
10 | this.conf = conf;
|
11 | this.baseDir = conf.get('baseDir');
|
12 | this.logLevel = conf.get('logLevel', 0);
|
13 | this.engines = new Map();
|
14 | this.cache = new Map();
|
15 |
|
16 | this.setDefaultEngine();
|
17 | this.setCustomEngine();
|
18 |
|
19 | this.render = this.render.bind(this);
|
20 | }
|
21 |
|
22 | async wrap(source, data = {}, templatePath, instance) {
|
23 |
|
24 | const wrapperTemplatePath = this.getWrapperTemplatePath(instance);
|
25 |
|
26 | if (!fs.existsSync(wrapperTemplatePath)) {
|
27 | return source;
|
28 | }
|
29 |
|
30 | const
|
31 | baseDir = this.conf.get('rootPath'),
|
32 | global = this.conf.get('app.globals');
|
33 |
|
34 | global.packageName = this.conf.get('package.name');
|
35 | global.packageVersion = this.conf.get('version', this.conf.get('package.version'));
|
36 |
|
37 | const
|
38 | options = extend({}, data, {
|
39 | global,
|
40 | source,
|
41 | templatePath,
|
42 | baseDir,
|
43 | cache: false,
|
44 | pretty: true
|
45 | });
|
46 |
|
47 | return this.engines.get('pug').render(wrapperTemplatePath, {}, options);
|
48 | }
|
49 |
|
50 | getWrapperTemplatePath(instance) {
|
51 | const filename = `../app/templates/wrapper/${instance}Template.pug`;
|
52 | return this.conf.get(`app.wrapperTemplates.${instance}`, path.join(__dirname, filename));
|
53 | }
|
54 |
|
55 | setDefaultEngine() {
|
56 | const
|
57 | engine = require('./templateEngines/pug'),
|
58 | options = {
|
59 | basedir: this.baseDir,
|
60 | pretty: true,
|
61 | cache: false,
|
62 | plugins: [require('./pugGlobLoaderPlugin')]
|
63 | };
|
64 | this.setEngine(new engine(options));
|
65 | }
|
66 |
|
67 | setCustomEngine() {
|
68 |
|
69 | const {conf} = this;
|
70 |
|
71 | if (conf.has('customTemplateEngine')) {
|
72 |
|
73 | if (conf.has('customTemplateEngine.name')) {
|
74 | const
|
75 | engine = require('./templateEngines/' + conf.get('customTemplateEngine.name')),
|
76 | options = conf.get('customTemplateEngine.options', {}),
|
77 | root = path.resolve(this.baseDir),
|
78 | ext = conf.get('templateExt');
|
79 |
|
80 | this.setEngine(new engine(options, root, ext));
|
81 | }
|
82 | else {
|
83 | this.setEngine(conf.get('customTemplateEngine'));
|
84 | }
|
85 | }
|
86 | }
|
87 |
|
88 | setEngine(engine) {
|
89 | this.engines.set(engine.ext, engine);
|
90 | }
|
91 |
|
92 | async render(file, global = {}, wrap = true) {
|
93 | const {filename, componentName, instance, timestamp, saveHtml, saveLocals, renderHook, extension} = file;
|
94 | const data = extend({}, file.data);
|
95 | const hash = file.hash ? file.hash : objectHash.MD5({componentName, data, global});
|
96 |
|
97 | if (this.cache.has(hash) && this.cache.get(hash).timestamp === timestamp) {
|
98 | if (this.logLevel > 0) {
|
99 | sgUtil.log(`Component: \u001b[1m${componentName}\u001b[22m use cached (${hash}).`);
|
100 | }
|
101 | return this.cache.get(hash);
|
102 | }
|
103 |
|
104 | let
|
105 | {locals, schema} = data,
|
106 | html = '',
|
107 | source = '';
|
108 |
|
109 | if (!fs.existsSync(filename)) {
|
110 | sgUtil.log(`Component: \u001b[1m${componentName}\u001b[22m template file missing.`, 'warn');
|
111 | return {html, source, data, locals};
|
112 | }
|
113 |
|
114 | delete data.locals;
|
115 |
|
116 | const engine = this.engines.get(extension)
|
117 |
|
118 | source = await engine.render(filename, locals, extend({}, {atomatic: data}, global));
|
119 |
|
120 | if (typeof source !== 'string' && source.error) {
|
121 |
|
122 | if (typeof saveLocals === 'function') {
|
123 | saveLocals({locals, schema});
|
124 | }
|
125 |
|
126 | if (typeof saveHtml === 'function') {
|
127 | saveHtml({html: source.error, source});
|
128 | }
|
129 |
|
130 | return source.error;
|
131 | }
|
132 |
|
133 | if (typeof renderHook === 'function') {
|
134 | ({source, locals} = renderHook(file, source));
|
135 | }
|
136 |
|
137 | if (wrap) {
|
138 | html = await this.wrap(source, data, filename, instance);
|
139 | }
|
140 |
|
141 | if (typeof saveHtml === 'function') {
|
142 | saveHtml({html, source});
|
143 | }
|
144 |
|
145 | if (typeof saveLocals === 'function') {
|
146 | saveLocals({locals, schema});
|
147 | }
|
148 |
|
149 | sgUtil.log(`Component: \u001b[1m${componentName}\u001b[22m rendered (${hash}).`, 'info');
|
150 | this.cache.set(hash, {html, source, data, locals, timestamp: timestamp});
|
151 |
|
152 | return this.cache.get(hash);
|
153 | }
|
154 |
|
155 | kill() {
|
156 | [...this.engines.values()]
|
157 | .filter(engine => typeof engine.kill === 'function')
|
158 | .map((engine) => engine.kill())
|
159 | }
|
160 | }
|
161 |
|
162 | module.exports = TemplateEngine;
|