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 + `${Node.tag}>` + 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();