Mime.js 2.8 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697
  1. 'use strict';
  2. /**
  3. * @param typeMap [Object] Map of MIME type -> Array[extensions]
  4. * @param ...
  5. */
  6. function Mime() {
  7. this._types = Object.create(null);
  8. this._extensions = Object.create(null);
  9. for (let i = 0; i < arguments.length; i++) {
  10. this.define(arguments[i]);
  11. }
  12. this.define = this.define.bind(this);
  13. this.getType = this.getType.bind(this);
  14. this.getExtension = this.getExtension.bind(this);
  15. }
  16. /**
  17. * Define mimetype -> extension mappings. Each key is a mime-type that maps
  18. * to an array of extensions associated with the type. The first extension is
  19. * used as the default extension for the type.
  20. *
  21. * e.g. mime.define({'audio/ogg', ['oga', 'ogg', 'spx']});
  22. *
  23. * If a type declares an extension that has already been defined, an error will
  24. * be thrown. To suppress this error and force the extension to be associated
  25. * with the new type, pass `force`=true. Alternatively, you may prefix the
  26. * extension with "*" to map the type to extension, without mapping the
  27. * extension to the type.
  28. *
  29. * e.g. mime.define({'audio/wav', ['wav']}, {'audio/x-wav', ['*wav']});
  30. *
  31. *
  32. * @param map (Object) type definitions
  33. * @param force (Boolean) if true, force overriding of existing definitions
  34. */
  35. Mime.prototype.define = function(typeMap, force) {
  36. for (let type in typeMap) {
  37. let extensions = typeMap[type].map(function(t) {
  38. return t.toLowerCase();
  39. });
  40. type = type.toLowerCase();
  41. for (let i = 0; i < extensions.length; i++) {
  42. const ext = extensions[i];
  43. // '*' prefix = not the preferred type for this extension. So fixup the
  44. // extension, and skip it.
  45. if (ext[0] === '*') {
  46. continue;
  47. }
  48. if (!force && (ext in this._types)) {
  49. throw new Error(
  50. 'Attempt to change mapping for "' + ext +
  51. '" extension from "' + this._types[ext] + '" to "' + type +
  52. '". Pass `force=true` to allow this, otherwise remove "' + ext +
  53. '" from the list of extensions for "' + type + '".'
  54. );
  55. }
  56. this._types[ext] = type;
  57. }
  58. // Use first extension as default
  59. if (force || !this._extensions[type]) {
  60. const ext = extensions[0];
  61. this._extensions[type] = (ext[0] !== '*') ? ext : ext.substr(1);
  62. }
  63. }
  64. };
  65. /**
  66. * Lookup a mime type based on extension
  67. */
  68. Mime.prototype.getType = function(path) {
  69. path = String(path);
  70. let last = path.replace(/^.*[/\\]/, '').toLowerCase();
  71. let ext = last.replace(/^.*\./, '').toLowerCase();
  72. let hasPath = last.length < path.length;
  73. let hasDot = ext.length < last.length - 1;
  74. return (hasDot || !hasPath) && this._types[ext] || null;
  75. };
  76. /**
  77. * Return file extension associated with a mime type
  78. */
  79. Mime.prototype.getExtension = function(type) {
  80. type = /^\s*([^;\s]*)/.test(type) && RegExp.$1;
  81. return type && this._extensions[type.toLowerCase()] || null;
  82. };
  83. module.exports = Mime;