forked from denoland/deno_std
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(media_types): single-export files (denoland#3001)
- Loading branch information
Showing
19 changed files
with
737 additions
and
694 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license. | ||
import db from "./vendor/mime-db.v1.52.0.ts"; | ||
import { type DBEntry, extensions } from "./_util.ts"; | ||
|
||
export type KeyOfDb = keyof typeof db; | ||
|
||
/** A map of the media type for a given extension */ | ||
export const types = new Map<string, KeyOfDb>(); | ||
|
||
/** Internal function to populate the maps based on the Mime DB. */ | ||
(function populateMaps() { | ||
const preference = ["nginx", "apache", undefined, "iana"]; | ||
|
||
for (const type of Object.keys(db) as KeyOfDb[]) { | ||
const mime = db[type] as DBEntry; | ||
const exts = mime.extensions; | ||
|
||
if (!exts || !exts.length) { | ||
continue; | ||
} | ||
|
||
// @ts-ignore work around denoland/dnt#148 | ||
extensions.set(type, exts); | ||
|
||
for (const ext of exts) { | ||
const current = types.get(ext); | ||
if (current) { | ||
const from = preference.indexOf((db[current] as DBEntry).source); | ||
const to = preference.indexOf(mime.source); | ||
|
||
if ( | ||
current !== "application/octet-stream" && | ||
(from > to || | ||
// @ts-ignore work around denoland/dnt#148 | ||
(from === to && current.startsWith("application/"))) | ||
) { | ||
continue; | ||
} | ||
} | ||
|
||
types.set(ext, type); | ||
} | ||
} | ||
})(); | ||
|
||
export { db }; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license. | ||
import { parseMediaType } from "./parse_media_type.ts"; | ||
import { typeByExtension } from "./type_by_extension.ts"; | ||
import { getCharset } from "./get_charset.ts"; | ||
import { formatMediaType } from "./format_media_type.ts"; | ||
import type { db } from "./_db.ts"; | ||
|
||
type DB = typeof db; | ||
type ContentTypeToExtension = { | ||
[K in keyof DB]: DB[K] extends { "extensions": readonly string[] } | ||
? DB[K]["extensions"][number] | ||
: never; | ||
}; | ||
|
||
type KnownExtensionOrType = | ||
| keyof ContentTypeToExtension | ||
| ContentTypeToExtension[keyof ContentTypeToExtension] | ||
| `.${ContentTypeToExtension[keyof ContentTypeToExtension]}`; | ||
|
||
/** | ||
* Given an extension or media type, return a full `Content-Type` or | ||
* `Content-Disposition` header value. | ||
* | ||
* The function will treat the `extensionOrType` as a media type when it | ||
* contains a `/`, otherwise it will process it as an extension, with or without | ||
* the leading `.`. | ||
* | ||
* Returns `undefined` if unable to resolve the media type. | ||
* | ||
* > Note: a side effect of `deno/x/media_types` was that you could pass a file | ||
* > name (e.g. `file.json`) and it would return the content type. This behavior | ||
* > is intentionally not supported here. If you want to get an extension for a | ||
* > file name, use `extname()` from `std/path/mod.ts` to determine the | ||
* > extension and pass it here. | ||
* | ||
* @example | ||
* ```ts | ||
* import { contentType } from "https://deno.land/std@$STD_VERSION/media_types/content_type.ts"; | ||
* | ||
* contentType(".json"); // `application/json; charset=UTF-8` | ||
* contentType("text/html"); // `text/html; charset=UTF-8` | ||
* contentType("text/html; charset=UTF-8"); // `text/html; charset=UTF-8` | ||
* contentType("txt"); // `text/plain; charset=UTF-8` | ||
* contentType("foo"); // undefined | ||
* contentType("file.json"); // undefined | ||
* ``` | ||
*/ | ||
export function contentType< | ||
// Workaround to autocomplete for parameters: https://github.com/microsoft/TypeScript/issues/29729#issuecomment-567871939 | ||
// deno-lint-ignore ban-types | ||
T extends (string & {}) | KnownExtensionOrType, | ||
>( | ||
extensionOrType: T, | ||
): Lowercase<T> extends KnownExtensionOrType ? string : string | undefined { | ||
try { | ||
const [mediaType, params = {}] = extensionOrType.includes("/") | ||
? parseMediaType(extensionOrType) | ||
: [typeByExtension(extensionOrType), undefined]; | ||
if (!mediaType) { | ||
return undefined as Lowercase<T> extends KnownExtensionOrType ? string | ||
: string | undefined; | ||
} | ||
if (!("charset" in params)) { | ||
const charset = getCharset(mediaType); | ||
if (charset) { | ||
params.charset = charset; | ||
} | ||
} | ||
return formatMediaType(mediaType, params); | ||
} catch { | ||
// just swallow returning undefined | ||
} | ||
return undefined as Lowercase<T> extends KnownExtensionOrType ? string | ||
: string | undefined; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license. | ||
|
||
import { contentType } from "./content_type.ts"; | ||
import { assertEquals } from "../testing/asserts.ts"; | ||
|
||
Deno.test({ | ||
name: "media_types - contentType()", | ||
fn() { | ||
const fixtures = [ | ||
[".json", "application/json; charset=UTF-8"], | ||
["text/html", "text/html; charset=UTF-8"], | ||
["txt", "text/plain; charset=UTF-8"], | ||
["text/plain; charset=ISO-8859-1", "text/plain; charset=ISO-8859-1"], | ||
["foo", undefined], | ||
["file.json", undefined], | ||
["application/foo", "application/foo"], | ||
] as const; | ||
for (const [fixture, expected] of fixtures) { | ||
assertEquals(contentType(fixture), expected); | ||
} | ||
}, | ||
}); | ||
|
||
Deno.test({ | ||
name: "media_types - contentType()", | ||
fn() { | ||
let _str: string; | ||
// For well-known content types, the return type is a string. | ||
// string is assignable to string | ||
_str = contentType(".json"); | ||
_str = contentType("text/html"); | ||
_str = contentType("txt"); | ||
|
||
// @ts-expect-error: string | undefined is not assignable to string | ||
_str = contentType("text/plain; charset=ISO-8859-1"); | ||
// @ts-expect-error: string | undefined is not assignable to string | ||
_str = contentType("foo"); | ||
// @ts-expect-error: string | undefined is not assignable to string | ||
_str = contentType("file.json"); | ||
// @ts-expect-error: string | undefined is not assignable to string | ||
_str = contentType("application/foo"); | ||
}, | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license. | ||
import { extensionsByType } from "./extensions_by_type.ts"; | ||
|
||
/** | ||
* For a given media type, return the most relevant extension, or `undefined` | ||
* if no extension can be found. | ||
* | ||
* Extensions are returned without a leading `.`. | ||
* | ||
* @example | ||
* ```ts | ||
* import { extension } from "https://deno.land/std@$STD_VERSION/media_types/extension.ts"; | ||
* | ||
* extension("text/plain"); // `txt` | ||
* extension("application/json"); // `json` | ||
* extension("text/html; charset=UTF-8"); // `html` | ||
* extension("application/foo"); // undefined | ||
* ``` | ||
*/ | ||
export function extension(type: string): string | undefined { | ||
const exts = extensionsByType(type); | ||
if (exts) { | ||
return exts[0]; | ||
} | ||
return undefined; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license. | ||
|
||
import { assertEquals } from "../testing/asserts.ts"; | ||
import { extension } from "./mod.ts"; | ||
|
||
Deno.test({ | ||
name: "media_types - extension()", | ||
fn() { | ||
const fixtures: [string, string | undefined][] = [ | ||
["image/gif", "gif"], | ||
["application/javascript", "js"], | ||
["text/html; charset=UTF-8", "html"], | ||
["application/foo", undefined], | ||
]; | ||
for (const [fixture, expected] of fixtures) { | ||
assertEquals(extension(fixture), expected); | ||
} | ||
}, | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license. | ||
import { parseMediaType } from "./parse_media_type.ts"; | ||
import { extensions } from "./_util.ts"; | ||
|
||
export { extensions }; | ||
|
||
/** | ||
* Returns the extensions known to be associated with the media type `type`. | ||
* The returned extensions will each begin with a leading dot, as in `.html`. | ||
* | ||
* When `type` has no associated extensions, the function returns `undefined`. | ||
* | ||
* Extensions are returned without a leading `.`. | ||
* | ||
* @example | ||
* ```ts | ||
* import { extensionsByType } from "https://deno.land/std@$STD_VERSION/media_types/extensions_by_type.ts"; | ||
* | ||
* extensionsByType("application/json"); // ["json", "map"] | ||
* extensionsByType("text/html; charset=UTF-8"); // ["html", "htm", "shtml"] | ||
* extensionsByType("application/foo"); // undefined | ||
* ``` | ||
*/ | ||
export function extensionsByType(type: string): string[] | undefined { | ||
try { | ||
const [mediaType] = parseMediaType(type); | ||
return extensions.get(mediaType); | ||
} catch { | ||
// just swallow errors, returning undefined | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license. | ||
|
||
import { assertEquals } from "../testing/asserts.ts"; | ||
import { extensionsByType } from "./mod.ts"; | ||
|
||
Deno.test({ | ||
name: "media_types - extensionsByType()", | ||
fn() { | ||
const fixtures: [string, string[] | undefined][] = [ | ||
["image/gif", ["gif"]], | ||
["application/javascript", ["js", "mjs"]], | ||
["text/html; charset=UTF-8", ["html", "htm", "shtml"]], | ||
["application/foo", undefined], | ||
]; | ||
for (const [fixture, expected] of fixtures) { | ||
assertEquals(extensionsByType(fixture), expected); | ||
} | ||
}, | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license. | ||
import { isIterator, isToken, needsEncoding } from "./_util.ts"; | ||
|
||
/** Serializes the media type and the optional parameters as a media type | ||
* conforming to RFC 2045 and RFC 2616. | ||
* | ||
* The type and parameter names are written in lower-case. | ||
* | ||
* When any of the arguments results in a standard violation then the return | ||
* value will be an empty string (`""`). | ||
* | ||
* @example | ||
* ```ts | ||
* import { formatMediaType } from "https://deno.land/std@$STD_VERSION/media_types/format_media_type.ts"; | ||
* | ||
* formatMediaType("text/plain", { charset: "UTF-8" }); // `text/plain; charset=UTF-8` | ||
* ``` | ||
*/ | ||
export function formatMediaType( | ||
type: string, | ||
param?: Record<string, string> | Iterable<[string, string]>, | ||
): string { | ||
let b = ""; | ||
const [major, sub] = type.split("/"); | ||
if (!sub) { | ||
if (!isToken(type)) { | ||
return ""; | ||
} | ||
b += type.toLowerCase(); | ||
} else { | ||
if (!isToken(major) || !isToken(sub)) { | ||
return ""; | ||
} | ||
b += `${major.toLowerCase()}/${sub.toLowerCase()}`; | ||
} | ||
|
||
if (param) { | ||
param = isIterator(param) ? Object.fromEntries(param) : param; | ||
const attrs = Object.keys(param); | ||
attrs.sort(); | ||
|
||
for (const attribute of attrs) { | ||
if (!isToken(attribute)) { | ||
return ""; | ||
} | ||
const value = param[attribute]; | ||
b += `; ${attribute.toLowerCase()}`; | ||
|
||
const needEnc = needsEncoding(value); | ||
if (needEnc) { | ||
b += "*"; | ||
} | ||
b += "="; | ||
|
||
if (needEnc) { | ||
b += `utf-8''${encodeURIComponent(value)}`; | ||
continue; | ||
} | ||
|
||
if (isToken(value)) { | ||
b += value; | ||
continue; | ||
} | ||
b += `"${value.replace(/["\\]/gi, (m) => `\\${m}`)}"`; | ||
} | ||
} | ||
return b; | ||
} |
Oops, something went wrong.