diff --git a/CHANGELOG.md b/CHANGELOG.md index 1e39a1f..82d6c5c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Added + +- `isImageMimeType`, `isPdfMimeType`, `isVideoMimeType`, `isAudioMimeType`, `isTextMimeType`, and `isApplicationMimeType` MIME type utility functions + ## [1.4.0] - 2025-06-24 ### Added diff --git a/src/index.ts b/src/index.ts index 7d4b9b4..b2fc510 100644 --- a/src/index.ts +++ b/src/index.ts @@ -4,6 +4,7 @@ export * from "./lib/date"; export * from "./lib/enum"; export * from "./lib/guid"; export * from "./lib/localStorage"; +export * from "./lib/mimeType"; export * from "./lib/number"; export * from "./lib/object"; export * from "./lib/string"; diff --git a/src/lib/mimeType.spec.ts b/src/lib/mimeType.spec.ts new file mode 100644 index 0000000..d23bb33 --- /dev/null +++ b/src/lib/mimeType.spec.ts @@ -0,0 +1,122 @@ +import { isImageMimeType, isPdfMimeType, isVideoMimeType, isAudioMimeType, isTextMimeType, isApplicationMimeType } from "./mimeType"; + +describe("mimeType tests", () => { + // Common invalid inputs + const invalidInputs: [string | null | undefined | number | Date, boolean][] = [ + [null as unknown as string, false], + [undefined as unknown as string, false], + [0 as unknown as string, false], + [new Date() as unknown as string, false], + ["", false], + [" ", false], + ["not-a-mime-type", false], + ]; + + test.each([ + ...invalidInputs, + // Valid image MIME types + ["image/jpeg", true], + ["image/png", true], + ["image/gif", true], + ["image/webp", true], + ["image/svg+xml", true], + ["IMAGE/JPEG", true], // Case insensitive + [" image/gif ", true], // Whitespace trimmed + // Invalid cases + ["application/pdf", false], + ["text/plain", false], + ["video/mp4", false], + ["image", false], + ["image/", false], + ["/image", false], + ])("isImageMimeType", (mimeType, expected) => { + expect(isImageMimeType(mimeType as string)).toBe(expected); + }); + + test.each([ + ...invalidInputs, + // Valid PDF MIME type + ["application/pdf", true], + ["APPLICATION/PDF", true], // Case insensitive + [" application/pdf ", true], // Whitespace trimmed + // Invalid cases + ["image/jpeg", false], + ["application/json", false], + ["pdf", false], + ["application/", false], + ])("isPdfMimeType", (mimeType, expected) => { + expect(isPdfMimeType(mimeType as string)).toBe(expected); + }); + + test.each([ + ...invalidInputs, + // Valid video MIME types + ["video/mp4", true], + ["video/mpeg", true], + ["video/webm", true], + ["VIDEO/MP4", true], // Case insensitive + [" video/webm ", true], // Whitespace trimmed + // Invalid cases + ["image/jpeg", false], + ["audio/mp3", false], + ["video", false], + ["video/", false], + ["/video", false], + ])("isVideoMimeType", (mimeType, expected) => { + expect(isVideoMimeType(mimeType as string)).toBe(expected); + }); + + test.each([ + ...invalidInputs, + // Valid audio MIME types + ["audio/mp3", true], + ["audio/mpeg", true], + ["audio/wav", true], + ["AUDIO/MP3", true], // Case insensitive + [" audio/ogg ", true], // Whitespace trimmed + // Invalid cases + ["video/mp4", false], + ["image/jpeg", false], + ["audio", false], + ["audio/", false], + ["/audio", false], + ])("isAudioMimeType", (mimeType, expected) => { + expect(isAudioMimeType(mimeType as string)).toBe(expected); + }); + + test.each([ + ...invalidInputs, + // Valid text MIME types + ["text/plain", true], + ["text/html", true], + ["text/css", true], + ["TEXT/PLAIN", true], // Case insensitive + [" text/css ", true], // Whitespace trimmed + // Invalid cases + ["application/pdf", false], + ["image/jpeg", false], + ["text", false], + ["text/", false], + ["/text", false], + ])("isTextMimeType", (mimeType, expected) => { + expect(isTextMimeType(mimeType as string)).toBe(expected); + }); + + test.each([ + ...invalidInputs, + // Valid application MIME types + ["application/pdf", true], + ["application/json", true], + ["application/xml", true], + ["APPLICATION/PDF", true], // Case insensitive + [" application/xml ", true], // Whitespace trimmed + // Invalid cases + ["text/plain", false], + ["image/jpeg", false], + ["application", false], + ["application/", false], + ["/application", false], + ])("isApplicationMimeType", (mimeType, expected) => { + expect(isApplicationMimeType(mimeType as string)).toBe(expected); + }); +}); diff --git a/src/lib/mimeType.ts b/src/lib/mimeType.ts new file mode 100644 index 0000000..3766380 --- /dev/null +++ b/src/lib/mimeType.ts @@ -0,0 +1,91 @@ +import { isNullOrEmpty } from "./string"; + +/** + * Private helper function to check if a MIME type matches a main type pattern + * @param mimeType The MIME type to check + * @param mainType The main MIME type (e.g., "image", "video") + * @returns true if the MIME type matches the pattern + */ +function isMainMimeType(mimeType: string, mainType: string): boolean { + const normalizedMimeType = mimeType.toLowerCase().trim(); + return normalizedMimeType.startsWith(`${mainType}/`) && normalizedMimeType.length > mainType.length + 1; +} + +/** + * Check if the MIME type represents an image + * @param mimeType The MIME type to check + * @returns true if the MIME type is for an image + */ +export function isImageMimeType(mimeType?: string): boolean { + if (isNullOrEmpty(mimeType)) { + return false; + } + + return isMainMimeType(mimeType!, "image"); +} + +/** + * Check if the MIME type represents a PDF document + * @param mimeType The MIME type to check + * @returns true if the MIME type is for a PDF document + */ +export function isPdfMimeType(mimeType?: string): boolean { + if (isNullOrEmpty(mimeType)) { + return false; + } + + const normalizedMimeType = mimeType!.toLowerCase().trim(); + return normalizedMimeType === "application/pdf"; +} + +/** + * Check if the MIME type represents a video + * @param mimeType The MIME type to check + * @returns true if the MIME type is for a video + */ +export function isVideoMimeType(mimeType?: string): boolean { + if (isNullOrEmpty(mimeType)) { + return false; + } + + return isMainMimeType(mimeType!, "video"); +} + +/** + * Check if the MIME type represents an audio file + * @param mimeType The MIME type to check + * @returns true if the MIME type is for an audio file + */ +export function isAudioMimeType(mimeType?: string): boolean { + if (isNullOrEmpty(mimeType)) { + return false; + } + + return isMainMimeType(mimeType!, "audio"); +} + +/** + * Check if the MIME type represents a text file + * @param mimeType The MIME type to check + * @returns true if the MIME type is for a text file + */ +export function isTextMimeType(mimeType?: string): boolean { + if (isNullOrEmpty(mimeType)) { + return false; + } + + return isMainMimeType(mimeType!, "text"); +} + +/** + * Check if the MIME type represents an application file + * @param mimeType The MIME type to check + * @returns true if the MIME type is for an application file + */ +export function isApplicationMimeType(mimeType?: string): boolean { + if (isNullOrEmpty(mimeType)) { + return false; + } + + return isMainMimeType(mimeType!, "application"); +}