forked from mralex/babel-middleware
-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathindex.js
149 lines (118 loc) · 3.99 KB
/
index.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
var babel = require('babel-core');
var Cache = require('./lib/cache');
var crypto = require('crypto');
var fs = require('fs');
var Logger = require('./lib/logger');
var micromatch = require('micromatch');
var path = require('path');
function lastModifiedHash(path, stats) {
var mtime = stats.mtime.getTime();
return crypto
.createHash('md5')
.update(mtime + '-' + path)
.digest('hex');
}
function getFileStats(src) {
var stats;
try {
stats = fs.lstatSync(src);
} catch(e) {
// path not found
return null;
}
if (! stats || ! stats.isFile()) {
// not a file
return null;
}
return stats;
}
function isExcluded(path, exclude) {
if (exclude.length) {
return micromatch.any(path.replace(/^\/+|\/+$/g, ''), exclude);
}
return false;
}
module.exports = function(options) {
options = options || {};
var srcPath = options.srcPath;
var cachePath = options.cachePath || 'memory';
var exclude = options.exclude || [];
var webConsoleErrors = options.consoleErrors || false;
var logger = new Logger(options.logLevel || 'none');
// filename to last known hash map
var hashMap = {};
var cache = new Cache(cachePath, logger, options);
var babelOptions = options.babelOptions || { presets: [] };
babelOptions.highlightCode = false;
function handleError(res, error) {
var errOutput = String(error).replace(/\'/g, '\\\'').replace(/\"/g, '\\\"');
logger.error(
'Babel parsing error from babel-middleware' +
'\n "' + errOutput + '"', '\n' + error.codeFrame
);
if (webConsoleErrors) {
res.send(
'/* Babel parsing error from babel-middleware */' +
'\n /* See error console output for details. */' +
'\n var output = ' + JSON.stringify(error) +
'\n console.error("' + errOutput + '\\n", output.codeFrame)'
);
} else {
res.status(500).send(error);
}
res.end();
}
function pathForHash(hash) {
return path.resolve(cachePath + '/' + hash + '.js');
}
return function(req, res, next) {
if (isExcluded(req.path, exclude)) {
logger.debug('Excluded: %s (%s)', req.path, exclude);
res.append('X-Babel-Cache', false);
return next();
}
var src = path.resolve(srcPath + '/' + req.path); // XXX Need the correct path
var stats = getFileStats(src);
if (! stats) {
// not a valid file, pass to the next middleware
return next();
}
var hash = lastModifiedHash(src, stats);
var lastKnownHash = hashMap[src];
// Clean up cached resources any time the
// hash has changed.
if (lastKnownHash && lastKnownHash !== hash) {
cache.remove(pathForHash(lastKnownHash));
}
logger.debug('Preparing: %s (%s)', src, hash);
res.append('X-Babel-Cache', true);
res.append('X-Babel-Cache-Hash', hash);
var hashPath = pathForHash(hash);
var code = cache.get(hashPath);
if (code) {
hashMap[src] = hash;
res.append('Content-Type', 'application/javascript');
res.append('X-Babel-Cache-Hit', true);
logger.debug('Serving (cached): %s', src);
res.write(code);
res.end();
return;
}
// expect an X-Babel-Cache-Hit header even on a parse error.
res.append('X-Babel-Cache-Hit', false);
var result;
try {
result = babel.transformFileSync(src, babelOptions);
} catch(e) {
handleError(res, e);
return;
}
code = result.code;
hashMap[src] = hash;
cache.store(hashPath, code);
logger.debug('Serving (uncached): %s', src);
res.append('Content-Type', 'application/javascript');
res.write(code);
res.end();
};
};