1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
10 | import sane from 'sane';
|
11 | import { resolve as resolvePath } from 'path';
|
12 | import { spawn } from 'child_process';
|
13 |
|
14 | process.env.PATH += ':./node_modules/.bin';
|
15 |
|
16 | const is_windows = process.platform === 'win32'
|
17 |
|
18 |
|
19 | const dev_null = is_windows ? 'NUL' : '/dev/null'
|
20 |
|
21 | var cmd = resolvePath(__dirname);
|
22 | var srcDir = resolvePath(cmd, './source');
|
23 |
|
24 | function exec(command, options) {
|
25 | return new Promise(function (resolve, reject) {
|
26 | var child = spawn(command, options, {
|
27 | cmd: cmd,
|
28 | env: process.env,
|
29 | stdio: 'inherit'
|
30 | });
|
31 | child.on('exit', function (code) {
|
32 | if (code === 0) {
|
33 | resolve(true);
|
34 | } else {
|
35 | reject(new Error('Error code: ' + code));
|
36 | }
|
37 | });
|
38 | });
|
39 | }
|
40 |
|
41 | var watcher = sane(srcDir, { glob: ['**/*.*'] })
|
42 | .on('ready', startWatch)
|
43 | .on('add', changeFile)
|
44 | .on('delete', deleteFile)
|
45 | .on('change', changeFile);
|
46 |
|
47 | process.on('SIGINT', function () {
|
48 | watcher.close();
|
49 | console.log(CLEARLINE + yellow(invert('stopped watching')));
|
50 | process.exit();
|
51 | });
|
52 |
|
53 | var isChecking;
|
54 | var needsCheck;
|
55 | var toCheck = {};
|
56 | var timeout;
|
57 |
|
58 | function startWatch() {
|
59 | process.stdout.write(CLEARSCREEN + green(invert('watching...')));
|
60 | }
|
61 |
|
62 | function changeFile(filepath, root, stat) {
|
63 | if (!stat.isDirectory()) {
|
64 | toCheck[filepath] = true;
|
65 | debouncedCheck();
|
66 | }
|
67 | }
|
68 |
|
69 | function deleteFile(filepath) {
|
70 | delete toCheck[filepath];
|
71 | debouncedCheck();
|
72 | }
|
73 |
|
74 | function debouncedCheck() {
|
75 | needsCheck = true;
|
76 | clearTimeout(timeout);
|
77 | timeout = setTimeout(guardedCheck, 250);
|
78 | }
|
79 |
|
80 | function guardedCheck() {
|
81 | if (isChecking || !needsCheck) {
|
82 | return;
|
83 | }
|
84 | isChecking = true;
|
85 | var filepaths = Object.keys(toCheck);
|
86 | toCheck = {};
|
87 | needsCheck = false;
|
88 | checkFiles(filepaths).then(() => {
|
89 | isChecking = false;
|
90 | process.nextTick(guardedCheck);
|
91 | });
|
92 | }
|
93 |
|
94 | function checkFiles(filepaths) {
|
95 | console.log('\u001b[2J');
|
96 |
|
97 |
|
98 |
|
99 | return runTests(filepaths)
|
100 | .catch(() => false)
|
101 | .then(success => {
|
102 | process.stdout.write(
|
103 | '\n' + (success ? '' : '\x07') + green(invert('watching...'))
|
104 | );
|
105 | });
|
106 | }
|
107 |
|
108 |
|
109 |
|
110 | function executable(command) {
|
111 | if (is_windows) {
|
112 | return command + '.cmd'
|
113 | }
|
114 | return command
|
115 | }
|
116 |
|
117 | function parseFiles(filepaths) {
|
118 | console.log('Checking Syntax');
|
119 |
|
120 | return Promise.all(filepaths.map(filepath => {
|
121 | if (isJS(filepath) && !isTest(filepath)) {
|
122 | return exec(executable('babel'), [
|
123 | '--optional', 'runtime',
|
124 | '--out-file', dev_null,
|
125 | srcPath(filepath)
|
126 | ]);
|
127 | }
|
128 | }));
|
129 | }
|
130 |
|
131 | function runTests(filepaths) {
|
132 | console.log('\nRunning Tests');
|
133 |
|
134 | return exec(executable('mocha'), [
|
135 | '--reporter', 'spec',
|
136 | '--bail',
|
137 | '--require', 'scripts/mocha-bootload'
|
138 | ].concat(
|
139 | allTests(filepaths) ? filepaths.map(srcPath) : ['test/**/*.js']
|
140 | )).catch(() => false);
|
141 | }
|
142 |
|
143 |
|
144 |
|
145 | function srcPath(filepath) {
|
146 | return resolvePath(srcDir, filepath);
|
147 | }
|
148 |
|
149 |
|
150 |
|
151 | function isJS(filepath) {
|
152 | return filepath.indexOf('.js') === filepath.length - 3;
|
153 | }
|
154 |
|
155 | function allTests(filepaths) {
|
156 | return filepaths.length > 0 && filepaths.every(isTest);
|
157 | }
|
158 |
|
159 | function isTest(filepath) {
|
160 | return isJS(filepath) && ~filepath.indexOf('__tests__/');
|
161 | }
|
162 |
|
163 |
|
164 |
|
165 | var CLEARSCREEN = '\u001b[2J';
|
166 | var CLEARLINE = '\r\x1B[K';
|
167 | var CHECK = green('\u2713');
|
168 | var X = red('\u2718');
|
169 |
|
170 | function invert(str) {
|
171 | return `\u001b[7m ${str} \u001b[27m`;
|
172 | }
|
173 |
|
174 | function red(str) {
|
175 | return `\x1B[K\u001b[1m\u001b[31m${str}\u001b[39m\u001b[22m`;
|
176 | }
|
177 |
|
178 | function green(str) {
|
179 | return `\x1B[K\u001b[1m\u001b[32m${str}\u001b[39m\u001b[22m`;
|
180 | }
|
181 |
|
182 | function yellow(str) {
|
183 | return `\x1B[K\u001b[1m\u001b[33m${str}\u001b[39m\u001b[22m`;
|
184 | } |
\ | No newline at end of file |