-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmacro.js
106 lines (91 loc) · 2.74 KB
/
macro.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
let { spawn } = require('child_process')
let path = require('path')
let deasync = require('deasync')
let { createMacro } = require('babel-plugin-macros')
let { parseExpression } = require('babylon')
let generate = require('babel-generator').default
module.exports = createMacro(tinkerMacro)
function tinkerMacro({ references, state, babel }) {
let { types: t } = babel
references.default &&
references.default.forEach(ref => {
if (ref.parentPath.type !== 'TaggedTemplateExpression') return
let php = ref.parentPath.node.quasi.quasis[0].value.cooked
let result = evaluate(php, state)
ref.parentPath.addComment('trailing', 'tinker.macro')
ref.parentPath.replaceWith(parseExpression(result))
})
Object.keys(references)
.filter(x => x !== 'default')
.forEach(x => {
references[x].forEach(ref => {
let expr = findExpressionParent(ref)
expr.traverse({
ObjectExpression(path) {
let obj = generate(path.node).code
eval(`obj = ${obj}`)
path.replaceWith(
t.callExpression(t.identifier('json_decode'), [
t.stringLiteral(JSON.stringify(obj)),
t.booleanLiteral(true)
])
)
}
})
let i = 0
let code = generate(expr.node).code.replace(
/\.(?![^(]*\))/g,
_ => (i++ === 0 ? '::' : '->')
)
if (i > 0) {
code = `App\\${code}`
}
let result = evaluate(code, state)
ref.parentPath.addComment('trailing', 'tinker.macro')
ref.parentPath.replaceWith(parseExpression(result))
})
})
}
function evaluate(php, state) {
let tinker = spawn('php', [
path.resolve(state.file.opts.sourceRoot, 'artisan'),
'tinker'
])
tinker.stdin.setEncoding('utf-8')
let done = false
let reallyDone = false
let result
tinker.stdout.on('data', data => {
if (data.toString().startsWith('=>')) {
if (done) {
result = data.toString().match(/=> (.*?)\n/)[1]
result = result.substr(1, result.length - 2)
reallyDone = true
}
done = true
}
if (reallyDone) {
return
}
if (data.toString().startsWith('>>>')) {
if (done) {
tinker.stdin.write(`json_encode($_)\n`)
} else {
tinker.stdin.write(`${php.replace(/[\n\r]/g, ' ').trim()}\n`)
}
}
})
deasync.loopWhile(() => !reallyDone)
tinker.stdin.end()
return result
}
function findExpressionParent(ref, pos) {
if (!pos) pos = ref.node.start
let nextRef = ref.findParent(
p => p.isCallExpression() || p.isMemberExpression()
)
if (nextRef && nextRef.node.start === pos) {
return findExpressionParent(nextRef, nextRef.node.start)
}
return ref
}