This commit is contained in:
2019-09-17 13:20:42 -04:00
parent d211d1dc34
commit bef10ce4c9
8352 changed files with 568242 additions and 51 deletions
+90
View File
@@ -0,0 +1,90 @@
# History
## v2.5.1 2019 January 21
- Fixed a readme documentation inconsistency
- Fixed node v0.12 and v4 support (regression since v2.5.0)
## v2.5.0 2019 January 21
As the detection algorithms are result returns, with the asynchronous signatures just wrappers, we have changed `isText`, `isBinary`, and `getEncoding` to return the result if no callback was provided to them, maintaining backwards compatibility, but encouraging intuitive usage of the methods with the least overhead.
- The following methods have had return signatures added to them, which should be the preferable usage:
- `isText`, which you should use instead of `isTextSync` (a method which only lingers for backwards compatibility)
- `isBinary`, which you should use instead of `isBinarySync` (a method which only lingers for backwards compatibility)
- `getEncoding`, which you should use instead of `getEncoding` (a method which only lingers for backwards compatibility)
- If you require callback usage, the following callback wrapper methods have been added:
- `isTextCallback`, which you should use instead of `isText`'s callback signature (a signature which only lingers for backwards compatibility)
- `isBinaryCallback`, which you should use instead of `isBinary`'s callback signature (a signature which only lingers for backwards compatibility)
- `getEncodingCallback`, which you should use instead of `getEncoding`'s callback signature (a signature which only lingers for backwards compatibility)
- If you require promise usage, the following promise wrapper methods have been added:
- `isTextPromise` which wraps `isText` with a promise signature
- `isBinaryPromise` which wraps `isBinary` with a promise signature
- `getEncodingPromise` which wraps `getEncoding` with a promise signature
- `isBinary` method now correctly returns `null` instead of `true` when no inputs are provided
- Added tests for all methods
## v2.4.2 2019 January 21
- Added more keywords to `package.json`
## v2.4.1 2019 January 21
- README now elaborates on the operation of this package
## v2.4.0 2019 January 20
- Asynchronous methods now `try...catch` the synchronous methods to ensure an error from invalid inputs would be given to the callback.
- Before they would not do any `try...catch` so if invalid inputs were given, the error would throw.
- The JSDoc documentation has been updated for accuracy.
- It previously indicated that the return types of the sync methods could have been an error instance, this was incorrect, they would throw if received invalid inputs.
- It previously indicated that the result for of the async `getEncoding` callback was a boolean, this was incorrect, it would be the string result of `getEncodingSync`.
- Updated [base files](https://github.com/bevry/base) and [editions](https://editions.bevry.me) using [boundation](https://github.com/bevry/boundation)
## v2.3.0 2018 November 7
- Ensure that [textextensions](https://github.com/bevry/textextensions) and [binaryextensions](https://github.com/bevry/binaryextensions) are the latest versions at the time of publishing
- Updated [base files](https://github.com/bevry/base) and [editions](https://github.com/bevry/editions) using [boundation](https://github.com/bevry/boundation)
## v2.2.1 2018 January 24
- Added missing development dependency
## v2.2.0 2018 January 24
- Fixed invalid `package.json` error
- Thanks to [Sean](https://github.com/AlbinoDrought) for [pull request #8](https://github.com/bevry/istextorbinary/pull/8)
- Updated base files
## v2.1.0 2016 May 10
- Support v2 of [textextensions](https://github.com/bevry/textextensions) and [binaryextensions](https://github.com/bevry/binaryextensions)
## v2.0.0 2016 May 2
- Converted from CoffeeScript to JavaScript
- Fixed `getEncoding` and `isText` not handling errors correctly
- Right-most extension takes preference, instead of left-most
- Thanks to [Ian Sibner](https://github.com/sibnerian) for [pull request #5](https://github.com/bevry/istextorbinary/pull/5)
- **This has bumped the major** as it changes the output result, which could potentially break some apps, despite the API remaining exactly the same
## v1.0.2 2015 January 16
- Fixed build
- Added test for text files
## v1.0.1 2015 January 16
- Cleaned up thanks to [Shunnosuke Watanabe](https://github.com/shinnn) for [pull request #2](https://github.com/bevry/istextorbinary/pull/2)
## v1.0.0 2013 October 25
- Initial release [extracted](https://github.com/balupton/bal-util/blob/6501d51bc0244fce3781fc0150136f7493099237/src/lib/paths.coffee#L100-L201) from [bal-util](https://npmjs.com/package/bal-util) where it was introduced [2012 September 24](https://github.com/balupton/bal-util/blob/master/HISTORY.md#v1137-2012-september-24).
+24
View File
@@ -0,0 +1,24 @@
<!-- LICENSEFILE/ -->
<h1>License</h1>
Unless stated otherwise all works are:
<ul><li>Copyright &copy; 2012+ <a href="http://bevry.me">Bevry Pty Ltd</a></li>
<li>Copyright &copy; 2011 <a href="http://balupton.com">Benjamin Lupton</a></li></ul>
and licensed under:
<ul><li><a href="http://spdx.org/licenses/MIT.html">MIT License</a></li></ul>
<h2>MIT License</h2>
<pre>
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
</pre>
<!-- /LICENSEFILE -->
+186
View File
@@ -0,0 +1,186 @@
<!-- TITLE/ -->
<h1>Is Text or Binary?</h1>
<!-- /TITLE -->
<!-- BADGES/ -->
<span class="badge-travisci"><a href="http://travis-ci.org/bevry/istextorbinary" title="Check this project's build status on TravisCI"><img src="https://img.shields.io/travis/bevry/istextorbinary/master.svg" alt="Travis CI Build Status" /></a></span>
<span class="badge-npmversion"><a href="https://npmjs.org/package/istextorbinary" title="View this project on NPM"><img src="https://img.shields.io/npm/v/istextorbinary.svg" alt="NPM version" /></a></span>
<span class="badge-npmdownloads"><a href="https://npmjs.org/package/istextorbinary" title="View this project on NPM"><img src="https://img.shields.io/npm/dm/istextorbinary.svg" alt="NPM downloads" /></a></span>
<span class="badge-daviddm"><a href="https://david-dm.org/bevry/istextorbinary" title="View the status of this project's dependencies on DavidDM"><img src="https://img.shields.io/david/bevry/istextorbinary.svg" alt="Dependency Status" /></a></span>
<span class="badge-daviddmdev"><a href="https://david-dm.org/bevry/istextorbinary#info=devDependencies" title="View the status of this project's development dependencies on DavidDM"><img src="https://img.shields.io/david/dev/bevry/istextorbinary.svg" alt="Dev Dependency Status" /></a></span>
<br class="badge-separator" />
<span class="badge-patreon"><a href="https://patreon.com/bevry" title="Donate to this project using Patreon"><img src="https://img.shields.io/badge/patreon-donate-yellow.svg" alt="Patreon donate button" /></a></span>
<span class="badge-flattr"><a href="https://flattr.com/profile/balupton" title="Donate to this project using Flattr"><img src="https://img.shields.io/badge/flattr-donate-yellow.svg" alt="Flattr donate button" /></a></span>
<span class="badge-liberapay"><a href="https://liberapay.com/bevry" title="Donate to this project using Liberapay"><img src="https://img.shields.io/badge/liberapay-donate-yellow.svg" alt="Liberapay donate button" /></a></span>
<span class="badge-thanksapp"><a href="https://givethanks.app/donate/npm/istextorbinary" title="Donate to this project using Thanks App"><img src="https://img.shields.io/badge/thanksapp-donate-yellow.svg" alt="Thanks App donate button" /></a></span>
<span class="badge-boostlab"><a href="https://boost-lab.app/bevry/istextorbinary" title="Donate to this project using Boost Lab"><img src="https://img.shields.io/badge/boostlab-donate-yellow.svg" alt="Boost Lab donate button" /></a></span>
<span class="badge-buymeacoffee"><a href="https://buymeacoffee.com/balupton" title="Donate to this project using Buy Me A Coffee"><img src="https://img.shields.io/badge/buy%20me%20a%20coffee-donate-yellow.svg" alt="Buy Me A Coffee donate button" /></a></span>
<span class="badge-opencollective"><a href="https://opencollective.com/bevry" title="Donate to this project using Open Collective"><img src="https://img.shields.io/badge/open%20collective-donate-yellow.svg" alt="Open Collective donate button" /></a></span>
<span class="badge-crypto"><a href="https://bevry.me/crypto" title="Donate to this project using Cryptocurrency"><img src="https://img.shields.io/badge/crypto-donate-yellow.svg" alt="crypto donate button" /></a></span>
<span class="badge-paypal"><a href="https://bevry.me/paypal" title="Donate to this project using Paypal"><img src="https://img.shields.io/badge/paypal-donate-yellow.svg" alt="PayPal donate button" /></a></span>
<span class="badge-wishlist"><a href="https://bevry.me/wishlist" title="Buy an item on our wishlist for us"><img src="https://img.shields.io/badge/wishlist-donate-yellow.svg" alt="Wishlist browse button" /></a></span>
<!-- /BADGES -->
<!-- DESCRIPTION/ -->
Determine if a filename and/or buffer is text or binary. Smarter detection than the other solutions.
<!-- /DESCRIPTION -->
Determination works like so:
1. Extension Check: If filename is available, check if any of its extensions (from right to left) are an [text extension](https://github.com/bevry/textextensions) or a [binary extension](https://github.com/bevry/binaryextensions), this is near instant.
2. Contents Check: If no filename was provided, or the extension check was indeterminate, then check the contents of the buffer.
The extension check will check each of the filename's extensions, from right to left. This is done as certain applications utilise multiple extensions for transformations, such as `app.x.y` may tell a compiler to transform from `x` format to `y` format, in this case perhaps `x` is not a recognized extension but `y` is, in which case we can make use of that to provide superior accuracy and convenience compared to just checking the rightmost extension.
The contents check (with the default options) will check 24 bytes at the start, middle, and end of the buffer. History has shown that checking all three locations is mandatory for accuracy, and that anything less is not accurate. This technique offers superior performance while still offering superior accuracy. Alternatives generally just do 1000 bytes at the start, which is slower, and inaccurate.
One cannot just do the contents check alone because UTF16 characters are indistinguishable from binary which would return an inaccurate result, hence why the combination is necessary for accuracy, with performance for known extensions a side-effect.
As such, this library's combination of extension check (if filename is provided), then contents check (if buffer is provided), offers superior performance and accuracy to alternatives.
Ever since 2012, this module's superior accuracy and performance has been essential to the operation of [DocPad](https://docpad.org) and its other dependents.
## Usage
[Complete API Documentation.](http://master.istextorbinary.bevry.surge.sh/docs/)
```javascript
const { isText, isBuffer, getEncoding } = require('istextorbinary')
isText(aFilename) // returns true if a text file otherwise false, checks only filename
isText(null, aBuffer) // returns true if a text file otherwise false, checks only buffer
isText(aFilename, aBuffer) // returns true if a text file otherwise false, checks filename then buffer
isText(null, null) // returns null
isBinary(aFilename) // returns true if a binary file otherwise false, checks only filename
isBinary(null, aBuffer) // returns true if a binary file otherwise false, checks only buffer
isBinary(aFilename, aBuffer) // returns true if a binary file otherwise false, checks filename then buffer
isBinary(null, null) // returns null
getEncoding(aBuffer) // returns 'binary' if it contained non-utf8 characters, otherwise returns 'utf8'
```
<!-- INSTALL/ -->
<h2>Install</h2>
<a href="https://npmjs.com" title="npm is a package manager for javascript"><h3>npm</h3></a>
<ul>
<li>Install: <code>npm install --save istextorbinary</code></li>
<li>Require: <code>require('istextorbinary')</code></li>
</ul>
<a href="https://jspm.io" title="Native ES Modules CDN"><h3>jspm</h3></a>
``` html
<script type="module">
import * as pkg from '//dev.jspm.io/istextorbinary'
</script>
```
<h3><a href="https://editions.bevry.me" title="Editions are the best way to produce and consume packages you care about.">Editions</a></h3>
<p>This package is published with the following editions:</p>
<ul><li><code>istextorbinary</code> aliases <code>istextorbinary/index.js</code> which uses <a href="https://editions.bevry.me" title="Editions are the best way to produce and consume packages you care about.">Editions</a> to automatically select the correct edition for the consumers environment</li>
<li><code>istextorbinary/source/index.js</code> is esnext source code with require for modules</li>
<li><code>istextorbinary/edition-browsers/index.js</code> is esnext compiled for browsers with require for modules</li>
<li><code>istextorbinary/edition-node-0.12/index.js</code> is esnext compiled for node.js 0.12 with require for modules</li></ul>
<h3><a href="https://www.typescriptlang.org/" title="TypeScript is a typed superset of JavaScript that compiles to plain JavaScript. ">TypeScript</a></h3>
This project provides its type information via inline <a href="http://usejsdoc.org" title="JSDoc is an API documentation generator for JavaScript, similar to Javadoc or phpDocumentor">JSDoc Comments</a>. To make use of this in <a href="https://www.typescriptlang.org/" title="TypeScript is a typed superset of JavaScript that compiles to plain JavaScript. ">TypeScript</a>, set your <code>maxNodeModuleJsDepth</code> compiler option to `5` or thereabouts. You can accomlish this via your `tsconfig.json` file like so:
``` json
{
"compilerOptions": {
"maxNodeModuleJsDepth": 5
}
}
```
<!-- /INSTALL -->
<!-- HISTORY/ -->
<h2>History</h2>
<a href="https://github.com/bevry/istextorbinary/blob/master/HISTORY.md#files">Discover the release history by heading on over to the <code>HISTORY.md</code> file.</a>
<!-- /HISTORY -->
<!-- CONTRIBUTE/ -->
<h2>Contribute</h2>
<a href="https://github.com/bevry/istextorbinary/blob/master/CONTRIBUTING.md#files">Discover how you can contribute by heading on over to the <code>CONTRIBUTING.md</code> file.</a>
<!-- /CONTRIBUTE -->
<!-- BACKERS/ -->
<h2>Backers</h2>
<h3>Maintainers</h3>
These amazing people are maintaining this project:
<ul><li><a href="http://balupton.com">Benjamin Lupton</a></li>
<li><a href="https://github.com/robloach">Rob Loach</a> — <a href="https://github.com/bevry/istextorbinary/commits?author=robloach" title="View the GitHub contributions of Rob Loach on repository bevry/istextorbinary">view contributions</a></li>
<li><a href="https://github.com/mikeumus">Michael Mooring</a> — <a href="https://github.com/bevry/istextorbinary/commits?author=mikeumus" title="View the GitHub contributions of Michael Mooring on repository bevry/istextorbinary">view contributions</a></li></ul>
<h3>Sponsors</h3>
No sponsors yet! Will you be the first?
<span class="badge-patreon"><a href="https://patreon.com/bevry" title="Donate to this project using Patreon"><img src="https://img.shields.io/badge/patreon-donate-yellow.svg" alt="Patreon donate button" /></a></span>
<span class="badge-flattr"><a href="https://flattr.com/profile/balupton" title="Donate to this project using Flattr"><img src="https://img.shields.io/badge/flattr-donate-yellow.svg" alt="Flattr donate button" /></a></span>
<span class="badge-liberapay"><a href="https://liberapay.com/bevry" title="Donate to this project using Liberapay"><img src="https://img.shields.io/badge/liberapay-donate-yellow.svg" alt="Liberapay donate button" /></a></span>
<span class="badge-thanksapp"><a href="https://givethanks.app/donate/npm/istextorbinary" title="Donate to this project using Thanks App"><img src="https://img.shields.io/badge/thanksapp-donate-yellow.svg" alt="Thanks App donate button" /></a></span>
<span class="badge-boostlab"><a href="https://boost-lab.app/bevry/istextorbinary" title="Donate to this project using Boost Lab"><img src="https://img.shields.io/badge/boostlab-donate-yellow.svg" alt="Boost Lab donate button" /></a></span>
<span class="badge-buymeacoffee"><a href="https://buymeacoffee.com/balupton" title="Donate to this project using Buy Me A Coffee"><img src="https://img.shields.io/badge/buy%20me%20a%20coffee-donate-yellow.svg" alt="Buy Me A Coffee donate button" /></a></span>
<span class="badge-opencollective"><a href="https://opencollective.com/bevry" title="Donate to this project using Open Collective"><img src="https://img.shields.io/badge/open%20collective-donate-yellow.svg" alt="Open Collective donate button" /></a></span>
<span class="badge-crypto"><a href="https://bevry.me/crypto" title="Donate to this project using Cryptocurrency"><img src="https://img.shields.io/badge/crypto-donate-yellow.svg" alt="crypto donate button" /></a></span>
<span class="badge-paypal"><a href="https://bevry.me/paypal" title="Donate to this project using Paypal"><img src="https://img.shields.io/badge/paypal-donate-yellow.svg" alt="PayPal donate button" /></a></span>
<span class="badge-wishlist"><a href="https://bevry.me/wishlist" title="Buy an item on our wishlist for us"><img src="https://img.shields.io/badge/wishlist-donate-yellow.svg" alt="Wishlist browse button" /></a></span>
<h3>Contributors</h3>
These amazing people have contributed code to this project:
<ul><li><a href="http://balupton.com">Benjamin Lupton</a></li>
<li><a href="http://shinnn.github.io">Shinnosuke Watanabe</a></li>
<li><a href="http://www.sibnerian.com/">Ian Sibner</a></li>
<li><a href="http://albinodrought.com/">Sean</a></li></ul>
<a href="https://github.com/bevry/istextorbinary/blob/master/CONTRIBUTING.md#files">Discover how you can contribute by heading on over to the <code>CONTRIBUTING.md</code> file.</a>
<!-- /BACKERS -->
<!-- LICENSE/ -->
<h2>License</h2>
Unless stated otherwise all works are:
<ul><li>Copyright &copy; 2012+ <a href="http://bevry.me">Bevry Pty Ltd</a></li>
<li>Copyright &copy; 2011 <a href="http://balupton.com">Benjamin Lupton</a></li></ul>
and licensed under:
<ul><li><a href="http://spdx.org/licenses/MIT.html">MIT License</a></li></ul>
<!-- /LICENSE -->
@@ -0,0 +1,356 @@
// @ts-check
/* eslint no-use-before-define:0 */
'use strict'; // Import
var pathUtil = require('path');
var textExtensions = require('textextensions');
var binaryExtensions = require('binaryextensions');
/**
* WIll be `null` if `buffer` was not provided. Otherwise will be either `'utf8'` or `'binary'`.
* @typedef {'utf8'|'binary'|null} EncodingResult
*/
/**
* WIll be `null` if neither `filename` nor `buffer` were provided. Otherwise will be a boolean value with the detection result.
* @typedef {boolean|null} TextOrBinaryResult
*/
/**
* @typedef {Object} EncodingOpts
* @property {number} [chunkLength = 24]
* @property {number} [chunkBegin = 0]
*/
/**
* @callback IsTextCallback
* @param {Error?} error
* @param {TextOrBinaryResult} [isTextResult]
*/
/**
* @callback IsBinaryCallback
* @param {Error?} error
* @param {TextOrBinaryResult} [isBinaryResult]
*/
/**
* @callback GetEncodingCallback
* @param {Error?} error
* @param {EncodingResult} [encoding]
*/
/**
* Determine if the filename and/or buffer is text.
* Determined by extension checks first (if filename is available), otherwise if unknown extension or no filename, will perform a slower buffer encoding detection.
* This order is done, as extension checks are quicker, and also because encoding checks cannot guarantee accuracy for chars between utf8 and utf16.
* The extension checks are performed using the resources https://github.com/bevry/textextensions and https://github.com/bevry/binaryextensions
* In a later major release, this function will become {@link isText} so you should use that instead.
* @param {string} [filename] The filename for the file/buffer if available
* @param {Buffer} [buffer] The buffer for the file if available
* @returns {TextOrBinaryResult}
*/
function isTextSync(filename, buffer) {
// Test extensions
if (filename) {
// Extract filename
var parts = pathUtil.basename(filename).split('.').reverse(); // Cycle extensions
var _iteratorNormalCompletion = true;
var _didIteratorError = false;
var _iteratorError = undefined;
try {
for (var _iterator = parts[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
var extension = _step.value;
if (textExtensions.indexOf(extension) !== -1) {
return true;
}
if (binaryExtensions.indexOf(extension) !== -1) {
return false;
}
}
} catch (err) {
_didIteratorError = true;
_iteratorError = err;
} finally {
try {
if (!_iteratorNormalCompletion && _iterator.return != null) {
_iterator.return();
}
} finally {
if (_didIteratorError) {
throw _iteratorError;
}
}
}
} // Fallback to encoding if extension check was not enough
if (buffer) {
return getEncodingSync(buffer) === 'utf8';
} // No buffer was provided
return null;
}
/**
* Callback wrapper for {@link isTextSync}.
* @param {string?} filename
* @param {Buffer?} buffer
* @param {IsTextCallback} callback
* @returns {void}
*/
function isTextCallback(filename, buffer, callback) {
var result;
try {
result = isTextSync(filename, buffer);
} catch (err) {
callback(err);
}
callback(null, result);
}
/**
* Promise wrapper for {@link isTextSync}.
* @param {string?} filename
* @param {Buffer?} buffer
* @returns {Promise<TextOrBinaryResult>}
*/
function isTextPromise(filename, buffer) {
try {
return Promise.resolve(isTextSync(filename, buffer));
} catch (err) {
return Promise.reject(err);
}
}
/**
* Wrapper around {@link isTextSync} for sync signature and {@link isTextCallback} async signature.
* In a later major release, {@link isTextSync}.will become this function, so if you prefer the callback interface you should use {@link isTextCallback}.
* @param {string?} filename
* @param {Buffer?} buffer
* @param {IsTextCallback} [callback] If provided, void will be returned, as the result will provided to the callback.
* @returns {TextOrBinaryResult|void} If no callback was provided, then the result is returned.
*/
function isText(filename, buffer, callback) {
if (callback) {
return isTextCallback(filename, buffer, callback);
} else return isTextSync(filename, buffer);
}
/**
* Inverse wrapper for {@link isTextSync}.
* In a later major release, this function will become {@link isBinary} so you should use that instead.
* @param {string} [filename]
* @param {Buffer} [buffer]
* @returns {TextOrBinaryResult}
*/
function isBinarySync(filename, buffer) {
var text = isTextSync(filename, buffer);
if (text == null) return null;
return !text;
}
/**
* Callback wrapper for {@link isBinarySync}.
* @param {string?} filename
* @param {Buffer?} buffer
* @param {IsTextCallback} callback
* @returns {void}
*/
function isBinaryCallback(filename, buffer, callback) {
var result;
try {
result = isBinarySync(filename, buffer);
} catch (err) {
callback(err);
}
callback(null, result);
}
/**
* Promise wrapper for {@link isBinarySync}.
* @param {string?} filename
* @param {Buffer?} buffer
* @returns {Promise<TextOrBinaryResult>}
*/
function isBinaryPromise(filename, buffer) {
try {
return Promise.resolve(isBinarySync(filename, buffer));
} catch (err) {
return Promise.reject(err);
}
}
/**
* Wrapper around {@link isBinarySync} for sync signature and {@link isBinaryCallback} async signature.
* In a later major release, {@link isBinarySync}.will become this function, so if you prefer the callback interface you should use {@link isBinaryCallback}.
* @param {string?} filename
* @param {Buffer?} buffer
* @param {IsTextCallback} [callback] If provided, void will be returned, as the result will provided to the callback.
* @returns {TextOrBinaryResult|void} If no callback was provided, then the result is returned.
*/
function isBinary(filename, buffer, callback) {
if (callback) {
return isBinaryCallback(filename, buffer, callback);
} else return isBinarySync(filename, buffer);
}
/**
* Get the encoding of a buffer.
* Checks the start, middle, and end of the buffer for characters that are unrecognized within UTF8 encoding.
* History has shown that inspection at all three locations is necessary.
* In a later major release, this function will become {@link getEncoding} so you should use that instead.
* @param {Buffer} buffer
* @param {EncodingOpts} [opts]
* @returns {EncodingResult}
*/
function getEncodingSync(buffer, opts) {
// Check
if (!buffer) return null; // Prepare
var textEncoding = 'utf8';
var binaryEncoding = 'binary'; // Discover
if (opts == null) {
// Start
var chunkLength = 24;
var encoding = getEncodingSync(buffer, {
chunkLength: chunkLength
});
if (encoding === textEncoding) {
// Middle
var chunkBegin = Math.max(0, Math.floor(buffer.length / 2) - chunkLength);
encoding = getEncodingSync(buffer, {
chunkLength: chunkLength,
chunkBegin: chunkBegin
});
if (encoding === textEncoding) {
// End
chunkBegin = Math.max(0, buffer.length - chunkLength);
encoding = getEncodingSync(buffer, {
chunkLength: chunkLength,
chunkBegin: chunkBegin
});
}
} // Return
return encoding;
} else {
// Extract
var _opts$chunkLength = opts.chunkLength,
_chunkLength = _opts$chunkLength === void 0 ? 24 : _opts$chunkLength,
_opts$chunkBegin = opts.chunkBegin,
_chunkBegin = _opts$chunkBegin === void 0 ? 0 : _opts$chunkBegin;
var chunkEnd = Math.min(buffer.length, _chunkBegin + _chunkLength);
var contentChunkUTF8 = buffer.toString(textEncoding, _chunkBegin, chunkEnd); // Detect encoding
for (var i = 0; i < contentChunkUTF8.length; ++i) {
var charCode = contentChunkUTF8.charCodeAt(i);
if (charCode === 65533 || charCode <= 8) {
// 8 and below are control characters (e.g. backspace, null, eof, etc.)
// 65533 is the unknown character
// console.log(charCode, contentChunkUTF8[i])
return binaryEncoding;
}
} // Return
return textEncoding;
}
}
/**
* Get the encoding of a buffer.
* Uses {@link getEncodingSync} behind the scenes.
* @param {Buffer} buffer
* @param {EncodingOpts} [opts]
* @param {GetEncodingCallback} callback
* @returns {void}
*/
function getEncodingCallback(buffer, opts, callback) {
if (typeof opts === 'function' && callback == null) return getEncodingCallback(buffer, null, opts);
/** @type {EncodingResult?} */
var result;
try {
result = getEncodingSync(buffer, opts);
} catch (err) {
callback(err);
}
callback(null, result);
}
/**
* Promise wrapper for {@link getEncodingSync}.
* @param {Buffer} buffer
* @param {EncodingOpts} [opts]
* @returns {Promise<EncodingResult>}
*/
function getEncodingPromise(buffer, opts) {
try {
return Promise.resolve(getEncodingSync(buffer, opts));
} catch (err) {
return Promise.reject(err);
}
}
/**
* Wrapper around {@link getEncodingSync} for sync signature and {@link getEncodingCallback} async signature.
* In a later major release, {@link getEncodingSync}.will become this function, so if you prefer the callback interface you should use {@link getEncodingCallback}.
* @param {Buffer} buffer
* @param {EncodingOpts} [opts]
* @param {GetEncodingCallback} [callback] If provided, void will be returned, as the result will provided to the callback.
* @returns {EncodingResult|void} If no callback was provided, then the result is returned.
*/
function getEncoding(buffer, opts, callback) {
if (callback || typeof opts === 'function') {
return getEncodingCallback(buffer, opts, callback);
} else return getEncodingSync(buffer, opts);
} // Export
module.exports = {
isTextSync: isTextSync,
isTextCallback: isTextCallback,
isTextPromise: isTextPromise,
isText: isText,
isBinarySync: isBinarySync,
isBinaryCallback: isBinaryCallback,
isBinaryPromise: isBinaryPromise,
isBinary: isBinary,
getEncoding: getEncoding,
getEncodingSync: getEncodingSync,
getEncodingPromise: getEncodingPromise,
getEncodingCallback: getEncodingCallback
};
@@ -0,0 +1,356 @@
// @ts-check
/* eslint no-use-before-define:0 */
'use strict'; // Import
var pathUtil = require('path');
var textExtensions = require('textextensions');
var binaryExtensions = require('binaryextensions');
/**
* WIll be `null` if `buffer` was not provided. Otherwise will be either `'utf8'` or `'binary'`.
* @typedef {'utf8'|'binary'|null} EncodingResult
*/
/**
* WIll be `null` if neither `filename` nor `buffer` were provided. Otherwise will be a boolean value with the detection result.
* @typedef {boolean|null} TextOrBinaryResult
*/
/**
* @typedef {Object} EncodingOpts
* @property {number} [chunkLength = 24]
* @property {number} [chunkBegin = 0]
*/
/**
* @callback IsTextCallback
* @param {Error?} error
* @param {TextOrBinaryResult} [isTextResult]
*/
/**
* @callback IsBinaryCallback
* @param {Error?} error
* @param {TextOrBinaryResult} [isBinaryResult]
*/
/**
* @callback GetEncodingCallback
* @param {Error?} error
* @param {EncodingResult} [encoding]
*/
/**
* Determine if the filename and/or buffer is text.
* Determined by extension checks first (if filename is available), otherwise if unknown extension or no filename, will perform a slower buffer encoding detection.
* This order is done, as extension checks are quicker, and also because encoding checks cannot guarantee accuracy for chars between utf8 and utf16.
* The extension checks are performed using the resources https://github.com/bevry/textextensions and https://github.com/bevry/binaryextensions
* In a later major release, this function will become {@link isText} so you should use that instead.
* @param {string} [filename] The filename for the file/buffer if available
* @param {Buffer} [buffer] The buffer for the file if available
* @returns {TextOrBinaryResult}
*/
function isTextSync(filename, buffer) {
// Test extensions
if (filename) {
// Extract filename
var parts = pathUtil.basename(filename).split('.').reverse(); // Cycle extensions
var _iteratorNormalCompletion = true;
var _didIteratorError = false;
var _iteratorError = undefined;
try {
for (var _iterator = parts[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
var extension = _step.value;
if (textExtensions.indexOf(extension) !== -1) {
return true;
}
if (binaryExtensions.indexOf(extension) !== -1) {
return false;
}
}
} catch (err) {
_didIteratorError = true;
_iteratorError = err;
} finally {
try {
if (!_iteratorNormalCompletion && _iterator.return != null) {
_iterator.return();
}
} finally {
if (_didIteratorError) {
throw _iteratorError;
}
}
}
} // Fallback to encoding if extension check was not enough
if (buffer) {
return getEncodingSync(buffer) === 'utf8';
} // No buffer was provided
return null;
}
/**
* Callback wrapper for {@link isTextSync}.
* @param {string?} filename
* @param {Buffer?} buffer
* @param {IsTextCallback} callback
* @returns {void}
*/
function isTextCallback(filename, buffer, callback) {
var result;
try {
result = isTextSync(filename, buffer);
} catch (err) {
callback(err);
}
callback(null, result);
}
/**
* Promise wrapper for {@link isTextSync}.
* @param {string?} filename
* @param {Buffer?} buffer
* @returns {Promise<TextOrBinaryResult>}
*/
function isTextPromise(filename, buffer) {
try {
return Promise.resolve(isTextSync(filename, buffer));
} catch (err) {
return Promise.reject(err);
}
}
/**
* Wrapper around {@link isTextSync} for sync signature and {@link isTextCallback} async signature.
* In a later major release, {@link isTextSync}.will become this function, so if you prefer the callback interface you should use {@link isTextCallback}.
* @param {string?} filename
* @param {Buffer?} buffer
* @param {IsTextCallback} [callback] If provided, void will be returned, as the result will provided to the callback.
* @returns {TextOrBinaryResult|void} If no callback was provided, then the result is returned.
*/
function isText(filename, buffer, callback) {
if (callback) {
return isTextCallback(filename, buffer, callback);
} else return isTextSync(filename, buffer);
}
/**
* Inverse wrapper for {@link isTextSync}.
* In a later major release, this function will become {@link isBinary} so you should use that instead.
* @param {string} [filename]
* @param {Buffer} [buffer]
* @returns {TextOrBinaryResult}
*/
function isBinarySync(filename, buffer) {
var text = isTextSync(filename, buffer);
if (text == null) return null;
return !text;
}
/**
* Callback wrapper for {@link isBinarySync}.
* @param {string?} filename
* @param {Buffer?} buffer
* @param {IsTextCallback} callback
* @returns {void}
*/
function isBinaryCallback(filename, buffer, callback) {
var result;
try {
result = isBinarySync(filename, buffer);
} catch (err) {
callback(err);
}
callback(null, result);
}
/**
* Promise wrapper for {@link isBinarySync}.
* @param {string?} filename
* @param {Buffer?} buffer
* @returns {Promise<TextOrBinaryResult>}
*/
function isBinaryPromise(filename, buffer) {
try {
return Promise.resolve(isBinarySync(filename, buffer));
} catch (err) {
return Promise.reject(err);
}
}
/**
* Wrapper around {@link isBinarySync} for sync signature and {@link isBinaryCallback} async signature.
* In a later major release, {@link isBinarySync}.will become this function, so if you prefer the callback interface you should use {@link isBinaryCallback}.
* @param {string?} filename
* @param {Buffer?} buffer
* @param {IsTextCallback} [callback] If provided, void will be returned, as the result will provided to the callback.
* @returns {TextOrBinaryResult|void} If no callback was provided, then the result is returned.
*/
function isBinary(filename, buffer, callback) {
if (callback) {
return isBinaryCallback(filename, buffer, callback);
} else return isBinarySync(filename, buffer);
}
/**
* Get the encoding of a buffer.
* Checks the start, middle, and end of the buffer for characters that are unrecognized within UTF8 encoding.
* History has shown that inspection at all three locations is necessary.
* In a later major release, this function will become {@link getEncoding} so you should use that instead.
* @param {Buffer} buffer
* @param {EncodingOpts} [opts]
* @returns {EncodingResult}
*/
function getEncodingSync(buffer, opts) {
// Check
if (!buffer) return null; // Prepare
var textEncoding = 'utf8';
var binaryEncoding = 'binary'; // Discover
if (opts == null) {
// Start
var chunkLength = 24;
var encoding = getEncodingSync(buffer, {
chunkLength: chunkLength
});
if (encoding === textEncoding) {
// Middle
var chunkBegin = Math.max(0, Math.floor(buffer.length / 2) - chunkLength);
encoding = getEncodingSync(buffer, {
chunkLength: chunkLength,
chunkBegin: chunkBegin
});
if (encoding === textEncoding) {
// End
chunkBegin = Math.max(0, buffer.length - chunkLength);
encoding = getEncodingSync(buffer, {
chunkLength: chunkLength,
chunkBegin: chunkBegin
});
}
} // Return
return encoding;
} else {
// Extract
var _opts$chunkLength = opts.chunkLength,
_chunkLength = _opts$chunkLength === void 0 ? 24 : _opts$chunkLength,
_opts$chunkBegin = opts.chunkBegin,
_chunkBegin = _opts$chunkBegin === void 0 ? 0 : _opts$chunkBegin;
var chunkEnd = Math.min(buffer.length, _chunkBegin + _chunkLength);
var contentChunkUTF8 = buffer.toString(textEncoding, _chunkBegin, chunkEnd); // Detect encoding
for (var i = 0; i < contentChunkUTF8.length; ++i) {
var charCode = contentChunkUTF8.charCodeAt(i);
if (charCode === 65533 || charCode <= 8) {
// 8 and below are control characters (e.g. backspace, null, eof, etc.)
// 65533 is the unknown character
// console.log(charCode, contentChunkUTF8[i])
return binaryEncoding;
}
} // Return
return textEncoding;
}
}
/**
* Get the encoding of a buffer.
* Uses {@link getEncodingSync} behind the scenes.
* @param {Buffer} buffer
* @param {EncodingOpts} [opts]
* @param {GetEncodingCallback} callback
* @returns {void}
*/
function getEncodingCallback(buffer, opts, callback) {
if (typeof opts === 'function' && callback == null) return getEncodingCallback(buffer, null, opts);
/** @type {EncodingResult?} */
var result;
try {
result = getEncodingSync(buffer, opts);
} catch (err) {
callback(err);
}
callback(null, result);
}
/**
* Promise wrapper for {@link getEncodingSync}.
* @param {Buffer} buffer
* @param {EncodingOpts} [opts]
* @returns {Promise<EncodingResult>}
*/
function getEncodingPromise(buffer, opts) {
try {
return Promise.resolve(getEncodingSync(buffer, opts));
} catch (err) {
return Promise.reject(err);
}
}
/**
* Wrapper around {@link getEncodingSync} for sync signature and {@link getEncodingCallback} async signature.
* In a later major release, {@link getEncodingSync}.will become this function, so if you prefer the callback interface you should use {@link getEncodingCallback}.
* @param {Buffer} buffer
* @param {EncodingOpts} [opts]
* @param {GetEncodingCallback} [callback] If provided, void will be returned, as the result will provided to the callback.
* @returns {EncodingResult|void} If no callback was provided, then the result is returned.
*/
function getEncoding(buffer, opts, callback) {
if (callback || typeof opts === 'function') {
return getEncodingCallback(buffer, opts, callback);
} else return getEncodingSync(buffer, opts);
} // Export
module.exports = {
isTextSync: isTextSync,
isTextCallback: isTextCallback,
isTextPromise: isTextPromise,
isText: isText,
isBinarySync: isBinarySync,
isBinaryCallback: isBinaryCallback,
isBinaryPromise: isBinaryPromise,
isBinary: isBinary,
getEncoding: getEncoding,
getEncodingSync: getEncodingSync,
getEncodingPromise: getEncodingPromise,
getEncodingCallback: getEncodingCallback
};
+4
View File
@@ -0,0 +1,4 @@
'use strict'
/** @type {typeof import("./source/index.js") } */
module.exports = require('editions').requirePackage(__dirname, require)
+220
View File
@@ -0,0 +1,220 @@
{
"title": "Is Text or Binary?",
"name": "istextorbinary",
"version": "2.5.1",
"description": "Determine if a filename and/or buffer is text or binary. Smarter detection than the other solutions.",
"homepage": "https://github.com/bevry/istextorbinary",
"license": "MIT",
"keywords": [
"bin",
"binary",
"check",
"detect",
"encoding",
"ext",
"extension",
"extensions",
"file",
"is binary file",
"is binary",
"is text file",
"is text or binary file",
"is text or binary",
"is text",
"is",
"isbinary",
"isbinaryfile",
"istext",
"istextfile",
"path",
"text"
],
"badges": {
"list": [
"travisci",
"npmversion",
"npmdownloads",
"daviddm",
"daviddmdev",
"---",
"patreon",
"flattr",
"liberapay",
"thanksapp",
"boostlab",
"buymeacoffee",
"opencollective",
"crypto",
"paypal",
"wishlist"
],
"config": {
"buymeacoffeeUsername": "balupton",
"cryptoURL": "https://bevry.me/crypto",
"flattrUsername": "balupton",
"liberapayUsername": "bevry",
"opencollectiveUsername": "bevry",
"patreonUsername": "bevry",
"paypalURL": "https://bevry.me/paypal",
"wishlistURL": "https://bevry.me/wishlist"
}
},
"author": "2012+ Bevry Pty Ltd <us@bevry.me> (http://bevry.me), 2011 Benjamin Lupton <b@lupton.cc> (http://balupton.com)",
"maintainers": [
"Benjamin Lupton <b@lupton.cc> (http://balupton.com)",
"Rob Loach <robloach@gmail.com> (https://github.com/robloach)",
"Michael Mooring <mike@mdm.cc> (https://github.com/mikeumus)"
],
"contributors": [
"Benjamin Lupton <b@lupton.cc> (http://balupton.com)",
"Shinnosuke Watanabe <snnskwtnb@gmail.com> (http://shinnn.github.io)",
"Ian Sibner <sibnerian@gmail.com> (http://www.sibnerian.com/)",
"Sean <albinodrought@gmail.com> (http://albinodrought.com/)"
],
"bugs": {
"url": "https://github.com/bevry/istextorbinary/issues"
},
"repository": {
"type": "git",
"url": "https://github.com/bevry/istextorbinary.git"
},
"engines": {
"node": ">=0.12"
},
"editions": [
{
"description": "esnext source code with require for modules",
"directory": "source",
"entry": "index.js",
"tags": [
"javascript",
"esnext",
"require"
],
"engines": {
"node": "6 || 8 || 10 || 11",
"browsers": false
}
},
{
"description": "esnext compiled for browsers with require for modules",
"directory": "edition-browsers",
"entry": "index.js",
"tags": [
"javascript",
"require"
],
"engines": {
"node": false,
"browsers": "defaults"
}
},
{
"description": "esnext compiled for node.js 0.12 with require for modules",
"directory": "edition-node-0.12",
"entry": "index.js",
"tags": [
"javascript",
"require"
],
"engines": {
"node": "0.12 || 4 || 6 || 8 || 10 || 11",
"browsers": false
}
}
],
"main": "index.js",
"browser": "edition-browsers/index.js",
"dependencies": {
"binaryextensions": "^2.1.2",
"editions": "^2.1.3",
"textextensions": "^2.4.0"
},
"devDependencies": {
"@babel/cli": "^7.2.3",
"@babel/core": "^7.2.2",
"@babel/plugin-proposal-object-rest-spread": "^7.2.0",
"@babel/preset-env": "^7.2.3",
"assert-helpers": "^4.9.6",
"eslint": "^5.12.1",
"eslint-config-bevry": "^1.1.2",
"eslint-config-prettier": "^3.6.0",
"eslint-plugin-prettier": "^3.0.1",
"jsdoc": "^3.5.5",
"kava": "^3.1.0",
"minami": "^1.2.3",
"prettier": "^1.15.3",
"projectz": "^1.7.4",
"surge": "^0.20.1",
"valid-directory": "^1.0.0"
},
"scripts": {
"our:clean": "rm -Rf ./docs ./edition* ./es2015 ./es5 ./out ./.next",
"our:compile": "npm run our:compile:edition-browsers && npm run our:compile:edition-node-0.12",
"our:compile:edition-browsers": "env BABEL_ENV=edition-browsers babel --out-dir ./edition-browsers ./source",
"our:compile:edition-node-0.12": "env BABEL_ENV=edition-node-0.12 babel --out-dir ./edition-node-0.12 ./source",
"our:deploy": "echo no need for this project",
"our:meta": "npm run our:meta:docs && npm run our:meta:projectz",
"our:meta:docs": "npm run our:meta:docs:jsdoc",
"our:meta:docs:jsdoc": "rm -Rf ./docs && jsdoc --recurse --pedantic --access all --destination ./docs --package ./package.json --readme ./README.md --template ./node_modules/minami ./source && mv ./docs/$npm_package_name/$npm_package_version/* ./docs/ && rm -Rf ./docs/$npm_package_name/$npm_package_version",
"our:meta:projectz": "projectz compile",
"our:release": "npm run our:release:prepare && npm run our:release:check-changelog && npm run our:release:check-dirty && npm run our:release:tag && npm run our:release:push",
"our:release:check-changelog": "cat ./HISTORY.md | grep v$npm_package_version || (echo add a changelog entry for v$npm_package_version && exit -1)",
"our:release:check-dirty": "git diff --exit-code",
"our:release:prepare": "npm run our:clean && npm run our:compile && npm run our:test && npm run our:meta",
"our:release:push": "git push origin master && git push origin --tags",
"our:release:tag": "export MESSAGE=$(cat ./HISTORY.md | sed -n \"/## v$npm_package_version/,/##/p\" | sed 's/## //' | awk 'NR>1{print buf}{buf = $0}') && test \"$MESSAGE\" || (echo 'proper changelog entry not found' && exit -1) && git tag v$npm_package_version -am \"$MESSAGE\"",
"our:setup": "npm run our:setup:npm",
"our:setup:npm": "npm install",
"our:test": "npm run our:verify && npm test",
"our:verify": "npm run our:verify:directory && npm run our:verify:eslint",
"our:verify:directory": "npx valid-directory",
"our:verify:eslint": "eslint --fix --ignore-pattern '**/*.d.ts' --ignore-pattern '**/vendor/' --ignore-pattern '**/node_modules/' --ext .mjs,.js,.jsx,.ts,.tsx ./source",
"test": "node ./test.js"
},
"eslintConfig": {
"extends": [
"bevry"
]
},
"prettier": {
"semi": false,
"singleQuote": true
},
"babel": {
"env": {
"edition-browsers": {
"sourceType": "script",
"presets": [
[
"@babel/preset-env",
{
"targets": "defaults",
"modules": "commonjs"
}
]
],
"plugins": [
"@babel/proposal-object-rest-spread"
]
},
"edition-node-0.12": {
"sourceType": "script",
"presets": [
[
"@babel/preset-env",
{
"targets": {
"node": "0.12"
},
"modules": "commonjs"
}
]
],
"plugins": [
"@babel/proposal-object-rest-spread"
]
}
}
}
}
+306
View File
@@ -0,0 +1,306 @@
// @ts-check
/* eslint no-use-before-define:0 */
'use strict'
// Import
const pathUtil = require('path')
const textExtensions = require('textextensions')
const binaryExtensions = require('binaryextensions')
/**
* WIll be `null` if `buffer` was not provided. Otherwise will be either `'utf8'` or `'binary'`.
* @typedef {'utf8'|'binary'|null} EncodingResult
*/
/**
* WIll be `null` if neither `filename` nor `buffer` were provided. Otherwise will be a boolean value with the detection result.
* @typedef {boolean|null} TextOrBinaryResult
*/
/**
* @typedef {Object} EncodingOpts
* @property {number} [chunkLength = 24]
* @property {number} [chunkBegin = 0]
*/
/**
* @callback IsTextCallback
* @param {Error?} error
* @param {TextOrBinaryResult} [isTextResult]
*/
/**
* @callback IsBinaryCallback
* @param {Error?} error
* @param {TextOrBinaryResult} [isBinaryResult]
*/
/**
* @callback GetEncodingCallback
* @param {Error?} error
* @param {EncodingResult} [encoding]
*/
/**
* Determine if the filename and/or buffer is text.
* Determined by extension checks first (if filename is available), otherwise if unknown extension or no filename, will perform a slower buffer encoding detection.
* This order is done, as extension checks are quicker, and also because encoding checks cannot guarantee accuracy for chars between utf8 and utf16.
* The extension checks are performed using the resources https://github.com/bevry/textextensions and https://github.com/bevry/binaryextensions
* In a later major release, this function will become {@link isText} so you should use that instead.
* @param {string} [filename] The filename for the file/buffer if available
* @param {Buffer} [buffer] The buffer for the file if available
* @returns {TextOrBinaryResult}
*/
function isTextSync(filename, buffer) {
// Test extensions
if (filename) {
// Extract filename
const parts = pathUtil
.basename(filename)
.split('.')
.reverse()
// Cycle extensions
for (const extension of parts) {
if (textExtensions.indexOf(extension) !== -1) {
return true
}
if (binaryExtensions.indexOf(extension) !== -1) {
return false
}
}
}
// Fallback to encoding if extension check was not enough
if (buffer) {
return getEncodingSync(buffer) === 'utf8'
}
// No buffer was provided
return null
}
/**
* Callback wrapper for {@link isTextSync}.
* @param {string?} filename
* @param {Buffer?} buffer
* @param {IsTextCallback} callback
* @returns {void}
*/
function isTextCallback(filename, buffer, callback) {
let result
try {
result = isTextSync(filename, buffer)
} catch (err) {
callback(err)
}
callback(null, result)
}
/**
* Promise wrapper for {@link isTextSync}.
* @param {string?} filename
* @param {Buffer?} buffer
* @returns {Promise<TextOrBinaryResult>}
*/
function isTextPromise(filename, buffer) {
try {
return Promise.resolve(isTextSync(filename, buffer))
} catch (err) {
return Promise.reject(err)
}
}
/**
* Wrapper around {@link isTextSync} for sync signature and {@link isTextCallback} async signature.
* In a later major release, {@link isTextSync}.will become this function, so if you prefer the callback interface you should use {@link isTextCallback}.
* @param {string?} filename
* @param {Buffer?} buffer
* @param {IsTextCallback} [callback] If provided, void will be returned, as the result will provided to the callback.
* @returns {TextOrBinaryResult|void} If no callback was provided, then the result is returned.
*/
function isText(filename, buffer, callback) {
if (callback) {
return isTextCallback(filename, buffer, callback)
} else return isTextSync(filename, buffer)
}
/**
* Inverse wrapper for {@link isTextSync}.
* In a later major release, this function will become {@link isBinary} so you should use that instead.
* @param {string} [filename]
* @param {Buffer} [buffer]
* @returns {TextOrBinaryResult}
*/
function isBinarySync(filename, buffer) {
const text = isTextSync(filename, buffer)
if (text == null) return null
return !text
}
/**
* Callback wrapper for {@link isBinarySync}.
* @param {string?} filename
* @param {Buffer?} buffer
* @param {IsTextCallback} callback
* @returns {void}
*/
function isBinaryCallback(filename, buffer, callback) {
let result
try {
result = isBinarySync(filename, buffer)
} catch (err) {
callback(err)
}
callback(null, result)
}
/**
* Promise wrapper for {@link isBinarySync}.
* @param {string?} filename
* @param {Buffer?} buffer
* @returns {Promise<TextOrBinaryResult>}
*/
function isBinaryPromise(filename, buffer) {
try {
return Promise.resolve(isBinarySync(filename, buffer))
} catch (err) {
return Promise.reject(err)
}
}
/**
* Wrapper around {@link isBinarySync} for sync signature and {@link isBinaryCallback} async signature.
* In a later major release, {@link isBinarySync}.will become this function, so if you prefer the callback interface you should use {@link isBinaryCallback}.
* @param {string?} filename
* @param {Buffer?} buffer
* @param {IsTextCallback} [callback] If provided, void will be returned, as the result will provided to the callback.
* @returns {TextOrBinaryResult|void} If no callback was provided, then the result is returned.
*/
function isBinary(filename, buffer, callback) {
if (callback) {
return isBinaryCallback(filename, buffer, callback)
} else return isBinarySync(filename, buffer)
}
/**
* Get the encoding of a buffer.
* Checks the start, middle, and end of the buffer for characters that are unrecognized within UTF8 encoding.
* History has shown that inspection at all three locations is necessary.
* In a later major release, this function will become {@link getEncoding} so you should use that instead.
* @param {Buffer} buffer
* @param {EncodingOpts} [opts]
* @returns {EncodingResult}
*/
function getEncodingSync(buffer, opts) {
// Check
if (!buffer) return null
// Prepare
const textEncoding = 'utf8'
const binaryEncoding = 'binary'
// Discover
if (opts == null) {
// Start
const chunkLength = 24
let encoding = getEncodingSync(buffer, { chunkLength })
if (encoding === textEncoding) {
// Middle
let chunkBegin = Math.max(0, Math.floor(buffer.length / 2) - chunkLength)
encoding = getEncodingSync(buffer, { chunkLength, chunkBegin })
if (encoding === textEncoding) {
// End
chunkBegin = Math.max(0, buffer.length - chunkLength)
encoding = getEncodingSync(buffer, { chunkLength, chunkBegin })
}
}
// Return
return encoding
} else {
// Extract
const { chunkLength = 24, chunkBegin = 0 } = opts
const chunkEnd = Math.min(buffer.length, chunkBegin + chunkLength)
const contentChunkUTF8 = buffer.toString(textEncoding, chunkBegin, chunkEnd)
// Detect encoding
for (let i = 0; i < contentChunkUTF8.length; ++i) {
const charCode = contentChunkUTF8.charCodeAt(i)
if (charCode === 65533 || charCode <= 8) {
// 8 and below are control characters (e.g. backspace, null, eof, etc.)
// 65533 is the unknown character
// console.log(charCode, contentChunkUTF8[i])
return binaryEncoding
}
}
// Return
return textEncoding
}
}
/**
* Get the encoding of a buffer.
* Uses {@link getEncodingSync} behind the scenes.
* @param {Buffer} buffer
* @param {EncodingOpts} [opts]
* @param {GetEncodingCallback} callback
* @returns {void}
*/
function getEncodingCallback(buffer, opts, callback) {
if (typeof opts === 'function' && callback == null)
return getEncodingCallback(buffer, null, opts)
/** @type {EncodingResult?} */
let result
try {
result = getEncodingSync(buffer, opts)
} catch (err) {
callback(err)
}
callback(null, result)
}
/**
* Promise wrapper for {@link getEncodingSync}.
* @param {Buffer} buffer
* @param {EncodingOpts} [opts]
* @returns {Promise<EncodingResult>}
*/
function getEncodingPromise(buffer, opts) {
try {
return Promise.resolve(getEncodingSync(buffer, opts))
} catch (err) {
return Promise.reject(err)
}
}
/**
* Wrapper around {@link getEncodingSync} for sync signature and {@link getEncodingCallback} async signature.
* In a later major release, {@link getEncodingSync}.will become this function, so if you prefer the callback interface you should use {@link getEncodingCallback}.
* @param {Buffer} buffer
* @param {EncodingOpts} [opts]
* @param {GetEncodingCallback} [callback] If provided, void will be returned, as the result will provided to the callback.
* @returns {EncodingResult|void} If no callback was provided, then the result is returned.
*/
function getEncoding(buffer, opts, callback) {
if (callback || typeof opts === 'function') {
return getEncodingCallback(buffer, opts, callback)
} else return getEncodingSync(buffer, opts)
}
// Export
module.exports = {
isTextSync,
isTextCallback,
isTextPromise,
isText,
isBinarySync,
isBinaryCallback,
isBinaryPromise,
isBinary,
getEncoding,
getEncodingSync,
getEncodingPromise,
getEncodingCallback
}