From 8ce5fe0dde8692094e0ac3c92c699c9fcf5d20b9 Mon Sep 17 00:00:00 2001 From: luojiyin Date: Mon, 7 Sep 2026 12:04:08 +0800 Subject: [PATCH] perf(source-map): avoid temporary value reconstruction MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Three changes eliminate unnecessary intermediate string and array allocations during source-map construction: 1. Inline code whitespace check: replace [...interior].some() spread with a charCodeAt loop — avoids allocating an array of characters. 2. Code segments validation: verify each span incrementally against value.slice() instead of rebuilding the full sourceValue string. 3. URL segments validation: track valueMatch boolean instead of concatenating decoded parts into a rebuilt URL string. --- src/source-map/build-source-map.ts | 15 +++++++++++---- src/source-map/code-segments.ts | 22 +++++++++++++++++----- 2 files changed, 28 insertions(+), 9 deletions(-) diff --git a/src/source-map/build-source-map.ts b/src/source-map/build-source-map.ts index 75cb3c0..83fd514 100644 --- a/src/source-map/build-source-map.ts +++ b/src/source-map/build-source-map.ts @@ -192,7 +192,13 @@ function buildInlineCodeSegments( let valueSourceEnd = interior.length; const leadingEnd = leadingWhitespaceEnd(); const trailingStart = trailingWhitespaceStart(); - const hasData = [...interior].some(char => !isWhitespace(char.charCodeAt(0))); + let hasData = false; + for (let i = 0; i < interior.length; i++) { + if (!isWhitespace(interior.charCodeAt(i))) { + hasData = true; + break; + } + } if (leadingEnd > 0 && trailingStart < interior.length && hasData) { valueSourceStart = leadingEnd; valueSourceEnd = trailingStart; @@ -249,8 +255,8 @@ function buildUrlSegments( : undefined; } const segments: MarkdownSourceMapSegment[] = []; - let value = ''; let valueOffset = 0; + let valueMatch = true; const add = (sourceStart: number, sourceEnd: number, output: string, kind: MarkdownSourceMapSegment['kind']) => { segments.push({ valueStart: valueOffset, @@ -259,7 +265,8 @@ function buildUrlSegments( sourceEnd, kind, }); - value += output; + if (valueMatch && !node.url.startsWith(output, valueOffset)) + valueMatch = false; valueOffset += output.length; }; let literalStart = bounds.start; @@ -305,7 +312,7 @@ function buildUrlSegments( offset++; } flushLiteral(bounds.end); - return value === node.url ? { segments } : undefined; + return valueMatch && valueOffset === node.url.length ? { segments } : undefined; } /** diff --git a/src/source-map/code-segments.ts b/src/source-map/code-segments.ts index 74db072..a188d1b 100644 --- a/src/source-map/code-segments.ts +++ b/src/source-map/code-segments.ts @@ -143,6 +143,20 @@ function trimTrailingLineEnding(md: string, spans: SourceSpan[]): void { spans.pop(); } +function equalRange( + left: string, + leftStart: number, + right: string, + rightStart: number, + length: number, +): boolean { + for (let i = 0; i < length; i++) { + if (left.charCodeAt(leftStart + i) !== right.charCodeAt(rightStart + i)) + return false; + } + return true; +} + function segmentsFromSpans( md: string, spans: SourceSpan[], @@ -150,12 +164,12 @@ function segmentsFromSpans( ): MarkdownSourceMapSegment[] | undefined { const segments: MarkdownSourceMapSegment[] = []; let valueOffset = 0; - let sourceValue = ''; for (const span of spans) { if (span.start >= span.end) continue; const length = span.end - span.start; - sourceValue += md.slice(span.start, span.end); + if (!equalRange(md, span.start, value, valueOffset, length)) + return undefined; const previous = segments[segments.length - 1]; if (previous && previous.sourceEnd === span.start && previous.valueEnd === valueOffset) { previous.sourceEnd = span.end; @@ -172,9 +186,7 @@ function segmentsFromSpans( } valueOffset += length; } - return valueOffset === value.length && sourceValue === value - ? segments - : undefined; + return valueOffset === value.length ? segments : undefined; } function buildFencedCodeSegments(