diff --git a/app/pages/package-code/[[org]]/[packageName]/v/[version]/[...filePath].vue b/app/pages/package-code/[[org]]/[packageName]/v/[version]/[...filePath].vue index cb1d3955d1..6a3cc71763 100644 --- a/app/pages/package-code/[[org]]/[packageName]/v/[version]/[...filePath].vue +++ b/app/pages/package-code/[[org]]/[packageName]/v/[version]/[...filePath].vue @@ -4,7 +4,6 @@ import type { PackageFileTreeResponse, PackageFileContentResponse, } from '#shared/types' -import { isBinaryFilePath } from '~/utils/file-types' definePageMeta({ name: 'code', @@ -107,7 +106,12 @@ const isViewingFile = computed(() => currentNode.value?.type === 'file') // Maximum file size we'll try to load (500KB) - must match server const MAX_FILE_SIZE = 500 * 1024 -const isBinaryFile = computed(() => !!filePath.value && isBinaryFilePath(filePath.value)) +// Estimate binary file based on mime type +const isBinaryFile = computed(() => { + const contentType = fileContent.value?.contentType + if (!contentType) return false + return isBinaryContentType(contentType) +}) const isFileTooLarge = computed(() => { const size = currentNode.value?.size @@ -117,13 +121,7 @@ const isFileTooLarge = computed(() => { // Fetch file content when a file is selected (and not too large) const fileContentUrl = computed(() => { // Don't fetch if no file path, file tree not loaded, file is too large, or it's a directory - if ( - !filePath.value || - !fileTree.value || - isFileTooLarge.value || - !isViewingFile.value || - isBinaryFile.value - ) { + if (!filePath.value || !fileTree.value || isFileTooLarge.value || !isViewingFile.value) { return null } return `/api/registry/file/${packageName.value}/v/${version.value}/${filePath.value}` @@ -533,7 +531,13 @@ defineOgImageComponent('Default', {

{{ $t('code.binary_file') }}

-

{{ $t('code.binary_rendering_warning') }}

+

+ {{ + $t('code.binary_rendering_warning', { + contentType: fileContent?.contentType ?? 'unknown', + }) + }} +

-1 ? filePath.slice(dotIndex + 1).toLowerCase() : '' - return BINARY_EXTENSIONS.has(ext) +export function isBinaryContentType(contentType: string): boolean { + for (const prefix of BINARY_MIME_PREFIXES) { + if (contentType.startsWith(prefix)) { + return true + } + } + return false } diff --git a/i18n/locales/en.json b/i18n/locales/en.json index c187265c73..93957b3a22 100644 --- a/i18n/locales/en.json +++ b/i18n/locales/en.json @@ -794,7 +794,7 @@ "file_path": "File path", "scroll_to_top": "Scroll to top", "binary_file": "Binary file", - "binary_rendering_warning": "File type not supported for preview." + "binary_rendering_warning": "File type \"{contentType}\" is not supported for preview." }, "badges": { "provenance": { diff --git a/server/api/registry/file/[...pkg].get.ts b/server/api/registry/file/[...pkg].get.ts index c1dd76c1b3..e3d0c37d2d 100644 --- a/server/api/registry/file/[...pkg].get.ts +++ b/server/api/registry/file/[...pkg].get.ts @@ -50,7 +50,7 @@ async function fetchFileContent( packageName: string, version: string, filePath: string, -): Promise { +): Promise<{ content: string; contentType: string | null }> { const url = `https://cdn.jsdelivr.net/npm/${packageName}@${version}/${filePath}` const response = await fetch(url) @@ -64,6 +64,8 @@ async function fetchFileContent( }) } + const contentType = response.headers.get('content-type') + // Check content-length header if available const contentLength = response.headers.get('content-length') if (contentLength && parseInt(contentLength, 10) > MAX_FILE_SIZE) { @@ -83,7 +85,7 @@ async function fetchFileContent( }) } - return content + return { content, contentType } } /** @@ -123,7 +125,7 @@ export default defineCachedEventHandler( filePath: rawFilePath, }) - const content = await fetchFileContent(packageName, version, filePath) + const { content, contentType } = await fetchFileContent(packageName, version, filePath) const language = getLanguageFromPath(filePath) // For JS/TS files, resolve dependency versions and relative imports for linking @@ -185,6 +187,7 @@ export default defineCachedEventHandler( version, path: filePath, language, + contentType, content, html, lines: content.split('\n').length, diff --git a/shared/types/npm-registry.ts b/shared/types/npm-registry.ts index 250a9218e6..629c9fbc20 100644 --- a/shared/types/npm-registry.ts +++ b/shared/types/npm-registry.ts @@ -381,6 +381,7 @@ export interface PackageFileContentResponse { version: string path: string language: string + contentType: string | null content: string html: string lines: number