diff --git a/linters/biome/biome.test.ts b/linters/biome/biome.test.ts index d0b29b37b..f0a64cbf3 100644 --- a/linters/biome/biome.test.ts +++ b/linters/biome/biome.test.ts @@ -1,5 +1,49 @@ -import { linterCheckTest, linterFmtTest } from "tests"; +import { linterCheckTest, linterFmtTest, TestCallback } from "tests"; linterCheckTest({ linterName: "biome", namedTestPrefixes: ["basic_check"] }); linterFmtTest({ linterName: "biome", namedTestPrefixes: ["basic_fmt", "basic_json"] }); + +// Verify warning-level findings ('!' marker) are captured by parse_regex, +// not just errors ('×'). Regression coverage for trunk-io/plugins#1113. +const enableWarnRule: TestCallback = (driver) => { + driver.writeFile( + "biome.json", + JSON.stringify({ + $schema: "https://biomejs.dev/schemas/2.3.4/schema.json", + linter: { + enabled: true, + rules: { + recommended: false, + style: { noNonNullAssertion: "warn" }, + }, + }, + }), + ); +}; +linterCheckTest({ + linterName: "biome", + namedTestPrefixes: ["warning_check"], + preCheck: enableWarnRule, +}); + +// Verify .gitignore reaches the sandbox so vcs.useIgnoreFile works. Without +// the symlinks: directive in plugin.yaml, biome aborts with internalError/fs +// when it can't find the ignore file and silently drops all findings. +// Regression coverage for trunk-io/plugins#1113 issue 2. +const setupGitignoreRepo: TestCallback = (driver) => { + driver.writeFile( + "biome.json", + JSON.stringify({ + $schema: "https://biomejs.dev/schemas/2.3.4/schema.json", + vcs: { clientKind: "git", enabled: true, useIgnoreFile: true }, + linter: { enabled: true, rules: { recommended: true } }, + }), + ); + driver.writeFile(".gitignore", ""); +}; +linterCheckTest({ + linterName: "biome", + namedTestPrefixes: ["gitignore_check"], + preCheck: setupGitignoreRepo, +}); diff --git a/linters/biome/plugin.yaml b/linters/biome/plugin.yaml index 85acf4a99..499279dab 100644 --- a/linters/biome/plugin.yaml +++ b/linters/biome/plugin.yaml @@ -22,9 +22,15 @@ lint: commands: - name: lint output: regex + # Match both errors (×) and warnings (!). The marker line must be + # the immediate next line (after a blank line) following the header, + # so each diagnostic is parsed as a self-contained block — the prior + # regex lazily matched across diagnostics and silently mis-attributed + # path/line/col to a different finding's message. Info-level (i) is + # intentionally excluded; biome surfaces those for hints, not gating. parse_regex: - ' *(?P.*?):(?P\d+):(?P\d+) (?P[^ ]+)(?:[^×]*\n).*× - (?P.*)\n' + ' *(?P[^\n]*?):(?P\d+):(?P\d+) (?P[^ \n]+)[^\n]*\n[ \t]*\n[ + \t]+[×!] (?P[^\n]+)' run: biome check ${target} success_codes: [0, 1] batch: true @@ -51,3 +57,10 @@ lint: version_command: parse_regex: biome CLI version ${semver} run: biome --version + symlinks: + # biome's vcs.useIgnoreFile reads .gitignore from the sandbox root. + # Without this, the per-target sandbox lacks .gitignore and biome + # aborts with internalError/fs, silently dropping all findings. + # See trunk-io/plugins#1113. + - from: ${workspace}/.gitignore + to: .gitignore diff --git a/linters/biome/test_data/biome_v2.3.4_basic_check.check.shot b/linters/biome/test_data/biome_v2.3.4_basic_check.check.shot index 85dbb77b7..6b6d8bbd8 100644 --- a/linters/biome/test_data/biome_v2.3.4_basic_check.check.shot +++ b/linters/biome/test_data/biome_v2.3.4_basic_check.check.shot @@ -4,14 +4,14 @@ exports[`Testing linter biome test basic_check 1`] = ` { "issues": [ { - "code": "lint/complexity/noUselessLoneBlockStatements", - "column": "3", + "code": "lint/correctness/noUnusedVariables", + "column": "7", "file": "test_data/basic_check.in.ts", "issueClass": "ISSUE_CLASS_EXISTING", "level": "LEVEL_HIGH", - "line": "13", + "line": "6", "linter": "biome", - "message": "Formatter would have printed the following content:", + "message": "This variable foo is unused.", "targetType": "typescript", }, ], diff --git a/linters/biome/test_data/biome_v2.3.4_gitignore_check.check.shot b/linters/biome/test_data/biome_v2.3.4_gitignore_check.check.shot new file mode 100644 index 000000000..f782d8737 --- /dev/null +++ b/linters/biome/test_data/biome_v2.3.4_gitignore_check.check.shot @@ -0,0 +1,51 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Testing linter biome test gitignore_check 1`] = ` +{ + "issues": [ + { + "code": "lint/correctness/noUnusedVariables", + "column": "7", + "file": "test_data/gitignore_check.in.ts", + "issueClass": "ISSUE_CLASS_EXISTING", + "level": "LEVEL_HIGH", + "line": "1", + "linter": "biome", + "message": "This variable unusedHelper is unused.", + "targetType": "typescript", + }, + ], + "lintActions": [ + { + "command": "fmt", + "fileGroupName": "typescript", + "linter": "biome", + "paths": [ + "test_data/gitignore_check.in.ts", + ], + "verb": "TRUNK_VERB_FMT", + }, + { + "command": "lint", + "fileGroupName": "typescript", + "linter": "biome", + "paths": [ + "test_data/gitignore_check.in.ts", + ], + "verb": "TRUNK_VERB_CHECK", + }, + { + "command": "lint", + "fileGroupName": "typescript", + "linter": "biome", + "paths": [ + "test_data/gitignore_check.in.ts", + ], + "upstream": true, + "verb": "TRUNK_VERB_CHECK", + }, + ], + "taskFailures": [], + "unformattedFiles": [], +} +`; diff --git a/linters/biome/test_data/biome_v2.3.4_warning_check.check.shot b/linters/biome/test_data/biome_v2.3.4_warning_check.check.shot new file mode 100644 index 000000000..f153f5c2a --- /dev/null +++ b/linters/biome/test_data/biome_v2.3.4_warning_check.check.shot @@ -0,0 +1,51 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Testing linter biome test warning_check 1`] = ` +{ + "issues": [ + { + "code": "lint/style/noNonNullAssertion", + "column": "16", + "file": "test_data/warning_check.in.ts", + "issueClass": "ISSUE_CLASS_EXISTING", + "level": "LEVEL_HIGH", + "line": "5", + "linter": "biome", + "message": "Forbidden non-null assertion.", + "targetType": "typescript", + }, + ], + "lintActions": [ + { + "command": "fmt", + "fileGroupName": "typescript", + "linter": "biome", + "paths": [ + "test_data/warning_check.in.ts", + ], + "verb": "TRUNK_VERB_FMT", + }, + { + "command": "lint", + "fileGroupName": "typescript", + "linter": "biome", + "paths": [ + "test_data/warning_check.in.ts", + ], + "verb": "TRUNK_VERB_CHECK", + }, + { + "command": "lint", + "fileGroupName": "typescript", + "linter": "biome", + "paths": [ + "test_data/warning_check.in.ts", + ], + "upstream": true, + "verb": "TRUNK_VERB_CHECK", + }, + ], + "taskFailures": [], + "unformattedFiles": [], +} +`; diff --git a/linters/biome/test_data/gitignore_check.in.ts b/linters/biome/test_data/gitignore_check.in.ts new file mode 100644 index 000000000..c94d07de7 --- /dev/null +++ b/linters/biome/test_data/gitignore_check.in.ts @@ -0,0 +1,2 @@ +const unusedHelper = (input: number) => input + 1; +export const sample = 42; diff --git a/linters/biome/test_data/warning_check.in.ts b/linters/biome/test_data/warning_check.in.ts new file mode 100644 index 000000000..b4b171bd2 --- /dev/null +++ b/linters/biome/test_data/warning_check.in.ts @@ -0,0 +1,6 @@ +// Triggers biome's lint/style/noNonNullAssertion at warning level when the +// rule is enabled with `level: "warn"`. Verifies that the lint parse_regex +// captures '!' (warning marker) findings, not just '×' (error marker). +const value: number | null = null; +const result = value!.toFixed(); +console.log(result);