blob: f136a68cd362d162b2f1ab6498a2a0e5e542e0d3 [file] [log] [blame]
// 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, '&quot;')}">
<![CDATA[${path}:${line}:${column} - ${kind} ${code}: ${message}]]>
</${tag}>`;
};