UNPKG

mime-detect

Version:

Detect mime type and encoding (aka Content-Type) from buffer, file content and filename.

124 lines (123 loc) 4.19 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.mimeToExt = exports.detectBufferMime = exports.detectFilenameMime = exports.detectFileMime = exports.enableTypescriptMime = void 0; const mime_types_1 = require("./mime.types"); const mime_ext_1 = require("./mime.ext"); function enableTypescriptMime(mime = 'text/typescript') { mime_types_1.ext_to_mime.ts = mime; } exports.enableTypescriptMime = enableTypescriptMime; let binarySuffix = '; charset=binary'; let plainTextPrefix = 'text/plain'; let binaryPrefix = 'application/octet-stream'; function detectFileMime(file, cb) { if (!cb) { return new Promise((resolve, reject) => detectFileMime(file, (error, mime) => error ? reject(error) : resolve(mime))); } let { spawn } = child_process(); let child = spawn('file', ['-bE', '--mime', file]); let stdout = ''; let stderr = ''; child.stdout.on('data', chunk => (stdout += chunk)); child.stderr.on('data', chunk => (stderr += chunk)); child.on('close', code => { if (code !== 0) { let message = (stdout + ' ' + stderr).trim(); if (message.includes('No such file or directory')) { cb(new Error('No such file or directory')); return; } cb(new Error(message)); return; } let mime = stdout.trim(); if (mime.endsWith(binarySuffix)) { mime = mime.slice(0, mime.length - binarySuffix.length); } mime = detectFilenameMime(file, mime); cb(null, mime); }); } exports.detectFileMime = detectFileMime; function detectFilenameMime(file, mime = binaryPrefix) { let prefix = mime.startsWith(plainTextPrefix) ? plainTextPrefix : mime.startsWith(binaryPrefix) ? binaryPrefix : undefined; if (prefix) { let ext = file.split('.').pop(); let result = mime_types_1.ext_to_mime[ext]; if (result) { mime = result + mime.slice(prefix.length); } } return mime; } exports.detectFilenameMime = detectFilenameMime; function detectBufferMime(buffer, cb) { if (!cb) { return new Promise((resolve, reject) => detectBufferMime(buffer, (error, mime) => error ? reject(error) : resolve(mime))); } if (buffer.length === 0) { cb(null, binaryPrefix); return; } let { exec } = child_process(); let childError; let child = exec(`file -bE --mime -`, {}, (error, stdout, stderr) => { if (childError) return; if (error) { let message = (stdout + ' ' + stderr).trim(); cb(message ? new Error(message) : error); return; } let mime = stdout.trim(); if (mime.endsWith(binarySuffix)) { mime = mime.slice(0, mime.length - binarySuffix.length); } cb(null, mime); }); if (child.stdin) { child.stdin.write(buffer); child.stdin.on('error', (error) => { if (error.code === 'EPIPE') { // early terminate before the mime is already detected return; } childError = error; cb(error); }); } } exports.detectBufferMime = detectBufferMime; /** * @description return file extension name without dot * e.g. "audio/mp4" -> "m4a" * e.g. "video/x-matroska" -> "mkv" * e.g. "image/jpeg" -> "jpg" * e.g. "application/octet-stream" -> "bin" */ function mimeToExt(mime) { mime = mime.split(';')[0]; let ext = mime_ext_1.mime_to_ext[mime]; if (ext) return ext; let exts = Object.entries(mime_types_1.ext_to_mime) .filter(([key, value]) => value == mime) .map(([key]) => key); if (exts.length > 1) { console.warn(`multiple extensions matched:`, { mime, exts }); } if (exts.length == 1) { return exts[0]; } return mime.split('/').pop(); } exports.mimeToExt = mimeToExt; // using eval to avoid error when bundling with esbuild // at least detectFilenameMime() is usable in browser function child_process() { return eval('require("child_process")'); }