UNPKG

mime-types

Version:

The ultimate javascript content-type utility.

212 lines (168 loc) 4.55 kB
/*! * mime-types * Copyright(c) 2014 Jonathan Ong * Copyright(c) 2015 Douglas Christopher Wilson * MIT Licensed */ 'use strict' /** * Module dependencies. * @private */ var db = require('mime-db') var extname = require('path').extname var mimeScore = require('./mimeScore') /** * Module variables. * @private */ var EXTRACT_TYPE_REGEXP = /^\s*([^;\s]*)(?:;|\s|$)/ var TEXT_TYPE_REGEXP = /^text\//i /** * Module exports. * @public */ exports.charset = charset exports.charsets = { lookup: charset } exports.contentType = contentType exports.extension = extension exports.extensions = Object.create(null) exports.lookup = lookup exports.types = Object.create(null) exports._extensionConflicts = [] // Populate the extensions/types maps populateMaps(exports.extensions, exports.types) /** * Get the default charset for a MIME type. * * @param {string} type * @return {boolean|string} */ function charset (type) { if (!type || typeof type !== 'string') { return false } // TODO: use media-typer var match = EXTRACT_TYPE_REGEXP.exec(type) var mime = match && db[match[1].toLowerCase()] if (mime && mime.charset) { return mime.charset } // default text/* to utf-8 if (match && TEXT_TYPE_REGEXP.test(match[1])) { return 'UTF-8' } return false } /** * Create a full Content-Type header given a MIME type or extension. * * @param {string} str * @return {boolean|string} */ function contentType (str) { // TODO: should this even be in this module? if (!str || typeof str !== 'string') { return false } var mime = str.indexOf('/') === -1 ? exports.lookup(str) : str if (!mime) { return false } // TODO: use content-type or other module if (mime.indexOf('charset') === -1) { var charset = exports.charset(mime) if (charset) mime += '; charset=' + charset.toLowerCase() } return mime } /** * Get the default extension for a MIME type. * * @param {string} type * @return {boolean|string} */ function extension (type) { if (!type || typeof type !== 'string') { return false } // TODO: use media-typer var match = EXTRACT_TYPE_REGEXP.exec(type) // get extensions var exts = match && exports.extensions[match[1].toLowerCase()] if (!exts || !exts.length) { return false } return exts[0] } /** * Lookup the MIME type for a file path/extension. * * @param {string} path * @return {boolean|string} */ function lookup (path) { if (!path || typeof path !== 'string') { return false } // get the extension ("ext" or ".ext" or full path) var extension = extname('x.' + path) .toLowerCase() .slice(1) if (!extension) { return false } return exports.types[extension] || false } /** * Populate the extensions and types maps. * @private */ function populateMaps (extensions, types) { Object.keys(db).forEach(function forEachMimeType (type) { var mime = db[type] var exts = mime.extensions if (!exts || !exts.length) { return } // mime -> extensions extensions[type] = exts // extension -> mime for (var i = 0; i < exts.length; i++) { var extension = exts[i] types[extension] = _preferredType(extension, types[extension], type) // DELETE (eventually): Capture extension->type maps that change as a // result of switching to mime-score. This is just to help make reviewing // PR #119 easier, and can be removed once that PR is approved. const legacyType = _preferredTypeLegacy( extension, types[extension], type ) if (legacyType !== types[extension]) { exports._extensionConflicts.push([extension, legacyType, types[extension]]) } } }) } // Resolve type conflict using mime-score function _preferredType (ext, type0, type1) { var score0 = type0 ? mimeScore(type0, db[type0].source) : 0 var score1 = type1 ? mimeScore(type1, db[type1].source) : 0 return score0 > score1 ? type0 : type1 } // Resolve type conflict using pre-mime-score logic function _preferredTypeLegacy (ext, type0, type1) { var SOURCE_RANK = ['nginx', 'apache', undefined, 'iana'] var score0 = type0 ? SOURCE_RANK.indexOf(db[type0].source) : 0 var score1 = type1 ? SOURCE_RANK.indexOf(db[type1].source) : 0 if ( exports.types[extension] !== 'application/octet-stream' && (score0 > score1 || (score0 === score1 && exports.types[extension]?.slice(0, 12) === 'application/')) ) { return type0 } return score0 > score1 ? type0 : type1 }