From 902ec070313fd40c9a8135ef4ef401f8a1db9472 Mon Sep 17 00:00:00 2001 From: Robert Kieffer Date: Thu, 11 May 2017 10:52:25 -0700 Subject: [PATCH] Use facets to prioritize when resolving type conflicts ... plus a couple other misc things --- .eslintrc.json | 46 ++++++++++++++++++++++++++++++++++++++++++++++ .gitignore | 2 ++ build/test.js | 19 ++++++++++++++++++- mime.js | 38 +++++++++++++++++++++++++++----------- package.json | 2 +- 5 files changed, 94 insertions(+), 13 deletions(-) create mode 100644 .eslintrc.json diff --git a/.eslintrc.json b/.eslintrc.json new file mode 100644 index 00000000..638b0a56 --- /dev/null +++ b/.eslintrc.json @@ -0,0 +1,46 @@ +{ + "root": true, + "env": { + "browser": true, + "commonjs": true, + "node": true, + "mocha": true + }, + "extends": ["eslint:recommended"], + "installedESLint": true, + "rules": { + "array-bracket-spacing": ["warn", "never"], + "arrow-body-style": ["warn", "as-needed"], + "arrow-parens": ["warn", "as-needed"], + "arrow-spacing": "warn", + "brace-style": "warn", + "camelcase": "warn", + "comma-spacing": ["warn", {"after": true}], + "dot-notation": "warn", + "indent": ["warn", 2, { + "SwitchCase": 1, + "FunctionDeclaration": {"parameters": 1}, + "MemberExpression": 1, + "CallExpression": {"arguments": 1} + }], + "key-spacing": ["warn", {"beforeColon": false, "afterColon": true, "mode": "minimum"}], + "keyword-spacing": "warn", + "no-console": "off", + "no-empty": "off", + "no-multi-spaces": "warn", + "no-redeclare": "off", + "no-restricted-globals": ["warn", "Promise"], + "no-trailing-spaces": "warn", + "no-undef": "error", + "no-unused-vars": ["warn", {"args": "none"}], + "padded-blocks": ["warn", "never"], + "object-curly-spacing": ["warn", "never"], + "quotes": ["warn", "single"], + "react/prop-types": "off", + "react/jsx-no-bind": "off", + "semi": ["warn", "always"], + "space-before-blocks": ["warn", "always"], + "space-before-function-paren": ["warn", "never"], + "space-in-parens": ["warn", "never"] + } +} diff --git a/.gitignore b/.gitignore index 93f13619..d8e713a9 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ node_modules npm-debug.log +*.sw* # VIM temp files +.DS_Store # Mac desktop services store diff --git a/build/test.js b/build/test.js index 58b9ba7c..28681368 100644 --- a/build/test.js +++ b/build/test.js @@ -4,7 +4,6 @@ var mime = require('../mime'); var assert = require('assert'); -var path = require('path'); // // Test mime lookups @@ -21,6 +20,24 @@ assert.equal('text/plain', mime.lookup('\\txt')); // Windows, extension-l assert.equal('application/octet-stream', mime.lookup('text.nope')); // unrecognized assert.equal('fallback', mime.lookup('text.fallback', 'fallback')); // alternate default +// +// Test types that are known to have conflicting definitions but different facet priorities +// + +assert.equal('application/octet-stream', mime.lookup('dmg')); +assert.equal('application/bdoc', mime.lookup('bdoc')); +assert.equal('application/octet-stream', mime.lookup('deb')); +assert.equal('application/octet-stream', mime.lookup('iso')); +assert.equal('application/octet-stream', mime.lookup('exe')); +assert.equal('application/octet-stream', mime.lookup('exe')); +assert.equal('application/octet-stream', mime.lookup('dll')); +assert.equal('application/octet-stream', mime.lookup('msi')); +assert.equal('application/vnd.palm', mime.lookup('pdb')); +assert.equal('audio/mp3', mime.lookup('mp3')); +assert.equal('audio/mp4', mime.lookup('m4a')); +assert.equal('font/opentype', mime.lookup('otf')); +assert.equal('image/bmp', mime.lookup('bmp')); + // // Test extensions // diff --git a/mime.js b/mime.js index cbe0e938..0892a262 100644 --- a/mime.js +++ b/mime.js @@ -1,6 +1,11 @@ -var path = require('path'); var fs = require('fs'); +// If two types claim the same extension, we resolve the conflict by checking +// facet precedence. https://tools.ietf.org/html/rfc6838#section-3 +// Facets listed here are in order of decreasing precedence +var FACETS = ['vnd.', 'x-', 'prs.']; +var FACET_RE = new RegExp('/(' + FACETS.join('|') + ')'); + function Mime() { // Map of extension -> mime type this.types = Object.create(null); @@ -18,16 +23,27 @@ function Mime() { * * @param map (Object) type definitions */ -Mime.prototype.define = function (map) { +Mime.prototype.define = function(map) { for (var type in map) { var exts = map[type]; + for (var i = 0; i < exts.length; i++) { - if (process.env.DEBUG_MIME && this.types[exts[i]]) { - console.warn((this._loading || "define()").replace(/.*\//, ''), 'changes "' + exts[i] + '" extension type from ' + - this.types[exts[i]] + ' to ' + type); + var ext = exts[i]; + var found = this.types[ext]; + + // If there's already a type for this extension ... + if (found) { + var pri0 = FACETS.indexOf(FACET_RE.test(found) && RegExp.$1); + var pri1 = FACETS.indexOf(FACET_RE.test(type) && RegExp.$1); + + if (process.env.DEBUG_MIME) console.warn('Type conflict for .' + ext + + ' (' + found + ' pri=' + pri0 + ', ' + type + ' pri=' + pri1 + ')'); + + // Prioritize based on facet precedence + if (pri0 <= pri1) continue; } - this.types[exts[i]] = type; + this.types[ext] = type; } // Default extension is the first one we encounter @@ -48,9 +64,9 @@ Mime.prototype.define = function (map) { Mime.prototype.load = function(file) { this._loading = file; // Read file and split into lines - var map = {}, - content = fs.readFileSync(file, 'ascii'), - lines = content.split(/[\r\n]+/); + var map = {}; + var content = fs.readFileSync(file, 'ascii'); + var lines = content.split(/[\r\n]+/); lines.forEach(function(line) { // Clean up whitespace/comments, and split into fields @@ -69,7 +85,7 @@ Mime.prototype.load = function(file) { Mime.prototype.lookup = function(path, fallback) { var ext = path.replace(/.*[\.\/\\]/, '').toLowerCase(); - return this.types[ext] || fallback || this.default_type; + return this.types[ext] || fallback || this.default_type; // eslint-disable-line camelcase }; /** @@ -87,7 +103,7 @@ var mime = new Mime(); mime.define(require('./types.json')); // Default type -mime.default_type = mime.lookup('bin'); +mime.default_type = mime.lookup('bin'); // eslint-disable-line camelcase // // Additional API specific to the default instance diff --git a/package.json b/package.json index a4108837..38482b6d 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,7 @@ "mime-db": "^1.22.0" }, "scripts": { - "prepublish": "node build/build.js > types.json", + "prepare": "node build/build.js > types.json", "test": "node build/test.js" }, "keywords": [