0
Fork 0
mirror of https://github.com/withastro/astro.git synced 2024-12-23 21:53:55 -05:00

Add support for custom non-html route encoding (#4549)

* Add custom encoding support for non-html routes

* Add changeset

* Add tests for png and json routes
This commit is contained in:
Alan 2022-09-09 08:58:24 -07:00 committed by GitHub
parent c706d845eb
commit 255636cc7b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 93 additions and 3 deletions

View file

@ -0,0 +1,5 @@
---
'astro': minor
---
Allow specifying custom encoding when using a non-html route. Only option before was 'utf-8' and now that is just the default.

View file

@ -1085,6 +1085,7 @@ export interface APIContext {
export interface EndpointOutput { export interface EndpointOutput {
body: Body; body: Body;
encoding?: BufferEncoding;
} }
export type APIRoute = ( export type APIRoute = (

View file

@ -391,6 +391,7 @@ async function generatePath(
}; };
let body: string; let body: string;
let encoding: BufferEncoding | undefined;
if (pageData.route.type === 'endpoint') { if (pageData.route.type === 'endpoint') {
const result = await callEndpoint(mod as unknown as EndpointHandler, options); const result = await callEndpoint(mod as unknown as EndpointHandler, options);
@ -398,6 +399,7 @@ async function generatePath(
throw new Error(`Returning a Response from an endpoint is not supported in SSG mode.`); throw new Error(`Returning a Response from an endpoint is not supported in SSG mode.`);
} }
body = result.body; body = result.body;
encoding = result.encoding;
} else { } else {
const response = await render(options); const response = await render(options);
@ -413,5 +415,5 @@ async function generatePath(
const outFile = getOutFile(astroConfig, outFolder, pathname, pageData.route.type); const outFile = getOutFile(astroConfig, outFolder, pathname, pageData.route.type);
pageData.route.distURL = outFile; pageData.route.distURL = outFile;
await fs.promises.mkdir(outFolder, { recursive: true }); await fs.promises.mkdir(outFolder, { recursive: true });
await fs.promises.writeFile(outFile, body, 'utf-8'); await fs.promises.writeFile(outFile, body, encoding ?? 'utf-8');
} }

View file

@ -21,6 +21,7 @@ type EndpointCallResult =
| { | {
type: 'simple'; type: 'simple';
body: string; body: string;
encoding?: BufferEncoding;
} }
| { | {
type: 'response'; type: 'response';
@ -52,5 +53,6 @@ export async function call(
return { return {
type: 'simple', type: 'simple',
body: response.body, body: response.body,
encoding: response.encoding,
}; };
} }

View file

@ -0,0 +1,8 @@
{
"name": "@test/non-html-pages",
"version": "0.0.0",
"private": true,
"dependencies": {
"astro": "workspace:*"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1 KiB

View file

@ -0,0 +1,11 @@
// Returns the file body for this non-HTML file.
// The content type is based off of the extension in the filename,
// in this case: about.json.
export async function get() {
return {
body: JSON.stringify({
name: 'Astro',
url: 'https://astro.build/',
}),
};
}

View file

@ -0,0 +1,17 @@
import { promises as fs } from 'node:fs';
import type { APIRoute } from 'astro';
export const get: APIRoute = async function get() {
try {
// Image is in the public domain. Sourced from
// https://en.wikipedia.org/wiki/File:Portrait_placeholder.png
const buffer = await fs.readFile('./test/fixtures/non-html-pages/src/images/placeholder.png');
return {
body: buffer.toString('binary'),
encoding: 'binary',
} as const;
} catch (error: unknown) {
throw new Error(`Something went wrong in placeholder.png route!: ${error as string}`);
}
};

View file

@ -0,0 +1,38 @@
import { expect } from 'chai';
import { loadFixture } from './test-utils.js';
describe('Non-HTML Pages', () => {
let fixture;
before(async () => {
fixture = await loadFixture({ root: './fixtures/non-html-pages/' });
await fixture.build();
});
describe('json', () => {
it('should match contents', async () => {
const json = JSON.parse(await fixture.readFile('/about.json'));
expect(json).to.have.property('name', 'Astro');
expect(json).to.have.property('url', 'https://astro.build/');
});
});
describe('png', () => {
it('should not have had its encoding mangled', async () => {
const buffer = await fixture.readFile('/placeholder.png', 'base64');
// Sanity check the first byte
const hex = Buffer.from(buffer, 'base64').toString('hex');
const firstHexByte = hex.slice(0, 2);
// If we accidentally utf8 encode the png, the first byte (in hex) will be 'c2'
expect(firstHexByte).to.not.equal('c2');
// and if correctly encoded in binary, it should be '89'
expect(firstHexByte).to.equal('89');
// Make sure the whole buffer (in base64) matches this snapshot
expect(buffer).to.equal(
'iVBORw0KGgoAAAANSUhEUgAAAGQAAACWCAYAAAAouC1GAAAD10lEQVR4Xu3ZbW4iMRCE4c1RuP+ZEEfZFZHIAgHGH9Xtsv3m94yx6qHaM+HrfD7//cOfTQJfgNhYfG8EEC8PQMw8AAHELQGz/XCGAGKWgNl2aAggZgmYbYeGAGKWgNl2aAggZgmYbYeGAGKWgNl2aAggZgmYbYeGAGKWgNl2aAggZgmYbYeGAGKWgNl2aAggZgmYbYeGAGKWgNl2aAggZgmYbWe6hpxOp6oIL5dL1fWjL54CpBbhXagz4FiDqCCegZxhLEGiIGaAsQPJwrjhuLXFBiQbwrUtFiCjMZzaMhzEBcMFZSiIG4YDyjAQV4zRKENA3DFGoqSDzIIxCgWQgn9eZb6rpILM1o57qyyUNJCZMTLHFyAFI2s5kBXakYWS0hBAymsYDrISRkZLACn/8j5cGfXUFQqyYjuiWwJIY0Out0W0JAxk5XZEtgQQGtKRgOGt6rEV0pAdxlXU2AKks3U0pDPAiNuVKDREIGQNstP5EXGOyBsCSF/lAOnL7/tuRpYgRPUSKhQaIpIBRBSkahlAVEmK1gFEFKRqGUuQHR951e8i0kMdkP6+SUGu29kVxXJkAUJD+hMQrUBDREGqlgFElaRgHRXGdSsc6oAIEjBbgoYAUpfAbu8i1g3Z7V1EiRFyqANSN02er5Y/Zd0+YJexNUVDdmmJGiNsZAHSPrbCRtYOKFM1ZHWQCIzQkbX64Q5I+1iW3xmFkdKQFUcXIPLvePuCkRhpDVmpJcuArIASjZHakNmfujIwAKk4SpYFmXF0ZWEMachsoysTYyjIDE3JxhgO4owyAsMCxBFlFIYNiBPKSAxAnh57R2PYgLj9/j4SJvQXw5L3LjeM+z2PgBkG4gzx/EXKhEkHmQliRFvSQGaFyEZJAVkB4wYTPb7CQVbCyEAJA1kRImN8hYCsjhHZFDnILhhRKICUvL0eXKM86KUgu7Uj4kyRgeyMoRxfEhAw/neld3x1g4Dx+4DpQQFEcKi/WqIVpQuEdrzXTAcB47haLSjNDQHkGOR6RS1KEwgYZRgtj8PVIGDUYdS2BJD6fJvuKB1dVSC0o8ni56YSFED6Mq66WwpCO6qyf3vxEUpxQwAxAgFDg1HyGFzUEECMQMDQYhy15LAhgBiBgBGD8ent/WNDAIkDeYcCSGzmH1d/9U7yFoR25Eg9owCSk3vxmzsgM4AwrnKV7sfWy4YAAkhuAmaf9rEhtCNfC5D8zA8/8Yby6wyhIYfZhVwASEis7Yu+BKEd7YH23glIb4IB919RHs4QGhKQcsWSgFSElXEpIBkpV3zGAwjjqiK5oEsBCQq2Z9l/4WuAC09sfQEAAAAASUVORK5CYII='
);
});
});
});

View file

@ -146,8 +146,8 @@ export async function loadFixture(inlineConfig) {
const previewServer = await preview(config, { logging, telemetry, ...opts }); const previewServer = await preview(config, { logging, telemetry, ...opts });
return previewServer; return previewServer;
}, },
readFile: (filePath) => readFile: (filePath, encoding) =>
fs.promises.readFile(new URL(filePath.replace(/^\//, ''), config.outDir), 'utf8'), fs.promises.readFile(new URL(filePath.replace(/^\//, ''), config.outDir), encoding ?? 'utf8'),
readdir: (fp) => fs.promises.readdir(new URL(fp.replace(/^\//, ''), config.outDir)), readdir: (fp) => fs.promises.readdir(new URL(fp.replace(/^\//, ''), config.outDir)),
glob: (p) => glob: (p) =>
fastGlob(p, { fastGlob(p, {

View file

@ -1666,6 +1666,12 @@ importers:
packages/astro/test/fixtures/multiple-renderers/renderers/two: packages/astro/test/fixtures/multiple-renderers/renderers/two:
specifiers: {} specifiers: {}
packages/astro/test/fixtures/non-html-pages:
specifiers:
astro: workspace:*
dependencies:
astro: link:../../..
packages/astro/test/fixtures/page-format: packages/astro/test/fixtures/page-format:
specifiers: specifiers:
astro: workspace:* astro: workspace:*