1 | #!/usr/bin/env node
|
2 | // Run "tsc" with watch, upon successful compilation run mocha tests.
|
3 |
|
4 | const chalk = require("chalk");
|
5 | const spawn = require("cross-spawn");
|
6 | const readline = require("readline");
|
7 | const yargs = require("yargs");
|
8 | const fs = require("fs");
|
9 |
|
10 | const argv = yargs
|
11 | .options({
|
12 | p: {
|
13 | alias: "project",
|
14 | demand: false,
|
15 | default: ".",
|
16 | describe: "Path to tsconfig file or directory containing tsconfig, passed to `tsc -p <value>`.",
|
17 | type: "string",
|
18 | },
|
19 | t: {
|
20 | alias: "tsc",
|
21 | demand: false,
|
22 | default: "tsc",
|
23 | describe: "Path to executable tsc, by default points to typescript installed as dependency, then to global tsc installation.",
|
24 | type: "string",
|
25 | },
|
26 | o: {
|
27 | alias: "opts",
|
28 | demand: false,
|
29 | default: "./test/mocha.opts",
|
30 | describe: "Path to mocha.opts file containing additional mocha configuration.",
|
31 | type: "string",
|
32 | },
|
33 | m: {
|
34 | alias: "mocha",
|
35 | demand: false,
|
36 | default: "mocha",
|
37 | describe: "Path to executable mocha, by default points to mocha installed as dependency, then to global mocha installation.",
|
38 | type: "string",
|
39 | },
|
40 | n: {
|
41 | alias: "times",
|
42 | demand: false,
|
43 | default: undefined,
|
44 | describe: "Number of iterations before the watcher process quits. For testing purposes only.",
|
45 | type: "number",
|
46 | },
|
47 | g: {
|
48 | alias: "grep",
|
49 | demand: false,
|
50 | default: undefined,
|
51 | describe: "Passed down to mocha: only run tests matching <pattern>",
|
52 | type: "string",
|
53 | },
|
54 | f: {
|
55 | alias: "fgrep",
|
56 | demand: false,
|
57 | default: undefined,
|
58 | describe: "Passed down to mocha: only run tests containing <string>",
|
59 | type: "string",
|
60 | },
|
61 | })
|
62 | .help("h")
|
63 | .alias("h", "help")
|
64 | .argv;
|
65 |
|
66 | const stdl = readline.createInterface({ input: process.stdin });
|
67 | stdl.on("line", (line) => {
|
68 | // TODO: handle "g <pattern>" or "f <pattern>" to run mocha with pattern
|
69 | // Ctrl + R may restart mocha test run?
|
70 | });
|
71 |
|
72 | let mochap = null;
|
73 | let errors = 0;
|
74 |
|
75 | function compilationStarted() {
|
76 | if (mochap) {
|
77 | mochap.kill("SIGINT");
|
78 | }
|
79 | mochap = null;
|
80 | errors = 0;
|
81 | }
|
82 | function foundErrors() {
|
83 | errors ++;
|
84 | }
|
85 | function compilationComplete() {
|
86 | if (errors) {
|
87 | console.log(chalk.red("TS errors!"));
|
88 | return;
|
89 | } else {
|
90 | console.log(chalk.gray("Run mocha."));
|
91 | }
|
92 |
|
93 | const mochaOptions = ["--colors"].concat(argv._);
|
94 | if (argv.opts && fs.existsSync(argv.opts)) {
|
95 | mochaOptions.push("--opts");
|
96 | mochaOptions.push(argv.opts);
|
97 | }
|
98 | if (argv.g) {
|
99 | mochaOptions.push("-g");
|
100 | mochaOptions.push(argv.g);
|
101 | }
|
102 | if (argv.f) {
|
103 | mochaOptions.push("-f");
|
104 | mochaOptions.push(argv.f);
|
105 | }
|
106 | mochap = spawn(argv.mocha, mochaOptions);
|
107 | mochap.on("close", (code) => {
|
108 | if (mochap) {
|
109 | if (code) {
|
110 | console.log(chalk.red("Exited with " + code));
|
111 | } else {
|
112 | }
|
113 | if (argv.times && (times >= argv.times)) {
|
114 | tscp.kill("SIGINT");
|
115 | }
|
116 | mochap = null;
|
117 | }
|
118 | });
|
119 | mochap.stdout.on("data", (chunk) => {
|
120 | // Ensure old processes won't interfere tsc, .pipe here may be good enough.
|
121 | if (mochap) {
|
122 | process.stdout.write(chunk);
|
123 | }
|
124 | });
|
125 | mochap.stderr.on("data", (chunk) => {
|
126 | // Ensure old processes won't interfere tsc, .pipe here may be good enough.
|
127 | if (mochap) {
|
128 | process.stderr.write(chunk);
|
129 | }
|
130 | });
|
131 | }
|
132 |
|
133 | const tscp = spawn(argv.tsc, ["-p", argv.project, "-w"]);
|
134 | const tscl = readline.createInterface({ input: tscp.stdout });
|
135 | let times = 0;
|
136 | tscl.on("line", (line) => {
|
137 | if (line.indexOf("Compilation complete.") >= 0 || line.indexOf("Found ") >= 0) {
|
138 | console.log(line);
|
139 | times++;
|
140 | compilationComplete();
|
141 | } else if (line.indexOf("File change detected.") >= 0) {
|
142 | compilationStarted();
|
143 | console.log(line);
|
144 | } else if (line.indexOf(": error TS") >= 0) {
|
145 | console.log(line);
|
146 | foundErrors();
|
147 | }
|
148 | });
|
149 |
|
150 | tscl.on("close", () => {
|
151 | stdl.close();
|
152 | tscl.close();
|
153 | });
|