From bd854125abc8249baf1b6f0dad4d4a2383b60a28 Mon Sep 17 00:00:00 2001 From: Borewit Date: Sun, 15 Dec 2024 14:19:02 +0100 Subject: [PATCH] Give API access to `FileTypeParser.detectors` This gives the user more control to determine the sequence of detectors. Resolves: sindresorhus/file-type#628 --- core.d.ts | 5 ++++- core.js | 15 ++++++--------- readme.md | 29 +++++++++++++++-------------- 3 files changed, 25 insertions(+), 24 deletions(-) diff --git a/core.d.ts b/core.d.ts index d782a9a5..85020571 100644 --- a/core.d.ts +++ b/core.d.ts @@ -180,7 +180,10 @@ This method can be handy to put in a stream pipeline, but it comes with a price. export function fileTypeStream(webStream: AnyWebReadableStream, options?: StreamOptions): Promise; export declare class FileTypeParser { - detectors: Iterable; + /** + Array if file-type detectors + */ + readonly detectors: Detector[]; constructor(options?: {customDetectors?: Iterable; signal?: AbortSignal}); diff --git a/core.js b/core.js index 53e5eb8a..f729e05e 100644 --- a/core.js +++ b/core.js @@ -57,19 +57,18 @@ export async function fileTypeStream(webStream, options) { export class FileTypeParser { constructor(options) { - this.detectors = options?.customDetectors; + this.detectors = options?.customDetectors ?? []; this.tokenizerOptions = { abortSignal: options?.signal, }; - this.fromTokenizer = this.fromTokenizer.bind(this); - this.fromBuffer = this.fromBuffer.bind(this); - this.parse = this.parse.bind(this); + this.detectors.push(this.parse); // Assign core file-type detector } async fromTokenizer(tokenizer) { const initialPosition = tokenizer.position; - for (const detector of this.detectors || []) { + // Iterate through all file-type detectors + for (const detector of this.detectors) { const fileType = await detector(tokenizer); if (fileType) { return fileType; @@ -79,8 +78,6 @@ export class FileTypeParser { return undefined; // Cannot proceed scanning of the tokenizer is at an arbitrary position } } - - return this.parse(tokenizer); } async fromBuffer(input) { @@ -163,7 +160,7 @@ export class FileTypeParser { return this.check(stringToBytes(header), options); } - async parse(tokenizer) { + parse = async tokenizer => { this.buffer = new Uint8Array(reasonableDetectionSizeInBytes); // Keep reading until EOF if the file size is unknown. @@ -1669,7 +1666,7 @@ export class FileTypeParser { }; } } - } + }; async readTiffTag(bigEndian) { const tagId = await this.tokenizer.readToken(bigEndian ? Token.UINT16_BE : Token.UINT16_LE); diff --git a/readme.md b/readme.md index f6afa9c6..71f21903 100644 --- a/readme.md +++ b/readme.md @@ -342,10 +342,12 @@ Returns a `Set` of supported MIME types. A custom detector is a function that allows specifying custom detection mechanisms. -An iterable of detectors can be provided via the `fileTypeOptions` argument for the `FileTypeParser` constructor. +An array of detectors can be provided via the `fileTypeOptions` argument for the `FileTypeParser` constructor. In Node.js, you should use `NodeFileTypeParser`, which extends `FileTypeParser` and provides access to Node.js specific functions. -The detectors are called before the default detections in the provided order. +Detectors can be added via the constructor options, or by adding it directly to `FileTypeParser.detectors`. + +The detectors provided via the constructor options, are called before the default detectors are called. Custom detectors can be used to add new `FileTypeResults` or to modify return behaviour of existing `FileTypeResult` detections. @@ -361,23 +363,22 @@ Example detector array which can be extended and provided to each public method ```js import {FileTypeParser} from 'file-type'; // or `NodeFileTypeParser` in Node.js -const customDetectors = [ - async tokenizer => { - const unicornHeader = [85, 78, 73, 67, 79, 82, 78]; // 'UNICORN' as decimal string +const customDetector = async tokenizer => { + const unicornHeader = [85, 78, 73, 67, 79, 82, 78]; // 'UNICORN' as decimal string - const buffer = new Uint8Array(7); - await tokenizer.peekBuffer(buffer, {length: unicornHeader.length, mayBeLess: true}); + const buffer = new Uint8Array(7); + await tokenizer.peekBuffer(buffer, {length: unicornHeader.length, mayBeLess: true}); - if (unicornHeader.every((value, index) => value === buffer[index])) { - return {ext: 'unicorn', mime: 'application/unicorn'}; - } + if (unicornHeader.every((value, index) => value === buffer[index])) { + return {ext: 'unicorn', mime: 'application/unicorn'}; + } - return undefined; - }, -]; + return undefined; +}; const buffer = new Uint8Array(new TextEncoder().encode('UNICORN')); -const parser = new FileTypeParser({customDetectors}); // `NodeFileTypeParser({customDetectors})` in Node.js +const parser = new FileTypeParser(); // `NodeFileTypeParser({customDetectors})` in Node.js +parser.detectors.unshift(customDetector); // Make customDetector the first detector const fileType = await parser.fromBuffer(buffer); console.log(fileType); ```