mirror of
https://github.com/denoland/std.git
synced 2024-11-21 20:50:22 +00:00
Add media_types collection (#97)
This commit is contained in:
parent
92bbca8166
commit
0e00fe9cd3
@ -14,6 +14,10 @@
|
||||
|
||||
Command line logging
|
||||
|
||||
- **[media_types](./media_types/)**
|
||||
|
||||
A library for resolving media types (MIME types) and extensions.
|
||||
|
||||
- **[mkdirp](./mkdirp/)**
|
||||
|
||||
Make directory branches.
|
||||
|
93
media_types/README.md
Normal file
93
media_types/README.md
Normal file
@ -0,0 +1,93 @@
|
||||
# media_types
|
||||
|
||||
A module that assists in resolving media types and extensions. It consumes the
|
||||
[mime-db](https://github.com/jshttp/mime-db) and provides API access to the
|
||||
information.
|
||||
|
||||
## Usage
|
||||
|
||||
### `lookup(path)`
|
||||
|
||||
Lookup the content type associated with a file. The path can be just the
|
||||
extension or the full path name. If the content type cannot be determined the
|
||||
function returns `undefined`:
|
||||
|
||||
```ts
|
||||
import { lookup } from "https://deno.land/x/std/media_types/mod.ts";
|
||||
|
||||
lookup("json"); // "application/json"
|
||||
lookup(".md"); // "text/markdown"
|
||||
lookup("folder/file.js"); // "application/javascript"
|
||||
lookup("folder/.htaccess"); // undefined
|
||||
```
|
||||
|
||||
### `contentType(type)`
|
||||
|
||||
Return a full `Content-Type` header value for a given content type or
|
||||
extension. When an extension is used, `lookup()` is used to resolve the
|
||||
content type first. A default charset is added if not present. The
|
||||
function will return `undefined` if the content type cannot be resolved:
|
||||
|
||||
```ts
|
||||
import { contentType } from "https://deno.land/x/std/media_types/mod.ts";
|
||||
import * as path from "https://deno.land/x/std/path/mod.ts";
|
||||
|
||||
contentType("markdown"); // "text/markdown; charset=utf-8"
|
||||
contentType("file.json"); // "application/json; charset=utf-8"
|
||||
contentType("text/html"); // "text/html; charset=utf-8"
|
||||
contentType("text/html; charset=iso-8859-1"); // "text/html; charset=iso-8859-1"
|
||||
|
||||
contentType(path.extname("/path/to/file.json")); // "application/json; charset=utf-8"
|
||||
```
|
||||
|
||||
### `extension(type)`
|
||||
|
||||
Return a default extension for a given content type. If there is not an
|
||||
appropriate extension, `undefined` is returned:
|
||||
|
||||
```ts
|
||||
import { extension } from "https://deno.land/x/std/media_types/mod.ts";
|
||||
|
||||
extension("application/octet-stream"); // "bin"
|
||||
```
|
||||
|
||||
### `charset(type)`
|
||||
|
||||
Lookup the implied default charset for a given content type. If the content
|
||||
type cannot be resolved, `undefined` is returned:
|
||||
|
||||
```ts
|
||||
import { charset } from "https://deno.land/x/std/media_types/mod.ts";
|
||||
|
||||
charset("text/markdown"); // "UTF-8"
|
||||
```
|
||||
|
||||
### `extensions`
|
||||
|
||||
A `Map` of extensions by content type, in priority order:
|
||||
|
||||
```ts
|
||||
import { extensions } from "https://deno.land/x/std/media_types/mod.ts";
|
||||
|
||||
extensions.get("application/javascript"); // [ "js", "mjs" ]
|
||||
```
|
||||
|
||||
### `types`
|
||||
|
||||
A `Map` of content types by extension:
|
||||
|
||||
```ts
|
||||
import { types } from "https://deno.land/x/std/media_types/mod.ts";
|
||||
|
||||
types.get("ts"); // "application/javascript"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
Adapted from [mime-type](https://github.com/jshttp/mime-types).
|
||||
|
||||
MIT License.
|
||||
|
||||
```
|
||||
|
||||
```
|
7688
media_types/db_1.37.0.json
Normal file
7688
media_types/db_1.37.0.json
Normal file
File diff suppressed because it is too large
Load Diff
15
media_types/deps.ts
Normal file
15
media_types/deps.ts
Normal file
@ -0,0 +1,15 @@
|
||||
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
export { extname } from "../fs/path.ts";
|
||||
|
||||
interface DB {
|
||||
[mediaType: string]: {
|
||||
source?: string;
|
||||
compressible?: boolean;
|
||||
charset?: string;
|
||||
extensions?: string[];
|
||||
};
|
||||
}
|
||||
|
||||
import * as _db from "./db_1.37.0.json";
|
||||
export const db: DB = _db;
|
149
media_types/mod.ts
Normal file
149
media_types/mod.ts
Normal file
@ -0,0 +1,149 @@
|
||||
/*!
|
||||
* Ported from: https://github.com/jshttp/mime-types and licensed as:
|
||||
*
|
||||
* (The MIT License)
|
||||
*
|
||||
* Copyright (c) 2014 Jonathan Ong <me@jongleberry.com>
|
||||
* Copyright (c) 2015 Douglas Christopher Wilson <doug@somethingdoug.com>
|
||||
* Copyright (c) 2019 the Deno authors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* 'Software'), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
import { db, extname } from "./deps.ts";
|
||||
|
||||
const EXTRACT_TYPE_REGEXP = /^\s*([^;\s]*)(?:;|\s|$)/;
|
||||
const TEXT_TYPE_REGEXP = /^text\//i;
|
||||
|
||||
/** A map of extensions for a given media type */
|
||||
export const extensions = new Map<string, string[]>();
|
||||
|
||||
/** A map of the media type for a given extension */
|
||||
export const types = new Map<string, string>();
|
||||
|
||||
/** Internal function to populate the maps based on the Mime DB */
|
||||
function populateMaps(
|
||||
extensions: Map<string, string[]>,
|
||||
types: Map<string, string>
|
||||
) {
|
||||
const preference = ["nginx", "apache", undefined, "iana"];
|
||||
|
||||
for (const type of Object.keys(db)) {
|
||||
const mime = db[type];
|
||||
const exts = mime.extensions;
|
||||
|
||||
if (!exts || !exts.length) {
|
||||
continue;
|
||||
}
|
||||
|
||||
extensions.set(type, exts);
|
||||
|
||||
for (const ext of exts) {
|
||||
if (types.has(ext)) {
|
||||
const current = types.get(ext)!;
|
||||
const from = preference.indexOf(db[current].source);
|
||||
const to = preference.indexOf(mime.source);
|
||||
|
||||
if (
|
||||
current !== "application/octet-stream" &&
|
||||
(from > to ||
|
||||
(from === to && current.substr(0, 12) === "application/"))
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
types.set(ext, type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Populate the maps upon module load
|
||||
populateMaps(extensions, types);
|
||||
|
||||
/** Given a media type return any default charset string. Returns `undefined`
|
||||
* if not resolvable.
|
||||
*/
|
||||
export function charset(type: string): string | undefined {
|
||||
const m = EXTRACT_TYPE_REGEXP.exec(type);
|
||||
if (!m) {
|
||||
return;
|
||||
}
|
||||
const [match] = m;
|
||||
const mime = db[match.toLowerCase()];
|
||||
|
||||
if (mime && mime.charset) {
|
||||
return mime.charset;
|
||||
}
|
||||
|
||||
if (TEXT_TYPE_REGEXP.test(match)) {
|
||||
return "UTF-8";
|
||||
}
|
||||
}
|
||||
|
||||
/** Given an extension or media type, return the full `Content-Type` header
|
||||
* string. Returns `undefined` if not resolvable.
|
||||
*/
|
||||
export function contentType(str: string): string | undefined {
|
||||
let mime = str.includes("/") ? str : lookup(str);
|
||||
|
||||
if (!mime) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!mime.includes("charset")) {
|
||||
const cs = charset(mime);
|
||||
if (cs) {
|
||||
mime += `; charset=${cs.toLowerCase()}`;
|
||||
}
|
||||
}
|
||||
|
||||
return mime;
|
||||
}
|
||||
|
||||
/** Given a media type, return the most appropriate extension or return
|
||||
* `undefined` if there is none.
|
||||
*/
|
||||
export function extension(type: string): string | undefined {
|
||||
const match = EXTRACT_TYPE_REGEXP.exec(type);
|
||||
|
||||
if (!match) {
|
||||
return;
|
||||
}
|
||||
|
||||
const exts = extensions.get(match[1].toLowerCase());
|
||||
|
||||
if (!exts || !exts.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
return exts[0];
|
||||
}
|
||||
|
||||
/** Given an extension, lookup the appropriate media type for that extension.
|
||||
* Likely you should be using `contentType()` though instead.
|
||||
*/
|
||||
export function lookup(path: string): string | undefined {
|
||||
const extension = extname("x." + path)
|
||||
.toLowerCase()
|
||||
.substr(1);
|
||||
|
||||
return types.get(extension);
|
||||
}
|
50
media_types/test.ts
Normal file
50
media_types/test.ts
Normal file
@ -0,0 +1,50 @@
|
||||
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
import { assertEqual, test } from "../testing/mod.ts";
|
||||
import {
|
||||
lookup,
|
||||
contentType,
|
||||
extension,
|
||||
charset,
|
||||
extensions,
|
||||
types
|
||||
} from "./mod.ts";
|
||||
|
||||
test(function testLookup() {
|
||||
assertEqual(lookup("json"), "application/json");
|
||||
assertEqual(lookup(".md"), "text/markdown");
|
||||
assertEqual(lookup("folder/file.js"), "application/javascript");
|
||||
assertEqual(lookup("folder/.htaccess"), undefined);
|
||||
});
|
||||
|
||||
test(function testContentType() {
|
||||
assertEqual(contentType("markdown"), "text/markdown; charset=utf-8");
|
||||
assertEqual(contentType("file.json"), "application/json; charset=utf-8");
|
||||
assertEqual(contentType("text/html"), "text/html; charset=utf-8");
|
||||
assertEqual(
|
||||
contentType("text/html; charset=iso-8859-1"),
|
||||
"text/html; charset=iso-8859-1"
|
||||
);
|
||||
assertEqual(contentType(".htaccess"), undefined);
|
||||
});
|
||||
|
||||
test(function testExtension() {
|
||||
assertEqual(extension("application/octet-stream"), "bin");
|
||||
assertEqual(extension("application/javascript"), "js");
|
||||
assertEqual(extension("text/html"), "html");
|
||||
});
|
||||
|
||||
test(function testCharset() {
|
||||
assertEqual(charset("text/markdown"), "UTF-8");
|
||||
assertEqual(charset("text/css"), "UTF-8");
|
||||
});
|
||||
|
||||
test(function testExtensions() {
|
||||
assertEqual(extensions.get("application/javascript"), ["js", "mjs"]);
|
||||
assertEqual(extensions.get("foo"), undefined);
|
||||
});
|
||||
|
||||
test(function testTypes() {
|
||||
assertEqual(types.get("js"), "application/javascript");
|
||||
assertEqual(types.get("foo"), undefined);
|
||||
});
|
@ -1,85 +0,0 @@
|
||||
{
|
||||
"": "application/octet-stream",
|
||||
".7z": "application/x-7z-compressed",
|
||||
".aac": "audio/aac",
|
||||
".abw": "application/x-abiword",
|
||||
".arc": "application/octet-stream",
|
||||
".avi": "video/x-msvideo",
|
||||
".azw": "application/vnd.amazon.ebook",
|
||||
".bin": "application/octet-stream",
|
||||
".bmp": "image/bmp",
|
||||
".bz": "application/x-bzip",
|
||||
".bz2": "application/x-bzip2",
|
||||
".csh": "application/x-csh",
|
||||
".css": "text/css",
|
||||
".csv": "text/csv",
|
||||
".doc": "application/msword",
|
||||
".docx": "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
||||
".eot": "application/vnd.ms-fontobject",
|
||||
".epub": "application/epub+zip",
|
||||
".es": "application/ecmascript",
|
||||
".gif": "image/gif",
|
||||
".gz": "application/gzip",
|
||||
".htm": "text/html",
|
||||
".html": "text/html",
|
||||
".ico": "image/x-icon",
|
||||
".ics": "text/calendar",
|
||||
".jar": "application/java-archive",
|
||||
".jpeg": "image/jpeg",
|
||||
".jpg": "image/jpeg",
|
||||
".js": "application/javascript",
|
||||
".json": "application/json",
|
||||
".md": "text/markdown",
|
||||
".mid": "audio/x-midi",
|
||||
".midi": "audio/x-midi",
|
||||
".mp3": "audio/mpeg",
|
||||
".mp4": "video/mpeg",
|
||||
".mpeg": "video/mpeg",
|
||||
".mpkg": "application/vnd.apple.installer+xml",
|
||||
".less": "text/less",
|
||||
".odp": "application/vnd.oasis.opendocument.presentation",
|
||||
".ods": "application/vnd.oasis.opendocument.spreadsheet",
|
||||
".odt": "application/vnd.oasis.opendocument.text",
|
||||
".oga": "audio/ogg",
|
||||
".ogv": "video/ogg",
|
||||
".ogx": "application/ogg",
|
||||
".otf": "font/otf",
|
||||
".png": "image/png",
|
||||
".pdf": "application/pdf",
|
||||
".ppm": "image/x-portable-pixmap",
|
||||
".pgm": "image/x-portable-graymap",
|
||||
".pmm": "image/x-portable-bitmap",
|
||||
".pnm": "image/x-portable-anymap",
|
||||
".ppt": "application/vnd.ms-powerpoint",
|
||||
".pptx": "application/vnd.openxmlformats-officedocument.presentationml.presentation",
|
||||
".rar": "application/x-rar-compressed",
|
||||
".rtf": "application/rtf",
|
||||
".sh": "application/x-sh",
|
||||
".sass": "text/x-sass",
|
||||
".scss": "text/x-scss",
|
||||
".svg": "image/svg+xml",
|
||||
".swf": "application/x-shockwave-flash",
|
||||
".tar": "application/x-tar",
|
||||
".tar.gz": "application/tar+gzip",
|
||||
".tif": "image/tiff",
|
||||
".tiff": "image/tiff",
|
||||
".toml": "application/toml",
|
||||
".ts": "application/typescript",
|
||||
".ttf": "font/ttf",
|
||||
".txt": "text/plain",
|
||||
".vsd": "application/vnd.visio",
|
||||
".wav": "audio/wav",
|
||||
".weba": "audio/webm",
|
||||
".webm": "video/webm",
|
||||
".webp": "image/webp",
|
||||
".woff": "font/woff",
|
||||
".woff2": "font/woff2",
|
||||
".xhtml": "application/xhtml+xml",
|
||||
".xls": "application/vnd.ms-excel",
|
||||
".xlsx": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
||||
".xml": "application/xml",
|
||||
".xul": "application/vnd.mozilla.xul+xml",
|
||||
".yml": "text/yaml",
|
||||
".yaml": "text/yaml",
|
||||
".zip": "application/zip"
|
||||
}
|
@ -13,7 +13,7 @@ import {
|
||||
} from "./http.ts";
|
||||
import { cwd, DenoError, ErrorKind, args, stat, readDir, open } from "deno";
|
||||
import { extname } from "../fs/path.ts";
|
||||
import * as extensionsMap from "./extension_map.json";
|
||||
import { contentType } from "../media_types/mod.ts";
|
||||
|
||||
const dirViewerTemplate = `
|
||||
<!DOCTYPE html>
|
||||
@ -162,30 +162,12 @@ async function serveDir(req: ServerRequest, dirPath: string, dirName: string) {
|
||||
return res;
|
||||
}
|
||||
|
||||
function guessContentType(filename: string): string {
|
||||
let extension = extname(filename);
|
||||
let contentType = extensionsMap[extension];
|
||||
|
||||
if (contentType) {
|
||||
return contentType;
|
||||
}
|
||||
|
||||
extension = extension.toLowerCase();
|
||||
contentType = extensionsMap[extension];
|
||||
|
||||
if (contentType) {
|
||||
return contentType;
|
||||
}
|
||||
|
||||
return extensionsMap[""];
|
||||
}
|
||||
|
||||
async function serveFile(req: ServerRequest, filename: string) {
|
||||
const file = await open(filename);
|
||||
const fileInfo = await stat(filename);
|
||||
const headers = new Headers();
|
||||
headers.set("content-length", fileInfo.len.toString());
|
||||
headers.set("content-type", guessContentType(filename));
|
||||
headers.set("content-type", contentType(extname(filename)) || "text/plain");
|
||||
|
||||
const res = {
|
||||
status: 200,
|
||||
|
@ -21,7 +21,7 @@ export function runTests(serverReadyPromise: Promise<any>) {
|
||||
const res = await fetch("http://localhost:4500/azure-pipelines.yml");
|
||||
assert(res.headers.has("access-control-allow-origin"));
|
||||
assert(res.headers.has("access-control-allow-headers"));
|
||||
assertEqual(res.headers.get("content-type"), "text/yaml");
|
||||
assertEqual(res.headers.get("content-type"), "text/yaml; charset=utf-8");
|
||||
const downloadedFile = await res.text();
|
||||
const localFile = new TextDecoder().decode(
|
||||
await readFile("./azure-pipelines.yml")
|
||||
|
Loading…
Reference in New Issue
Block a user