Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add TypeScript definition #188

Merged
merged 5 commits into from
Mar 5, 2019
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/pull_request_template.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ If you're adding support for a new file type, please follow the below steps:
- Add a fixture file named `fixture.<extension>` to the `fixture` directory.
- Add the file extension to the `types` array in `test.js`.
- Add the file type detection logic to the `index.js` file.
- Add the file extension to the `FileType` type in `index.d.ts`.
- Add the file extension to the `Supported file types` section in the readme, in the format ```- [`<extension>`](URL) - Format name```, for example, ```- [`png`](https://en.wikipedia.org/wiki/Portable_Network_Graphics) - Portable Network Graphics```
- Add the file extension to the `keywords` array in the `package.json` file.
- Run `$ npm test` to ensure the tests pass.
Expand Down
139 changes: 139 additions & 0 deletions index.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
/// <reference types="node"/>
import {Readable} from 'stream';
BendingBender marked this conversation as resolved.
Show resolved Hide resolved

export type FileType =
| 'jpg'
| 'png'
| 'gif'
| 'webp'
| 'flif'
| 'cr2'
| 'tif'
| 'bmp'
| 'jxr'
| 'psd'
| 'zip'
| 'tar'
| 'rar'
| 'gz'
| 'bz2'
| '7z'
| 'dmg'
| 'mp4'
| 'm4v'
| 'mid'
| 'mkv'
| 'webm'
| 'mov'
| 'avi'
| 'wmv'
| 'mpg'
| 'mp2'
| 'mp3'
| 'm4a'
| 'ogg'
| 'opus'
| 'flac'
| 'wav'
| 'qcp'
| 'amr'
| 'pdf'
| 'epub'
| 'mobi'
| 'exe'
| 'swf'
| 'rtf'
| 'woff'
| 'woff2'
| 'eot'
| 'ttf'
| 'otf'
| 'ico'
| 'flv'
| 'ps'
| 'xz'
| 'sqlite'
| 'nes'
| 'crx'
| 'xpi'
| 'cab'
| 'deb'
| 'ar'
| 'rpm'
| 'Z'
| 'lz'
| 'msi'
| 'mxf'
| 'mts'
| 'wasm'
| 'blend'
| 'bpg'
| 'docx'
| 'pptx'
| 'xlsx'
| '3gp'
| 'jp2'
| 'jpm'
| 'jpx'
| 'mj2'
| 'aif'
| 'odt'
| 'ods'
| 'odp'
| 'xml'
| 'heic'
| 'cur'
| 'ktx'
| 'ape'
| 'wv'
| 'asf'
| 'wma'
| 'wmv'
| 'dcm'
| 'mpc'
| 'ics'
| 'glb'
| 'pcap';
BendingBender marked this conversation as resolved.
Show resolved Hide resolved

export interface FileTypeResult {
/**
* One of the supported [file types](https://github.com/sindresorhus/file-type#supported-file-types).
*/
ext: FileType;

/**
* The detected [MIME type](https://en.wikipedia.org/wiki/Internet_media_type).
*/
mime: string;
}

export type ReadableWithFileType = Readable & {
readonly fileType: FileTypeResult | null;
};

export interface FileTypeModule {
(buffer: Buffer | Uint8Array): FileTypeResult | null;

/**
* The minimum amount of bytes needed to detect a file type. Currently, it's 4100 bytes, but it can change, so don't hard-code it.
*/
readonly minimumBytes: number;

/**
* Detect the file type of a readable stream.
*
* @param readableStream - A readable stream containing a file to examine, see: [`stream.Readable`](https://nodejs.org/api/stream.html#stream_class_stream_readable).
* @returns A `Promise` which resolves to the original readable stream argument, but with an added `fileType` property, which is an object like the one returned from `fileType()`.
*/
readonly stream: (readableStream: Readable) => Promise<ReadableWithFileType>;
}

/**
* Detect the file type of a `Buffer`/`Uint8Array`. The file type is detected by checking the [magic number](https://en.wikipedia.org/wiki/Magic_number_(programming)#Magic_numbers_in_files) of the buffer.
*
* @param buffer - It only needs the first `.minimumBytes` bytes. The exception is detection of `docx`, `pptx`, and `xlsx` which potentially requires reading the whole file.
* @returns An object with the detected file type and MIME type or `null` when there was no match.
*/
declare const fileType: FileTypeModule;

