mime-detect
Version:
Detect mime type and encoding (aka Content-Type) from buffer, file content and filename.
124 lines (123 loc) • 4.19 kB
JavaScript
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")');
}
;