1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
10 |
|
11 |
|
12 | {deferred} = require 'also'
|
13 | {watcher} = require './watcher'
|
14 | {environment} = require './environment'
|
15 | {inspector} = require './inspector'
|
16 | {readFileSync, readdirSync, lstatSync} = require 'fs'
|
17 | {normalize} = require 'path'
|
18 | {spawn} = require 'child_process'
|
19 | {sep} = require 'path'
|
20 | {compile} = require 'coffee-script'
|
21 | colors = require 'colors'
|
22 | program = require 'commander'
|
23 |
|
24 | keypress = require 'keypress'
|
25 | keypress process.stdin
|
26 |
|
27 | program.version JSON.parse(
|
28 | readFileSync __dirname + '/../package.json'
|
29 | 'utf8'
|
30 | ).version
|
31 |
|
32 | program.option '-w, --no-watch', 'Dont watch spec and src dirs.'
|
33 | program.option '-n, --no-env', 'Dont load .env.test'
|
34 | program.option '-m, --mocha', 'Use mocha.'
|
35 | program.option '-e, --alt-env [name]', 'Loads .env.name'
|
36 | program.option ' --spec [dir]', 'Specify alternate spec dir.', 'spec'
|
37 | program.option ' --src [dir]', 'Specify alternate src dir.', 'src'
|
38 | program.option ' --lib [dir]', 'Specify alternate compile target.', 'lib'
|
39 |
|
40 |
|
41 | {env, altEnv, mocha, watch, spec, src, lib, env} = program.parse process.argv
|
42 |
|
43 |
|
44 | kids = []
|
45 |
|
46 |
|
47 |
|
48 |
|
49 | test = deferred ({resolve}, file) ->
|
50 |
|
51 | unless mocha
|
52 | console.log '\nipso: ' + "Unspecified spec scaffold.".red, "ipso --mocha"
|
53 | refresh()
|
54 | return
|
55 |
|
56 |
|
57 |
|
58 |
|
59 |
|
60 |
|
61 |
|
62 |
|
63 |
|
64 |
|
65 |
|
66 | ipsoPath = normalize __dirname + '/ipso'
|
67 | bin = normalize __dirname + '/../node_modules/.bin/mocha'
|
68 | args = [
|
69 | '--colors'
|
70 | '--compilers', 'coffee:coffee-script'
|
71 | '--require', 'should'
|
72 | file
|
73 | ]
|
74 |
|
75 |
|
76 |
|
77 |
|
78 |
|
79 |
|
80 | console.log '\nipso: ' + "node_modules/.bin/mocha #{args.join ' '}".grey
|
81 | process.env.IPSO_SRC = src
|
82 | running = spawn bin, args, stdio: 'inherit'
|
83 |
|
84 |
|
85 |
|
86 | running.on 'exit', resolve
|
87 |
|
88 | compile = deferred ({resolve}) ->
|
89 |
|
90 |
|
91 |
|
92 |
|
93 |
|
94 | bin = normalize __dirname + '/../node_modules/.bin/coffee'
|
95 | args = [ '-c', '-b', '-o', lib, src ]
|
96 |
|
97 |
|
98 |
|
99 |
|
100 |
|
101 |
|
102 |
|
103 |
|
104 | console.log '\nipso: ' + "node_modules/.bin/coffee #{args.join ' '}".grey
|
105 | running = spawn bin, args, stdio: 'inherit'
|
106 |
|
107 |
|
108 | running.on 'exit', resolve
|
109 |
|
110 |
|
111 | if env or typeof altEnv is 'string' then environment altEnv
|
112 |
|
113 | if watch
|
114 |
|
115 | watcher
|
116 | path: program.spec || 'spec'
|
117 | handler:
|
118 | change: (file, stats) ->
|
119 | test( file ).then -> refresh()
|
120 |
|
121 | watcher
|
122 | path: program.src || 'src'
|
123 | handler:
|
124 | change: (file, stats) ->
|
125 | return unless file.match /\.coffee/
|
126 | compile().then ->
|
127 | refresh()
|
128 | specFile = file.replace /\.coffee$/, '_spec.coffee'
|
129 | specFile = specFile.replace process.cwd() + sep + src, spec
|
130 | test specFile
|
131 | .then -> refresh()
|
132 |
|
133 |
|
134 | prompt = '> '
|
135 | input = ''
|
136 | argsHint = ''
|
137 |
|
138 | actions =
|
139 | 'inspect':
|
140 | args: ' [<web-port>, <debug-port>] <script>'
|
141 | secondary: 'pathWalker'
|
142 |
|
143 | primaryTabComplete = ->
|
144 |
|
145 |
|
146 |
|
147 |
|
148 |
|
149 | matches = []
|
150 | for action of actions
|
151 | matches.push action if action.match new RegExp "^#{input}"
|
152 | if matches.length == 0
|
153 |
|
154 |
|
155 |
|
156 |
|
157 |
|
158 | input = ''
|
159 | return primaryTabComplete()
|
160 |
|
161 | return matches
|
162 |
|
163 |
|
164 | secondaryTabComplete = (act) ->
|
165 |
|
166 |
|
167 |
|
168 |
|
169 |
|
170 | try secondaryType = actions[act].secondary
|
171 | return [] unless secondaryType
|
172 |
|
173 | if secondaryType == 'pathWalker'
|
174 |
|
175 |
|
176 |
|
177 |
|
178 |
|
179 |
|
180 |
|
181 |
|
182 |
|
183 |
|
184 | try all = input.split(' ').pop()
|
185 | parts = all.split sep
|
186 | last = parts.pop()
|
187 | path = process.cwd() + sep + parts.join( sep ) + sep
|
188 | files = readdirSync path
|
189 | select = files.filter (file) -> file.match new RegExp "^#{last}"
|
190 |
|
191 | if select.length == 1
|
192 |
|
193 | input += select[0][last.length..]
|
194 | file = input.split(' ').pop()
|
195 | stat = lstatSync process.cwd() + sep + file
|
196 | if stat.isDirectory() then input += sep
|
197 |
|
198 |
|
199 | else
|
200 |
|
201 | console.log()
|
202 | for part in select
|
203 | stat = lstatSync path + part
|
204 | if stat.isDirectory() then console.log part + sep
|
205 | else console.log part
|
206 |
|
207 | return []
|
208 |
|
209 |
|
210 |
|
211 | refresh = (output, stream) ->
|
212 |
|
213 |
|
214 |
|
215 |
|
216 |
|
217 |
|
218 | if output?
|
219 | switch stream
|
220 | when 'stderr' then process.stdout.write output.red
|
221 | else process.stdout.write output
|
222 |
|
223 | input = input.replace ' ', ' '
|
224 | process.stdout.clearLine()
|
225 | process.stdout.cursorTo 0
|
226 | process.stdout.write prompt + input + argsHint
|
227 | process.stdout.cursorTo (prompt + input).length
|
228 |
|
229 |
|
230 | shutdown = (code) ->
|
231 |
|
232 | kid.kill() for kid in kids
|
233 | process.exit code
|
234 |
|
235 | doAction = ->
|
236 |
|
237 | return if input == ''
|
238 | [act, args...] = input.split ' '
|
239 | trimmed = args.filter (arg) -> arg isnt ''
|
240 | input = ''
|
241 |
|
242 | switch act
|
243 | when 'inspect'
|
244 | inspector args: args, kids, refresh
|
245 | else console.log action: act, args: trimmed if act?
|
246 |
|
247 |
|
248 | run = ->
|
249 |
|
250 | stdin = process.openStdin()
|
251 | process.stdin.setRawMode true
|
252 | refresh()
|
253 | process.stdin.on 'keypress', (chunk, key) ->
|
254 |
|
255 | argsHint = ''
|
256 |
|
257 | try {name, ctrl, meta, shift, sequence} = key
|
258 | if ctrl
|
259 | switch name
|
260 | when 'd' then shutdown 0
|
261 | when 'c'
|
262 | input = ''
|
263 | refresh()
|
264 |
|
265 | return
|
266 |
|
267 | if name is 'backspace'
|
268 | input = input[0..-2]
|
269 | return refresh()
|
270 |
|
271 | if name is 'tab'
|
272 |
|
273 | try [m,act] = input.match /^(.*?)\s/
|
274 | if act? then matches = secondaryTabComplete act
|
275 | else matches = primaryTabComplete()
|
276 |
|
277 | if matches.length == 1
|
278 | input = matches[0]
|
279 | argsHint = ' ' + actions[matches[0]].args.grey
|
280 | return refresh()
|
281 | else
|
282 | console.log()
|
283 | console.log action, actions[action].args.grey for action in matches
|
284 | return refresh()
|
285 |
|
286 |
|
287 | if name is 'return'
|
288 | process.stdout.write '\n'
|
289 | doAction()
|
290 | process.stdout.write prompt + input
|
291 | return
|
292 |
|
293 | return unless chunk
|
294 | input += chunk.toString()
|
295 | refresh()
|
296 |
|
297 | run()
|