Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix multibyte character handling #906

Merged
merged 12 commits into from
Dec 15, 2023
5 changes: 5 additions & 0 deletions .changeset/dull-bees-grab.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@astrojs/compiler': patch
---

Fixed an `index out of range` error when multibyte characters were rendered as markup
3 changes: 2 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,6 @@
"go.toolsEnvVars": {
"GOOS": "js",
"GOARCH": "wasm"
}
},
"editor.unusualLineTerminators": "off"
}
26 changes: 26 additions & 0 deletions internal/printer/printer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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: `<style>a { font-size: 16px; }</style><a class="test">ツ</a>`,
want: want{
code: `${$$maybeRenderHead($$result)}<a class="test astro-7vm74pjk">ツ</a>`,
},
},
{
name: "multibyte characters",
source: `---
---
<h1>こんにちは</h1>`,
want: want{
code: `${$$maybeRenderHead($$result)}<h1>こんにちは</h1>`,
},
},

{
name: "multibyte character + script",
source: `<script>console.log('foo')</script><a class="test">ツ</a>`,
want: want{
code: `${$$maybeRenderHead($$result)}<a class="test">ツ</a>`,
metadata: metadata{hoisted: []string{fmt.Sprintf(`{ type: 'inline', value: %sconsole.log('foo')%s }`, BACKTICK, BACKTICK)}},
},
},

{
name: "transition:name with an expression",
source: `<div transition:name={one + '-' + 'two'}></div>`,
Expand Down
15 changes: 8 additions & 7 deletions internal/sourcemap/sourcemap.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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)
Expand Down
53 changes: 53 additions & 0 deletions packages/compiler/test/tsx-sourcemaps/multibyte.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { test } from 'uvu';
import * as assert from 'uvu/assert';
import { testTSXSourcemap } from '../utils';

test('multibyte content', async () => {
const input = `<h1>ツ</h1>`;

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 = `<h1>ツ</h1><p>foobar</p>`;

const output = await testTSXSourcemap(input, 'foobar');
assert.equal(output, {
source: 'index.astro',
line: 1,
column: 13,
name: null,
});
});

test('many characters', async () => {
const input = `<h1>こんにちは</h1>`;

const output = await testTSXSourcemap(input, 'ん');
assert.equal(output, {
source: 'index.astro',
line: 1,
column: 5,
name: null,
});
});

test('many characters', async () => {
const input = `<h1>こんにちは</h1>`;

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

test.run();
Loading