| // Copyright 2022 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| import { execFile as execFileOrig } from 'child_process'; |
| import * as fs from 'fs/promises'; |
| import * as util from 'util'; |
| const execFile = util.promisify(execFileOrig); |
| |
| const outXML = process.env.TYPECHECK_XML_OUTPUT; |
| |
| // first, write some failure XML to be extra sure we get some output |
| await fs.writeFile(outXML, `<?xml version="1.0" encoding="UTF-8"?> |
| <testsuites> |
| <testsuite name="tsc" tests="1" failures="0" errors="1"> |
| <testcase name="tsc" status="notrun" time="0"> |
| <failure message="tsc didn't run for some reason, see target logs"/> |
| </testcase> |
| </testsuite> |
| </testsuites> |
| `); |
| |
| // then, run our tests |
| let code = 0; |
| let stdout; |
| let stderr; |
| try { |
| ( { stdout, stderr } = await execFile('npx', ['tsc', '--pretty', 'false']) ); // pretty=false should be the default but better safe than sorry |
| } catch (ex) { |
| if (ex.errno !== undefined) { |
| // this was something like a failure to run the command at all, just bail |
| // now (we won't have a numeric code, so choose 1) |
| console.error(`couldn't run tsc: ${ex}`); |
| process.exit(1); |
| } |
| console.error(`tsc returned code ${ex.code} with error\n${ex}`); |
| ( { code, stdout, stderr } = ex ); |
| } |
| |
| // results are on stdout, so stderr is purely info, so just print it so it gets |
| // put in the log file |
| console.error(stderr); |
| |
| console.log(stdout); // just in case |
| |
| // chop of the last empty newline if it's there |
| // but keep other empty lines in the middle in case they're |
| // meaningful |
| const lines = stdout.split('\n'); |
| if (lines.length > 0 && lines[lines.length-1] == '') { |
| lines.pop(); |
| } |
| |
| // process the results |
| let infos = lines. |
| map((raw) => { |
| // some/file.ts(line,col): kind code: message |
| let match = raw.match(/^([^\(]+)\((\d+),(\d+)\): (\w+) (\w+): (.+)$/); |
| if (!match) { |
| console.error(`non-error: ${raw}`); |
| return undefined; |
| } |
| let [_full, path, line, column, kind, code, message] = match; |
| return { path, line, column, kind, code, message }; |
| }). |
| filter((info) => !!info); // filter out the invalid outputs |
| |
| // write the results |
| const numErrs = infos.filter((info) => info.kind === 'error').length; |
| const numOther = infos.length - numErrs; |
| await fs.writeFile(outXML, `<testsuites> |
| <testsuite name="tsc" tests="1" errors="${numErrs}" failures="${numOther}"> |
| <testcase name="tsc" tests="1" errors="${numErrs}" failures="${numOther}"> |
| ${infos.map((info) => formatMessage(info)).join("\n")} |
| </testcase> |
| </testsuite> |
| </testsuites>`); |
| |
| // finally, exit with the appropriate code to mark this as a success or failure |
| process.exit(code); |
| |
| function formatMessage({ path, line, column, kind, code, message }) { |
| // error --> error, everything else (warning, etc) --> failure |
| const tag = kind === 'error' ? 'error' : 'failure'; |
| |
| // need to escape quotes in the message so that we can surround it with quotes safely |
| return `<${tag} message="${message.replace(/"/g, '"')}"> |
| <![CDATA[${path}:${line}:${column} - ${kind} ${code}: ${message}]]> |
| </${tag}>`; |
| }; |