mime-type
Version:
the custom more powerful mime-type utility can work with mime-db.
340 lines (328 loc) • 10.5 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.MimeType = MimeType;
exports.default = void 0;
var _posix = _interopRequireDefault(require("picomatch/posix"));
var _utilEx = require("util-ex");
var _path = _interopRequireDefault(require("path.js"));
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
function isMatch(str, pattern, options) {
return (0, _posix.default)(pattern, options)(str);
}
const extractTypeRegExp = /^\s*([^;\s]*)(?:;|\s|$)/;
const textTypeRegExp = /^text\//i;
const indexOf = Array.prototype.indexOf;
const refSources = ['nginx', 'apache', undefined, 'iana'];
const shouldDebug = typeof process === 'object' && process && process.env && process.env.DEBUG_MIME;
MimeType.dupDefault = MimeType.prototype.dupDefault = 0;
MimeType.dupSkip = MimeType.prototype.dupSkip = 1;
MimeType.dupOverwrite = MimeType.prototype.dupOverwrite = 2;
MimeType.dupAppend = MimeType.prototype.dupAppend = 3;
/**
* Constructs a MimeType object to manage MIME types and their associated extensions.
* @param {Object} db - An initial database containing MIME type mappings.
* @param {number} [duplicationProcessWay] - A flag specifying how to handle duplicate MIME type entries.
* If provided, it should be one of:
* - `dupDefault: 0`: Default strategy that resolves duplicates based on source priority.
* See the description for details on this strategy.
* - `dupSkip: 1`: Skip adding the new MIME type if a duplicate entry already exists.
* - `dupOverwrite: 2`: Replace the existing MIME type with the new one if a duplicate is found.
* - `dupAppend: 3`: Add the new MIME type to the end of the existing ones if a duplicate is encountered.
* If not specified, it defaults to the `dupDefault` strategy,
* The `dupDefault` strategy works as follows:
* - If the existing MIME type is 'application/octet-stream', it's not overwritten.
* - If sources are equal and the existing type starts with 'application/', it's preserved.
* - Otherwise, if the new source is considered more authoritative (earlier in the `refSources` array),
* the new type overwrites the old one. The `refSources` array may include server configurations
* and standards bodies, such as ['nginx', 'apache', undefined, 'iana'], with `undefined` representing
* an unspecified or less authoritative source.
*/
function MimeType(db, duplicationProcessWay) {
if (!(this instanceof MimeType)) {
return new MimeType(db, duplicationProcessWay);
}
(0, _utilEx.defineProperty)(this, 'types', {});
(0, _utilEx.defineProperty)(this, 'dup', this.dupDefault);
(0, _utilEx.defineProperty)(this, 'extensions', undefined, {
get: function () {
var j, k, len, mime, ref, result;
result = {};
ref = Object.keys(this);
for (j = 0, len = ref.length; j < len; j++) {
k = ref[j];
mime = this[k];
result[k] = mime.extensions;
}
return result;
}
});
if (duplicationProcessWay && indexOf.call([0, 1, 2, 3], duplicationProcessWay) >= 0) {
this.dup = duplicationProcessWay;
}
if (db) {
this.load(db);
}
}
/*
* Get the default charset for a MIME type.
*
* @param {string} type
* @return {boolean|string}
*/
MimeType.prototype.charset = function (type) {
let result;
if (type && (0, _utilEx.isString)(type)) {
try {
const match = extractTypeRegExp.exec(type);
const mime = match && this[match[1].toLowerCase()];
if (mime && mime.charset) {
result = mime.charset;
}
// default text/* to utf-8
if (!result && match && textTypeRegExp.test(match[1])) {
result = 'utf-8';
}
} catch (error) {}
}
return result;
};
/*
* Create a full Content-Type header given a MIME type or extension.
*
* @param {string} str
* @return {boolean|string}
*/
MimeType.prototype.contentType = function (str) {
var charset;
var charset, mime;
if (str && (0, _utilEx.isString)(str)) {
mime = str.indexOf('/') === -1 ? this.lookup(str) : str;
if (mime) {
if (mime.indexOf('charset') === -1) {
charset = this.charset(mime);
if (charset) {
mime += '; charset=' + charset.toLowerCase();
}
}
}
}
return mime;
};
/*
* Get the default extension for a MIME type.
*
* @param {string} type
* @return {boolean|string}
*/
MimeType.prototype.extension = function (type) {
var result;
if (type && (0, _utilEx.isString)(type)) {
result = extractTypeRegExp.exec(type);
result = result && this[result[1].toLowerCase()];
if (result) {
result = result.defaultExtension || result.extensions[0];
}
}
return result;
};
/*
* Lookup the MIME types for a file path/extension.
*
* @param {string} path
* @return {undefined|string|array}
*/
MimeType.prototype.lookup = function (aPath) {
var extension, result;
if (aPath && (0, _utilEx.isString)(aPath)) {
// get the extension ("ext" or ".ext" or full path)
extension = _path.default.extname('x.' + aPath).toLowerCase().slice(1);
if (extension) {
if (/[*?!+|{]/.test(extension)) {
result = Object.keys(this.types).filter(name => isMatch(name, extension));
result = result.length ? result.map(ext => this.types[ext]) : undefined;
} else {
result = this.types[extension];
}
}
}
return result;
};
/*
* Return all MIME types which matching a pattern
* [spec](http://tools.ietf.org/html/rfc2616#section-14.1)
* @param {string} pattern the mime type pattern, For example "video/*", "audio/*", ..
* @return {array}
*/
MimeType.prototype.glob = function (pattern) {
var result;
if (pattern === '*/*') {
return ['application/octet-stream'];
}
result = Object.keys(this).filter(function (name) {
return isMatch(name, pattern);
});
return result;
};
/*
* Whether the mime type is exist.
* @param {string} type the mime type
* @return {boolean}
*/
MimeType.prototype.exist = Object.prototype.hasOwnProperty;
/*
* Add a custom mime/extension mapping and handles potential conflicts.
* @param (string) type: mime type
* @param (object) mime: mime object
* * "source": "iana",
* * "charset": "UTF-8",
* * "compressible": true,
* * "extensions": ["js"]
* @param {number} [dup=this.dup] - The conflict resolution strategy. Can be one of:
* - `this.dupSkip`: Skip the existing mapping.
* - `this.dupAppend`: Append the new type to the existing mapping.
* - `this.dupOverwrite`: Overwrite the existing mapping.
* - `this.dupDefault`: Uses a default strategy where:
* - If the existing MIME type is 'application/octet-stream', it is not overwritten.
* - If sources are equal and the existing type starts with 'application/', it is retained.
* - Otherwise, if the new source is considered more authoritative (appears earlier in `refSources`),
* the new type will overwrite the old one. The `refSources` array includes server configurations
* and standards bodies, e.g., ['nginx', 'apache', undefined, 'iana'], with `undefined` marking
* an unspecified or less authoritative source.
* @return {array<string>}: the added extensions
*/
MimeType.prototype.define = function (type, mime, dup) {
var extension, exts, from, j, len, ref, t, to;
if (!(type && mime && mime.extensions && !this.hasOwnProperty(type))) {
return;
}
if (dup == null) {
dup = this.dup;
}
exts = mime.extensions;
if (!(0, _utilEx.isArray)(exts)) {
mime.extensions = [exts];
}
exts = [];
if (mime.charset) {
mime.charset = mime.charset.toLowerCase();
}
ref = mime.extensions;
for (j = 0, len = ref.length; j < len; j++) {
extension = ref[j];
t = this.types[extension];
if (t) {
switch (dup) {
case this.dupSkip:
continue;
case this.dupAppend:
if ((0, _utilEx.isString)(t)) {
t = [t];
}
if (indexOf.call(t, type) < 0) {
t.push(type);
}
break;
case this.dupOverwrite:
t = type;
break;
case this.dupDefault:
if ((0, _utilEx.isArray)(t)) {
t = t[0];
}
from = refSources.indexOf(this[t].source);
to = refSources.indexOf(mime.source);
if (t !== 'application/octet-stream' && from > to || from === to && t.substr(0, 12) === 'application/') {
if (shouldDebug) {
console.warn("defineMime(" + type + "): the " + extension + " extension is exists on\n" + t + " skipped it.");
}
continue;
} else {
t = type;
}
}
} else {
t = type;
}
this.types[extension] = t;
exts.push(extension);
}
if (exts.length) {
mime.extensions = exts;
this[type] = mime;
}
return exts;
};
/*
* load mime-types from db.
*/
MimeType.prototype.load = function (mimes, duplicationProcessWay) {
var result;
result = 0;
Object.keys(mimes).forEach(function (_this) {
return function (type) {
var t;
t = _this.define(type, mimes[type], duplicationProcessWay);
if (t && t.length) {
return result++;
}
};
}(this));
return result;
};
/*
* remove the specified mime-type.
*/
MimeType.prototype.delete = function (type) {
var i, k, ref, result, v;
result = this.exist(type);
if (result) {
ref = this.types;
for (k in ref) {
v = ref[k];
if ((0, _utilEx.isArray)(v)) {
i = v.indexOf(type);
if (i !== -1) {
v.splice(i, 1);
if (v.length === 1) {
this.types[k] = v[0];
}
}
} else if (type === v) {
delete this.types[k];
}
}
delete this[type];
}
return result;
};
/*
* clear the mime-types.
*/
MimeType.prototype.clear = function (filter) {
var k, ref, result, v;
result = 0;
ref = this;
for (k in ref) {
v = ref[k];
if (this.hasOwnProperty(k)) {
if ((0, _utilEx.isFunction)(filter)) {
if (filter(k, v)) {
this["delete"](k);
result++;
}
} else if ((0, _utilEx.isString)(filter)) {
if (isMatch(k, filter)) {
this["delete"](k);
result++;
}
} else {
this["delete"](k);
result++;
}
}
}
return result;
};
var _default = exports.default = MimeType;