export default fileType;
16 changes: 13 additions & 3 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,11 @@ function readUInt64LE(buf, offset = 0) {
mul *= 0x100;
n += buf[offset + i] * mul;
}

return n;
}

module.exports = input => {
const fileType = input => {
if (!(input instanceof Uint8Array || Buffer.isBuffer(input))) {
throw new TypeError(`Expected the \`input\` argument to be of type \`Uint8Array\` or \`Buffer\`, got \`${typeof input}\``);
}
Expand Down Expand Up @@ -344,12 +345,14 @@ module.exports = input => {
mime: 'video/vnd.avi'
};
}

if (check([0x57, 0x41, 0x56, 0x45], {offset: 8})) {
return {
ext: 'wav',
mime: 'audio/vnd.wave'
};
}

// QLCM, QCP file
if (check([0x51, 0x4C, 0x43, 0x4D], {offset: 8})) {
return {
Expand Down Expand Up @@ -386,6 +389,7 @@ module.exports = input => {

break;
}

offset += objectSize;
} while (offset + 24 <= buf.length);

Expand Down Expand Up @@ -481,13 +485,15 @@ module.exports = input => {
mime: 'video/ogg'
};
}

// If '\x01video' in header.
if (check([0x01, 0x76, 0x69, 0x64, 0x65, 0x6F, 0x00], {offset: 28})) {
return {
ext: 'ogm',
mime: 'video/ogg'
};
}

// If ' FLAC' in header https://xiph.org/flac/faq.html
if (check([0x7F, 0x46, 0x4C, 0x41, 0x43], {offset: 28})) {
return {
Expand Down Expand Up @@ -917,15 +923,19 @@ module.exports = input => {
return null;
};

Object.defineProperty(module.exports, 'minimumBytes', {value: 4100});
module.exports = fileType;
module.exports.default = fileType;

Object.defineProperty(fileType, 'minimumBytes', {value: 4100});

module.exports.stream = readableStream => new Promise(resolve => {
// using `eval` to work around issues when bundling with Webpack
const stream = eval('require')('stream'); // eslint-disable-line no-eval

readableStream.once('readable', () => {
const pass = new stream.PassThrough();
const chunk = readableStream.read(module.exports.minimumBytes) || readableStream.read();
pass.fileType = module.exports(chunk);
pass.fileType = fileType(chunk);
readableStream.unshift(chunk);

if (stream.pipeline) {
Expand Down
19 changes: 19 additions & 0 deletions index.test-d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import {expectType} from 'tsd-check';
import fileType, {FileTypeResult, FileType, ReadableWithFileType} from '.';
import * as fs from 'fs';

expectType<FileTypeResult | null>(fileType(new Buffer([0xff, 0xd8, 0xff])));
expectType<FileTypeResult | null>(fileType(new Uint8Array([0xff, 0xd8, 0xff])));

const result = fileType(new Buffer([0xff, 0xd8, 0xff]));
if (result != null) {
expectType<FileType>(result.ext);
expectType<string>(result.mime);
}

expectType<number>(fileType.minimumBytes);

const readableStream = fs.createReadStream('file.png');
const streamWithFileType = fileType.stream(readableStream);
expectType<Promise<ReadableWithFileType>>(streamWithFileType);
expectType<FileTypeResult>((await streamWithFileType).fileType);
11 changes: 7 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,11 @@
"node": ">=6"
},
"scripts": {
"test": "xo && ava"
"test": "xo && ava && tsd-check"
},
"files": [
"index.js"
"index.js",
"index.d.ts"
],
"keywords": [
"mime",
Expand Down Expand Up @@ -120,9 +121,11 @@
"pcap"
],
"devDependencies": {
"ava": "^1.0.1",
"@types/node": "^11.10.4",
"ava": "^1.2.1",
"pify": "^4.0.1",
"read-chunk": "^3.0.0",
"xo": "^0.23.0"
"tsd-check": "^0.3.0",
"xo": "^0.24.0"
}
}
16 changes: 8 additions & 8 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

> Detect the file type of a Buffer/Uint8Array

The file type is detected by checking the [magic number](http://en.wikipedia.org/wiki/Magic_number_(programming)#Magic_numbers_in_files) of the buffer.
The file type is detected by checking the [magic number](https://en.wikipedia.org/wiki/Magic_number_(programming)#Magic_numbers_in_files) of the buffer.


## Install
Expand Down Expand Up @@ -36,7 +36,7 @@ Or from a remote location:
const http = require('http');
const fileType = require('file-type');

const url = 'http://assets-cdn.github.com/images/spinners/octocat-spinner-32.gif';
const url = 'https://assets-cdn.github.com/images/spinners/octocat-spinner-32.gif';

http.get(url, response => {
response.on('readable', () => {
Expand Down Expand Up @@ -93,7 +93,7 @@ xhr.send();
Returns an `Object` with:

- `ext` - One of the [supported file types](#supported-file-types)
- `mime` - The [MIME type](http://en.wikipedia.org/wiki/Internet_media_type)
- `mime` - The [MIME type](https://en.wikipedia.org/wiki/Internet_media_type)

Or `null` when there is no match.

Expand Down Expand Up @@ -127,7 +127,7 @@ Type: [`stream.Readable`](https://nodejs.org/api/stream.html#stream_class_stream
- [`gif`](https://en.wikipedia.org/wiki/GIF)
- [`webp`](https://en.wikipedia.org/wiki/WebP)
- [`flif`](https://en.wikipedia.org/wiki/Free_Lossless_Image_Format)
- [`cr2`](http://fileinfo.com/extension/cr2)
- [`cr2`](https://fileinfo.com/extension/cr2)
- [`tif`](https://en.wikipedia.org/wiki/Tagged_Image_File_Format)
- [`bmp`](https://en.wikipedia.org/wiki/BMP_file_format)
- [`jxr`](https://en.wikipedia.org/wiki/JPEG_XR)
Expand Down Expand Up @@ -173,14 +173,14 @@ Type: [`stream.Readable`](https://nodejs.org/api/stream.html#stream_class_stream
- [`ps`](https://en.wikipedia.org/wiki/Postscript)
- [`xz`](https://en.wikipedia.org/wiki/Xz)
- [`sqlite`](https://www.sqlite.org/fileformat2.html)
- [`nes`](http://fileinfo.com/extension/nes)
- [`nes`](https://fileinfo.com/extension/nes)
- [`crx`](https://developer.chrome.com/extensions/crx)
- [`xpi`](https://en.wikipedia.org/wiki/XPInstall)
- [`cab`](https://en.wikipedia.org/wiki/Cabinet_(file_format))
- [`deb`](https://en.wikipedia.org/wiki/Deb_(file_format))
- [`ar`](https://en.wikipedia.org/wiki/Ar_(Unix))
- [`rpm`](http://fileinfo.com/extension/rpm)
- [`Z`](http://fileinfo.com/extension/z)
- [`rpm`](https://fileinfo.com/extension/rpm)
- [`Z`](https://fileinfo.com/extension/z)
- [`lz`](https://en.wikipedia.org/wiki/Lzip)
- [`msi`](https://en.wikipedia.org/wiki/Windows_Installer)
- [`mxf`](https://en.wikipedia.org/wiki/Material_Exchange_Format)
Expand All @@ -201,7 +201,7 @@ Type: [`stream.Readable`](https://nodejs.org/api/stream.html#stream_class_stream
- [`ods`](https://en.wikipedia.org/wiki/OpenDocument) - OpenDocument for spreadsheets
- [`odp`](https://en.wikipedia.org/wiki/OpenDocument) - OpenDocument for presentations
- [`xml`](https://en.wikipedia.org/wiki/XML)
- [`heic`](http://nokiatech.github.io/heif/technical.html)
- [`heic`](https://nokiatech.github.io/heif/technical.html)
- [`cur`](https://en.wikipedia.org/wiki/ICO_(file_format))
- [`ktx`](https://www.khronos.org/opengles/sdk/tools/KTX/file_format_spec/)
- [`ape`](https://en.wikipedia.org/wiki/Monkey%27s_Audio) - Monkey's Audio
Expand Down