Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement SARIF formatter for Eslint #4956

Merged
merged 26 commits into from
Oct 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
4f03791
[heft-lint-plugin] Support outputting SARIF logs
atingmicrosoft Oct 1, 2024
4527b76
fixed type issues in typescript for Sarifformmater. Upgrade @types/es…
atingmicrosoft Oct 2, 2024
43530f3
PR fixes for creating SARIF Formatter
atingmicrosoft Oct 2, 2024
c3b019c
Merge branch 'main' of https://github.com/atingmicrosoft/rushstack in…
atingmicrosoft Oct 2, 2024
1a816a8
rush update
atingmicrosoft Oct 2, 2024
e6d4fb6
rush change
atingmicrosoft Oct 2, 2024
9c7f7fe
rush update
atingmicrosoft Oct 3, 2024
dfff00a
Merge branch 'main' of https://github.com/atingmicrosoft/rushstack in…
atingmicrosoft Oct 3, 2024
1a735c9
rush update
atingmicrosoft Oct 3, 2024
2342332
updated test snapshot
atingmicrosoft Oct 3, 2024
bbf1287
rush change
atingmicrosoft Oct 3, 2024
d267129
pnpm file fix
atingmicrosoft Oct 3, 2024
f60e827
updated snapshot
atingmicrosoft Oct 3, 2024
433dacd
2nd round PR fixes for sarifFormatter
atingmicrosoft Oct 3, 2024
0669aaa
Merge branch 'main' of https://github.com/atingmicrosoft/rushstack in…
atingmicrosoft Oct 3, 2024
9f547f7
resolve repo-state.json merge conflict
atingmicrosoft Oct 3, 2024
b641d6c
deleted change file for webpack5-localization-plugin. Changes were re…
atingmicrosoft Oct 3, 2024
f11ec5f
PR changes 3 for implementing SARIF formatter
atingmicrosoft Oct 5, 2024
3843d89
3rd round PR changes for SARIF formatter
atingmicrosoft Oct 7, 2024
f1b8755
made IRegion interface exportable
atingmicrosoft Oct 7, 2024
de829a6
created snapshot tests and updated schema descriptions
atingmicrosoft Oct 7, 2024
1f1a5c8
minor fixes and improvements to formatEslintResultsAsSARIF function
atingmicrosoft Oct 8, 2024
38be04f
edited rush changes
atingmicrosoft Oct 8, 2024
000961e
fixed functionality of sarifFormmater to properly track rule and arti…
atingmicrosoft Oct 8, 2024
01f2bf5
fixed bug with importing linter
atingmicrosoft Oct 8, 2024
79f4ffc
imporved efficiency of formatAsASARIF function
atingmicrosoft Oct 9, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"changes": [
{
"packageName": "@rushstack/eslint-patch",
"comment": "",
"type": "none"
}
],
"packageName": "@rushstack/eslint-patch"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"changes": [
{
"packageName": "@rushstack/eslint-plugin-packlets",
"comment": "",
"type": "none"
}
],
"packageName": "@rushstack/eslint-plugin-packlets"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"changes": [
{
"packageName": "@rushstack/eslint-plugin-security",
"comment": "",
"type": "none"
}
],
"packageName": "@rushstack/eslint-plugin-security"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"changes": [
{
"packageName": "@rushstack/eslint-plugin",
"comment": "",
"type": "none"
}
],
"packageName": "@rushstack/eslint-plugin"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"changes": [
{
"packageName": "@rushstack/heft-lint-plugin",
"comment": "Add an option `sarifLogPath` that, when specified, will emit logs in the SARIF format: https://sarifweb.azurewebsites.net/. Note that this is only supported by ESLint.",
"type": "minor"
}
],
"packageName": "@rushstack/heft-lint-plugin"
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@
{
"pnpmShrinkwrapHash": "5b75a8ef91af53a8caf52319e5eb0042c4d06852",
"preferredVersionsHash": "ce857ea0536b894ec8f346aaea08cfd85a5af648",
"packageJsonInjectedDependenciesHash": "8927ca4e0147b9436659f98a2ff8ca347107d52f"
"packageJsonInjectedDependenciesHash": "5ff2fabbffcfb22bb3e29f56c997f7c762e89d20"
}
32 changes: 16 additions & 16 deletions common/config/subspaces/default/pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion common/config/subspaces/default/repo-state.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// DO NOT MODIFY THIS FILE MANUALLY BUT DO COMMIT IT. It is generated and used by Rush.
{
"pnpmShrinkwrapHash": "5afee1d392a0c2404869d7eda687b5571fe88515",
"pnpmShrinkwrapHash": "673f7de41244835915a7947c11b6dbe80f944010",
"preferredVersionsHash": "ce857ea0536b894ec8f346aaea08cfd85a5af648"
}
2 changes: 1 addition & 1 deletion eslint/eslint-patch/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
"devDependencies": {
"@rushstack/heft": "0.67.2",
"@rushstack/heft-node-rig": "2.6.31",
"@types/eslint": "8.2.0",
"@types/eslint": "8.56.10",
"@types/node": "18.17.15",
"@typescript-eslint/types": "~5.59.2",
"eslint": "~8.57.0",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ export async function runEslintAsync(files: string[], mode: 'suppress' | 'prune'

if (results.length > 0) {
const stylishFormatter: ESLint.Formatter = await eslint.loadFormatter();
const formattedResults: string = stylishFormatter.format(results);
const formattedResults: string = await Promise.resolve(stylishFormatter.format(results));
atingmicrosoft marked this conversation as resolved.
Show resolved Hide resolved
console.log(formattedResults);
}

Expand Down
2 changes: 1 addition & 1 deletion eslint/eslint-plugin-packlets/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
"devDependencies": {
"@rushstack/heft": "0.67.2",
"@rushstack/heft-node-rig": "2.6.31",
"@types/eslint": "8.2.0",
"@types/eslint": "8.56.10",
"@types/estree": "1.0.5",
"@types/heft-jest": "1.0.1",
"@types/node": "18.17.15",
Expand Down
2 changes: 1 addition & 1 deletion eslint/eslint-plugin-security/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
"@eslint/eslintrc": "~3.0.0",
"@rushstack/heft": "0.67.2",
"@rushstack/heft-node-rig": "2.6.31",
"@types/eslint": "8.2.0",
"@types/eslint": "8.56.10",
"@types/estree": "1.0.5",
"@types/heft-jest": "1.0.1",
"@types/node": "18.17.15",
Expand Down
2 changes: 1 addition & 1 deletion eslint/eslint-plugin/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
"@eslint/eslintrc": "~3.0.0",
"@rushstack/heft": "0.67.2",
"@rushstack/heft-node-rig": "2.6.31",
"@types/eslint": "8.2.0",
"@types/eslint": "8.56.10",
"@types/estree": "1.0.5",
"@types/heft-jest": "1.0.1",
"@types/node": "18.17.15",
Expand Down
2 changes: 1 addition & 1 deletion heft-plugins/heft-lint-plugin/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
"@rushstack/heft-typescript-plugin": "workspace:*",
"@rushstack/heft-node-rig": "2.6.31",
"@rushstack/terminal": "workspace:*",
"@types/eslint": "8.2.0",
"@types/eslint": "8.56.10",
"@types/heft-jest": "1.0.1",
"@types/node": "18.17.15",
"@types/semver": "7.5.0",
Expand Down
56 changes: 38 additions & 18 deletions heft-plugins/heft-lint-plugin/src/Eslint.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import * as semver from 'semver';
import type * as TTypescript from 'typescript';
import type * as TEslint from 'eslint';
import { performance } from 'perf_hooks';
import { FileError } from '@rushstack/node-core-library';
import { FileError, FileSystem } from '@rushstack/node-core-library';

import { LinterBase, type ILinterBaseOptions } from './LinterBase';

Expand Down Expand Up @@ -59,12 +59,22 @@ export class Eslint extends LinterBase<TEslint.ESLint.LintResult> {
private readonly _currentFixMessages: TEslint.Linter.LintMessage[] = [];
private readonly _fixMessagesByResult: Map<TEslint.ESLint.LintResult, TEslint.Linter.LintMessage[]> =
new Map();
private readonly _sarifLogPath: string | undefined;

protected constructor(options: IEslintOptions) {
super('eslint', options);

const { buildFolderPath, eslintPackage, linterConfigFilePath, tsProgram, eslintTimings, fix } = options;
const {
buildFolderPath,
eslintPackage,
linterConfigFilePath,
tsProgram,
eslintTimings,
fix,
sarifLogPath
} = options;
this._eslintPackage = eslintPackage;
this._sarifLogPath = sarifLogPath;

let overrideConfig: TEslint.Linter.Config | undefined;
let fixFn: Exclude<TEslint.ESLint.Options['fix'], boolean>;
Expand Down Expand Up @@ -166,17 +176,10 @@ export class Eslint extends LinterBase<TEslint.ESLint.LintResult> {
return lintResult.fixableErrorCount + lintResult.fixableWarningCount > 0;
}));

const failures: TEslint.ESLint.LintResult[] = [];
for (const lintResult of lintResults) {
if (lintResult.messages.length > 0 || lintResult.output) {
failures.push(lintResult);
}
}

return failures;
return lintResults;
atingmicrosoft marked this conversation as resolved.
Show resolved Hide resolved
}

protected async lintingFinishedAsync(lintFailures: TEslint.ESLint.LintResult[]): Promise<void> {
protected async lintingFinishedAsync(lintResults: TEslint.ESLint.LintResult[]): Promise<void> {
let omittedRuleCount: number = 0;
const timings: [string, number][] = Array.from(this._eslintTimings).sort(
(x: [string, number], y: [string, number]) => {
Expand All @@ -196,24 +199,23 @@ export class Eslint extends LinterBase<TEslint.ESLint.LintResult> {
}

if (this._fix && this._fixMessagesByResult.size > 0) {
await this._eslintPackage.ESLint.outputFixes(lintFailures);
await this._eslintPackage.ESLint.outputFixes(lintResults);
}

for (const lintFailure of lintFailures) {
for (const lintResult of lintResults) {
// Report linter fixes to the logger. These will only be returned when the underlying failure was fixed
const fixMessages: TEslint.Linter.LintMessage[] | undefined =
this._fixMessagesByResult.get(lintFailure);
const fixMessages: TEslint.Linter.LintMessage[] | undefined = this._fixMessagesByResult.get(lintResult);
if (fixMessages) {
for (const fixMessage of fixMessages) {
const formattedMessage: string = `[FIXED] ${getFormattedErrorMessage(fixMessage)}`;
const errorObject: FileError = this._getLintFileError(lintFailure, fixMessage, formattedMessage);
const errorObject: FileError = this._getLintFileError(lintResult, fixMessage, formattedMessage);
this._scopedLogger.emitWarning(errorObject);
}
}

// Report linter errors and warnings to the logger
for (const lintMessage of lintFailure.messages) {
const errorObject: FileError = this._getLintFileError(lintFailure, lintMessage);
for (const lintMessage of lintResult.messages) {
const errorObject: FileError = this._getLintFileError(lintResult, lintMessage);
switch (lintMessage.severity) {
case EslintMessageSeverity.error: {
this._scopedLogger.emitError(errorObject);
Expand All @@ -227,6 +229,24 @@ export class Eslint extends LinterBase<TEslint.ESLint.LintResult> {
}
}
}

const sarifLogPath: string | undefined = this._sarifLogPath;
if (sarifLogPath) {
const rulesMeta: TEslint.ESLint.LintResultData['rulesMeta'] =
this._linter.getRulesMetaForResults(lintResults);
const { formatEslintResultsAsSARIF } = await import('./SarifFormatter');
const sarifString: string = JSON.stringify(
formatEslintResultsAsSARIF(lintResults, rulesMeta, {
ignoreSuppressed: false,
atingmicrosoft marked this conversation as resolved.
Show resolved Hide resolved
eslintVersion: this._eslintPackage.ESLint.version,
buildFolderPath: this._buildFolderPath
}),
undefined,
2
);

await FileSystem.writeFileAsync(sarifLogPath, sarifString, { ensureFolderExists: true });
}
}

protected async isFileExcludedAsync(filePath: string): Promise<boolean> {
Expand Down
12 changes: 11 additions & 1 deletion heft-plugins/heft-lint-plugin/src/LintPlugin.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
// See LICENSE in the project root for license information.

import path from 'node:path';

import { FileSystem } from '@rushstack/node-core-library';
import type {
HeftConfiguration,
Expand Down Expand Up @@ -28,13 +30,15 @@ const ESLINTRC_CJS_FILENAME: string = '.eslintrc.cjs';

interface ILintPluginOptions {
alwaysFix?: boolean;
sarifLogPath?: string;
}

interface ILintOptions {
taskSession: IHeftTaskSession;
heftConfiguration: HeftConfiguration;
tsProgram: IExtendedProgram;
fix?: boolean;
sarifLogPath?: string;
changedFiles?: ReadonlySet<IExtendedSourceFile>;
}

Expand Down Expand Up @@ -67,6 +71,10 @@ export default class LintPlugin implements IHeftTaskPlugin<ILintPluginOptions> {
fix = false;
}

const relativeSarifLogPath: string | undefined = pluginOptions?.sarifLogPath;
const sarifLogPath: string | undefined =
relativeSarifLogPath && path.resolve(heftConfiguration.buildFolderPath, relativeSarifLogPath);
atingmicrosoft marked this conversation as resolved.
Show resolved Hide resolved

// Use the changed files hook to kick off linting asynchronously
taskSession.requestAccessToPluginByName(
'@rushstack/heft-typescript-plugin',
Expand All @@ -80,6 +88,7 @@ export default class LintPlugin implements IHeftTaskPlugin<ILintPluginOptions> {
taskSession,
heftConfiguration,
fix,
sarifLogPath,
tsProgram: changedFilesHookOptions.program as IExtendedProgram,
changedFiles: changedFilesHookOptions.changedFiles as ReadonlySet<IExtendedSourceFile>
});
Expand Down Expand Up @@ -144,7 +153,7 @@ export default class LintPlugin implements IHeftTaskPlugin<ILintPluginOptions> {
}

private async _lintAsync(options: ILintOptions): Promise<void> {
const { taskSession, heftConfiguration, tsProgram, changedFiles, fix } = options;
const { taskSession, heftConfiguration, tsProgram, changedFiles, fix, sarifLogPath } = options;

// Ensure that we have initialized. This promise is cached, so calling init
// multiple times will only init once.
Expand All @@ -155,6 +164,7 @@ export default class LintPlugin implements IHeftTaskPlugin<ILintPluginOptions> {
const eslintLinter: Eslint = await Eslint.initializeAsync({
tsProgram,
fix,
sarifLogPath,
scopedLogger: taskSession.logger,
linterToolPath: this._eslintToolPath,
linterConfigFilePath: this._eslintConfigFilePath,
Expand Down
Loading
Loading