-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcoffeebars.coffee
97 lines (85 loc) · 2.43 KB
/
coffeebars.coffee
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
#!/usr/bin/env coffee
global = do -> this
pimatch = /\{\{\{?([\w\W]*?)\}\}\}?/g
templates = {}
exports.render = render = (context, source, write) ->
template = templates[source] ?= exports.toFunction source
template context, write
exports.toFunction = (source) -> eval exports.toJavaScript source
exports.toJavaScript = (source) ->
script = require('coffee-script').compile exports.toCoffeeScript source
# remove trailing semicolon
script.replace /;\s*$/, ''
exports.toCoffeeScript = (source) ->
# top down parsing starting with processing instructions
array = []
indent = 1
getIndent = ->
value = ""
for i in [0...indent]
value += '\t'
value
push = (text, dent = 0) ->
array.push getIndent()
array.push text, '\n'
indent += dent
lastIndex = 0
writeText = (match) ->
text = source.substring lastIndex, match?.index ? source.length
lastIndex = pimatch.lastIndex
push "write #{JSON.stringify text}"
fixIndent = (pi, match) ->
smallest = Number.MAX_VALUE
lines = pi.split /\r\n|\r|\n/
for line, index in lines when line.trim().length > 0
if index is 0
throw new Error "Multiline script cannot start on same line as opening brace:\n#{match[0]}"
lineIndent = line.match(/^\s*/)?[0].length
if lineIndent?
smallest = Math.min smallest, lineIndent
# now return the lines, fixing the indent
currentIndent = getIndent()
lines.map((x) -> currentIndent + x.substring smallest).join('\n')
while match = pimatch.exec source
writeText match
pi = match[1]
dent = 0
if /\r|\n/.test pi # multi line literal
pi = fixIndent pi, match
else if /^\s*(else|catch)\b\s*([\w\W]+)/.test pi
indent--
dent = 1
pi = pi.trim()
else if /^\s*(if|for|while|unless|try)\b\s*([\w\W]+)/.test(pi) and not /\b(then|end)\b/.test(pi)
dent = 1
pi = pi.trim()
else if /^\s*end\s*$/.test pi
indent--
pi = null
else
escape = match[0].substring(0,3) isnt '{{{'
pi = "write (#{pi}), #{escape}"
push pi, dent if pi?
writeText match
source = array.join ''
template = """
template = (write) ->
#{source}
return
return (context, write) ->
buffer = null
if not write
buffer = []
write = (text) -> buffer.push text if text?
template.call context, write
buffer?.join ''
"""
template
if require.main is module
args = process.argv.slice 2
if args.length is 0
console.log """
Usage: coffeebars template
"""
else
console.log exports.parse require('fs').readFileSync args[0], 'utf8'