1 | 'use strict';
|
2 |
|
3 | module.exports = function (config) {
|
4 | var dust = require('dustjs-helpers');
|
5 | var mincer = require('mincer');
|
6 | var fs = require('fs');
|
7 | var path = require('path');
|
8 | var glob = require('glob');
|
9 | var inputFilters = require('./input-filter')(config);
|
10 | var eachModule = require('each-module');
|
11 |
|
12 | var hostAppDir = config.path.root;
|
13 | var modulesPaths = config.modules.map(function (module) {
|
14 | return path.join(hostAppDir, 'node_modules', module);
|
15 | });
|
16 |
|
17 | mincer.logger.use(config.log);
|
18 |
|
19 | var initExtensions = function () {
|
20 | var paths = [].slice.call(arguments, 0, -1);
|
21 | var callback = arguments[arguments.length - 1];
|
22 | var locations = [config.path.shunterRoot].concat(modulesPaths, hostAppDir);
|
23 |
|
24 | if (typeof callback === 'function') {
|
25 | locations.forEach(function (dir) {
|
26 | var extensionPath = path.join.apply(path, [dir].concat(paths));
|
27 | eachModule(extensionPath, callback);
|
28 | });
|
29 | }
|
30 | };
|
31 |
|
32 |
|
33 | initExtensions(config.structure.filters, config.structure.filtersInput, function (name, inputFilter) {
|
34 | if (typeof inputFilter === 'function') {
|
35 | inputFilters.add(inputFilter);
|
36 | }
|
37 | });
|
38 |
|
39 |
|
40 | initExtensions(config.structure.mincer, function (name, initMincerExtension) {
|
41 | if (typeof initMincerExtension === 'function') {
|
42 | initMincerExtension(mincer, config);
|
43 | }
|
44 | });
|
45 |
|
46 | var environment = new mincer.Environment();
|
47 | var manifest = new mincer.Manifest(environment, config.path.publicResources);
|
48 |
|
49 |
|
50 | var assetPath = function (name) {
|
51 | var isProduction = config.env.isProduction();
|
52 | var asset = (isProduction) ? manifest.assets[name] : environment.findAsset(name);
|
53 | if (!asset) {
|
54 | return '';
|
55 | }
|
56 |
|
57 | return (
|
58 | isProduction ?
|
59 | path.join(config.web.publicResources, asset) :
|
60 | path.join(config.web.resources, asset.digestPath)
|
61 | );
|
62 | };
|
63 |
|
64 | environment.registerHelper('asset_path', assetPath);
|
65 |
|
66 | var assetTypes = [config.structure.fonts, config.structure.images, config.structure.styles, config.structure.scripts];
|
67 | var themeResourcesPath = config.path.resources;
|
68 |
|
69 |
|
70 | assetTypes.forEach(function (assetType) {
|
71 | var newPath = path.join(themeResourcesPath, assetType);
|
72 | if (fs.existsSync(newPath)) {
|
73 | environment.appendPath(newPath);
|
74 | }
|
75 | modulesPaths.reverse().forEach(function (modulePath) {
|
76 | var newPath = path.join(modulePath, 'resources', assetType);
|
77 | if (fs.existsSync(newPath)) {
|
78 | environment.appendPath(newPath);
|
79 | }
|
80 | });
|
81 | });
|
82 |
|
83 |
|
84 | initExtensions(config.structure.ejs, function (name, initEjsHelper) {
|
85 | if (typeof initEjsHelper === 'function') {
|
86 | initEjsHelper(environment, manifest, config);
|
87 | }
|
88 | });
|
89 |
|
90 | return {
|
91 | TEMPLATE_CACHE_KEY_PREFIX: 'root',
|
92 |
|
93 | dust: dust,
|
94 | environment: environment,
|
95 | manifest: manifest,
|
96 | assetPath: assetPath,
|
97 |
|
98 | assetServer: function () {
|
99 | return mincer.createServer(environment);
|
100 | },
|
101 |
|
102 | initDustExtensions: function () {
|
103 | require('./dust')(dust, this, config);
|
104 | initExtensions(config.structure.dust, function (name, initDustExtension) {
|
105 | if (typeof initDustExtension === 'function') {
|
106 | initDustExtension(dust, this, config);
|
107 | }
|
108 | }.bind(this));
|
109 | },
|
110 |
|
111 | compileFile: function (fp) {
|
112 | var ext = config.structure.templateExt;
|
113 | var id;
|
114 | var compiled;
|
115 | var sandboxNS;
|
116 | var splitPath;
|
117 | var timer;
|
118 |
|
119 | if (path.extname(fp) === ext) {
|
120 | sandboxNS = path.relative(config.path.themes, fp);
|
121 |
|
122 | sandboxNS = sandboxNS.substring(sandboxNS.indexOf('view'));
|
123 | splitPath = sandboxNS.split(path.sep);
|
124 | if (splitPath.indexOf(config.structure.templates) > -1) {
|
125 |
|
126 | splitPath.splice(splitPath.indexOf(config.structure.templates), 1);
|
127 | }
|
128 |
|
129 | splitPath[splitPath.length - 1] = path.basename(fp, ext);
|
130 | splitPath.unshift(this.TEMPLATE_CACHE_KEY_PREFIX);
|
131 |
|
132 | id = splitPath.join('__');
|
133 |
|
134 | timer = config.timer();
|
135 | try {
|
136 | compiled = dust.compile(fs.readFileSync(fp, 'utf8'), id);
|
137 | dust.loadSource(compiled);
|
138 | } catch (err) {
|
139 | config.log.error('Compilation error: ' + err.message + ' in ' + fp);
|
140 | }
|
141 | timer('Compiling ' + fp + ' as ' + id);
|
142 | }
|
143 | },
|
144 |
|
145 |
|
146 | compilePaths: function (paths) {
|
147 | var self = this;
|
148 | if (typeof paths === 'string') {
|
149 | paths = [].slice.call(arguments, 0);
|
150 | }
|
151 | paths.forEach(function (name) {
|
152 |
|
153 |
|
154 |
|
155 |
|
156 | if (fs.existsSync(path.join(config.path.themes, name))) {
|
157 |
|
158 | self.compileFile(path.join(config.path.themes, name));
|
159 | } else if (fs.existsSync(path.join(config.path.templates, name))) {
|
160 |
|
161 | self.compileFile(path.join(config.path.templates, name));
|
162 | } else if (fs.existsSync(name)) {
|
163 |
|
164 | self.compileFile(name);
|
165 | } else {
|
166 | config.log.info('Could not find template ' + name);
|
167 | }
|
168 |
|
169 | });
|
170 | },
|
171 |
|
172 | compileTemplates: function (forTests) {
|
173 | var fullFiles = [];
|
174 |
|
175 | config.modules.forEach(function (module) {
|
176 | var moduleResourcesPath = (forTests) ? forTests : path.join(hostAppDir, 'node_modules', module);
|
177 |
|
178 | var templates = [moduleResourcesPath, config.structure.templates, '**', ('*' + config.structure.templateExt)].join('/');
|
179 | fullFiles = fullFiles.concat(glob.sync(templates, {}));
|
180 | });
|
181 |
|
182 |
|
183 | var templates = [config.path.themes, config.structure.templates, '**', ('*' + config.structure.templateExt)].join('/');
|
184 | fullFiles = fullFiles.concat(glob.sync(templates, {}));
|
185 | this.compileFileList(fullFiles);
|
186 | },
|
187 |
|
188 | compileOnDemand: function (name) {
|
189 | var self = this;
|
190 | var localPath = path.join(config.structure.templates, name.split('__').join(path.sep) + config.structure.templateExt);
|
191 | config.modules.map(function (module) {
|
192 | return path.join(hostAppDir, 'node_modules', module, localPath);
|
193 | }).concat([
|
194 | path.join(config.path.themes, localPath)
|
195 | ]).filter(function (file) {
|
196 | return fs.existsSync(file);
|
197 | }).forEach(function (file) {
|
198 | self.compileFile(file);
|
199 | });
|
200 | },
|
201 |
|
202 |
|
203 | compileFileList: function (fileArr) {
|
204 | var self = this;
|
205 | fileArr.forEach(function (file) {
|
206 | self.compileFile(file);
|
207 | });
|
208 | },
|
209 |
|
210 | watchTemplates: function () {
|
211 | var watchTree;
|
212 | var watcher;
|
213 | var folders = [config.path.templates];
|
214 | var self = this;
|
215 |
|
216 | var compile = function (fp) {
|
217 | self.compileFile(fp);
|
218 | };
|
219 |
|
220 | modulesPaths.forEach(function (mp) {
|
221 | folders.push(path.join(mp, config.structure.templates));
|
222 | });
|
223 | watchTree = require('./watcher')(config.structure.templateExt).watchTree;
|
224 | watcher = watchTree(folders, config.log);
|
225 | watcher.on('fileModified', compile);
|
226 | watcher.on('fileCreated', compile);
|
227 | config.log.debug('Watching ' + folders.join(', ') + ' for changes');
|
228 | },
|
229 |
|
230 | watchDustExtensions: function () {
|
231 | var watchTree;
|
232 | var watcher;
|
233 | var folders = [config.path.dust];
|
234 | var self = this;
|
235 |
|
236 | var compile = function (fp) {
|
237 | config.log.info('Loading Dust extension ' + fp);
|
238 | delete require.cache[require.resolve(fp)];
|
239 | require(fp)(dust, self, config);
|
240 | };
|
241 |
|
242 | modulesPaths.forEach(function (mp) {
|
243 | folders.push(path.join(mp, config.structure.dust));
|
244 | });
|
245 | watchTree = require('./watcher')('.js').watchTree;
|
246 | watcher = watchTree(folders, config.log);
|
247 | watcher.on('fileModified', compile);
|
248 | watcher.on('fileCreated', compile);
|
249 | config.log.debug('Watching ' + folders.join(', ') + ' for changes');
|
250 | },
|
251 |
|
252 | render: function (req, res, data, callback) {
|
253 | var name = (data && data.layout && data.layout.template) ? data.layout.template : 'layout';
|
254 | this.renderPartial(name, req, res, data, callback);
|
255 | },
|
256 |
|
257 | renderPartial: function (partial, req, res, data, callback) {
|
258 | inputFilters.run(req, res, data, function (data) {
|
259 | var ns = (data && data.layout && data.layout.namespace) ? data.layout.namespace : null;
|
260 | var base = dust.makeBase({
|
261 | namespace: ns
|
262 | }, {
|
263 | namespace: ns
|
264 | });
|
265 | dust.render(partial, base.push(data), function (err, out) {
|
266 | callback(err, out);
|
267 | });
|
268 | });
|
269 | }
|
270 | };
|
271 | };
|