Skip to content

Commit 47c6498

Browse files
committed
feat(lint): allow for custom commit parser function
This change makes it possible to pass a custom function to the top level lint function, which is passed through to the parse function and consequently passed through to the parser exposed by convential-commits-parser. In linting it is important to parse content to be linted with a high level of fidelity so linting can be applied accurately. The default parser the is used, conventional-commits-parser makes many assumptions about the format of the commit to perform its parsing which leads to parsing errors and inaccuracies which in turn lead to many false positives in linting, and incorrect or invalid errors in additional confusing error messages. This change will allow end users utilize a more accurate parser for their linting purposes in the short term while issues with the default parser are reconciled Resolves: #4816
1 parent 9cd6a50 commit 47c6498

3 files changed

Lines changed: 81 additions & 1 deletion

File tree

@commitlint/lint/src/lint.test.ts

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { test, expect } from "vitest";
2+
import type { Parser } from "@commitlint/types";
23
import { RuleConfigSeverity } from "@commitlint/types";
34

45
import lint from "./lint.js";
@@ -308,3 +309,79 @@ test("passes for async rule", async () => {
308309

309310
expect(report.valid).toBe(true);
310311
});
312+
313+
test("uses custom parser when provided", async () => {
314+
const customParser: Parser = (message) => ({
315+
type: "feat",
316+
scope: null,
317+
subject: "my-feature",
318+
body: null,
319+
footer: null,
320+
header: message.trim(),
321+
});
322+
323+
const report = await lint(
324+
"any message",
325+
{
326+
"type-enum": [RuleConfigSeverity.Error, "always", ["feat"]],
327+
},
328+
{ parser: customParser },
329+
);
330+
331+
expect(report.valid).toBe(true);
332+
expect(report.errors.length).toBe(0);
333+
});
334+
335+
test("custom parser output is used by rules", async () => {
336+
const customParser: Parser = (_message) => ({
337+
type: null,
338+
scope: "auth",
339+
subject: "fix login",
340+
body: null,
341+
footer: null,
342+
header: "scope(subject): description",
343+
});
344+
345+
const report = await lint(
346+
"any message",
347+
{
348+
"type-empty": [RuleConfigSeverity.Error, "never"],
349+
},
350+
{ parser: customParser },
351+
);
352+
353+
expect(report.valid).toBe(false);
354+
expect(report.errors.length).toBe(1);
355+
});
356+
357+
test("custom parser works alongside parserOpts", async () => {
358+
const customParser: Parser = (_message) =>
359+
({
360+
type: "chore",
361+
subject: "dep bump",
362+
body: null,
363+
footer: "Refs #42",
364+
header: "chore: dep bump",
365+
references: [
366+
{
367+
raw: "Refs #42",
368+
action: "Refs",
369+
owner: null,
370+
repository: null,
371+
issue: "42",
372+
prefix: "#",
373+
},
374+
],
375+
}) as unknown as ReturnType<Parser>;
376+
377+
const report = await lint(
378+
"any message",
379+
{
380+
"type-enum": [RuleConfigSeverity.Error, "always", ["chore"]],
381+
"references-empty": [RuleConfigSeverity.Error, "never"],
382+
},
383+
{ parser: customParser },
384+
);
385+
386+
expect(report.valid).toBe(true);
387+
});

@commitlint/lint/src/lint.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ export default async function lint(
3737
const parsed =
3838
message === ""
3939
? { header: null, body: null, footer: null }
40-
: await parse(message, undefined, opts.parserOpts);
40+
: await parse(message, opts.parser, opts.parserOpts);
4141

4242
if (parsed.header === null && parsed.body === null && parsed.footer === null) {
4343
// Commit is empty, skip

@commitlint/types/src/lint.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import type { ParserOptions as Options } from "conventional-commits-parser";
22
import { IsIgnoredOptions } from "./is-ignored.js";
33
import { PluginRecords } from "./load.js";
4+
import { Parser } from "./parse.js";
45
import { RuleConfigSeverity, RuleConfigTuple } from "./rules.js";
56

67
export type LintRuleConfig = Record<
@@ -15,6 +16,8 @@ export interface LintOptions {
1516
ignores?: IsIgnoredOptions["ignores"];
1617
/** The parser configuration to use when linting the commit */
1718
parserOpts?: Options;
19+
/** An optional custom parser function for parsing commit messages */
20+
parser?: Parser;
1821

1922
plugins?: PluginRecords;
2023
helpUrl?: string;

0 commit comments

Comments
 (0)