diff --git a/.changeset/dull-bees-grab.md b/.changeset/dull-bees-grab.md new file mode 100644 index 000000000..d35f80931 --- /dev/null +++ b/.changeset/dull-bees-grab.md @@ -0,0 +1,5 @@ +--- +'@astrojs/compiler': patch +--- + +Fixed an `index out of range` error when multibyte characters were rendered as markup diff --git a/.vscode/settings.json b/.vscode/settings.json index d9ffe7d80..84d6f0b1d 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -2,5 +2,6 @@ "go.toolsEnvVars": { "GOOS": "js", "GOARCH": "wasm" - } + }, + "editor.unusualLineTerminators": "off" } diff --git a/internal/printer/printer_test.go b/internal/printer/printer_test.go index a4294316e..68eab698f 100644 --- a/internal/printer/printer_test.go +++ b/internal/printer/printer_test.go @@ -2872,6 +2872,32 @@ const items = ["Dog", "Cat", "Platipus"]; code: `${($$render` + BACKTICK + `${$$renderComponent($$result,'Fragment',Fragment,{},{"default": () => $$render` + BACKTICK + `${$$renderComponent($$result,'Fragment',Fragment,{},{"default": () => $$render` + BACKTICK + `${$$unescapeHTML(` + BACKTICK + `<${Node.tag} ${stringifyAttributes(Node.attributes)}>` + BACKTICK + `)}` + BACKTICK + `,})}${Node.children.map((child) => ($$render` + BACKTICK + `${$$renderComponent($$result,'Astro.self',Astro.self,{"node":(child)})}` + BACKTICK + `))}${$$renderComponent($$result,'Fragment',Fragment,{},{"default": () => $$render` + BACKTICK + `${$$unescapeHTML(` + BACKTICK + `` + BACKTICK + `)}` + BACKTICK + `,})}` + BACKTICK + `,})}` + BACKTICK + `)}`, }, }, + { + name: "multibyte character + style", + source: ``, + want: want{ + code: `${$$maybeRenderHead($$result)}`, + }, + }, + { + name: "multibyte characters", + source: `--- +--- +

こんにちは

`, + want: want{ + code: `${$$maybeRenderHead($$result)}

こんにちは

`, + }, + }, + + { + name: "multibyte character + script", + source: ``, + want: want{ + code: `${$$maybeRenderHead($$result)}`, + metadata: metadata{hoisted: []string{fmt.Sprintf(`{ type: 'inline', value: %sconsole.log('foo')%s }`, BACKTICK, BACKTICK)}}, + }, + }, + { name: "transition:name with an expression", source: `
`, diff --git a/internal/sourcemap/sourcemap.go b/internal/sourcemap/sourcemap.go index 988201b18..3d2feff58 100644 --- a/internal/sourcemap/sourcemap.go +++ b/internal/sourcemap/sourcemap.go @@ -73,12 +73,11 @@ var base64 = []byte("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456 // bit. The continuation bit tells us whether there are more digits in this // value following this digit. // -// Continuation -// | Sign -// | | -// V V -// 101011 -// +// Continuation +// | Sign +// | | +// V V +// 101011 func EncodeVLQ(value int) []byte { var vlq int if value < 0 { @@ -675,7 +674,9 @@ func (b *ChunkBuilder) AddSourceMapping(location loc.Loc, output []byte) { 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)]) + if len(line.columnsForNonASCII) > originalColumn-int(line.byteOffsetToFirstNonASCII) { + originalColumn = int(line.columnsForNonASCII[originalColumn-int(line.byteOffsetToFirstNonASCII)]) + } } b.updateGeneratedLineAndColumn(output) diff --git a/packages/compiler/test/tsx-sourcemaps/multibyte.ts b/packages/compiler/test/tsx-sourcemaps/multibyte.ts new file mode 100644 index 000000000..b6001d64b --- /dev/null +++ b/packages/compiler/test/tsx-sourcemaps/multibyte.ts @@ -0,0 +1,53 @@ +import { test } from 'uvu'; +import * as assert from 'uvu/assert'; +import { testTSXSourcemap } from '../utils'; + +test('multibyte content', async () => { + const input = `

`; + + const output = await testTSXSourcemap(input, 'ツ'); + assert.equal(output, { + source: 'index.astro', + line: 1, + column: 4, + name: null, + }); +}); + +test('content after multibyte character', async () => { + const input = `

foobar

`; + + const output = await testTSXSourcemap(input, 'foobar'); + assert.equal(output, { + source: 'index.astro', + line: 1, + column: 13, + name: null, + }); +}); + +test('many characters', async () => { + const input = `

こんにちは

`; + + const output = await testTSXSourcemap(input, 'ん'); + assert.equal(output, { + source: 'index.astro', + line: 1, + column: 5, + name: null, + }); +}); + +test('many characters', async () => { + const input = `

こんにちは

`; + + const output = await testTSXSourcemap(input, 'に'); + assert.equal(output, { + source: 'index.astro', + line: 1, + column: 6, + name: null, + }); +}); + +test.run();