UNPKG

8.12 kBtext/coffeescriptView Raw
1#
2# PRE-RELEASE - ALPHA - CLI
3# =========================
4#
5# ipso -h
6#
7# * Expect no consistency between versions (worst case)
8# * Care **has already been taken** to minimize changes
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'
21colors = require 'colors'
22program = require 'commander'
23# MochaRunner = require './mocha_runner'
24keypress = require 'keypress'
25keypress process.stdin
26
27program.version JSON.parse(
28 readFileSync __dirname + '/../package.json'
29 'utf8'
30).version
31
32program.option '-w, --no-watch', 'Dont watch spec and src dirs.'
33program.option '-n, --no-env', 'Dont load .env.test'
34program.option '-m, --mocha', 'Use mocha.'
35program.option '-e, --alt-env [name]', 'Loads .env.name'
36program.option ' --spec [dir]', 'Specify alternate spec dir.', 'spec'
37program.option ' --src [dir]', 'Specify alternate src dir.', 'src'
38program.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
44kids = []
45
46# if mocha then testRunner = MochaRunner.create reporter: 'Dot'
47# MochaRunner.on 'spec_event', (payload) -> console.log payload
48
49test = deferred ({resolve}, file) ->
50
51 unless mocha
52 console.log '\nipso: ' + "Unspecified spec scaffold.".red, "ipso --mocha"
53 refresh()
54 return
55
56 #
57 # mocha is not the default
58 # there is no default yet
59 #
60
61 # console.log run: file
62 # testRunner.run [file], resolve
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 # * TODO: consider posibilities behind spec report to facto
77 # * related notes below
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 # running.stdout.on 'data', (chunk) -> refresh chunk.toString()
84 # running.stderr.on 'data', (chunk) -> refresh chunk.toString(), 'stderr'
85
86 running.on 'exit', resolve
87
88compile = deferred ({resolve}) ->
89
90 #
91 # TODO: optional compile per file, (and not spawned)
92 #
93
94 bin = normalize __dirname + '/../node_modules/.bin/coffee'
95 args = [ '-c', '-b', '-o', lib, src ]
96
97 #
98 # TODO: consider posibilities behind source diffs to facto
99 # * team's view of each developer progress / attempt
100 # * others can observe / assist a stuck team member
101 # * detecting stuck
102 #
103
104 console.log '\nipso: ' + "node_modules/.bin/coffee #{args.join ' '}".grey
105 running = spawn bin, args, stdio: 'inherit'
106 # running.stdout.on 'data', (chunk) -> refresh chunk.toString()
107 # running.stderr.on 'data', (chunk) -> refresh chunk.toString(), 'stderr'
108 running.on 'exit', resolve
109
110
111if env or typeof altEnv is 'string' then environment altEnv
112
113if 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
134prompt = '> '
135input = ''
136argsHint = ''
137
138actions =
139 'inspect':
140 args: ' [<web-port>, <debug-port>] <script>'
141 secondary: 'pathWalker'
142
143primaryTabComplete = ->
144
145 #
146 # produce list of actions according to partial input without whitespace
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 # no matches, reset and recurse for whole action list
156 #
157
158 input = ''
159 return primaryTabComplete()
160
161 return matches
162
163
164secondaryTabComplete = (act) ->
165
166 #
167 # partial input has white space (ie command is present)
168 #
169
170 try secondaryType = actions[act].secondary
171 return [] unless secondaryType
172
173 if secondaryType == 'pathWalker'
174
175 #
176 # pathWalker - secondary tab completion walks the file tree (up or down)
177 # ----------------------------------------------------------------------
178 #
179 # * TODO: find furtherest common match on tab
180 #
181 # ie. all files start with 'ser', tab on 's' should populate input to '**/ser'
182 #
183
184 try all = input.split(' ').pop() # whitespace in path not supported in path...
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
211refresh = (output, stream) ->
212
213 #
214 # write stream chunks to console but preserve prompt and partial input
215 # stderr in red
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
230shutdown = (code) ->
231
232 kid.kill() for kid in kids
233 process.exit code
234
235doAction = ->
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
248run = ->
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
297run()