Document application modes (development/debug/production) Add global file drop handler, order column normalization, SPA hash fix Serve CDN assets via /_vendor/ URLs instead of merging into bundles Add production minification with license preservation Improve JSON formatting for debugging and production optimization Add CDN asset caching with CSS URL inlining for production builds Add three-mode system (development, debug, production) Update Manifest CLAUDE.md to reflect helper class architecture Refactor Manifest.php into helper classes for better organization Pre-manifest-refactor checkpoint: Add app_mode documentation 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
98 lines
2.8 KiB
JavaScript
Executable File
98 lines
2.8 KiB
JavaScript
Executable File
'use strict';
|
|
|
|
/**
|
|
* @param typeMap [Object] Map of MIME type -> Array[extensions]
|
|
* @param ...
|
|
*/
|
|
function Mime() {
|
|
this._types = Object.create(null);
|
|
this._extensions = Object.create(null);
|
|
|
|
for (let i = 0; i < arguments.length; i++) {
|
|
this.define(arguments[i]);
|
|
}
|
|
|
|
this.define = this.define.bind(this);
|
|
this.getType = this.getType.bind(this);
|
|
this.getExtension = this.getExtension.bind(this);
|
|
}
|
|
|
|
/**
|
|
* Define mimetype -> extension mappings. Each key is a mime-type that maps
|
|
* to an array of extensions associated with the type. The first extension is
|
|
* used as the default extension for the type.
|
|
*
|
|
* e.g. mime.define({'audio/ogg', ['oga', 'ogg', 'spx']});
|
|
*
|
|
* If a type declares an extension that has already been defined, an error will
|
|
* be thrown. To suppress this error and force the extension to be associated
|
|
* with the new type, pass `force`=true. Alternatively, you may prefix the
|
|
* extension with "*" to map the type to extension, without mapping the
|
|
* extension to the type.
|
|
*
|
|
* e.g. mime.define({'audio/wav', ['wav']}, {'audio/x-wav', ['*wav']});
|
|
*
|
|
*
|
|
* @param map (Object) type definitions
|
|
* @param force (Boolean) if true, force overriding of existing definitions
|
|
*/
|
|
Mime.prototype.define = function(typeMap, force) {
|
|
for (let type in typeMap) {
|
|
let extensions = typeMap[type].map(function(t) {
|
|
return t.toLowerCase();
|
|
});
|
|
type = type.toLowerCase();
|
|
|
|
for (let i = 0; i < extensions.length; i++) {
|
|
const ext = extensions[i];
|
|
|
|
// '*' prefix = not the preferred type for this extension. So fixup the
|
|
// extension, and skip it.
|
|
if (ext[0] === '*') {
|
|
continue;
|
|
}
|
|
|
|
if (!force && (ext in this._types)) {
|
|
throw new Error(
|
|
'Attempt to change mapping for "' + ext +
|
|
'" extension from "' + this._types[ext] + '" to "' + type +
|
|
'". Pass `force=true` to allow this, otherwise remove "' + ext +
|
|
'" from the list of extensions for "' + type + '".'
|
|
);
|
|
}
|
|
|
|
this._types[ext] = type;
|
|
}
|
|
|
|
// Use first extension as default
|
|
if (force || !this._extensions[type]) {
|
|
const ext = extensions[0];
|
|
this._extensions[type] = (ext[0] !== '*') ? ext : ext.substr(1);
|
|
}
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Lookup a mime type based on extension
|
|
*/
|
|
Mime.prototype.getType = function(path) {
|
|
path = String(path);
|
|
let last = path.replace(/^.*[/\\]/, '').toLowerCase();
|
|
let ext = last.replace(/^.*\./, '').toLowerCase();
|
|
|
|
let hasPath = last.length < path.length;
|
|
let hasDot = ext.length < last.length - 1;
|
|
|
|
return (hasDot || !hasPath) && this._types[ext] || null;
|
|
};
|
|
|
|
/**
|
|
* Return file extension associated with a mime type
|
|
*/
|
|
Mime.prototype.getExtension = function(type) {
|
|
type = /^\s*([^;\s]*)/.test(type) && RegExp.$1;
|
|
return type && this._extensions[type.toLowerCase()] || null;
|
|
};
|
|
|
|
module.exports = Mime;
|