UNPKG

@oazmi/esbuild-plugin-deno

Version:

A portable non-invasive suite of esbuild plugins for loading http, jsr, npm, and import-maps. Supports Deno, Node, Bun, Web. Alternate to @luca/esbuild-deno-loader , while compatible with other plugins and native esbuild resolvers (e.g. css loader).

74 lines (73 loc) 4.37 kB
/** utility submodule for identifying an http response's mime type ("content-type" header), and mapping it to an esbuild loader. * * TODO: currently, the mime and extension to esbuild loader mapping is immutable. * the end user might benefit more if they could directly modify it, * instead of only being able to modify it via `mimeTypeLoaderMap` and `extensionTypeLoaderMap`. * but on the other hand, making them immutable would mean that we will not be able to use a an `InvertibleMap` class, * and instead have to always generate the inverse map on every call to `guessMimeLoaders`, which is highly inefficient. * or alternatively, we might have to store the data in an inverted map in the first place to prevent the need for inverting. * but that will make its representation quite ugly. * * @module */ import { InvertibleMap, object_entries, parseFilepathInfo } from "../deps.js"; import extensionMap from "./extensions.js"; import mimeMap from "./mimes.js"; const mime_map_entries = object_entries(mimeMap).map(([loader_name, mime_types]) => { return [loader_name, new Set(mime_types)]; }); const extension_map_entries = object_entries(extensionMap).map(([loader_name, extension_types]) => { return [loader_name, new Set(extension_types)]; }); /** a pre-defined invertible-mapping between http-header `"content-type"` (aka mime-type) and esbuild's default loader selection. * * the mapping definitions are located in the {@link mimeMap | `./mimes.json`} file. */ export const mimeTypeLoaderMap = new InvertibleMap(new Map(mime_map_entries)); /** a pre-defined invertible-mapping between path-extensions (suffix) and esbuild's default loader selection. * * the mapping definitions are located in the {@link extensionMap | `./extensions.json`} file. */ export const extensionTypeLoaderMap = new InvertibleMap(new Map(extension_map_entries)); /** guesses the potential esbuild loader types for a given mime type. * * @param content_type the mime string that you wish to guess the loader of * @returns a set of possible esbuild loaders appropriate for the given `content_type` mime. */ export const guessMimeLoaders = (content_type) => { // first we normalize the mime string by discarding everything that comes after the semicolon (since it contains optional parameters). // and then we trim any whitespaces and set all characters to lowercase. const [mime_type, ...rest] = content_type.split(";"), normalized_mime_type = mime_type.trim().toLowerCase(); return mimeTypeLoaderMap.rget(normalized_mime_type); }; /** guesses the potential esbuild loader types for a given mime type. * * @param content_type the mime string that you wish to guess the loader of * @returns a set of possible esbuild loaders appropriate for the given `content_type` mime. */ export const guessExtensionLoaders = (file_path) => { // we need a special case for ".module.css" extension because `parseFilepathInfo` would only return ".css" as its `extname`. const file_extension = file_path.endsWith(".module.css") ? ".module.css" : parseFilepathInfo(file_path).extname; return extensionTypeLoaderMap.rget(file_extension); }; /** given an http response, this function guesses the appropriate {@link esbuild.Loader | esbuild loaders} for it. * it does so by looking at the `"content-type"` header of the response, and also by inspecting the file extension of the request's url path. * * @param response your http response object. * @returns a set of zero or more esbuild loaders that are accepted by the current http response. */ export const guessHttpResponseLoaders = (response) => { const { headers, url } = response, content_type = headers.get("content-type") ?? "", mime_loaders = guessMimeLoaders(content_type) ?? new Set(), extension_loaders = guessExtensionLoaders(url) ?? new Set(); let common_loaders = mime_loaders.intersection(extension_loaders); // if there is no intersection between `mime_loaders` and `extension_loaders`, then we will give preference to `extension_loaders`, // otherwise, if even that turns out to be empty, then we will use the loaders from `mime_loaders`. if (common_loaders.size <= 0) { common_loaders = extension_loaders; } if (common_loaders.size <= 0) { common_loaders = mime_loaders; } return common_loaders; };