mirror of
https://github.com/github/codeql-action.git
synced 2025-12-26 17:20:10 +08:00
171 lines
4.3 KiB
JavaScript
171 lines
4.3 KiB
JavaScript
import path from 'node:path';
|
|
import process from 'node:process';
|
|
import {fileURLToPath, pathToFileURL} from 'node:url';
|
|
|
|
import cleanYamlObject from 'clean-yaml-object';
|
|
import concordance from 'concordance';
|
|
import isError from 'is-error';
|
|
import StackUtils from 'stack-utils';
|
|
|
|
import {AssertionError} from './assert.js';
|
|
import concordanceOptions from './concordance-options.js';
|
|
|
|
function isAvaAssertionError(source) {
|
|
return source instanceof AssertionError;
|
|
}
|
|
|
|
function filter(propertyName, isRoot) {
|
|
return !isRoot || (propertyName !== 'message' && propertyName !== 'name' && propertyName !== 'stack');
|
|
}
|
|
|
|
function normalizeFile(file, ...base) {
|
|
return file.startsWith('file://') ? file : pathToFileURL(path.resolve(...base, file)).toString();
|
|
}
|
|
|
|
const stackUtils = new StackUtils();
|
|
function extractSource(stack, testFile) {
|
|
if (!stack || !testFile) {
|
|
return null;
|
|
}
|
|
|
|
testFile = normalizeFile(testFile);
|
|
|
|
for (const line of stack.split('\n')) {
|
|
const callSite = stackUtils.parseLine(line);
|
|
if (callSite && normalizeFile(callSite.file) === testFile) {
|
|
return {
|
|
isDependency: false,
|
|
isWithinProject: true,
|
|
file: testFile,
|
|
line: callSite.line,
|
|
};
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
function buildSource(source) {
|
|
if (!source) {
|
|
return null;
|
|
}
|
|
|
|
// Assume the CWD is the project directory. This holds since this function
|
|
// is only called in test workers, which are created with their working
|
|
// directory set to the project directory.
|
|
const projectDir = process.cwd();
|
|
|
|
const file = normalizeFile(source.file.trim(), projectDir);
|
|
const rel = path.relative(projectDir, fileURLToPath(file));
|
|
|
|
const [segment] = rel.split(path.sep);
|
|
const isWithinProject = segment !== '..' && (process.platform !== 'win32' || !segment.includes(':'));
|
|
const isDependency = isWithinProject && path.dirname(rel).split(path.sep).includes('node_modules');
|
|
|
|
return {
|
|
isDependency,
|
|
isWithinProject,
|
|
file,
|
|
line: source.line,
|
|
};
|
|
}
|
|
|
|
function trySerializeError(error, shouldBeautifyStack, testFile) {
|
|
const stack = error.savedError ? error.savedError.stack : error.stack;
|
|
|
|
const retval = {
|
|
avaAssertionError: isAvaAssertionError(error),
|
|
nonErrorObject: false,
|
|
source: extractSource(stack, testFile),
|
|
stack,
|
|
shouldBeautifyStack,
|
|
};
|
|
|
|
if (error.actualStack) {
|
|
retval.stack = error.actualStack;
|
|
}
|
|
|
|
if (retval.avaAssertionError) {
|
|
retval.improperUsage = error.improperUsage;
|
|
retval.message = error.message;
|
|
retval.name = error.name;
|
|
retval.values = error.values;
|
|
|
|
if (error.fixedSource) {
|
|
const source = buildSource(error.fixedSource);
|
|
if (source) {
|
|
retval.source = source;
|
|
}
|
|
}
|
|
|
|
if (error.assertion) {
|
|
retval.assertion = error.assertion;
|
|
}
|
|
|
|
if (error.operator) {
|
|
retval.operator = error.operator;
|
|
}
|
|
} else {
|
|
retval.object = cleanYamlObject(error, filter); // Cleanly copy non-standard properties
|
|
if (typeof error.message === 'string') {
|
|
retval.message = error.message;
|
|
}
|
|
|
|
if (typeof error.name === 'string') {
|
|
retval.name = error.name;
|
|
}
|
|
}
|
|
|
|
if (typeof error.stack === 'string') {
|
|
const lines = error.stack.split('\n');
|
|
if (error.name === 'SyntaxError' && !lines[0].startsWith('SyntaxError')) {
|
|
retval.summary = '';
|
|
for (const line of lines) {
|
|
retval.summary += line + '\n';
|
|
if (line.startsWith('SyntaxError')) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
retval.summary = retval.summary.trim();
|
|
} else {
|
|
retval.summary = '';
|
|
for (let index = 0; index < lines.length; index++) {
|
|
if (lines[index].startsWith(' at')) {
|
|
break;
|
|
}
|
|
|
|
const next = index + 1;
|
|
const end = next === lines.length || lines[next].startsWith(' at');
|
|
retval.summary += end ? lines[index] : lines[index] + '\n';
|
|
}
|
|
}
|
|
}
|
|
|
|
return retval;
|
|
}
|
|
|
|
export default function serializeError(origin, shouldBeautifyStack, error, testFile) {
|
|
if (!isError(error)) {
|
|
return {
|
|
avaAssertionError: false,
|
|
nonErrorObject: true,
|
|
formatted: concordance.formatDescriptor(concordance.describe(error, concordanceOptions), concordanceOptions),
|
|
};
|
|
}
|
|
|
|
try {
|
|
return trySerializeError(error, shouldBeautifyStack, testFile);
|
|
} catch {
|
|
const replacement = new Error(`${origin}: Could not serialize error`);
|
|
return {
|
|
avaAssertionError: false,
|
|
nonErrorObject: false,
|
|
name: replacement.name,
|
|
message: replacement.message,
|
|
stack: replacement.stack,
|
|
summary: replacement.message,
|
|
};
|
|
}
|
|
}
|