Skip to content

Commit 935b5eb

Browse files
committed
ci(release): leave quoted fences and multi-backtick code unlinked
- Fences inside block quotes count, and end with their quote. - Inline code with longer backtick runs (``like this``) is left alone.
1 parent 145c6e1 commit 935b5eb

1 file changed

Lines changed: 42 additions & 34 deletions

File tree

‎.github/scripts/record-linear-release.cjs‎

Lines changed: 42 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -20,32 +20,41 @@ const MAX_BASE_CANDIDATES = 20;
2020
/**
2121
* Splits markdown into fenced code blocks and the text between them. A fence opens with three or more
2222
* backticks or tildes, and closes with the same character, at least as many, on a line of its own.
23-
* A fence that never closes runs to the end, as in CommonMark.
23+
* Fences inside block quotes count, and end with their quote. A fence that never closes runs to the
24+
* end, as in CommonMark.
2425
* @returns {{ code: boolean, text: string }[]}
2526
*/
2627
function splitFences(text) {
2728
const parts = [];
2829
let buffer = '';
2930
let fence = null;
3031
for (const line of text.split(/(?<=\n)/)) {
31-
if (!fence) {
32-
const open = line.match(/^ {0,3}(`{3,}|~{3,})/);
33-
// A backtick fence's info string can't contain a backtick.
34-
if (open && !(open[1][0] === '`' && line.slice(open[0].length).includes('`'))) {
35-
parts.push({ code: false, text: buffer });
36-
buffer = line;
37-
fence = open[1];
38-
} else {
39-
buffer += line;
40-
}
41-
} else {
32+
const quote = line.match(/^(?: {0,3}> ?)*/)[0];
33+
const depth = (quote.match(/>/g) || []).length;
34+
const rest = line.slice(quote.length);
35+
if (fence && depth < fence.depth) {
36+
parts.push({ code: true, text: buffer });
37+
buffer = '';
38+
fence = null;
39+
}
40+
if (fence) {
4241
buffer += line;
43-
const close = line.match(/^ {0,3}(`{3,}|~{3,})[ \t]*\r?\n?$/);
44-
if (close && close[1][0] === fence[0] && close[1].length >= fence.length) {
42+
const close = rest.match(/^ {0,3}(`{3,}|~{3,})[ \t]*\r?\n?$/);
43+
if (close && close[1][0] === fence.marker[0] && close[1].length >= fence.marker.length) {
4544
parts.push({ code: true, text: buffer });
4645
buffer = '';
4746
fence = null;
4847
}
48+
continue;
49+
}
50+
const open = rest.match(/^ {0,3}(`{3,}|~{3,})/);
51+
// A backtick fence's info string can't contain a backtick.
52+
if (open && !(open[1][0] === '`' && rest.slice(open[0].length).includes('`'))) {
53+
parts.push({ code: false, text: buffer });
54+
buffer = line;
55+
fence = { marker: open[1], depth };
56+
} else {
57+
buffer += line;
4958
}
5059
}
5160
parts.push({ code: fence !== null, text: buffer });
@@ -55,30 +64,29 @@ function splitFences(text) {
5564
/**
5665
* Writes every GitHub reference out as a full link. On GitHub, "#12" is a PR or issue of the same repo;
5766
* Linear matches a bare "#12" against every repo it knows, so it can link a PR from another repo.
58-
* Code (fenced or inline), existing links and URLs are left alone. GitHub redirects /pull/N to
59-
* /issues/N when N is an issue.
67+
* Code (fenced, or inline with any number of backticks), existing links and URLs are left alone.
68+
* GitHub redirects /pull/N to /issues/N when N is an issue.
6069
*/
6170
function linkReferences(text, owner, repo) {
62-
const untouched = /(`[^`\n]*`|\[[^\]\n]*\]\([^)\n]*\)|<[^>\n]*>|https?:\/\/[^\s)]+)/;
63-
const link = (prose) =>
64-
prose
65-
.split(untouched)
66-
.map((part, i) =>
67-
i % 2
68-
? part
69-
: part
70-
.replace(
71-
/(^|[^\w/#&])([\w.-]+)\/([\w.-]+)#(\d+)\b/g,
72-
(_, pre, o, r, n) => `${pre}[${o}/${r}#${n}](https://github.com/${o}/${r}/pull/${n})`,
73-
)
74-
.replace(
75-
/(^|[^\w/#&[]|\bPR ?)#(\d+)\b/g,
76-
(_, pre, n) => `${pre}[#${n}](https://github.com/${owner}/${repo}/pull/${n})`,
77-
),
71+
const untouched = /(?<!`)(`+)(?!`)[\s\S]*?(?<!`)\1(?!`)|\[[^\]\n]*\]\([^)\n]*\)|<[^>\n]*>|https?:\/\/[^\s)]+/g;
72+
const linkPlain = (plain) =>
73+
plain
74+
.replace(
75+
/(^|[^\w/#&])([\w.-]+)\/([\w.-]+)#(\d+)\b/g,
76+
(_, pre, o, r, n) => `${pre}[${o}/${r}#${n}](https://github.com/${o}/${r}/pull/${n})`,
7877
)
79-
.join('');
78+
.replace(/(^|[^\w/#&[]|\bPR ?)#(\d+)\b/g, (_, pre, n) => `${pre}[#${n}](https://github.com/${owner}/${repo}/pull/${n})`);
79+
const linkProse = (prose) => {
80+
let out = '';
81+
let last = 0;
82+
for (const match of prose.matchAll(untouched)) {
83+
out += linkPlain(prose.slice(last, match.index)) + match[0];
84+
last = match.index + match[0].length;
85+
}
86+
return out + linkPlain(prose.slice(last));
87+
};
8088
return splitFences(text)
81-
.map((part) => (part.code ? part.text : link(part.text)))
89+
.map((part) => (part.code ? part.text : linkProse(part.text)))
8290
.join('');
8391
}
8492

0 commit comments

Comments
 (0)