-
Notifications
You must be signed in to change notification settings - Fork 0
/
attachHook.js
100 lines (83 loc) · 2.82 KB
/
attachHook.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
const fs = require('fs')
const falafel = require('falafel')
function parse (src) {
let localName
const nodes = {
requires: []
}
nodes.root = falafel(src, function (node) {
// stop looking after we find an `hp(...)`
if (nodes.hpCall) { return }
// credits: https://github.com/stackcss/sheetify/blob/master/transform.js#L95
if (node.type === 'CallExpression'
&& node.callee && node.callee.name === 'require'
&& node.arguments.length === 1
&& node.arguments[0].value === 'hot-pockets') {
localName = node.parent.id.name
}
if (node.type === 'VariableDeclaration') {
// Keep track of requires so we can un-hoist them into the hot-pocket.
// This means we'll always get the new version when a pure module is hot-swapped.
if (node.source().includes('require(')) {
nodes.requires.push(node)
}
}
if (node.type === 'MemberExpression'
&& node.object.name === localName
&& node.property.name === 'pure') {
nodes.pureCall = node.parent
}
if (node.type === 'CallExpression'
&& localName
&& node.callee.name === localName) {
nodes.hpCall = node
nodes.hpCallFunc = node.arguments[0]
}
})
return nodes
}
module.exports = function attachHook(sources, pureModules, { pauseOnEvalError = true, extension }) {
const existingHook = require.extensions[extension]
require.extensions[extension] = function hook (m, filename) {
// ignore 3rd-party modules
if (filename.includes('node_modules')) {
return existingHook(m, filename)
}
let src = fs.readFileSync(filename, 'utf8')
if (src.includes('//@hp:ignore')) {
// case: ignore this module
// TODO: add an option to ignore by regex, so you don't need to modify files.
return existingHook(m, filename)
}
const nodes = parse(src)
if (nodes.pureCall) {
// case: pure module
nodes.pureCall.update(`hp.pure(__filename)`)
} else if (nodes.hpCall) {
// case: hotpocket
sources[filename] = nodes.root.toString().replace(nodes.hpCallFunc.source(), '')
nodes.hpCall.update(`hp(__filename, ${nodes.hpCallFunc.source()}, function (src) {
${nodes.requires.map(n => n.source()).join('\n')}
eval(\`try { \` + src + \` } catch (e) {
console.error(e);
${pauseOnEvalError ? 'debugger' : ''}
}\`)
})`)
} else {
// case: no hotpocket calls. Consider this a "pure" module.
pureModules[filename] = true
return existingHook(m, filename)
}
try {
return m._compile(nodes.root.toString(), filename)
} catch (e) {
return m._compile(`
console.error('Syntax error in ${filename}: ' + ${JSON.stringify(e.message)})
console.error(${JSON.stringify(e.stack)})
return
${nodes.root.toString()}
`, filename)
}
};
};
module.exports.parse = parse