Skip to content

Commit 13a7b75

Browse files
aspiersAdam Spiers
authored andcommitted
feat(rules): make body-max-line-length ignore lines with URLs
Whilst the `body-max-line-length` is almost always desirable, there is one fairly frequent scenario in which it gets in the way: when lines contain URLs exceeding the maximum line length. In this case, the URL cannot be split across lines without breaking it, and minifying it results in obfuscation and a dependency on a third-party service, both of which are undesirable. So make an exception in this rule for any line which contains a URL. Allow other content on the same line, in order to support use of various rich-text formats such as Markdown, which are often used in order to render links in more elegant ways.
1 parent f959a32 commit 13a7b75

File tree

3 files changed

+56
-12
lines changed

3 files changed

+56
-12
lines changed
Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,13 @@
11
import ensure from "./max-length.js";
22

3+
// Allow an exception for long lines which contain URLs.
4+
//
5+
// This is overly lenient, in order to avoid costly regexps which
6+
// have to worry about all the many edge cases of valid URLs.
7+
const URL_REGEX = /\bhttps?:\/\/\S+/;
8+
39
export default (value: string, max: number): boolean =>
410
typeof value === "string" &&
5-
value.split(/\r?\n/).every((line) => ensure(line, max));
11+
value
12+
.split(/\r?\n/)
13+
.every((line) => URL_REGEX.test(line) || ensure(line, max));

@commitlint/rules/src/body-max-line-length.test.ts

Lines changed: 45 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
import { test, expect } from "vitest";
22
import parse from "@commitlint/parse";
3+
import type { Commit } from "conventional-commits-parser";
34
import { bodyMaxLineLength } from "./body-max-line-length.js";
45

56
const short = "a";
67
const long = "ab";
8+
const url = "https://example.com/URL/with/a/very/long/path";
79

810
const value = short.length;
911

@@ -13,16 +15,30 @@ const messages = {
1315
long: `test: subject\n${long}`,
1416
shortMultipleLines: `test:subject\n${short}\n${short}\n${short}`,
1517
longMultipleLines: `test:subject\n${short}\n${long}\n${short}`,
16-
};
18+
urlStandalone: `test:subject\n${short}\n${url}\n${short}`,
19+
urlMarkdownLinkInline: `test:subject
20+
21+
This is a [link](${url}).`,
22+
urlMarkdownLinkInList: `test:subject
23+
24+
Link in a list:
25+
26+
- ${url}`,
27+
urlMarkdownLinkInFooter: `test:subject
28+
29+
Finally, [link][] via footer.
1730
18-
const parsed = {
19-
empty: parse(messages.empty),
20-
short: parse(messages.short),
21-
long: parse(messages.long),
22-
shortMultipleLines: parse(messages.shortMultipleLines),
23-
longMultipleLines: parse(messages.longMultipleLines),
31+
[link]: ${url}`,
2432
};
2533

34+
const parsed = Object.entries(messages).reduce(
35+
(_parsed, [key, message]) =>
36+
Object.assign(_parsed, {
37+
[key]: parse(message),
38+
}),
39+
{} as Record<keyof typeof messages, Promise<Commit>>,
40+
);
41+
2642
test("with empty should succeed", async () => {
2743
const [actual] = bodyMaxLineLength(await parsed.empty, undefined, value);
2844
const expected = true;
@@ -45,7 +61,7 @@ test("with short with multiple lines should succeed", async () => {
4561
const [actual] = bodyMaxLineLength(
4662
await parsed.shortMultipleLines,
4763
undefined,
48-
value
64+
value,
4965
);
5066
const expected = true;
5167
expect(actual).toEqual(expected);
@@ -55,8 +71,28 @@ test("with long with multiple lines should fail", async () => {
5571
const [actual] = bodyMaxLineLength(
5672
await parsed.longMultipleLines,
5773
undefined,
58-
value
74+
value,
5975
);
6076
const expected = false;
6177
expect(actual).toEqual(expected);
6278
});
79+
80+
test("with multiple lines and standalone URL should succeed", async () => {
81+
const [actual] = bodyMaxLineLength(
82+
await parsed.urlStandalone,
83+
undefined,
84+
value,
85+
);
86+
const expected = true;
87+
expect(actual).toEqual(expected);
88+
});
89+
90+
test("with multiple lines and URL in inline Markdown link should succeed", async () => {
91+
const [actual] = bodyMaxLineLength(
92+
await parsed.urlMarkdownLinkInline,
93+
undefined,
94+
30,
95+
);
96+
const expected = true;
97+
expect(actual).toEqual(expected);
98+
});

docs/reference/rules.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232

3333
## body-max-line-length
3434

35-
- **condition**: `body` lines has `value` or less characters
35+
- **condition**: `body` lines have `value` or less characters, or contain a URL
3636
- **rule**: `always`
3737
- **value**
3838

@@ -97,7 +97,7 @@
9797

9898
## footer-max-line-length
9999

100-
- **condition**: `footer` lines has `value` or less characters
100+
- **condition**: `footer` lines have `value` or less characters
101101
- **rule**: `always`
102102
- **value**
103103

0 commit comments

Comments
 (0)