diff --git a/README.md b/README.md index 4579db6..1cbe350 100644 --- a/README.md +++ b/README.md @@ -56,6 +56,21 @@ mime.lookup('folder/.htaccess') // false mime.lookup('cats') // false ``` +### mime.lookupAll(path) + +Find all MIME type associated with a file. + +```js +mime.lookupAll('json') // ['application/json'] +mime.lookupAll('.rtf') // ['application/rtf', 'text/rtf'] +mime.lookupAll('file.bmp') // ['image/bmp', 'image/x-ms-bmp'] +mime.lookupAll('folder/file.js') // ['application/javascript'] +mime.lookupAll('folder/.htaccess') // [] + +mime.lookupAll('cats') // [] +mime.lookupAll(42) // false +``` + ### mime.contentType(type) Create a full content-type header given a content-type or extension. diff --git a/index.js b/index.js index 6e0ea43..ca91454 100644 --- a/index.js +++ b/index.js @@ -22,6 +22,7 @@ var extname = require('path').extname var extractTypeRegExp = /^\s*([^;\s]*)(?:;|\s|$)/ var textTypeRegExp = /^text\//i +var typeSets = {} /** * Module exports. @@ -34,11 +35,15 @@ exports.contentType = contentType exports.extension = extension exports.extensions = Object.create(null) exports.lookup = lookup +exports.lookupAll = lookupAll exports.types = Object.create(null) // Populate the extensions/types maps populateMaps(exports.extensions, exports.types) +// Populate the extensions->[types] set +populateTypeSets(typeSets) + /** * Get the default charset for a MIME type. * @@ -123,13 +128,16 @@ function extension (type) { } /** - * Lookup the MIME type for a file path/extension. + * Obtain the extension of a filename or filepath. + * If the path is not a string or a proper extension isn't found, + * false is returned. + * The path is case insensitive (so hello.html and HELLO.HTML are equal). * * @param {string} path - * @return {boolean|string} + * @return {boolean|string} the file extension if available. false otherwise. */ -function lookup (path) { +function extractExtension (path) { if (!path || typeof path !== 'string') { return false } @@ -143,9 +151,43 @@ function lookup (path) { return false } + return extension +} + +/** + * Lookup the MIME type for a file path/extension. + * + * @param {string} path + * @return {boolean|string} + */ + +function lookup (path) { + var extension = extractExtension(path) + + if (!extension) { + return false + } + return exports.types[extension] || false } +/** + * Find all MIME types that are associated with a file extensions. + * + * @param {string} path or file extension + * @return {boolean|array} + */ + +function lookupAll (path) { + var extension = extractExtension(path) + + if (!extension) { + return false + } + + return typeSets[extension] || [] +} + /** * Populate the extensions and types maps. * @private @@ -186,3 +228,29 @@ function populateMaps (extensions, types) { } }) } + +/** + * Populate the set where extension->[types] + * An example is .rtf -> ['application/rtf', 'text/rtf'] + * + * @private + * @param {object>} the map to fill in + */ + +function populateTypeSets (typeSets) { + Object.keys(db).forEach(function (type) { + var exts = db[type].extensions + + if (!exts || !exts.length) { + return + } + + exts.forEach(function (ext) { + if (typeSets[ext]) { + typeSets[ext].push(type) + } else { + typeSets[ext] = [type] + } + }) + }) +} diff --git a/test/test.js b/test/test.js index 472cbcc..b30af35 100644 --- a/test/test.js +++ b/test/test.js @@ -2,6 +2,12 @@ var assert = require('assert') var mimeTypes = require('..') +var allMatch = function (actual, expected) { + expected.forEach(function (expectedType) { + assert.ok(actual.indexOf(expectedType) > -1) + }) +} + describe('mimeTypes', function () { describe('.charset(type)', function () { it('should return "UTF-8" for "application/json"', function () { @@ -224,4 +230,77 @@ describe('mimeTypes', function () { }) }) }) + + describe('.lookupAll(extension)', function () { + it('should return a list with multiple mime types when many exist', function () { + allMatch(mimeTypes.lookupAll('.rtf'), ['application/rtf', 'text/rtf']) + }) + + it('should return a list with one mime type when only one exists', function () { + allMatch(mimeTypes.lookupAll('.html'), ['text/html']) + }) + + it('should work without a leading dot', function () { + allMatch(mimeTypes.lookupAll('rtf'), ['application/rtf', 'text/rtf']) + }) + + it('should be case-insensitive', function () { + allMatch(mimeTypes.lookupAll('RtF'), ['application/rtf', 'text/rtf']) + allMatch(mimeTypes.lookupAll('.HTML'), ['text/html']) + }) + + it('should return an empty list for an unknown extension', function () { + allMatch(mimeTypes.lookupAll('bogus'), []) + }) + + it('should return false for non-strings', function () { + assert.strictEqual(mimeTypes.lookupAll(null), false) + assert.strictEqual(mimeTypes.lookupAll(undefined), false) + assert.strictEqual(mimeTypes.lookupAll(3.141592), false) + assert.strictEqual(mimeTypes.lookupAll({}), false) + }) + }) + + describe('.lookupAll(path)', function () { + it('should return mime type for file name', function () { + allMatch(mimeTypes.lookupAll('page.html'), ['text/html']) + }) + + it('should return mime type for relative path', function () { + allMatch(mimeTypes.lookupAll('path/to/page.html'), ['text/html']) + allMatch(mimeTypes.lookupAll('path\\to\\doc.rtf'), ['application/rtf', 'text/rtf']) + }) + + it('should return mime type for absolute path', function () { + allMatch(mimeTypes.lookupAll('/path/to/page.html'), ['text/html']) + allMatch(mimeTypes.lookupAll('C:\\path\\to\\doc.rtf'), ['application/rtf', 'text/rtf']) + }) + + it('should be case insensitive', function () { + allMatch(mimeTypes.lookupAll('/path/to/PAGE.HTML'), ['text/html']) + allMatch(mimeTypes.lookupAll('C:\\path\\to\\DOC.RTF'), ['application/rtf', 'text/rtf']) + }) + + it('should return an empty list for unknown extension', function () { + allMatch(mimeTypes.lookupAll('/path/to/file.bogus'), []) + }) + + it('should return false for path without extension', function () { + assert.strictEqual(mimeTypes.lookupAll('/path/to/json'), false) + }) + + describe('lookupAll() - path with dotfile', function () { + it('should return false when extension-less', function () { + assert.strictEqual(mimeTypes.lookupAll('/path/to/.json'), false) + }) + + it('should return all mime types when there is extension', function () { + allMatch(mimeTypes.lookupAll('/path/to/.config.json'), ['application/json']) + }) + + it('should return all mime types when there is extension, but no path', function () { + allMatch(mimeTypes.lookupAll('.config.json'), ['application/json']) + }) + }) + }) })