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

Add support for custom non-html route encoding #4549

Merged
merged 3 commits into from
Sep 9, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/stale-camels-invent.md
Original file line number Diff line number Diff line change
@@ -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.
1 change: 1 addition & 0 deletions packages/astro/src/@types/astro.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1082,6 +1082,7 @@ export interface APIContext {

export interface EndpointOutput {
body: Body;
encoding?: BufferEncoding;
}

export type APIRoute = (
Expand Down
4 changes: 3 additions & 1 deletion packages/astro/src/core/build/generate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -366,13 +366,15 @@ async function generatePath(
};

let body: string;
let encoding: BufferEncoding | undefined;
if (pageData.route.type === 'endpoint') {
const result = await callEndpoint(mod as unknown as EndpointHandler, options);

if (result.type === 'response') {
throw new Error(`Returning a Response from an endpoint is not supported in SSG mode.`);
}
body = result.body;
encoding = result.encoding;
} else {
const response = await render(options);

Expand All @@ -388,5 +390,5 @@ async function generatePath(
const outFile = getOutFile(astroConfig, outFolder, pathname, pageData.route.type);
pageData.route.distURL = outFile;
await fs.promises.mkdir(outFolder, { recursive: true });
await fs.promises.writeFile(outFile, body, 'utf-8');
await fs.promises.writeFile(outFile, body, encoding ?? 'utf-8');
}
2 changes: 2 additions & 0 deletions packages/astro/src/core/endpoint/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ type EndpointCallResult =
| {
type: 'simple';
body: string;
encoding?: BufferEncoding;
}
| {
type: 'response';
Expand Down Expand Up @@ -52,5 +53,6 @@ export async function call(
return {
type: 'simple',
body: response.body,
encoding: response.encoding,
};
}
8 changes: 8 additions & 0 deletions packages/astro/test/fixtures/non-html-pages/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"name": "@test/non-html-pages",
"version": "0.0.0",
"private": true,
"dependencies": {
"astro": "workspace:*"
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -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/',
}),
};
}
Original file line number Diff line number Diff line change
@@ -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}`);
}
};
38 changes: 38 additions & 0 deletions packages/astro/test/non-html-pages.test.js
Original file line number Diff line number Diff line change
@@ -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='
);
});
});
});
4 changes: 2 additions & 2 deletions packages/astro/test/test-utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -144,8 +144,8 @@ export async function loadFixture(inlineConfig) {
const previewServer = await preview(config, { logging, telemetry, ...opts });
return previewServer;
},
readFile: (filePath) =>
fs.promises.readFile(new URL(filePath.replace(/^\//, ''), config.outDir), 'utf8'),
readFile: (filePath, encoding) =>
fs.promises.readFile(new URL(filePath.replace(/^\//, ''), config.outDir), encoding ?? 'utf8'),
readdir: (fp) => fs.promises.readdir(new URL(fp.replace(/^\//, ''), config.outDir)),
clean: async () => {
await fs.promises.rm(config.outDir, { maxRetries: 10, recursive: true, force: true });
Expand Down
6 changes: 6 additions & 0 deletions pnpm-lock.yaml

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