diff --git a/.changeset/kind-cheetahs-listen.md b/.changeset/kind-cheetahs-listen.md new file mode 100644 index 000000000..ef23daf5c --- /dev/null +++ b/.changeset/kind-cheetahs-listen.md @@ -0,0 +1,5 @@ +--- +'@astrojs/compiler': patch +--- + +Fixes a sourcemap-related crash when using multibyte characters diff --git a/internal/sourcemap/sourcemap.go b/internal/sourcemap/sourcemap.go index 3d2feff58..463fae594 100644 --- a/internal/sourcemap/sourcemap.go +++ b/internal/sourcemap/sourcemap.go @@ -616,7 +616,7 @@ func (b *ChunkBuilder) GetLineAndColumnForLocation(location loc.Loc) []int { for count > 0 { step := count / 2 i := originalLine + step - if lineOffsetTables[i].byteOffsetToStartOfLine <= location.Start { + if len(lineOffsetTables) > i && lineOffsetTables[i].byteOffsetToStartOfLine <= location.Start { originalLine = i + 1 count = count - step - 1 } else { @@ -629,7 +629,10 @@ func (b *ChunkBuilder) GetLineAndColumnForLocation(location loc.Loc) []int { line := &lineOffsetTables[originalLine] originalColumn := int(location.Start - line.byteOffsetToStartOfLine) if line.columnsForNonASCII != nil && originalColumn >= int(line.byteOffsetToFirstNonASCII) { - originalColumn = int(line.columnsForNonASCII[originalColumn-int(line.byteOffsetToFirstNonASCII)]) + newColumn := originalColumn - int(line.byteOffsetToFirstNonASCII) + if len(line.columnsForNonASCII) > newColumn { + originalColumn = int(line.columnsForNonASCII[newColumn]) + } } // 1-based line, 1-based column diff --git a/packages/compiler/test/parse/multibyte-characters.ts b/packages/compiler/test/parse/multibyte-characters.ts new file mode 100644 index 000000000..4cd02ba02 --- /dev/null +++ b/packages/compiler/test/parse/multibyte-characters.ts @@ -0,0 +1,21 @@ +import { test } from 'uvu'; +import * as assert from 'uvu/assert'; +import { parse } from '@astrojs/compiler'; + +const FIXTURE = `{foo},`; + +test('does not crash', async () => { + const result = await parse(FIXTURE); + assert.ok(result.ast, 'does not crash'); +}); + +test('properly maps the position', async () => { + const { + ast: { children }, + } = await parse(FIXTURE); + const text = children[1]; + assert.equal(text.position.start.offset, 5, 'properly maps the text start position'); + assert.equal(text.position.end.offset, 8, 'properly maps the text end position'); +}); + +test.run();