/
bake.js
187 lines (150 loc) · 5.49 KB
/
bake.js
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
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
var fs = require('fs');
var path = require('path');
var props = require('props');
var dive = require('dive');
var ejs = require('ejs');
var async = require('async');
var clone = require('clone');
var append = require('append');
// Main function
var bake = function(conf, hooks, cb) {
// File counter
var todo = 0;
if (typeof conf != 'object')
return cb(new Error('parameter conf must be a valid configuration object'));
// Set values for inputDir, outputDir and tplDir
var root = conf.root || process.cwd();
var inputDir = conf.directories.input || 'pub';
var outputDir = conf.directories.output || 'pub';
var tplDir = conf.directories.templates || 'tpl';
inputDir = path.resolve(root, inputDir);
outputDir = path.resolve(root, outputDir);
tplDir = path.resolve(root, tplDir);
// Set values for fileExt
var fileExt = conf.fileExtensions || { txt: 'html' };
var fileExtPattern
= new RegExp('\\.(' + Object.keys(fileExt).join('|') + ')$', 'i');
var ignore = conf.ignore || [];
// Status log
console.log('Beginning to bake ' + inputDir + '.');
// Dive into the public directory
dive(inputDir, function(err, master) {
// Throw errors
if (err)
return cb(err);
// Matching variable
var match;
var pathFromRoot = master.replace(inputDir, '').substr(1);
// Match the master-file's name against enabled file extensions
if ((match = master.match(fileExtPattern))
&& ignore.indexOf(pathFromRoot) == -1) {
// Get the file extension of the master file
var masterExt = match[1];
// Increase file counter
++todo;
// Read the master-file's contents
fs.readFile(master, 'utf8', function(err, data) {
// Throw errors
if (err)
return cb(err);
// Get the properties
// prop is the file specific property object
var prop = props(data);
// Amend prop by properties in conf.properties if defined
if (conf.properties)
prop = append(clone(conf.properties), prop);
// Assert that prop.template is set
if (typeof prop.template == 'undefined')
prop.template = 'default.tpl';
// __propBefore hook
if (hooks.__propBefore)
hooks.__propBefore(master, prop, propBeforeCB);
else
propBeforeCB(null, prop);
function propBeforeCB(err, prop) {
if (err)
return cb(err);
// tasks for async
var tasks = {};
// Various property hooks
for (var key in prop) {(function (prop, key) {
if (hooks[key])
tasks[key] = function (callback) {
return hooks[key](master, prop, callback);
};
else
tasks[key] = function (callback) {
return callback(null, prop[key]);
};
})(prop, key);}
// run tasks in parallel
async.parallel(tasks, function (err, prop) {
if (err)
return cb(err);
// __propAfter hook
if (hooks.__propAfter)
hooks.__propAfter(master, prop, propAfterCB);
else
propAfterCB(null, prop);
function propAfterCB(err, prop) {
if (err)
return cb(err);
// Read the template file
fs.readFile(path.resolve(tplDir, prop.template), 'utf8',
function(err, result) {
// Throw errors
if (err)
return cb(err);
// (Pre-)Insert the content (so ejs-tags in
// prop.__content are parsed, too.
result = result.replace(/<%=\s+__content\s+%>/g,
prop.__content);
// Result's filename
var resName = master.replace(fileExtPattern,
'.' + fileExt[masterExt]);
// New file's path
if (typeof prop._id == 'undefined') {
prop._id = resName.replace(inputDir, '');
prop._id = prop._id.replace(new RegExp('\\\\', 'g'), '/');
}
// Render ejs-template
result = ejs.render(result, { locals: prop });
// Remove first slash
if (/^\//.test(prop._id))
prop._id = prop._id.substring(1);
// absolute path
resName = path.resolve(outputDir, prop._id);
// Write contents
fs.writeFile(resName, result, function(err) {
// Throw errors
if (err)
return cb(err);
// __writeAfter hook
if (hooks.__writeAfter)
hooks.__writeAfter(master, prop, writeAfterCB);
else
writeAfterCB(null);
function writeAfterCB(err) {
if (err)
return cb(err);
// Log status on success
console.log(' * ' + resName + ' written.');
// When file counter is zero
if (!--todo) {
// __complete hook
if (hooks.__complete)
hooks.__complete(master, prop, cb);
else
return cb(null);
}
}
});
});
}
});
}
});
}
});
};
module.exports = bake;