Skip to content

Commit

Permalink
feat: utility for MIME type detection (detect func) (#24)
Browse files Browse the repository at this point in the history
## Summary
- Integrated a MIME type detection utility in the `fepack/image`
package. This enhancement will allow users to easily identify and work
with various image formats.
- Being able to reliably determine the MIME type of an image ensures
better compatibility and error handling. potentially eliminating a
number of runtime errors.

<!-- Please summarize your changes. -->

<!-- Please link to any applicable information (forum posts, bug
reports, etc.). -->

## Checks

<!-- For completed items, change [ ] to [x]. -->

<!-- If you leave this checklist empty, your PR will very likely be
closed. -->

Please check the following:

- [x] I have written documents and tests, if needed.
  • Loading branch information
tooooo1 authored Sep 18, 2023
1 parent 02d5e8e commit 8615b96
Show file tree
Hide file tree
Showing 7 changed files with 131 additions and 22 deletions.
5 changes: 5 additions & 0 deletions .changeset/stale-bulldogs-report.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@fepack/image": patch
---

feat: utility for MIME type detection (detect func)
5 changes: 5 additions & 0 deletions .changeset/sweet-gifts-grow.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@fepack/image": patch
---

feat: utility for MIME type detection (detect func)
1 change: 1 addition & 0 deletions packages/image/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
"@fepack/eslint-config-js": "workspace:*",
"@fepack/eslint-config-ts": "workspace:*",
"@fepack/tsconfig": "workspace:*",
"@types/node": "^20.6.2",
"@vitest/browser": "^0.34.4",
"@vitest/coverage-istanbul": "^0.34.4",
"esbuild": "^0.18.11",
Expand Down
33 changes: 33 additions & 0 deletions packages/image/src/__test__/detect.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { Buffer } from "buffer";
import { describe, expect, test } from "vitest";
import { detect } from "..";
import { FILE_TYPES } from "../detect";

describe("MIME type detection utility", () => {
test("should return null for unknown signatures", () => {
const unknownSignature = Buffer.from([0x00, 0x00, 0x00, 0x00]);
expect(detect(unknownSignature)).toBeNull();
});

Object.keys(FILE_TYPES).forEach((key) => {
const { mime, signature } = FILE_TYPES[key];

test(`should detect ${key} files`, () => {
const signatureBuffer = Buffer.from(signature);
expect(detect(signatureBuffer)).toEqual(mime);
});
});

test("should return null for partial signatures", () => {
const partialJPEGSignature = Buffer.from([0xff, 0xd8]);
expect(detect(partialJPEGSignature)).toBeNull();
});

test("should detect MIME type even with extra data", () => {
const jpegWithExtraData = Buffer.concat([
Buffer.from(FILE_TYPES.JPEG.signature),
Buffer.from([0x00, 0x01, 0x02]),
]);
expect(detect(jpegWithExtraData)).toEqual(FILE_TYPES.JPEG.mime);
});
});
57 changes: 57 additions & 0 deletions packages/image/src/detect.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/**
* Represents the file signatures for various MIME types.
* @see https://en.wikipedia.org/wiki/List_of_file_signatures
*/
export const FILE_TYPES = {
JPEG: {
mime: "image/jpeg",
signature: [0xff, 0xd8, 0xff],
},
PNG: {
mime: "image/png",
signature: [0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a],
},
GIF: {
mime: "image/gif",
signature: [0x47, 0x49, 0x46, 0x38],
},
WEBP: {
mime: "image/webp",
signature: [0x52, 0x49, 0x46, 0x46, 0, 0, 0, 0, 0x57, 0x45, 0x42, 0x50],
},
SVG: {
mime: "image/svg+xml",
signature: [0x3c, 0x3f, 0x78, 0x6d, 0x6c],
},
AVIF: {
mime: "image/avif",
signature: [0, 0, 0, 0, 0x66, 0x74, 0x79, 0x70, 0x61, 0x76, 0x69, 0x66],
},
ICO: {
mime: "image/x-icon",
signature: [0x00, 0x00, 0x01, 0x00],
},
};

const FILE_TYPE_KEYS = Object.keys(
FILE_TYPES,
) as readonly (keyof typeof FILE_TYPES)[];

/**
* Inspects the first few bytes of a buffer to determine if
* it matches a known file signature.
*
* @param {Buffer} buffer - The buffer containing the file's first few bytes.
* @returns The detected MIME type or null if no known signature is matched.
*/
export function detect(buffer: Buffer) {
for (const key of FILE_TYPE_KEYS) {
const { mime, signature } = FILE_TYPES[key];

if (signature.every((byte, index) => byte === buffer[index])) {
return mime;
}
}

return null;
}
1 change: 1 addition & 0 deletions packages/image/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export { default as checkWebPSupport } from "./checkWebPSupport";
export { detect } from "./detect";
export { load, type ImageSource } from "./load";
51 changes: 29 additions & 22 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 8615b96

Please sign in to comment.