diff --git a/packages/core/__tests__/utils.test.ts b/packages/core/__tests__/utils.test.ts new file mode 100644 index 00000000..b4388fb5 --- /dev/null +++ b/packages/core/__tests__/utils.test.ts @@ -0,0 +1,26 @@ +import {toAnnotationProperties} from '../src/utils' + +describe('@actions/core/src/utils', () => { + describe('.toAnnotationProperties', () => { + it('extracts title only from Error instance without a parseable stack', () => { + const error = new TypeError('Test error') + error.stack = '' + expect(toAnnotationProperties(error)).toEqual({ + title: 'TypeError', + file: undefined, + startLine: undefined, + startColumn: undefined + }) + }) + + it('extracts AnnotationProperties from Error instance', () => { + const error = new ReferenceError('Test error') + expect(toAnnotationProperties(error)).toEqual({ + title: 'ReferenceError', + file: expect.stringMatching(/utils\.test\.ts$/), + startLine: expect.any(Number), + startColumn: expect.any(Number) + }) + }) + }) +}) diff --git a/packages/core/package-lock.json b/packages/core/package-lock.json index 7b1cf7bb..868e541d 100644 --- a/packages/core/package-lock.json +++ b/packages/core/package-lock.json @@ -11,6 +11,7 @@ "dependencies": { "@actions/exec": "^1.1.1", "@actions/http-client": "^2.0.1", + "error-stack-parser": "^2.1.4", "uuid": "^8.3.2" }, "devDependencies": { @@ -51,6 +52,19 @@ "integrity": "sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw==", "dev": true }, + "node_modules/error-stack-parser": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/error-stack-parser/-/error-stack-parser-2.1.4.tgz", + "integrity": "sha512-Sk5V6wVazPhq5MhpO+AUxJn5x7XSXGl1R93Vn7i+zS15KDVxQijejNCrz8340/2bgLBjR9GtEG8ZVKONDjcqGQ==", + "dependencies": { + "stackframe": "^1.3.4" + } + }, + "node_modules/stackframe": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/stackframe/-/stackframe-1.3.4.tgz", + "integrity": "sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw==" + }, "node_modules/tunnel": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz", @@ -102,6 +116,19 @@ "integrity": "sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw==", "dev": true }, + "error-stack-parser": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/error-stack-parser/-/error-stack-parser-2.1.4.tgz", + "integrity": "sha512-Sk5V6wVazPhq5MhpO+AUxJn5x7XSXGl1R93Vn7i+zS15KDVxQijejNCrz8340/2bgLBjR9GtEG8ZVKONDjcqGQ==", + "requires": { + "stackframe": "^1.3.4" + } + }, + "stackframe": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/stackframe/-/stackframe-1.3.4.tgz", + "integrity": "sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw==" + }, "tunnel": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz", diff --git a/packages/core/package.json b/packages/core/package.json index 2eda27b5..67bd2918 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -38,10 +38,11 @@ "dependencies": { "@actions/exec": "^1.1.1", "@actions/http-client": "^2.0.1", + "error-stack-parser": "^2.1.4", "uuid": "^8.3.2" }, "devDependencies": { "@types/node": "^12.0.2", "@types/uuid": "^8.3.4" } -} \ No newline at end of file +} diff --git a/packages/core/src/utils.ts b/packages/core/src/utils.ts index c43f3870..d197087b 100644 --- a/packages/core/src/utils.ts +++ b/packages/core/src/utils.ts @@ -3,6 +3,7 @@ import {AnnotationProperties} from './core' import {CommandProperties} from './command' +import ErrorStackParser from 'error-stack-parser' /** * Sanitizes an input into a string so it can be passed into issueCommand safely @@ -39,3 +40,21 @@ export function toCommandProperties( endColumn: annotationProperties.endColumn } } + +export function toAnnotationProperties(error: Error): AnnotationProperties { + let firstFrame + + try { + const stack = ErrorStackParser.parse(error) + firstFrame = stack?.[0] + } catch (parseError) { + // If we can't parse the stack, we'll just skip it + } + + return { + title: error.name, + file: firstFrame?.fileName, + startLine: firstFrame?.lineNumber, + startColumn: firstFrame?.columnNumber + } +}