From 71f73db5a9eb7e0504c306d8fecbb3c7f12b998e Mon Sep 17 00:00:00 2001 From: MK Date: Fri, 3 Jul 2026 22:53:02 +0800 Subject: [PATCH] fix(cli): declare content-type for .md and other text assets on R2 upload Add md/markdown/yaml/yml/toml to ASSET_MIME_TYPES so static deploys (e.g. VitePress with the llms plugin) upload markdown to R2 with the correct content-type instead of application/octet-stream. --- packages/void/src/cli/package.ts | 5 +++ packages/void/test/unit/asset-hashing.test.ts | 34 +++++++++++++++++-- 2 files changed, 37 insertions(+), 2 deletions(-) diff --git a/packages/void/src/cli/package.ts b/packages/void/src/cli/package.ts index 3e5edf744..22b3a0caa 100644 --- a/packages/void/src/cli/package.ts +++ b/packages/void/src/cli/package.ts @@ -182,6 +182,11 @@ const ASSET_MIME_TYPES: Record = { svg: 'image/svg+xml; charset=utf-8', txt: 'text/plain; charset=utf-8', csv: 'text/csv; charset=utf-8', + md: 'text/markdown; charset=utf-8', + markdown: 'text/markdown; charset=utf-8', + yaml: 'text/yaml; charset=utf-8', + yml: 'text/yaml; charset=utf-8', + toml: 'application/toml', png: 'image/png', jpg: 'image/jpeg', jpeg: 'image/jpeg', diff --git a/packages/void/test/unit/asset-hashing.test.ts b/packages/void/test/unit/asset-hashing.test.ts index c6ee97298..7c4fe556a 100644 --- a/packages/void/test/unit/asset-hashing.test.ts +++ b/packages/void/test/unit/asset-hashing.test.ts @@ -19,8 +19,13 @@ vi.mock('node:fs', async () => { const fsModule = await import('node:fs'); const readFileSyncMock = fsModule.readFileSync as unknown as ReturnType; -const { collectAndHashAssets, computeAssetHash, computeAssetHashes, computeMd5 } = - await import('../../src/cli/package.ts'); +const { + collectAndHashAssets, + computeAssetHash, + computeAssetHashes, + computeMd5, + getAssetContentType, +} = await import('../../src/cli/package.ts'); const TMP = join(import.meta.dirname, '.tmp-asset-hashing-test'); @@ -135,3 +140,28 @@ describe('collectAndHashAssets — v2 manifest entries', () => { expect(Object.keys(assetManifest).sort()).toEqual(['/a.js', '/b.css', '/c.html']); }); }); + +describe('getAssetContentType', () => { + it('returns text/markdown for .md files', () => { + expect(getAssetContentType('/guide/github-actions-cache.md')).toBe( + 'text/markdown; charset=utf-8', + ); + }); + + it('returns text/markdown for .markdown files', () => { + expect(getAssetContentType('/notes.markdown')).toBe('text/markdown; charset=utf-8'); + }); + + it('returns text/yaml for .yaml and .yml files', () => { + expect(getAssetContentType('/config.yaml')).toBe('text/yaml; charset=utf-8'); + expect(getAssetContentType('/config.yml')).toBe('text/yaml; charset=utf-8'); + }); + + it('returns application/toml for .toml files', () => { + expect(getAssetContentType('/config.toml')).toBe('application/toml'); + }); + + it('returns application/octet-stream for unknown extensions', () => { + expect(getAssetContentType('/file.xyz')).toBe('application/octet-stream'); + }); +});