webpack
Version:
Packs ECMAScript/CommonJs/AMD modules for the browser. Allows you to split your codebase into multiple bundles, which can be loaded on demand. Supports loaders to preprocess files, i.e. json, jsx, es7, css, less, ... and your custom stuff.
177 lines (140 loc) • 4.34 kB
JavaScript
/*
MIT License http://www.opensource.org/licenses/mit-license.php
*/
"use strict";
const { extname } = require("path");
const db = require("mime-db");
/**
* RegExp to match type in RFC 6838
*
* type-name = restricted-name
* subtype-name = restricted-name
* restricted-name = restricted-name-first *126restricted-name-chars
* restricted-name-first = ALPHA / DIGIT
* restricted-name-chars = ALPHA / DIGIT / "!" / "#" / "$" / "&" / "-" / "^" / "_"
* restricted-name-chars =/ "." ; Characters before first dot always specify a facet name
* restricted-name-chars =/ "+" ; Characters after last plus always specify a structured syntax suffix
* ALPHA = %x41-5A / %x61-7A ; A-Z / a-z
* DIGIT = %x30-39 ; 0-9
*/
const TYPE_REGEXP =
/^ *(([A-Za-z0-9][A-Za-z0-9!#$&^_-]{0,126})\/([A-Za-z0-9][A-Za-z0-9!#$&^_.+-]{0,126})) *(?:;.*)?$/;
const extensions = Object.create(null);
const types = Object.create(null);
// Score RFC facets (see https://tools.ietf.org/html/rfc6838#section-3)
/** @type {Record<string, number>} */
const FACET_SCORES = {
"prs.": 100,
"x-": 200,
"x.": 300,
"vnd.": 400,
default: 900
};
/** @typedef {"nginx" | "apache" | "iana" | "default"} SourceScore */
// Score mime source (Logic originally from `jshttp/mime-types` module)
/** @type {Record<SourceScore, number>} */
const SOURCE_SCORES = {
nginx: 10,
apache: 20,
iana: 40,
default: 30
};
/** @type {Record<string, number>} */
const TYPE_SCORES = {
// prefer application/xml over text/xml
// prefer application/rtf over text/rtf
application: 1,
// prefer font/woff over application/font-woff
font: 2,
// prefer video/mp4 over audio/mp4 over application/mp4
// See https://www.rfc-editor.org/rfc/rfc4337.html#section-2
audio: 2,
video: 3,
default: 0
};
/**
* @param {string} mimeType mime type
* @param {SourceScore=} source source
* @returns {number} min score
*/
function mimeScore(mimeType, source = "default") {
if (mimeType === "application/octet-stream") {
return 0;
}
const [type, subtype] = mimeType.split("/");
const facet = subtype.replace(/(\.|x-).*/, "$1");
const facetScore = FACET_SCORES[facet] || FACET_SCORES.default;
const sourceScore = SOURCE_SCORES[source] || SOURCE_SCORES.default;
const typeScore = TYPE_SCORES[type] || TYPE_SCORES.default;
// All else being equal prefer shorter types
const lengthScore = 1 - mimeType.length / 100;
return facetScore + sourceScore + typeScore + lengthScore;
}
/**
* @param {string} ext extension
* @param {string} type0 the first type
* @param {string} type1 the second type
* @returns {string} preferred type
*/
const preferredType = (ext, type0, type1) => {
const score0 = type0 ? mimeScore(type0, db[type0].source) : 0;
const score1 = type1 ? mimeScore(type1, db[type1].source) : 0;
return score0 > score1 ? type0 : type1;
};
/**
* @param {Record<string, readonly string[]>} extensions extensions
* @param {Record<string, string>} types types
*/
const populate = (extensions, types) => {
for (const type of Object.keys(db)) {
const mime = db[type];
const foundExtensions = mime.extensions;
if (!foundExtensions || foundExtensions.length === 0) {
continue;
}
// mime -> extensions
extensions[type] = foundExtensions;
// extension -> mime
for (let i = 0; i < foundExtensions.length; i++) {
const extension = foundExtensions[i];
types[extension] = preferredType(extension, types[extension], type);
}
}
};
populate(extensions, types);
/**
* Get the default extension for a MIME type.
* @param {string} type type
* @returns {undefined | string} resolve extension
*/
const extension = (type) => {
if (!type) {
return;
}
const match = TYPE_REGEXP.exec(type);
if (!match) {
return;
}
const possibleExtensions = extensions[match[1].toLowerCase()];
if (!possibleExtensions || possibleExtensions.length === 0) {
return;
}
return possibleExtensions[0];
};
/**
* Lookup the MIME type for a file path/extension.
* @param {string} path path
* @returns {undefined | string} resolved MIME type
*/
const lookup = (path) => {
if (!path) {
return;
}
// get the extension ("ext" or ".ext" or full path)
const extension = extname(`x.${path}`).toLowerCase().slice(1);
if (!extension) {
return;
}
return types[extension];
};
module.exports = { extension, lookup };