1 | var child = require('child_process');
|
2 | var chokidar = require('chokidar');
|
3 | var fs = require('fs');
|
4 | var http = require('http');
|
5 | var mime = require('mime');
|
6 | var path = require('path');
|
7 | var rdbg = require('rdbg');
|
8 | var util = require('util');
|
9 | var temp = require('temp');
|
10 | var which = require('which');
|
11 |
|
12 | function serve(options, callback) {
|
13 | var server = http.createServer(function(request, response) {
|
14 | if (request.url == '/') {
|
15 | response.setHeader('content-type', 'text/html');
|
16 | response.write('<!doctype html><head><meta charset="utf-8"></head><body>');
|
17 |
|
18 | for (var key in options.scripts) {
|
19 | response.write('<script src="' + key + '"></script>');
|
20 | }
|
21 |
|
22 | response.end('</body>');
|
23 | } else {
|
24 | var filename = options.scripts[path.basename(request.url)];
|
25 | if (!filename) {
|
26 | var filename = path.join(options.cwd, request.url);
|
27 | }
|
28 |
|
29 | fs.exists(filename, function(exists) {
|
30 | if (exists) {
|
31 | response.setHeader('content-type', mime.lookup(filename));
|
32 | response.writeHead(200);
|
33 |
|
34 | var file = fs.createReadStream(filename);
|
35 | file.pipe(response);
|
36 | } else {
|
37 | response.writeHead(404);
|
38 | response.end('404');
|
39 | }
|
40 | });
|
41 | }
|
42 | });
|
43 |
|
44 | server.listen(options.port, options.host, callback);
|
45 | return server;
|
46 | }
|
47 |
|
48 | function compile(options, callback) {
|
49 | if (options.output == undefined) {
|
50 | var dirpath = temp.mkdirSync('amok-output');
|
51 | options.output = path.join(dirpath, 'bundle.js');
|
52 | }
|
53 |
|
54 | switch (options.compiler) {
|
55 | case 'browserify':
|
56 | var command = which.sync('watchify');
|
57 | var args = [
|
58 | '-o',
|
59 | options.output,
|
60 | ];
|
61 | break;
|
62 |
|
63 | case 'webpack':
|
64 | var command = which.sync('webpack');
|
65 | var args = [
|
66 | '--watch',
|
67 | '--output-file',
|
68 | options.output,
|
69 | ];
|
70 | break;
|
71 |
|
72 | case 'typescript':
|
73 | var command = which.sync('tsc');
|
74 | var args = [
|
75 | '--watch',
|
76 | '--out',
|
77 | options.output,
|
78 | ];
|
79 | break;
|
80 |
|
81 | case 'coffeescript':
|
82 | var command = which.sync('coffee');
|
83 | var args = [
|
84 | '--watch',
|
85 | '--out',
|
86 | options.output,
|
87 | ];
|
88 | break;
|
89 |
|
90 | case 'babel':
|
91 | var command = which.sync('babel');
|
92 | var args = [
|
93 | '--watch',
|
94 | '--out-file',
|
95 | options.output,
|
96 | ];
|
97 | break;
|
98 |
|
99 | default:
|
100 | var args = options.compiler.match(/'[^"]*'|"[^"]*"|\S+/g);
|
101 | var command = args.shift();
|
102 | break;
|
103 | }
|
104 |
|
105 | args = args.concat(options.args);
|
106 |
|
107 | var compiler = child.spawn(command, args);
|
108 | compiler.output = options.output;
|
109 |
|
110 | process.nextTick(function tick() {
|
111 | fs.exists(compiler.output, function(exists) {
|
112 | if (exists) {
|
113 | callback(null, compiler.stdout, compiler.stderr);
|
114 | } else {
|
115 | setTimeout(tick, 100);
|
116 | }
|
117 | });
|
118 | });
|
119 |
|
120 | return compiler;
|
121 | }
|
122 |
|
123 | function watch(options, callback) {
|
124 | var files = Object.keys(options.scripts).map(function(key) {
|
125 | return path.dirname(options.scripts[key]);
|
126 | });
|
127 |
|
128 | var watcher = chokidar.watch(files, {
|
129 | persistent: true
|
130 | });
|
131 |
|
132 | if (callback) {
|
133 | watcher.once('ready', callback);
|
134 | }
|
135 |
|
136 | return watcher;
|
137 | }
|
138 |
|
139 | function debug(options, callback) {
|
140 | var bugger = rdbg.connect(options.debuggerPort, options.debuggerHost, function(target) {
|
141 | callback(null, target);
|
142 | });
|
143 |
|
144 | bugger.targets(function(targets) {
|
145 | var target = targets.filter(function(target) {
|
146 | return target.url.indexOf(options.host) > -1 && target.webSocketDebuggerUrl;
|
147 | })[0];
|
148 |
|
149 | bugger.attach(target);
|
150 | });
|
151 |
|
152 | return bugger;
|
153 | }
|
154 |
|
155 | function open(options, callback) {
|
156 | switch (options.client) {
|
157 | case 'chrome':
|
158 | var command = (function() {
|
159 | if (process.platform == 'win32') {
|
160 | var suffix = '\\Google\\Chrome\\Application\\chrome.exe';
|
161 | var prefixes = [
|
162 | process.env['LOCALAPPDATA'],
|
163 | process.env['PROGRAMFILES'],
|
164 | process.env['PROGRAMFILES(X86)'],
|
165 | ];
|
166 |
|
167 | var executables = prefixes.map(function(prefix) {
|
168 | return prefix + suffix;
|
169 | }).filter(function(path) {
|
170 | return fs.existsSync(path);
|
171 | });
|
172 | return executables[0];
|
173 | } else if (process.platform == 'darwin') {
|
174 | return '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome';
|
175 | } else {
|
176 | return which.sync('google-chrome');
|
177 | }
|
178 | }());
|
179 |
|
180 | var args = [
|
181 | '--remote-debugging-port=' + options.debuggerPort,
|
182 | '--no-first-run',
|
183 | '--no-default-browser-check',
|
184 | '--disable-translate',
|
185 | '--disable-default-apps',
|
186 | '--disable-popup-blocking',
|
187 | '--disable-zero-browsers-open-for-tests',
|
188 | '--user-data-dir=' + temp.mkdirSync('amok-chrome'),
|
189 | ];
|
190 | break;
|
191 |
|
192 | default:
|
193 | var args = options.client.match(/'[^"]*'|"[^"]*"|\S+/g);
|
194 | var command = args.shift();
|
195 | break;
|
196 | }
|
197 |
|
198 | var url = util.format('http://%s:%d', options.host, options.port);
|
199 | args.push(url);
|
200 |
|
201 | var client = child.spawn(command, args);
|
202 | setTimeout(function() {
|
203 | callback(null, client.stdout, client.stderr);
|
204 | }, 1000);
|
205 |
|
206 | return client;
|
207 | }
|
208 |
|
209 | module.exports.serve = serve;
|
210 | module.exports.watch = watch;
|
211 | module.exports.compile = compile;
|
212 | module.exports.debug = debug;
|
213 | module.exports.open = open;
|