function getBlockRangeMap(text, node, comments) {
	/*
	 * The parser sets the fenced code block's start offset to wherever content
	 * should normally begin (typically the first column of the line, but more
	 * inside a list item, for example). The code block's opening fence may be
	 * further indented by up to three characters. If the code block has
	 * additional indenting, the opening fence's first backtick may be up to
	 * three whitespace characters after the start offset.
	 */
	const startOffset = getBeginningOfLineOffset(node);

	/*
	 * Extract the Markdown source to determine the leading whitespace for each
	 * line.
	 */
	const code = text.slice(startOffset, node.position.end.offset);
	const lines = code.split("\n");

	/*
	 * The parser trims leading whitespace from each line of code within the
	 * fenced code block up to the opening fence's first backtick. The first
	 * backtick's column is the AST node's starting column plus any additional
	 * indentation.
	 */
	const baseIndent = getIndentText(text, node).length;

	/*
	 * Track the length of any inserted configuration comments at the beginning
	 * of the linted JS and start the JS offset lookup keys at this index.
	 */
	const commentLength = comments.reduce(
		(len, comment) => len + comment.length + 1,
		0,
	);

	/*
	 * In case there are configuration comments, initialize the map so that the
	 * first lookup index is always 0. If there are no configuration comments,
	 * the lookup index will also be 0, and the lookup should always go to the
	 * last range that matches, skipping this initialization entry.
	 */
	const rangeMap = [
		{
			indent: baseIndent,
			js: 0,
			md: 0,
		},
	];

	// Start the JS offset after any configuration comments.
	let jsOffset = commentLength;

	/*
	 * Start the Markdown offset at the beginning of the block's first line of
	 * actual code. The first line of the block is always the opening fence, so
	 * the code begins on the second line.
	 */
	let mdOffset = startOffset + lines[0].length + 1;

	/*
	 * For each line, determine how much leading whitespace was trimmed due to
	 * indentation. Increase the JS lookup offset by the length of the line
	 * post-trimming and the Markdown offset by the total line length.
	 */
	for (let i = 0; i + 1 < lines.length; i++) {
		const line = lines[i + 1];
		const leadingWhitespaceLength =
			leadingWhitespaceRegex.exec(line)[0].length;

		// The parser trims leading whitespace up to the level of the opening
		// fence, so keep any additional indentation beyond that.
		const trimLength = Math.min(baseIndent, leadingWhitespaceLength);

		rangeMap.push({
			indent: trimLength,
			js: jsOffset,

			// Advance `trimLength` character from the beginning of the Markdown
			// line to the beginning of the equivalent JS line, then compute the
			// delta.
			md: mdOffset + trimLength - jsOffset,
		});

		// Accumulate the current line in the offsets, and don't forget the
		// newline.
		mdOffset += line.length + 1;
		jsOffset += line.length - trimLength + 1;
	}

	return rangeMap;
}
When applying fixes, the postprocess step needs to know how to map fix ranges
from their location in the linted JS to the original offset in the Markdown.
Configuration comments and indentation trimming both complicate this process.

Configuration comments appear in the linted JS but not in the Markdown code
block. Fixes to configuration comments would cause undefined behavior and
should be ignored during postprocessing. Fixes to actual code after
configuration comments need to be mapped back to the code block after
removing any offset due to configuration comments.

Fenced code blocks can be indented by up to three spaces at the opening
fence. Inside of a list, for example, this indent can be in addition to the
indent already required for list item children. Leading whitespace inside
indented code blocks is trimmed up to the level of the opening fence and does
not appear in the linted code. Further, lines can have less leading
whitespace than the opening fence, so not all lines are guaranteed to have
the same column offset as the opening fence.

The source code of a non-configuration-comment line in the linted JS is a
suffix of the corresponding line in the Markdown code block. There are no
differences within the line, so the mapping need only provide the offset
delta at the beginning of each line.
@param {string} text The text of the file.
@param {Node} node A Markdown code block AST node.
@param {string[]} comments List of configuration comment strings that will be
    inserted at the beginning of the code block.
@returns {RangeMap[]} A list of offset-based adjustments, where lookups are
    done based on the `js` key, which represents the range in the linted JS,
    and the `md` key is the offset delta that, when added to the JS range,
    returns the corresponding location in the original Markdown source.