UNPKG

4.34 kBPlain TextView Raw
1#!/usr/bin/env node
2// Run "tsc" with watch, upon successful compilation run mocha tests.
3
4const chalk = require("chalk");
5const spawn = require("cross-spawn");
6const readline = require("readline");
7const yargs = require("yargs");
8const fs = require("fs");
9
10const 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
66const stdl = readline.createInterface({ input: process.stdin });
67stdl.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
72let mochap = null;
73let errors = 0;
74
75function compilationStarted() {
76 if (mochap) {
77 mochap.kill("SIGINT");
78 }
79 mochap = null;
80 errors = 0;
81}
82function foundErrors() {
83 errors ++;
84}
85function 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
133const tscp = spawn(argv.tsc, ["-p", argv.project, "-w"]);
134const tscl = readline.createInterface({ input: tscp.stdout });
135let times = 0;
136tscl.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
150tscl.on("close", () => {
151 stdl.close();
152 tscl.close();
153});