Skip to content

Commit

Permalink
Fix markdown charset as utf-8 by default (#8795)
Browse files Browse the repository at this point in the history
  • Loading branch information
bluwy authored Oct 11, 2023
1 parent a964a14 commit f999365
Show file tree
Hide file tree
Showing 10 changed files with 115 additions and 38 deletions.
5 changes: 5 additions & 0 deletions .changeset/olive-laws-work.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'astro': patch
---

Fix markdown page charset to be utf-8 by default (same as Astro 2)
5 changes: 5 additions & 0 deletions packages/astro/src/runtime/server/render/page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,11 @@ export async function renderPage(
body = encoder.encode(body);
headers.set('Content-Length', body.byteLength.toString());
}
// TODO: Revisit if user should manually set charset by themselves in Astro 4
// This code preserves the existing behaviour for markdown pages since Astro 2
if (route?.component.endsWith('.md')) {
headers.set('Content-Type', 'text/html; charset=utf-8');
}
const response = new Response(body, { ...init, headers });
return response;
}
118 changes: 87 additions & 31 deletions packages/astro/test/astro-basic.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,31 @@ import * as cheerio from 'cheerio';
import { loadFixture } from './test-utils.js';

describe('Astro basics', () => {
/** @type {import('./test-utils').Fixture} */
let fixture;
let previewServer;

before(async () => {
fixture = await loadFixture({
root: './fixtures/astro-basic/',
});
await fixture.build();
previewServer = await fixture.preview();
});

// important: close preview server (free up port and connection)
after(async () => {
await previewServer.stop();
});

describe('build', () => {
let previewServer;

before(async () => {
fixture = await loadFixture({
root: './fixtures/astro-basic/',
});
await fixture.build();
previewServer = await fixture.preview();
});

// important: close preview server (free up port and connection)
after(async () => {
await previewServer.stop();
});

it('Can load page', async () => {
const html = await fixture.readFile(`/index.html`);
const $ = cheerio.load(html);
Expand Down Expand Up @@ -108,39 +116,87 @@ describe('Astro basics', () => {
const $ = cheerio.load(html);
expect($('#rendered-order').text()).to.eq('Rendered order: A, B');
});
});

it('Supports void elements whose name is a string (#2062)', async () => {
const html = await fixture.readFile('/input/index.html');
const $ = cheerio.load(html);
it('renders markdown in utf-8 by default', async () => {
const html = await fixture.readFile('/chinese-encoding-md/index.html');
const $ = cheerio.load(html);
expect($('h1').text()).to.equal('我的第一篇博客文章');
});

it('renders MDX in utf-8 by default', async () => {
const html = await fixture.readFile('/chinese-encoding-mdx/index.html');
const $ = cheerio.load(html);
expect($('h1').text()).to.equal('我的第一篇博客文章');
});

it('Supports void elements whose name is a string (#2062)', async () => {
const html = await fixture.readFile('/input/index.html');
const $ = cheerio.load(html);

// <Input />
expect($('body > :nth-child(1)').prop('outerHTML')).to.equal('<input>');

// <Input />
expect($('body > :nth-child(1)').prop('outerHTML')).to.equal('<input>');
// <Input type="password" />
expect($('body > :nth-child(2)').prop('outerHTML')).to.equal('<input type="password">');

// <Input type="password" />
expect($('body > :nth-child(2)').prop('outerHTML')).to.equal('<input type="password">');
// <Input type="text" />
expect($('body > :nth-child(3)').prop('outerHTML')).to.equal('<input type="text">');

// <Input type="text" />
expect($('body > :nth-child(3)').prop('outerHTML')).to.equal('<input type="text">');
// <Input type="select"><option>option</option></Input>
expect($('body > :nth-child(4)').prop('outerHTML')).to.equal(
'<select><option>option</option></select>'
);

// <Input type="select"><option>option</option></Input>
expect($('body > :nth-child(4)').prop('outerHTML')).to.equal(
'<select><option>option</option></select>'
);
// <Input type="textarea">textarea</Input>
expect($('body > :nth-child(5)').prop('outerHTML')).to.equal('<textarea>textarea</textarea>');
});

describe('preview', () => {
it('returns 200 for valid URLs', async () => {
const result = await fixture.fetch('/');
expect(result.status).to.equal(200);
});

// <Input type="textarea">textarea</Input>
expect($('body > :nth-child(5)').prop('outerHTML')).to.equal('<textarea>textarea</textarea>');
it('returns 404 for invalid URLs', async () => {
const result = await fixture.fetch('/bad-url');
expect(result.status).to.equal(404);
});
});
});

describe('preview', () => {
it('returns 200 for valid URLs', async () => {
const result = await fixture.fetch('/');
expect(result.status).to.equal(200);
describe('development', () => {
/** @type {import('./test-utils').DevServer} */
let devServer;

before(async () => {
devServer = await fixture.startDevServer();
});
after(async () => {
await devServer.stop();
});

it('Renders markdown in utf-8 by default', async () => {
const res = await fixture.fetch('/chinese-encoding-md');
expect(res.status).to.equal(200);
const html = await res.text();
const $ = cheerio.load(html);
expect($('h1').text()).to.equal('我的第一篇博客文章');
const isUtf8 =
res.headers.get('content-type').includes('charset=utf-8') ||
html.includes('<meta charset="utf-8">');
expect(isUtf8).to.be.true;
});

it('returns 404 for invalid URLs', async () => {
const result = await fixture.fetch('/bad-url');
expect(result.status).to.equal(404);
it('Renders MDX in utf-8 by default', async () => {
const res = await fixture.fetch('/chinese-encoding-mdx');
expect(res.status).to.equal(200);
const html = await res.text();
const $ = cheerio.load(html);
expect($('h1').text()).to.equal('我的第一篇博客文章');
const isUtf8 =
res.headers.get('content-type').includes('charset=utf-8') ||
html.includes('<meta charset="utf-8">');
expect(isUtf8).to.be.true;
});
});
});
3 changes: 2 additions & 1 deletion packages/astro/test/fixtures/astro-basic/astro.config.mjs
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { defineConfig } from 'astro/config';
import preact from '@astrojs/preact';
import mdx from '@astrojs/mdx';

// https://astro.build/config
export default defineConfig({
integrations: [preact()],
integrations: [preact(), mdx()],
// make sure CLI flags have precedence
server: () => ({ port: 4321 })
});
1 change: 1 addition & 0 deletions packages/astro/test/fixtures/astro-basic/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
"version": "0.0.0",
"private": true,
"dependencies": {
"@astrojs/mdx": "workspace:*",
"@astrojs/preact": "workspace:*",
"astro": "workspace:*",
"preact": "^10.17.1"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# 我的第一篇博客文章

发表于:2022-07-01
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# 我的第一篇博客文章

发表于:2022-07-01
6 changes: 3 additions & 3 deletions packages/astro/test/units/render/head.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ describe('core/render', () => {

const PageModule = createAstroModule(Page);
const ctx = await createRenderContext({
route: { type: 'page', pathname: '/index' },
route: { type: 'page', pathname: '/index', component: 'src/pages/index.astro' },
request: new Request('http://example.com/'),
links: [{ name: 'link', props: { rel: 'stylesheet', href: '/main.css' }, children: '' }],
mod: PageModule,
Expand Down Expand Up @@ -171,7 +171,7 @@ describe('core/render', () => {

const PageModule = createAstroModule(Page);
const ctx = await createRenderContext({
route: { type: 'page', pathname: '/index' },
route: { type: 'page', pathname: '/index', component: 'src/pages/index.astro' },
request: new Request('http://example.com/'),
links: [{ name: 'link', props: { rel: 'stylesheet', href: '/main.css' }, children: '' }],
env,
Expand Down Expand Up @@ -218,7 +218,7 @@ describe('core/render', () => {

const PageModule = createAstroModule(Page);
const ctx = await createRenderContext({
route: { type: 'page', pathname: '/index' },
route: { type: 'page', pathname: '/index', component: 'src/pages/index.astro' },
request: new Request('http://example.com/'),
links: [{ name: 'link', props: { rel: 'stylesheet', href: '/main.css' }, children: '' }],
env,
Expand Down
6 changes: 3 additions & 3 deletions packages/astro/test/units/render/jsx.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ describe('core/render', () => {

const mod = createAstroModule(Page);
const ctx = await createRenderContext({
route: { type: 'page', pathname: '/index' },
route: { type: 'page', pathname: '/index', component: 'src/pages/index.mdx' },
request: new Request('http://example.com/'),
env,
mod,
Expand Down Expand Up @@ -91,7 +91,7 @@ describe('core/render', () => {

const mod = createAstroModule(Page);
const ctx = await createRenderContext({
route: { type: 'page', pathname: '/index' },
route: { type: 'page', pathname: '/index', component: 'src/pages/index.mdx' },
request: new Request('http://example.com/'),
env,
mod,
Expand All @@ -117,7 +117,7 @@ describe('core/render', () => {

const mod = createAstroModule(Page);
const ctx = await createRenderContext({
route: { type: 'page', pathname: '/index' },
route: { type: 'page', pathname: '/index', component: 'src/pages/index.mdx' },
request: new Request('http://example.com/'),
env,
mod,
Expand Down
3 changes: 3 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit f999365

Please sign in to comment.