From 255636cc7b4ed5f72045f75a2411ebd84a2bdb0d Mon Sep 17 00:00:00 2001 From: Alan Date: Fri, 9 Sep 2022 08:58:24 -0700 Subject: [PATCH] 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 --- .changeset/stale-camels-invent.md | 5 +++ packages/astro/src/@types/astro.ts | 1 + packages/astro/src/core/build/generate.ts | 4 +- packages/astro/src/core/endpoint/index.ts | 2 + .../test/fixtures/non-html-pages/package.json | 8 ++++ .../non-html-pages/src/images/placeholder.png | Bin 0 -> 1040 bytes .../non-html-pages/src/pages/about.json.ts | 11 +++++ .../src/pages/placeholder.png.ts | 17 ++++++++ packages/astro/test/non-html-pages.test.js | 38 ++++++++++++++++++ packages/astro/test/test-utils.js | 4 +- pnpm-lock.yaml | 6 +++ 11 files changed, 93 insertions(+), 3 deletions(-) create mode 100644 .changeset/stale-camels-invent.md create mode 100644 packages/astro/test/fixtures/non-html-pages/package.json create mode 100644 packages/astro/test/fixtures/non-html-pages/src/images/placeholder.png create mode 100644 packages/astro/test/fixtures/non-html-pages/src/pages/about.json.ts create mode 100644 packages/astro/test/fixtures/non-html-pages/src/pages/placeholder.png.ts create mode 100644 packages/astro/test/non-html-pages.test.js diff --git a/.changeset/stale-camels-invent.md b/.changeset/stale-camels-invent.md new file mode 100644 index 0000000000..e3d88bc33a --- /dev/null +++ b/.changeset/stale-camels-invent.md @@ -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. diff --git a/packages/astro/src/@types/astro.ts b/packages/astro/src/@types/astro.ts index b2347c4106..c358d63e79 100644 --- a/packages/astro/src/@types/astro.ts +++ b/packages/astro/src/@types/astro.ts @@ -1085,6 +1085,7 @@ export interface APIContext { export interface EndpointOutput { body: Body; + encoding?: BufferEncoding; } export type APIRoute = ( diff --git a/packages/astro/src/core/build/generate.ts b/packages/astro/src/core/build/generate.ts index 406186a21d..b6ebf46971 100644 --- a/packages/astro/src/core/build/generate.ts +++ b/packages/astro/src/core/build/generate.ts @@ -391,6 +391,7 @@ 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); @@ -398,6 +399,7 @@ async function generatePath( 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); @@ -413,5 +415,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'); } diff --git a/packages/astro/src/core/endpoint/index.ts b/packages/astro/src/core/endpoint/index.ts index 9e974ee366..73c96ae644 100644 --- a/packages/astro/src/core/endpoint/index.ts +++ b/packages/astro/src/core/endpoint/index.ts @@ -21,6 +21,7 @@ type EndpointCallResult = | { type: 'simple'; body: string; + encoding?: BufferEncoding; } | { type: 'response'; @@ -52,5 +53,6 @@ export async function call( return { type: 'simple', body: response.body, + encoding: response.encoding, }; } diff --git a/packages/astro/test/fixtures/non-html-pages/package.json b/packages/astro/test/fixtures/non-html-pages/package.json new file mode 100644 index 0000000000..c3a215d98d --- /dev/null +++ b/packages/astro/test/fixtures/non-html-pages/package.json @@ -0,0 +1,8 @@ +{ + "name": "@test/non-html-pages", + "version": "0.0.0", + "private": true, + "dependencies": { + "astro": "workspace:*" + } +} diff --git a/packages/astro/test/fixtures/non-html-pages/src/images/placeholder.png b/packages/astro/test/fixtures/non-html-pages/src/images/placeholder.png new file mode 100644 index 0000000000000000000000000000000000000000..62841efdbff9f711167ef42e9a6f984137800c72 GIT binary patch literal 1040 zcmeAS@N?(olHy`uVBq!ia0vp^DL_1pgAGV(?9g>%U|_!P>Eakt5%>0HZl02%K+D6k zfjj=s6mY*OI`IS(<1zWGZ#VsY_PuB0tA)3WAHJ@!`~Uawd|#&ch8qzz`78qZ{0?Vq z7#NS}GH(7`(8iE7t>I=_28Ti#=ZxHT>T%O|waxKczKTPCy7$$ui}mL*EfISdyTbTE zMDq%Td5v=tbsQHdq%~}0K6z-z!yQ|XvZ+WP+Nvw2u-I_UEhCvDtcO`sH9Dl*m`@o9 zg!f$3aCW$3=(cD{^O+_?F4hT6^>L~DRxfEfbFHs>wayfi2{R3ij*Ba>DrZ|x5WO|A zbu%o^JzA8)=;ZuM zSYWM#vy0=ZX^bvYmn6uF9xK(BdDXxnWDwod#t=09K;C<$YrHq##=3M07Oh|oGWVUg zewuyiUAu(o8%>KF9Wm{`Wb4FB z6GYv`6+XSM_}w&7B9k|ArRwR1H4Q?&6K_qpIe(Xt0mDiQuW76^3rkWHc`{GrEY=oI z);PiAd-9kB-=iKQro&7}1XD%YcsqnodGI*QldE=5zR|Gb?>{>swJ+j_Cw@zu&T%s>Iq_v)K^JI)B#6( zuwsAIBGIMXEoFPI`>-=U-Vvp%ah8|yiNupCS6we*4vAC=-TuQ@6%wu_UfISn;aAP< zcN(&>p$c0TpM3OunUILXFQaw4qPd)`S8b}V{wU~G=*(cq#OT}{_(>w>ihx6p!+{G) zGFzW)P;8KBY!Gbz`10-FWM_s>=E`;^kDO=n;q_nrK8dxIJ@K$z%rI$k^~c%P3dRiv z>x!nnJAUnS8v~O^9#h) { + 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=' + ); + }); + }); +}); diff --git a/packages/astro/test/test-utils.js b/packages/astro/test/test-utils.js index c6150b26bb..59a9253140 100644 --- a/packages/astro/test/test-utils.js +++ b/packages/astro/test/test-utils.js @@ -146,8 +146,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)), glob: (p) => fastGlob(p, { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5e7d1dce1a..e5c2a6f1ef 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1666,6 +1666,12 @@ importers: packages/astro/test/fixtures/multiple-renderers/renderers/two: specifiers: {} + packages/astro/test/fixtures/non-html-pages: + specifiers: + astro: workspace:* + dependencies: + astro: link:../../.. + packages/astro/test/fixtures/page-format: specifiers: astro: workspace:*