├── .gitignore ├── .npmignore ├── LICENSE ├── README.md ├── file-source.sublime-project ├── index.js ├── package.json └── test ├── hello.txt ├── index-test.js └── utf8.txt /.gitignore: -------------------------------------------------------------------------------- 1 | *.sublime-workspace 2 | .DS_Store 3 | dist/ 4 | node_modules 5 | npm-debug.log 6 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | *.sublime-* 2 | dist/*.zip 3 | test/ 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013-2016, Michael Bostock 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | * The name Michael Bostock may not be used to endorse or promote products 15 | derived from this software without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 20 | DISCLAIMED. IN NO EVENT SHALL MICHAEL BOSTOCK BE LIABLE FOR ANY DIRECT, 21 | INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 22 | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 24 | OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 25 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 26 | EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # file-source 2 | 3 | A [sliceable](https://github.com/mbostock/slice-source), [readable stream reader](https://streams.spec.whatwg.org/#readable-stream-reader) implementation on top of a Node [file read stream](https://nodejs.org/api/fs.html#fs_fs_createreadstream_path_options). This library allows you to write code that takes a *source* as input, and can work with either native readable streams or Node streams. For example: 4 | 5 | ```js 6 | var file = require("file-source"); 7 | 8 | file("README.md") 9 | .then(function read(source) { 10 | return source.slice(40).then(value => { 11 | if (value == null) return; 12 | process.stdout.write(value); 13 | return read(source); 14 | }); 15 | }) 16 | .catch(error => console.error(error.stack)); 17 | ``` 18 | 19 | ## API Reference 20 | 21 | # file(path[, options]) [<>](https://github.com/mbostock/file-source/blob/master/index.js "Source") 22 | 23 | Returns a Promise that yields a *source* for the file at the specified *path* when the underlying file is open. The following options are allowed: 24 | 25 | * `highWaterMark` - the stream’s internal buffer size; defaults to 65,536 26 | 27 | # source.slice(length) [<>](https://github.com/mbostock/stream-source/blob/master/slice.js "Source") 28 | 29 | Returns a Promise for the next chunk of data from the underlying stream, yielding a [Uint8Array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array) (a [Buffer](https://nodejs.org/api/buffer.html)) of *length* bytes, or the remaining bytes of the underlying stream if the underlying stream has more than zero but fewer than *length* bytes remaining, or null when no bytes remain in the stream. 30 | 31 | # source.read() [<>](https://github.com/mbostock/stream-source/blob/master/read.js "Source") 32 | 33 | Returns a Promise for the next chunk of data from the underlying stream. The yielded result is an object with the following properties: 34 | 35 | * `value` - a [Uint8Array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array) (a [Buffer](https://nodejs.org/api/buffer.html)), or undefined if the stream ended 36 | * `done` - a boolean which is true if the stream ended 37 | 38 | # source.cancel() [<>](https://github.com/mbostock/slice-source/blob/master/cancel.js "Source") 39 | 40 | Returns a Promise which is resolved when the underlying stream has been destroyed. 41 | -------------------------------------------------------------------------------- /file-source.sublime-project: -------------------------------------------------------------------------------- 1 | { 2 | "folders": [ 3 | { 4 | "path": ".", 5 | "file_exclude_patterns": [ 6 | "*.sublime-workspace" 7 | ], 8 | "folder_exclude_patterns": [ 9 | "dist" 10 | ] 11 | } 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | var fs = require("fs"), 2 | stream = require("stream-source"); 3 | 4 | module.exports = function(path, options) { 5 | var highWaterMark = 65536; 6 | if (options && options.highWaterMark != null) highWaterMark = options.highWaterMark; 7 | return new Promise(function(resolve, reject) { 8 | var f = fs.createReadStream(path, {highWaterMark: highWaterMark}); 9 | f.once("open", function() { resolve(stream(f)); }); 10 | f.once("error", reject); 11 | }); 12 | }; 13 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "file-source", 3 | "version": "0.6.1", 4 | "description": "Read binary files in chunks, on demand, with promises.", 5 | "keywords": [ 6 | "binary", 7 | "file", 8 | "reader", 9 | "fs" 10 | ], 11 | "homepage": "https://github.com/mbostock/file-source", 12 | "license": "BSD-3-Clause", 13 | "author": { 14 | "name": "Mike Bostock", 15 | "url": "https://bost.ocks.org/mike" 16 | }, 17 | "main": "index.js", 18 | "repository": { 19 | "type": "git", 20 | "url": "http://github.com/mbostock/file-source.git" 21 | }, 22 | "scripts": { 23 | "test": "tape 'test/**/*-test.js'", 24 | "postpublish": "git push && git push --tags" 25 | }, 26 | "dependencies": { 27 | "stream-source": "0.3" 28 | }, 29 | "devDependencies": { 30 | "tape": "4" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /test/hello.txt: -------------------------------------------------------------------------------- 1 | Hello, world! 2 | -------------------------------------------------------------------------------- /test/index-test.js: -------------------------------------------------------------------------------- 1 | var file = require("../"), 2 | tape = require("tape"); 3 | 4 | tape("file(path) rejects if the specified file does not exist", function(test) { 5 | file("test/does-not-exist.txt") 6 | .then(source => { 7 | test.end("File shouldn’t exist!"); 8 | }, error => { 9 | test.equal(error.code, "ENOENT"); 10 | test.end(); 11 | }); 12 | }); 13 | 14 | tape("file(path, options) can set the highWaterMark", function(test) { 15 | file("test/hello.txt", {highWaterMark: 4}) 16 | .then(source => source.read() 17 | .then(result => { 18 | test.equal(result.done, false); 19 | test.equal(result.value.toString(), "Hell"); 20 | return source.cancel(); 21 | })) 22 | .then(() => test.end()) 23 | .catch(error => test.end(error)); 24 | }); 25 | 26 | tape("source.read() yields {value: buffer, done: false} from the underlying file", function(test) { 27 | file("test/hello.txt") 28 | .then(source => source.read() 29 | .then(result => { 30 | test.equal(result.done, false); 31 | test.equal(result.value.toString(), "Hello, world!\n"); 32 | test.end(); 33 | })) 34 | .catch(error => test.end(error)); 35 | }); 36 | 37 | tape("source.slice(length) yields a buffer from the underlying file", function(test) { 38 | file("test/hello.txt") 39 | .then(source => source.slice(7) 40 | .then(value => { 41 | test.equal(value.toString(), "Hello, "); 42 | return source.slice(7); 43 | }) 44 | .then(value => { 45 | test.equal(value.toString(), "world!\n"); 46 | return source.slice(7); 47 | }) 48 | .then(value => { 49 | test.equal(value, null); 50 | test.end(); 51 | })) 52 | .catch(error => test.end(error)); 53 | }); 54 | 55 | tape("source.read() yields {value: undefined, done: true} after reading the underlying file", function(test) { 56 | file("test/hello.txt") 57 | .then(source => source.read() 58 | .then(result => { 59 | test.equal(result.done, false); 60 | test.equal(result.value.toString(), "Hello, world!\n"); 61 | return source.read(); 62 | }) 63 | .then(result => { 64 | test.equal(result.done, true); 65 | test.equal(result.value, undefined); 66 | test.end(); 67 | })) 68 | .catch(error => test.end(error)); 69 | }); 70 | -------------------------------------------------------------------------------- /test/utf8.txt: -------------------------------------------------------------------------------- 1 | Héllø, wörld! 2 | --------------------------------------------------------------------------------