remove script
This commit is contained in:
-19
@@ -1,19 +0,0 @@
|
||||
The MIT License
|
||||
|
||||
Copyright (c) 2013 Brian J. Brennan
|
||||
|
||||
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.
|
||||
-47
@@ -1,47 +0,0 @@
|
||||
# buffer-crc32
|
||||
|
||||
[](http://travis-ci.org/brianloveswords/buffer-crc32)
|
||||
|
||||
crc32 that works with binary data and fancy character sets, outputs
|
||||
buffer, signed or unsigned data and has tests.
|
||||
|
||||
Derived from the sample CRC implementation in the PNG specification: http://www.w3.org/TR/PNG/#D-CRCAppendix
|
||||
|
||||
# install
|
||||
```
|
||||
npm install buffer-crc32
|
||||
```
|
||||
|
||||
# example
|
||||
```js
|
||||
var crc32 = require('buffer-crc32');
|
||||
// works with buffers
|
||||
var buf = Buffer([0x00, 0x73, 0x75, 0x70, 0x20, 0x62, 0x72, 0x6f, 0x00])
|
||||
crc32(buf) // -> <Buffer 94 5a ab 4a>
|
||||
|
||||
// has convenience methods for getting signed or unsigned ints
|
||||
crc32.signed(buf) // -> -1805997238
|
||||
crc32.unsigned(buf) // -> 2488970058
|
||||
|
||||
// will cast to buffer if given a string, so you can
|
||||
// directly use foreign characters safely
|
||||
crc32('自動販売機') // -> <Buffer cb 03 1a c5>
|
||||
|
||||
// and works in append mode too
|
||||
var partialCrc = crc32('hey');
|
||||
var partialCrc = crc32(' ', partialCrc);
|
||||
var partialCrc = crc32('sup', partialCrc);
|
||||
var partialCrc = crc32(' ', partialCrc);
|
||||
var finalCrc = crc32('bros', partialCrc); // -> <Buffer 47 fa 55 70>
|
||||
```
|
||||
|
||||
# tests
|
||||
This was tested against the output of zlib's crc32 method. You can run
|
||||
the tests with`npm test` (requires tap)
|
||||
|
||||
# see also
|
||||
https://github.com/alexgorbatchev/node-crc, `crc.buffer.crc32` also
|
||||
supports buffer inputs and return unsigned ints (thanks @tjholowaychuk).
|
||||
|
||||
# license
|
||||
MIT/X11
|
||||
-111
@@ -1,111 +0,0 @@
|
||||
var Buffer = require('buffer').Buffer;
|
||||
|
||||
var CRC_TABLE = [
|
||||
0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419,
|
||||
0x706af48f, 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4,
|
||||
0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07,
|
||||
0x90bf1d91, 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de,
|
||||
0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, 0x136c9856,
|
||||
0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9,
|
||||
0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4,
|
||||
0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,
|
||||
0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3,
|
||||
0x45df5c75, 0xdcd60dcf, 0xabd13d59, 0x26d930ac, 0x51de003a,
|
||||
0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599,
|
||||
0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
|
||||
0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190,
|
||||
0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f,
|
||||
0x9fbfe4a5, 0xe8b8d433, 0x7807c9a2, 0x0f00f934, 0x9609a88e,
|
||||
0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01,
|
||||
0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed,
|
||||
0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
|
||||
0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3,
|
||||
0xfbd44c65, 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2,
|
||||
0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a,
|
||||
0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5,
|
||||
0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, 0xbe0b1010,
|
||||
0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
|
||||
0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17,
|
||||
0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6,
|
||||
0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615,
|
||||
0x73dc1683, 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8,
|
||||
0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, 0xf00f9344,
|
||||
0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,
|
||||
0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a,
|
||||
0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5,
|
||||
0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1,
|
||||
0xa6bc5767, 0x3fb506dd, 0x48b2364b, 0xd80d2bda, 0xaf0a1b4c,
|
||||
0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef,
|
||||
0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
|
||||
0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe,
|
||||
0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31,
|
||||
0x2cd99e8b, 0x5bdeae1d, 0x9b64c2b0, 0xec63f226, 0x756aa39c,
|
||||
0x026d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713,
|
||||
0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b,
|
||||
0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
|
||||
0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1,
|
||||
0x18b74777, 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c,
|
||||
0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, 0xa00ae278,
|
||||
0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7,
|
||||
0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, 0x40df0b66,
|
||||
0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
|
||||
0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605,
|
||||
0xcdd70693, 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8,
|
||||
0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b,
|
||||
0x2d02ef8d
|
||||
];
|
||||
|
||||
if (typeof Int32Array !== 'undefined') {
|
||||
CRC_TABLE = new Int32Array(CRC_TABLE);
|
||||
}
|
||||
|
||||
function ensureBuffer(input) {
|
||||
if (Buffer.isBuffer(input)) {
|
||||
return input;
|
||||
}
|
||||
|
||||
var hasNewBufferAPI =
|
||||
typeof Buffer.alloc === "function" &&
|
||||
typeof Buffer.from === "function";
|
||||
|
||||
if (typeof input === "number") {
|
||||
return hasNewBufferAPI ? Buffer.alloc(input) : new Buffer(input);
|
||||
}
|
||||
else if (typeof input === "string") {
|
||||
return hasNewBufferAPI ? Buffer.from(input) : new Buffer(input);
|
||||
}
|
||||
else {
|
||||
throw new Error("input must be buffer, number, or string, received " +
|
||||
typeof input);
|
||||
}
|
||||
}
|
||||
|
||||
function bufferizeInt(num) {
|
||||
var tmp = ensureBuffer(4);
|
||||
tmp.writeInt32BE(num, 0);
|
||||
return tmp;
|
||||
}
|
||||
|
||||
function _crc32(buf, previous) {
|
||||
buf = ensureBuffer(buf);
|
||||
if (Buffer.isBuffer(previous)) {
|
||||
previous = previous.readUInt32BE(0);
|
||||
}
|
||||
var crc = ~~previous ^ -1;
|
||||
for (var n = 0; n < buf.length; n++) {
|
||||
crc = CRC_TABLE[(crc ^ buf[n]) & 0xff] ^ (crc >>> 8);
|
||||
}
|
||||
return (crc ^ -1);
|
||||
}
|
||||
|
||||
function crc32() {
|
||||
return bufferizeInt(_crc32.apply(null, arguments));
|
||||
}
|
||||
crc32.signed = function () {
|
||||
return _crc32.apply(null, arguments);
|
||||
};
|
||||
crc32.unsigned = function () {
|
||||
return _crc32.apply(null, arguments) >>> 0;
|
||||
};
|
||||
|
||||
module.exports = crc32;
|
||||
-39
@@ -1,39 +0,0 @@
|
||||
{
|
||||
"author": "Brian J. Brennan <brianloveswords@gmail.com>",
|
||||
"name": "buffer-crc32",
|
||||
"description": "A pure javascript CRC32 algorithm that plays nice with binary data",
|
||||
"version": "0.2.13",
|
||||
"licenses": [
|
||||
{
|
||||
"type": "MIT",
|
||||
"url": "https://github.com/brianloveswords/buffer-crc32/raw/master/LICENSE"
|
||||
}
|
||||
],
|
||||
"contributors": [
|
||||
{
|
||||
"name": "Vladimir Kuznetsov",
|
||||
"github": "mistakster"
|
||||
}
|
||||
],
|
||||
"homepage": "https://github.com/brianloveswords/buffer-crc32",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git://github.com/brianloveswords/buffer-crc32.git"
|
||||
},
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "./node_modules/.bin/tap tests/*.test.js"
|
||||
},
|
||||
"dependencies": {},
|
||||
"devDependencies": {
|
||||
"tap": "~0.2.5"
|
||||
},
|
||||
"optionalDependencies": {},
|
||||
"engines": {
|
||||
"node": "*"
|
||||
},
|
||||
"license": "MIT",
|
||||
"files": [
|
||||
"index.js"
|
||||
]
|
||||
}
|
||||
-33
@@ -1,33 +0,0 @@
|
||||
{
|
||||
"rules": {
|
||||
"indent": [
|
||||
2,
|
||||
4,
|
||||
{ "SwitchCase": 1 }
|
||||
],
|
||||
"quotes": [
|
||||
2,
|
||||
"double"
|
||||
],
|
||||
"linebreak-style": [
|
||||
2,
|
||||
"unix"
|
||||
],
|
||||
"semi": [
|
||||
2,
|
||||
"always"
|
||||
],
|
||||
"no-console": [
|
||||
0
|
||||
],
|
||||
"no-trailing-spaces":
|
||||
[
|
||||
2
|
||||
]
|
||||
},
|
||||
"env": {
|
||||
"node": true,
|
||||
"mocha": true
|
||||
},
|
||||
"extends": "eslint:recommended"
|
||||
}
|
||||
Generated
Vendored
-17
@@ -1,17 +0,0 @@
|
||||
language: node_js
|
||||
node_js:
|
||||
- "0.10"
|
||||
- "0.12"
|
||||
- "1.0"
|
||||
- "1.8"
|
||||
- "2.0"
|
||||
- "2.5"
|
||||
- "3.0"
|
||||
- "3.3"
|
||||
- "4.0"
|
||||
- "4.2"
|
||||
- "5.0"
|
||||
- "6"
|
||||
sudo: false
|
||||
script:
|
||||
- "npm run lint && npm test"
|
||||
-22
@@ -1,22 +0,0 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2015 Sarosia
|
||||
|
||||
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.
|
||||
|
||||
-33
@@ -1,33 +0,0 @@
|
||||
# buffer-indexof-polyfill
|
||||
|
||||
[![Build Status][travis-image]][travis-url]
|
||||
[![NPM Version][npm-image]][npm-url]
|
||||
[![NPM Downloads][downloads-image]][downloads-url]
|
||||
|
||||
This is a polyfill for [`Buffer#indexOf`](https://nodejs.org/api/buffer.html#buffer_buf_indexof_value_byteoffset) and Buffer#lastIndexOf introduced in NodeJS 4.0.
|
||||
|
||||
## Example
|
||||
|
||||
```js
|
||||
require("buffer-indexof-polyfill");
|
||||
|
||||
new Buffer("buffer").indexOf("uff") // return 1
|
||||
new Buffer("buffer").indexOf("abc") // return -1
|
||||
```
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
npm install buffer-indexof-polyfill
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
[MIT](LICENSE)
|
||||
|
||||
[npm-image]: https://img.shields.io/npm/v/buffer-indexof-polyfill.svg
|
||||
[npm-url]: https://npmjs.org/package/buffer-indexof-polyfill
|
||||
[downloads-image]: https://img.shields.io/npm/dm/buffer-indexof-polyfill.svg
|
||||
[downloads-url]: https://npmjs.org/package/buffer-indexof-polyfill
|
||||
[travis-image]: https://travis-ci.org/sarosia/buffer-indexof-polyfill.svg?branch=master
|
||||
[travis-url]: https://travis-ci.org/sarosia/buffer-indexof-polyfill
|
||||
-73
@@ -1,73 +0,0 @@
|
||||
"use strict";
|
||||
var initBuffer = require("./init-buffer");
|
||||
|
||||
if (!Buffer.prototype.indexOf) {
|
||||
Buffer.prototype.indexOf = function (value, offset) {
|
||||
offset = offset || 0;
|
||||
|
||||
// Always wrap the input as a Buffer so that this method will support any
|
||||
// data type such as array octet, string or buffer.
|
||||
if (typeof value === "string" || value instanceof String) {
|
||||
value = initBuffer(value);
|
||||
} else if (typeof value === "number" || value instanceof Number) {
|
||||
value = initBuffer([ value ]);
|
||||
}
|
||||
|
||||
var len = value.length;
|
||||
|
||||
for (var i = offset; i <= this.length - len; i++) {
|
||||
var mismatch = false;
|
||||
for (var j = 0; j < len; j++) {
|
||||
if (this[i + j] != value[j]) {
|
||||
mismatch = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!mismatch) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
};
|
||||
}
|
||||
|
||||
function bufferLastIndexOf (value, offset) {
|
||||
|
||||
// Always wrap the input as a Buffer so that this method will support any
|
||||
// data type such as array octet, string or buffer.
|
||||
if (typeof value === "string" || value instanceof String) {
|
||||
value = initBuffer(value);
|
||||
} else if (typeof value === "number" || value instanceof Number) {
|
||||
value = initBuffer([ value ]);
|
||||
}
|
||||
|
||||
var len = value.length;
|
||||
offset = offset || this.length - len;
|
||||
|
||||
for (var i = offset; i >= 0; i--) {
|
||||
var mismatch = false;
|
||||
for (var j = 0; j < len; j++) {
|
||||
if (this[i + j] != value[j]) {
|
||||
mismatch = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!mismatch) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
if (Buffer.prototype.lastIndexOf) {
|
||||
// check Buffer#lastIndexOf is usable: https://github.com/nodejs/node/issues/4604
|
||||
if (initBuffer("ABC").lastIndexOf ("ABC") === -1)
|
||||
Buffer.prototype.lastIndexOf = bufferLastIndexOf;
|
||||
} else {
|
||||
Buffer.prototype.lastIndexOf = bufferLastIndexOf;
|
||||
}
|
||||
Generated
Vendored
-8
@@ -1,8 +0,0 @@
|
||||
module.exports = function initBuffer(val) {
|
||||
// assume old version
|
||||
var nodeVersion = process && process.version ? process.version : "v5.0.0";
|
||||
var major = nodeVersion.split(".")[0].replace("v", "");
|
||||
return major < 6
|
||||
? new Buffer(val)
|
||||
: Buffer.from(val);
|
||||
};
|
||||
Generated
Vendored
-34
@@ -1,34 +0,0 @@
|
||||
{
|
||||
"name": "buffer-indexof-polyfill",
|
||||
"version": "1.0.2",
|
||||
"description": "This is a polyfill for Buffer#indexOf introduced in NodeJS 4.0.",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "mocha",
|
||||
"lint": "eslint .",
|
||||
"fix": "eslint . --fix"
|
||||
},
|
||||
"author": "https://github.com/sarosia",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.10"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/sarosia/buffer-indexof-polyfill.git"
|
||||
},
|
||||
"devDependencies": {
|
||||
"chai": "^3.3.0",
|
||||
"eslint": "^1.10.3",
|
||||
"mocha": "^2.3.3"
|
||||
},
|
||||
"keywords": [
|
||||
"buffer",
|
||||
"indexof",
|
||||
"polyfill"
|
||||
],
|
||||
"bugs": {
|
||||
"url": "https://github.com/sarosia/buffer-indexof-polyfill/issues"
|
||||
},
|
||||
"homepage": "https://github.com/sarosia/buffer-indexof-polyfill#readme"
|
||||
}
|
||||
Generated
Vendored
-128
@@ -1,128 +0,0 @@
|
||||
"use strict";
|
||||
|
||||
var expect = require("chai").expect;
|
||||
var initBuffer = require("../init-buffer");
|
||||
|
||||
require("../index.js");
|
||||
|
||||
describe("Buffer#indexOf", function () {
|
||||
|
||||
it("Buffer as value", function () {
|
||||
var buffer = initBuffer("ABC");
|
||||
|
||||
expect(buffer.indexOf(initBuffer("ABC"))).to.be.equal(0);
|
||||
expect(buffer.indexOf(initBuffer("AB"))).to.be.equal(0);
|
||||
expect(buffer.indexOf(initBuffer("BC"))).to.be.equal(1);
|
||||
expect(buffer.indexOf(initBuffer("C"))).to.be.equal(2);
|
||||
expect(buffer.indexOf(initBuffer("CC"))).to.be.equal(-1);
|
||||
expect(buffer.indexOf(initBuffer("CA"))).to.be.equal(-1);
|
||||
|
||||
expect(buffer.indexOf(initBuffer("ABC"), 1)).to.be.equal(-1);
|
||||
expect(buffer.indexOf(initBuffer("AB"), 1)).to.be.equal(-1);
|
||||
expect(buffer.indexOf(initBuffer("BC"), 1)).to.be.equal(1);
|
||||
expect(buffer.indexOf(initBuffer("C"), 1)).to.be.equal(2);
|
||||
expect(buffer.indexOf(initBuffer("CC"), 1)).to.be.equal(-1);
|
||||
expect(buffer.indexOf(initBuffer("CA"), 1)).to.be.equal(-1);
|
||||
});
|
||||
|
||||
it("String as value", function () {
|
||||
var buffer = initBuffer("ABC");
|
||||
expect(buffer.indexOf("ABC")).to.be.equal(0);
|
||||
expect(buffer.indexOf("AB")).to.be.equal(0);
|
||||
expect(buffer.indexOf("BC")).to.be.equal(1);
|
||||
expect(buffer.indexOf("C")).to.be.equal(2);
|
||||
expect(buffer.indexOf("CC")).to.be.equal(-1);
|
||||
expect(buffer.indexOf("CA")).to.be.equal(-1);
|
||||
|
||||
expect(buffer.indexOf("ABC", 1)).to.be.equal(-1);
|
||||
expect(buffer.indexOf("AB", 1)).to.be.equal(-1);
|
||||
expect(buffer.indexOf("BC", 1)).to.be.equal(1);
|
||||
expect(buffer.indexOf("C", 1)).to.be.equal(2);
|
||||
expect(buffer.indexOf("CC", 1)).to.be.equal(-1);
|
||||
expect(buffer.indexOf("CA", 1)).to.be.equal(-1);
|
||||
});
|
||||
|
||||
it("Number as value", function () {
|
||||
var buffer = initBuffer([ 1, 2, 3 ]);
|
||||
expect(buffer.indexOf(1)).to.be.equal(0);
|
||||
expect(buffer.indexOf(2)).to.be.equal(1);
|
||||
expect(buffer.indexOf(3)).to.be.equal(2);
|
||||
expect(buffer.indexOf(4)).to.be.equal(-1);
|
||||
|
||||
expect(buffer.indexOf(1, 1)).to.be.equal(-1);
|
||||
expect(buffer.indexOf(2, 1)).to.be.equal(1);
|
||||
expect(buffer.indexOf(3, 1)).to.be.equal(2);
|
||||
expect(buffer.indexOf(4, 1)).to.be.equal(-1);
|
||||
});
|
||||
});
|
||||
|
||||
describe("Buffer#lastIndexOf", function () {
|
||||
|
||||
it("Buffer as value", function () {
|
||||
var buffer = initBuffer("ABCABC");
|
||||
|
||||
expect(buffer.lastIndexOf(initBuffer("ABC"))).to.be.equal(3);
|
||||
expect(buffer.lastIndexOf(initBuffer("AB"))).to.be.equal(3);
|
||||
expect(buffer.lastIndexOf(initBuffer("BC"))).to.be.equal(4);
|
||||
expect(buffer.lastIndexOf(initBuffer("C"))).to.be.equal(5);
|
||||
expect(buffer.lastIndexOf(initBuffer("CC"))).to.be.equal(-1);
|
||||
expect(buffer.lastIndexOf(initBuffer("CA"))).to.be.equal(2);
|
||||
|
||||
expect(buffer.lastIndexOf(initBuffer("ABC"), 1)).to.be.equal(0);
|
||||
expect(buffer.lastIndexOf(initBuffer("AB"), 1)).to.be.equal(0);
|
||||
expect(buffer.lastIndexOf(initBuffer("BC"), 1)).to.be.equal(1);
|
||||
expect(buffer.lastIndexOf(initBuffer("C"), 1)).to.be.equal(-1);
|
||||
expect(buffer.lastIndexOf(initBuffer("CC"), 1)).to.be.equal(-1);
|
||||
expect(buffer.lastIndexOf(initBuffer("CA"), 1)).to.be.equal(-1);
|
||||
});
|
||||
|
||||
it("String as value", function () {
|
||||
var buffer = initBuffer("ABCABC");
|
||||
|
||||
expect(buffer.lastIndexOf("ABC")).to.be.equal(3);
|
||||
expect(buffer.lastIndexOf("AB")).to.be.equal(3);
|
||||
expect(buffer.lastIndexOf("BC")).to.be.equal(4);
|
||||
expect(buffer.lastIndexOf("C")).to.be.equal(5);
|
||||
expect(buffer.lastIndexOf("CC")).to.be.equal(-1);
|
||||
expect(buffer.lastIndexOf("CA")).to.be.equal(2);
|
||||
|
||||
expect(buffer.lastIndexOf("ABC", 1)).to.be.equal(0);
|
||||
expect(buffer.lastIndexOf("AB", 1)).to.be.equal(0);
|
||||
expect(buffer.lastIndexOf("BC", 1)).to.be.equal(1);
|
||||
expect(buffer.lastIndexOf("C", 1)).to.be.equal(-1);
|
||||
expect(buffer.lastIndexOf("CC", 1)).to.be.equal(-1);
|
||||
expect(buffer.lastIndexOf("CA", 1)).to.be.equal(-1);
|
||||
|
||||
// make sure it works predictable
|
||||
buffer = buffer.toString();
|
||||
|
||||
expect(buffer.lastIndexOf("ABC")).to.be.equal(3);
|
||||
expect(buffer.lastIndexOf("AB")).to.be.equal(3);
|
||||
expect(buffer.lastIndexOf("BC")).to.be.equal(4);
|
||||
expect(buffer.lastIndexOf("C")).to.be.equal(5);
|
||||
expect(buffer.lastIndexOf("CC")).to.be.equal(-1);
|
||||
expect(buffer.lastIndexOf("CA")).to.be.equal(2);
|
||||
|
||||
expect(buffer.lastIndexOf("ABC", 1)).to.be.equal(0);
|
||||
expect(buffer.lastIndexOf("AB", 1)).to.be.equal(0);
|
||||
expect(buffer.lastIndexOf("BC", 1)).to.be.equal(1);
|
||||
expect(buffer.lastIndexOf("C", 1)).to.be.equal(-1);
|
||||
expect(buffer.lastIndexOf("CC", 1)).to.be.equal(-1);
|
||||
expect(buffer.lastIndexOf("CA", 1)).to.be.equal(-1);
|
||||
|
||||
});
|
||||
|
||||
it("Number as value", function () {
|
||||
var buffer = initBuffer([ 1, 2, 3, 1, 2, 3]);
|
||||
|
||||
expect(buffer.lastIndexOf(1)).to.be.equal(3);
|
||||
expect(buffer.lastIndexOf(2)).to.be.equal(4);
|
||||
expect(buffer.lastIndexOf(3)).to.be.equal(5);
|
||||
expect(buffer.lastIndexOf(4)).to.be.equal(-1);
|
||||
|
||||
expect(buffer.lastIndexOf(1, 1)).to.be.equal(0);
|
||||
expect(buffer.lastIndexOf(2, 1)).to.be.equal(1);
|
||||
expect(buffer.lastIndexOf(3, 1)).to.be.equal(-1);
|
||||
expect(buffer.lastIndexOf(4, 1)).to.be.equal(-1);
|
||||
});
|
||||
});
|
||||
-122
@@ -1,122 +0,0 @@
|
||||
buffers
|
||||
=======
|
||||
|
||||
Treat a collection of Buffers as a single contiguous partially mutable Buffer.
|
||||
|
||||
Where possible, operations execute without creating a new Buffer and copying
|
||||
everything over.
|
||||
|
||||
This is a cleaner more Buffery rehash of
|
||||
[bufferlist](http://github.com/substack/node-bufferlist).
|
||||
|
||||
example
|
||||
=======
|
||||
|
||||
slice
|
||||
-----
|
||||
|
||||
var Buffers = require('buffers');
|
||||
var bufs = Buffers();
|
||||
bufs.push(new Buffer([1,2,3]));
|
||||
bufs.push(new Buffer([4,5,6,7]));
|
||||
bufs.push(new Buffer([8,9,10]));
|
||||
|
||||
console.dir(bufs.slice(2,8))
|
||||
|
||||
output:
|
||||
|
||||
$ node examples/slice.js
|
||||
<Buffer 03 04 05 06 07 08>
|
||||
|
||||
splice
|
||||
------
|
||||
|
||||
var Buffers = require('buffers');
|
||||
var bufs = Buffers([
|
||||
new Buffer([1,2,3]),
|
||||
new Buffer([4,5,6,7]),
|
||||
new Buffer([8,9,10]),
|
||||
]);
|
||||
|
||||
var removed = bufs.splice(2, 4);
|
||||
console.dir({
|
||||
removed : removed.slice(),
|
||||
bufs : bufs.slice(),
|
||||
});
|
||||
|
||||
output:
|
||||
|
||||
$ node examples/splice.js
|
||||
{ removed: <Buffer 03 04 05 06>,
|
||||
bufs: <Buffer 01 02 07 08 09 0a> }
|
||||
|
||||
methods
|
||||
=======
|
||||
|
||||
Buffers(buffers)
|
||||
----------------
|
||||
|
||||
Create a Buffers with an array of `Buffer`s if specified, else `[]`.
|
||||
|
||||
.push(buf1, buf2...)
|
||||
--------------------
|
||||
|
||||
Push buffers onto the end. Just like `Array.prototype.push`.
|
||||
|
||||
.unshift(buf1, buf2...)
|
||||
-----------------------
|
||||
|
||||
Unshift buffers onto the head. Just like `Array.prototype.unshift`.
|
||||
|
||||
.slice(i, j)
|
||||
------------
|
||||
|
||||
Slice a range out of the buffer collection as if it were contiguous.
|
||||
Works just like the `Array.prototype.slice` version.
|
||||
|
||||
.splice(i, howMany, replacements)
|
||||
---------------------------------
|
||||
|
||||
Splice the buffer collection as if it were contiguous.
|
||||
Works just like `Array.prototype.splice`, even the replacement part!
|
||||
|
||||
.copy(dst, dstStart, start, end)
|
||||
--------------------------------
|
||||
|
||||
Copy the buffer collection as if it were contiguous to the `dst` Buffer with the
|
||||
specified bounds.
|
||||
Works just like `Buffer.prototype.copy`.
|
||||
|
||||
.get(i)
|
||||
-------
|
||||
|
||||
Get a single element at index `i`.
|
||||
|
||||
.set(i, x)
|
||||
----------
|
||||
|
||||
Set a single element's value at index `i`.
|
||||
|
||||
.indexOf(needle, offset)
|
||||
----------
|
||||
|
||||
Find a string or buffer `needle` inside the buffer collection. Returns
|
||||
the position of the search string or -1 if the search string was not
|
||||
found.
|
||||
|
||||
Provide an `offset` to skip that number of characters at the beginning
|
||||
of the search. This can be used to find additional matches.
|
||||
|
||||
This function will return the correct result even if the search string
|
||||
is spread out over multiple internal buffers.
|
||||
|
||||
.toBuffer()
|
||||
-----------
|
||||
|
||||
Convert the buffer collection to a single buffer, equivalent with `.slice(0, buffers.length)`;
|
||||
|
||||
.toString(encoding, start, end)
|
||||
-----------
|
||||
|
||||
Decodes and returns a string from the buffer collection.
|
||||
Works just like `Buffer.prototype.toString`
|
||||
-9
@@ -1,9 +0,0 @@
|
||||
var Buffers = require('buffers');
|
||||
var bufs = Buffers();
|
||||
bufs.push(new Buffer([1,2,3]));
|
||||
bufs.push(new Buffer([4,5,6,7]));
|
||||
bufs.push(new Buffer([8,9,10]));
|
||||
|
||||
console.dir(bufs.slice(2,8))
|
||||
|
||||
// Output: <Buffer 03 04 05 06 07 08>
|
||||
-17
@@ -1,17 +0,0 @@
|
||||
var Buffers = require('buffers');
|
||||
var bufs = Buffers([
|
||||
new Buffer([1,2,3]),
|
||||
new Buffer([4,5,6,7]),
|
||||
new Buffer([8,9,10]),
|
||||
]);
|
||||
|
||||
var removed = bufs.splice(2, 4, new Buffer('ab'), new Buffer('cd'));
|
||||
console.dir({
|
||||
removed : removed.slice(),
|
||||
bufs : bufs.slice(),
|
||||
});
|
||||
|
||||
/* Output:
|
||||
{ removed: <Buffer 03 04 05 06>,
|
||||
bufs: <Buffer 01 02 07 08 09 0a> }
|
||||
*/
|
||||
-269
@@ -1,269 +0,0 @@
|
||||
module.exports = Buffers;
|
||||
|
||||
function Buffers (bufs) {
|
||||
if (!(this instanceof Buffers)) return new Buffers(bufs);
|
||||
this.buffers = bufs || [];
|
||||
this.length = this.buffers.reduce(function (size, buf) {
|
||||
return size + buf.length
|
||||
}, 0);
|
||||
}
|
||||
|
||||
Buffers.prototype.push = function () {
|
||||
for (var i = 0; i < arguments.length; i++) {
|
||||
if (!Buffer.isBuffer(arguments[i])) {
|
||||
throw new TypeError('Tried to push a non-buffer');
|
||||
}
|
||||
}
|
||||
|
||||
for (var i = 0; i < arguments.length; i++) {
|
||||
var buf = arguments[i];
|
||||
this.buffers.push(buf);
|
||||
this.length += buf.length;
|
||||
}
|
||||
return this.length;
|
||||
};
|
||||
|
||||
Buffers.prototype.unshift = function () {
|
||||
for (var i = 0; i < arguments.length; i++) {
|
||||
if (!Buffer.isBuffer(arguments[i])) {
|
||||
throw new TypeError('Tried to unshift a non-buffer');
|
||||
}
|
||||
}
|
||||
|
||||
for (var i = 0; i < arguments.length; i++) {
|
||||
var buf = arguments[i];
|
||||
this.buffers.unshift(buf);
|
||||
this.length += buf.length;
|
||||
}
|
||||
return this.length;
|
||||
};
|
||||
|
||||
Buffers.prototype.copy = function (dst, dStart, start, end) {
|
||||
return this.slice(start, end).copy(dst, dStart, 0, end - start);
|
||||
};
|
||||
|
||||
Buffers.prototype.splice = function (i, howMany) {
|
||||
var buffers = this.buffers;
|
||||
var index = i >= 0 ? i : this.length - i;
|
||||
var reps = [].slice.call(arguments, 2);
|
||||
|
||||
if (howMany === undefined) {
|
||||
howMany = this.length - index;
|
||||
}
|
||||
else if (howMany > this.length - index) {
|
||||
howMany = this.length - index;
|
||||
}
|
||||
|
||||
for (var i = 0; i < reps.length; i++) {
|
||||
this.length += reps[i].length;
|
||||
}
|
||||
|
||||
var removed = new Buffers();
|
||||
var bytes = 0;
|
||||
|
||||
var startBytes = 0;
|
||||
for (
|
||||
var ii = 0;
|
||||
ii < buffers.length && startBytes + buffers[ii].length < index;
|
||||
ii ++
|
||||
) { startBytes += buffers[ii].length }
|
||||
|
||||
if (index - startBytes > 0) {
|
||||
var start = index - startBytes;
|
||||
|
||||
if (start + howMany < buffers[ii].length) {
|
||||
removed.push(buffers[ii].slice(start, start + howMany));
|
||||
|
||||
var orig = buffers[ii];
|
||||
//var buf = new Buffer(orig.length - howMany);
|
||||
var buf0 = new Buffer(start);
|
||||
for (var i = 0; i < start; i++) {
|
||||
buf0[i] = orig[i];
|
||||
}
|
||||
|
||||
var buf1 = new Buffer(orig.length - start - howMany);
|
||||
for (var i = start + howMany; i < orig.length; i++) {
|
||||
buf1[ i - howMany - start ] = orig[i]
|
||||
}
|
||||
|
||||
if (reps.length > 0) {
|
||||
var reps_ = reps.slice();
|
||||
reps_.unshift(buf0);
|
||||
reps_.push(buf1);
|
||||
buffers.splice.apply(buffers, [ ii, 1 ].concat(reps_));
|
||||
ii += reps_.length;
|
||||
reps = [];
|
||||
}
|
||||
else {
|
||||
buffers.splice(ii, 1, buf0, buf1);
|
||||
//buffers[ii] = buf;
|
||||
ii += 2;
|
||||
}
|
||||
}
|
||||
else {
|
||||
removed.push(buffers[ii].slice(start));
|
||||
buffers[ii] = buffers[ii].slice(0, start);
|
||||
ii ++;
|
||||
}
|
||||
}
|
||||
|
||||
if (reps.length > 0) {
|
||||
buffers.splice.apply(buffers, [ ii, 0 ].concat(reps));
|
||||
ii += reps.length;
|
||||
}
|
||||
|
||||
while (removed.length < howMany) {
|
||||
var buf = buffers[ii];
|
||||
var len = buf.length;
|
||||
var take = Math.min(len, howMany - removed.length);
|
||||
|
||||
if (take === len) {
|
||||
removed.push(buf);
|
||||
buffers.splice(ii, 1);
|
||||
}
|
||||
else {
|
||||
removed.push(buf.slice(0, take));
|
||||
buffers[ii] = buffers[ii].slice(take);
|
||||
}
|
||||
}
|
||||
|
||||
this.length -= removed.length;
|
||||
|
||||
return removed;
|
||||
};
|
||||
|
||||
Buffers.prototype.slice = function (i, j) {
|
||||
var buffers = this.buffers;
|
||||
if (j === undefined) j = this.length;
|
||||
if (i === undefined) i = 0;
|
||||
|
||||
if (j > this.length) j = this.length;
|
||||
|
||||
var startBytes = 0;
|
||||
for (
|
||||
var si = 0;
|
||||
si < buffers.length && startBytes + buffers[si].length <= i;
|
||||
si ++
|
||||
) { startBytes += buffers[si].length }
|
||||
|
||||
var target = new Buffer(j - i);
|
||||
|
||||
var ti = 0;
|
||||
for (var ii = si; ti < j - i && ii < buffers.length; ii++) {
|
||||
var len = buffers[ii].length;
|
||||
|
||||
var start = ti === 0 ? i - startBytes : 0;
|
||||
var end = ti + len >= j - i
|
||||
? Math.min(start + (j - i) - ti, len)
|
||||
: len
|
||||
;
|
||||
|
||||
buffers[ii].copy(target, ti, start, end);
|
||||
ti += end - start;
|
||||
}
|
||||
|
||||
return target;
|
||||
};
|
||||
|
||||
Buffers.prototype.pos = function (i) {
|
||||
if (i < 0 || i >= this.length) throw new Error('oob');
|
||||
var l = i, bi = 0, bu = null;
|
||||
for (;;) {
|
||||
bu = this.buffers[bi];
|
||||
if (l < bu.length) {
|
||||
return {buf: bi, offset: l};
|
||||
} else {
|
||||
l -= bu.length;
|
||||
}
|
||||
bi++;
|
||||
}
|
||||
};
|
||||
|
||||
Buffers.prototype.get = function get (i) {
|
||||
var pos = this.pos(i);
|
||||
|
||||
return this.buffers[pos.buf].get(pos.offset);
|
||||
};
|
||||
|
||||
Buffers.prototype.set = function set (i, b) {
|
||||
var pos = this.pos(i);
|
||||
|
||||
return this.buffers[pos.buf].set(pos.offset, b);
|
||||
};
|
||||
|
||||
Buffers.prototype.indexOf = function (needle, offset) {
|
||||
if ("string" === typeof needle) {
|
||||
needle = new Buffer(needle);
|
||||
} else if (needle instanceof Buffer) {
|
||||
// already a buffer
|
||||
} else {
|
||||
throw new Error('Invalid type for a search string');
|
||||
}
|
||||
|
||||
if (!needle.length) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!this.length) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
var i = 0, j = 0, match = 0, mstart, pos = 0;
|
||||
|
||||
// start search from a particular point in the virtual buffer
|
||||
if (offset) {
|
||||
var p = this.pos(offset);
|
||||
i = p.buf;
|
||||
j = p.offset;
|
||||
pos = offset;
|
||||
}
|
||||
|
||||
// for each character in virtual buffer
|
||||
for (;;) {
|
||||
while (j >= this.buffers[i].length) {
|
||||
j = 0;
|
||||
i++;
|
||||
|
||||
if (i >= this.buffers.length) {
|
||||
// search string not found
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
var char = this.buffers[i][j];
|
||||
|
||||
if (char == needle[match]) {
|
||||
// keep track where match started
|
||||
if (match == 0) {
|
||||
mstart = {
|
||||
i: i,
|
||||
j: j,
|
||||
pos: pos
|
||||
};
|
||||
}
|
||||
match++;
|
||||
if (match == needle.length) {
|
||||
// full match
|
||||
return mstart.pos;
|
||||
}
|
||||
} else if (match != 0) {
|
||||
// a partial match ended, go back to match starting position
|
||||
// this will continue the search at the next character
|
||||
i = mstart.i;
|
||||
j = mstart.j;
|
||||
pos = mstart.pos;
|
||||
match = 0;
|
||||
}
|
||||
|
||||
j++;
|
||||
pos++;
|
||||
}
|
||||
};
|
||||
|
||||
Buffers.prototype.toBuffer = function() {
|
||||
return this.slice();
|
||||
}
|
||||
|
||||
Buffers.prototype.toString = function(encoding, start, end) {
|
||||
return this.slice(start, end).toString(encoding);
|
||||
}
|
||||
-14
@@ -1,14 +0,0 @@
|
||||
{
|
||||
"name" : "buffers",
|
||||
"description" : "Treat a collection of Buffers as a single contiguous partially mutable Buffer.",
|
||||
"version" : "0.1.1",
|
||||
"repository" : "http://github.com/substack/node-buffers.git",
|
||||
"author" : "James Halliday <mail@substack.net> (http://substack.net)",
|
||||
"main" : "./index",
|
||||
"scripts" : {
|
||||
"test" : "expresso"
|
||||
},
|
||||
"engines" : {
|
||||
"node" : ">=0.2.0"
|
||||
}
|
||||
}
|
||||
-209
@@ -1,209 +0,0 @@
|
||||
var assert = require('assert');
|
||||
var Buffers = require('../');
|
||||
|
||||
function create (xs, split) {
|
||||
var bufs = Buffers();
|
||||
var offset = 0;
|
||||
split.forEach(function (i) {
|
||||
bufs.push(new Buffer(xs.slice(offset, offset + i)));
|
||||
offset += i;
|
||||
});
|
||||
return bufs;
|
||||
}
|
||||
|
||||
exports.slice = function () {
|
||||
var xs = [0,1,2,3,4,5,6,7,8,9];
|
||||
var splits = [ [4,2,3,1], [2,2,2,2,2], [1,6,3,1], [9,2], [10], [5,5] ];
|
||||
|
||||
splits.forEach(function (split) {
|
||||
var bufs = create(xs, split);
|
||||
assert.eql(new Buffer(xs), bufs.slice(),
|
||||
'[' + xs.join(',') + ']'
|
||||
+ ' != ' +
|
||||
'[' + [].join.call(bufs.slice(), ',') + ']'
|
||||
);
|
||||
|
||||
for (var i = 0; i < xs.length; i++) {
|
||||
for (var j = i; j < xs.length; j++) {
|
||||
var a = bufs.slice(i,j);
|
||||
var b = new Buffer(xs.slice(i,j));
|
||||
|
||||
assert.eql(a, b,
|
||||
'[' + [].join.call(a, ',') + ']'
|
||||
+ ' != ' +
|
||||
'[' + [].join.call(b, ',') + ']'
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
exports.splice = function () {
|
||||
var xs = [0,1,2,3,4,5,6,7,8,9];
|
||||
var splits = [ [4,2,3,1], [2,2,2,2,2], [1,6,3,1], [9,2], [10], [5,5] ];
|
||||
|
||||
splits.forEach(function (split) {
|
||||
for (var i = 0; i < xs.length; i++) {
|
||||
for (var j = i; j < xs.length; j++) {
|
||||
var bufs = create(xs, split);
|
||||
var xs_ = xs.slice();
|
||||
|
||||
var a_ = bufs.splice(i,j);
|
||||
var a = [].slice.call(a_.slice());
|
||||
var b = xs_.splice(i,j);
|
||||
assert.eql(a, b,
|
||||
'[' + a.join(',') + ']'
|
||||
+ ' != ' +
|
||||
'[' + b.join(',') + ']'
|
||||
);
|
||||
|
||||
assert.eql(bufs.slice(), new Buffer(xs_),
|
||||
'[' + [].join.call(bufs.slice(), ',') + ']'
|
||||
+ ' != ' +
|
||||
'[' + [].join.call(xs_, ',') + ']'
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
exports.spliceRep = function () {
|
||||
var xs = [0,1,2,3,4,5,6,7,8,9];
|
||||
var splits = [ [4,2,3,1], [2,2,2,2,2], [1,6,3,1], [9,2], [10], [5,5] ];
|
||||
var reps = [ [], [1], [5,6], [3,1,3,3,7], [9,8,7,6,5,4,3,2,1,2,3,4,5] ];
|
||||
|
||||
splits.forEach(function (split) {
|
||||
reps.forEach(function (rep) {
|
||||
for (var i = 0; i < xs.length; i++) {
|
||||
for (var j = i; j < xs.length; j++) {
|
||||
var bufs = create(xs, split);
|
||||
var xs_ = xs.slice();
|
||||
|
||||
var a_ = bufs.splice.apply(
|
||||
bufs, [ i, j ].concat(new Buffer(rep))
|
||||
);
|
||||
var a = [].slice.call(a_.slice());
|
||||
var b = xs_.splice.apply(xs_, [ i, j ].concat(rep));
|
||||
|
||||
assert.eql(a, b,
|
||||
'[' + a.join(',') + ']'
|
||||
+ ' != ' +
|
||||
'[' + b.join(',') + ']'
|
||||
);
|
||||
|
||||
assert.eql(bufs.slice(), new Buffer(xs_),
|
||||
'[' + [].join.call(bufs.slice(), ',') + ']'
|
||||
+ ' != ' +
|
||||
'[' + [].join.call(xs_, ',') + ']'
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
exports.copy = function () {
|
||||
var xs = [0,1,2,3,4,5,6,7,8,9];
|
||||
var splits = [ [4,2,3,1], [2,2,2,2,2], [1,6,3,1], [9,2], [10], [5,5] ];
|
||||
|
||||
splits.forEach(function (split) {
|
||||
var bufs = create(xs, split);
|
||||
var buf = new Buffer(xs);
|
||||
|
||||
for (var i = 0; i < xs.length; i++) {
|
||||
for (var j = i; j < xs.length; j++) {
|
||||
var t0 = new Buffer(j - i);
|
||||
var t1 = new Buffer(j - i);
|
||||
|
||||
assert.eql(
|
||||
bufs.copy(t0, 0, i, j),
|
||||
buf.copy(t1, 0, i, j)
|
||||
);
|
||||
|
||||
assert.eql(
|
||||
[].slice.call(t0),
|
||||
[].slice.call(t1)
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
exports.push = function () {
|
||||
var bufs = Buffers();
|
||||
bufs.push(new Buffer([0]));
|
||||
bufs.push(new Buffer([1,2,3]));
|
||||
bufs.push(new Buffer([4,5]));
|
||||
bufs.push(new Buffer([6,7,8,9]));
|
||||
assert.eql(
|
||||
[].slice.call(bufs.slice()),
|
||||
[0,1,2,3,4,5,6,7,8,9]
|
||||
);
|
||||
|
||||
assert.throws(function () {
|
||||
bufs.push(new Buffer([11,12]), 'moo');
|
||||
});
|
||||
assert.eql(bufs.buffers.length, 4);
|
||||
};
|
||||
|
||||
exports.unshift = function () {
|
||||
var bufs = Buffers();
|
||||
bufs.unshift(new Buffer([6,7,8,9]));
|
||||
bufs.unshift(new Buffer([4,5]));
|
||||
bufs.unshift(new Buffer([1,2,3]));
|
||||
bufs.unshift(new Buffer([0]));
|
||||
assert.eql(
|
||||
[].slice.call(bufs.slice()),
|
||||
[0,1,2,3,4,5,6,7,8,9]
|
||||
);
|
||||
assert.throws(function () {
|
||||
bufs.unshift(new Buffer([-2,-1]), 'moo');
|
||||
});
|
||||
assert.eql(bufs.buffers.length, 4);
|
||||
};
|
||||
|
||||
exports.get = function () {
|
||||
var bufs = Buffers();
|
||||
bufs.unshift(new Buffer([6,7,8,9]));
|
||||
bufs.unshift(new Buffer([4,5]));
|
||||
bufs.unshift(new Buffer([1,2,3]));
|
||||
bufs.unshift(new Buffer([0]));
|
||||
assert.eql( bufs.get(0), 0 );
|
||||
assert.eql( bufs.get(1), 1 );
|
||||
assert.eql( bufs.get(2), 2 );
|
||||
assert.eql( bufs.get(3), 3 );
|
||||
assert.eql( bufs.get(4), 4 );
|
||||
assert.eql( bufs.get(5), 5 );
|
||||
assert.eql( bufs.get(6), 6 );
|
||||
assert.eql( bufs.get(7), 7 );
|
||||
assert.eql( bufs.get(8), 8 );
|
||||
assert.eql( bufs.get(9), 9 );
|
||||
};
|
||||
|
||||
exports.set = function () {
|
||||
var bufs = Buffers();
|
||||
bufs.push(new Buffer("Hel"));
|
||||
bufs.push(new Buffer("lo"));
|
||||
bufs.push(new Buffer("!"));
|
||||
bufs.set(0, 'h'.charCodeAt(0) );
|
||||
bufs.set(3, 'L'.charCodeAt(0) );
|
||||
bufs.set(5, '.'.charCodeAt(0) );
|
||||
assert.eql( bufs.slice(0).toString(), 'helLo.' );
|
||||
};
|
||||
|
||||
exports.indexOf = function () {
|
||||
var bufs = Buffers();
|
||||
bufs.push(new Buffer("Hel"));
|
||||
bufs.push(new Buffer("lo,"));
|
||||
bufs.push(new Buffer(" how are "));
|
||||
bufs.push(new Buffer("you"));
|
||||
bufs.push(new Buffer("?"));
|
||||
assert.eql( bufs.indexOf("Hello"), 0 );
|
||||
assert.eql( bufs.indexOf("Hello", 1), -1 );
|
||||
assert.eql( bufs.indexOf("ello"), 1 );
|
||||
assert.eql( bufs.indexOf("ello", 1), 1 );
|
||||
assert.eql( bufs.indexOf("ello", 2), -1 );
|
||||
assert.eql( bufs.indexOf("e"), 1 );
|
||||
assert.eql( bufs.indexOf("e", 2), 13 );
|
||||
assert.eql( bufs.indexOf(new Buffer([0x65]), 2), 13 );
|
||||
};
|
||||
-1
@@ -1 +0,0 @@
|
||||
node_modules
|
||||
-157
@@ -1,157 +0,0 @@
|
||||
Chainsaw
|
||||
========
|
||||
|
||||
Build chainable fluent interfaces the easy way in node.js.
|
||||
|
||||
With this meta-module you can write modules with chainable interfaces.
|
||||
Chainsaw takes care of all of the boring details and makes nested flow control
|
||||
super simple too.
|
||||
|
||||
Just call `Chainsaw` with a constructor function like in the examples below.
|
||||
In your methods, just do `saw.next()` to move along to the next event and
|
||||
`saw.nest()` to create a nested chain.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
add_do.js
|
||||
---------
|
||||
|
||||
This silly example adds values with a chainsaw.
|
||||
|
||||
var Chainsaw = require('chainsaw');
|
||||
|
||||
function AddDo (sum) {
|
||||
return Chainsaw(function (saw) {
|
||||
this.add = function (n) {
|
||||
sum += n;
|
||||
saw.next();
|
||||
};
|
||||
|
||||
this.do = function (cb) {
|
||||
saw.nest(cb, sum);
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
AddDo(0)
|
||||
.add(5)
|
||||
.add(10)
|
||||
.do(function (sum) {
|
||||
if (sum > 12) this.add(-10);
|
||||
})
|
||||
.do(function (sum) {
|
||||
console.log('Sum: ' + sum);
|
||||
})
|
||||
;
|
||||
|
||||
Output:
|
||||
Sum: 5
|
||||
|
||||
prompt.js
|
||||
---------
|
||||
|
||||
This example provides a wrapper on top of stdin with the help of
|
||||
[node-lazy](https://github.com/pkrumins/node-lazy) for line-processing.
|
||||
|
||||
var Chainsaw = require('chainsaw');
|
||||
var Lazy = require('lazy');
|
||||
|
||||
module.exports = Prompt;
|
||||
function Prompt (stream) {
|
||||
var waiting = [];
|
||||
var lines = [];
|
||||
var lazy = Lazy(stream).lines.map(String)
|
||||
.forEach(function (line) {
|
||||
if (waiting.length) {
|
||||
var w = waiting.shift();
|
||||
w(line);
|
||||
}
|
||||
else lines.push(line);
|
||||
})
|
||||
;
|
||||
|
||||
var vars = {};
|
||||
return Chainsaw(function (saw) {
|
||||
this.getline = function (f) {
|
||||
var g = function (line) {
|
||||
saw.nest(f, line, vars);
|
||||
};
|
||||
|
||||
if (lines.length) g(lines.shift());
|
||||
else waiting.push(g);
|
||||
};
|
||||
|
||||
this.do = function (cb) {
|
||||
saw.nest(cb, vars);
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
And now for the new Prompt() module in action:
|
||||
|
||||
var util = require('util');
|
||||
var stdin = process.openStdin();
|
||||
|
||||
Prompt(stdin)
|
||||
.do(function () {
|
||||
util.print('x = ');
|
||||
})
|
||||
.getline(function (line, vars) {
|
||||
vars.x = parseInt(line, 10);
|
||||
})
|
||||
.do(function () {
|
||||
util.print('y = ');
|
||||
})
|
||||
.getline(function (line, vars) {
|
||||
vars.y = parseInt(line, 10);
|
||||
})
|
||||
.do(function (vars) {
|
||||
if (vars.x + vars.y < 10) {
|
||||
util.print('z = ');
|
||||
this.getline(function (line) {
|
||||
vars.z = parseInt(line, 10);
|
||||
})
|
||||
}
|
||||
else {
|
||||
vars.z = 0;
|
||||
}
|
||||
})
|
||||
.do(function (vars) {
|
||||
console.log('x + y + z = ' + (vars.x + vars.y + vars.z));
|
||||
process.exit();
|
||||
})
|
||||
;
|
||||
|
||||
Installation
|
||||
============
|
||||
|
||||
With [npm](http://github.com/isaacs/npm), just do:
|
||||
npm install chainsaw
|
||||
|
||||
or clone this project on github:
|
||||
|
||||
git clone http://github.com/substack/node-chainsaw.git
|
||||
|
||||
To run the tests with [expresso](http://github.com/visionmedia/expresso),
|
||||
just do:
|
||||
|
||||
expresso
|
||||
|
||||
|
||||
Light Mode vs Full Mode
|
||||
=======================
|
||||
|
||||
`node-chainsaw` supports two different modes. In full mode, every
|
||||
action is recorded, which allows you to replay actions using the
|
||||
`jump()`, `trap()` and `down()` methods.
|
||||
|
||||
However, if your chainsaws are long-lived, recording every action can
|
||||
consume a tremendous amount of memory, so we also offer a "light" mode
|
||||
where actions are not recorded and the aforementioned methods are
|
||||
disabled.
|
||||
|
||||
To enable light mode simply use `Chainsaw.light()` to construct your
|
||||
saw, instead of `Chainsaw()`.
|
||||
|
||||
|
||||
-25
@@ -1,25 +0,0 @@
|
||||
var Chainsaw = require('chainsaw');
|
||||
|
||||
function AddDo (sum) {
|
||||
return Chainsaw(function (saw) {
|
||||
this.add = function (n) {
|
||||
sum += n;
|
||||
saw.next();
|
||||
};
|
||||
|
||||
this.do = function (cb) {
|
||||
saw.nest(cb, sum);
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
AddDo(0)
|
||||
.add(5)
|
||||
.add(10)
|
||||
.do(function (sum) {
|
||||
if (sum > 12) this.add(-10);
|
||||
})
|
||||
.do(function (sum) {
|
||||
console.log('Sum: ' + sum);
|
||||
})
|
||||
;
|
||||
-67
@@ -1,67 +0,0 @@
|
||||
var Chainsaw = require('chainsaw');
|
||||
var Lazy = require('lazy');
|
||||
|
||||
module.exports = Prompt;
|
||||
function Prompt (stream) {
|
||||
var waiting = [];
|
||||
var lines = [];
|
||||
var lazy = Lazy(stream).lines.map(String)
|
||||
.forEach(function (line) {
|
||||
if (waiting.length) {
|
||||
var w = waiting.shift();
|
||||
w(line);
|
||||
}
|
||||
else lines.push(line);
|
||||
})
|
||||
;
|
||||
|
||||
var vars = {};
|
||||
return Chainsaw(function (saw) {
|
||||
this.getline = function (f) {
|
||||
var g = function (line) {
|
||||
saw.nest(f, line, vars);
|
||||
};
|
||||
|
||||
if (lines.length) g(lines.shift());
|
||||
else waiting.push(g);
|
||||
};
|
||||
|
||||
this.do = function (cb) {
|
||||
saw.nest(cb, vars);
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
var util = require('util');
|
||||
if (__filename === process.argv[1]) {
|
||||
var stdin = process.openStdin();
|
||||
Prompt(stdin)
|
||||
.do(function () {
|
||||
util.print('x = ');
|
||||
})
|
||||
.getline(function (line, vars) {
|
||||
vars.x = parseInt(line, 10);
|
||||
})
|
||||
.do(function () {
|
||||
util.print('y = ');
|
||||
})
|
||||
.getline(function (line, vars) {
|
||||
vars.y = parseInt(line, 10);
|
||||
})
|
||||
.do(function (vars) {
|
||||
if (vars.x + vars.y < 10) {
|
||||
util.print('z = ');
|
||||
this.getline(function (line) {
|
||||
vars.z = parseInt(line, 10);
|
||||
})
|
||||
}
|
||||
else {
|
||||
vars.z = 0;
|
||||
}
|
||||
})
|
||||
.do(function (vars) {
|
||||
console.log('x + y + z = ' + (vars.x + vars.y + vars.z));
|
||||
process.exit();
|
||||
})
|
||||
;
|
||||
}
|
||||
-145
@@ -1,145 +0,0 @@
|
||||
var Traverse = require('traverse');
|
||||
var EventEmitter = require('events').EventEmitter;
|
||||
|
||||
module.exports = Chainsaw;
|
||||
function Chainsaw (builder) {
|
||||
var saw = Chainsaw.saw(builder, {});
|
||||
var r = builder.call(saw.handlers, saw);
|
||||
if (r !== undefined) saw.handlers = r;
|
||||
saw.record();
|
||||
return saw.chain();
|
||||
};
|
||||
|
||||
Chainsaw.light = function ChainsawLight (builder) {
|
||||
var saw = Chainsaw.saw(builder, {});
|
||||
var r = builder.call(saw.handlers, saw);
|
||||
if (r !== undefined) saw.handlers = r;
|
||||
return saw.chain();
|
||||
};
|
||||
|
||||
Chainsaw.saw = function (builder, handlers) {
|
||||
var saw = new EventEmitter;
|
||||
saw.handlers = handlers;
|
||||
saw.actions = [];
|
||||
|
||||
saw.chain = function () {
|
||||
var ch = Traverse(saw.handlers).map(function (node) {
|
||||
if (this.isRoot) return node;
|
||||
var ps = this.path;
|
||||
|
||||
if (typeof node === 'function') {
|
||||
this.update(function () {
|
||||
saw.actions.push({
|
||||
path : ps,
|
||||
args : [].slice.call(arguments)
|
||||
});
|
||||
return ch;
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
process.nextTick(function () {
|
||||
saw.emit('begin');
|
||||
saw.next();
|
||||
});
|
||||
|
||||
return ch;
|
||||
};
|
||||
|
||||
saw.pop = function () {
|
||||
return saw.actions.shift();
|
||||
};
|
||||
|
||||
saw.next = function () {
|
||||
var action = saw.pop();
|
||||
|
||||
if (!action) {
|
||||
saw.emit('end');
|
||||
}
|
||||
else if (!action.trap) {
|
||||
var node = saw.handlers;
|
||||
action.path.forEach(function (key) { node = node[key] });
|
||||
node.apply(saw.handlers, action.args);
|
||||
}
|
||||
};
|
||||
|
||||
saw.nest = function (cb) {
|
||||
var args = [].slice.call(arguments, 1);
|
||||
var autonext = true;
|
||||
|
||||
if (typeof cb === 'boolean') {
|
||||
var autonext = cb;
|
||||
cb = args.shift();
|
||||
}
|
||||
|
||||
var s = Chainsaw.saw(builder, {});
|
||||
var r = builder.call(s.handlers, s);
|
||||
|
||||
if (r !== undefined) s.handlers = r;
|
||||
|
||||
// If we are recording...
|
||||
if ("undefined" !== typeof saw.step) {
|
||||
// ... our children should, too
|
||||
s.record();
|
||||
}
|
||||
|
||||
cb.apply(s.chain(), args);
|
||||
if (autonext !== false) s.on('end', saw.next);
|
||||
};
|
||||
|
||||
saw.record = function () {
|
||||
upgradeChainsaw(saw);
|
||||
};
|
||||
|
||||
['trap', 'down', 'jump'].forEach(function (method) {
|
||||
saw[method] = function () {
|
||||
throw new Error("To use the trap, down and jump features, please "+
|
||||
"call record() first to start recording actions.");
|
||||
};
|
||||
});
|
||||
|
||||
return saw;
|
||||
};
|
||||
|
||||
function upgradeChainsaw(saw) {
|
||||
saw.step = 0;
|
||||
|
||||
// override pop
|
||||
saw.pop = function () {
|
||||
return saw.actions[saw.step++];
|
||||
};
|
||||
|
||||
saw.trap = function (name, cb) {
|
||||
var ps = Array.isArray(name) ? name : [name];
|
||||
saw.actions.push({
|
||||
path : ps,
|
||||
step : saw.step,
|
||||
cb : cb,
|
||||
trap : true
|
||||
});
|
||||
};
|
||||
|
||||
saw.down = function (name) {
|
||||
var ps = (Array.isArray(name) ? name : [name]).join('/');
|
||||
var i = saw.actions.slice(saw.step).map(function (x) {
|
||||
if (x.trap && x.step <= saw.step) return false;
|
||||
return x.path.join('/') == ps;
|
||||
}).indexOf(true);
|
||||
|
||||
if (i >= 0) saw.step += i;
|
||||
else saw.step = saw.actions.length;
|
||||
|
||||
var act = saw.actions[saw.step - 1];
|
||||
if (act && act.trap) {
|
||||
// It's a trap!
|
||||
saw.step = act.step;
|
||||
act.cb();
|
||||
}
|
||||
else saw.next();
|
||||
};
|
||||
|
||||
saw.jump = function (step) {
|
||||
saw.step = step;
|
||||
saw.next();
|
||||
};
|
||||
};
|
||||
-23
@@ -1,23 +0,0 @@
|
||||
{
|
||||
"name" : "chainsaw",
|
||||
"version" : "0.1.0",
|
||||
"description" : "Build chainable fluent interfaces the easy way... with a freakin' chainsaw!",
|
||||
"main" : "./index.js",
|
||||
"repository" : {
|
||||
"type" : "git",
|
||||
"url" : "http://github.com/substack/node-chainsaw.git"
|
||||
},
|
||||
"dependencies" : {
|
||||
"traverse" : ">=0.3.0 <0.4"
|
||||
},
|
||||
"keywords" : [
|
||||
"chain",
|
||||
"fluent",
|
||||
"interface",
|
||||
"monad",
|
||||
"monadic"
|
||||
],
|
||||
"author" : "James Halliday <mail@substack.net> (http://substack.net)",
|
||||
"license" : "MIT/X11",
|
||||
"engine" : { "node" : ">=0.4.0" }
|
||||
}
|
||||
-418
@@ -1,418 +0,0 @@
|
||||
var assert = require('assert');
|
||||
var Chainsaw = require('../index');
|
||||
|
||||
exports.getset = function () {
|
||||
var to = setTimeout(function () {
|
||||
assert.fail('builder never fired');
|
||||
}, 1000);
|
||||
|
||||
var ch = Chainsaw(function (saw) {
|
||||
clearTimeout(to);
|
||||
var num = 0;
|
||||
|
||||
this.get = function (cb) {
|
||||
cb(num);
|
||||
saw.next();
|
||||
};
|
||||
|
||||
this.set = function (n) {
|
||||
num = n;
|
||||
saw.next();
|
||||
};
|
||||
|
||||
var ti = setTimeout(function () {
|
||||
assert.fail('end event not emitted');
|
||||
}, 50);
|
||||
|
||||
saw.on('end', function () {
|
||||
clearTimeout(ti);
|
||||
assert.equal(times, 3);
|
||||
});
|
||||
});
|
||||
|
||||
var times = 0;
|
||||
ch
|
||||
.get(function (x) {
|
||||
assert.equal(x, 0);
|
||||
times ++;
|
||||
})
|
||||
.set(10)
|
||||
.get(function (x) {
|
||||
assert.equal(x, 10);
|
||||
times ++;
|
||||
})
|
||||
.set(20)
|
||||
.get(function (x) {
|
||||
assert.equal(x, 20);
|
||||
times ++;
|
||||
})
|
||||
;
|
||||
};
|
||||
|
||||
exports.nest = function () {
|
||||
var ch = (function () {
|
||||
var vars = {};
|
||||
return Chainsaw(function (saw) {
|
||||
this.do = function (cb) {
|
||||
saw.nest(cb, vars);
|
||||
};
|
||||
});
|
||||
})();
|
||||
|
||||
var order = [];
|
||||
var to = setTimeout(function () {
|
||||
assert.fail("Didn't get to the end");
|
||||
}, 50);
|
||||
|
||||
ch
|
||||
.do(function (vars) {
|
||||
vars.x = 'y';
|
||||
order.push(1);
|
||||
|
||||
this
|
||||
.do(function (vs) {
|
||||
order.push(2);
|
||||
vs.x = 'x';
|
||||
})
|
||||
.do(function (vs) {
|
||||
order.push(3);
|
||||
vs.z = 'z';
|
||||
})
|
||||
;
|
||||
})
|
||||
.do(function (vars) {
|
||||
vars.y = 'y';
|
||||
order.push(4);
|
||||
})
|
||||
.do(function (vars) {
|
||||
assert.eql(order, [1,2,3,4]);
|
||||
assert.eql(vars, { x : 'x', y : 'y', z : 'z' });
|
||||
clearTimeout(to);
|
||||
})
|
||||
;
|
||||
};
|
||||
|
||||
exports.nestWait = function () {
|
||||
var ch = (function () {
|
||||
var vars = {};
|
||||
return Chainsaw(function (saw) {
|
||||
this.do = function (cb) {
|
||||
saw.nest(cb, vars);
|
||||
};
|
||||
|
||||
this.wait = function (n) {
|
||||
setTimeout(function () {
|
||||
saw.next();
|
||||
}, n);
|
||||
};
|
||||
});
|
||||
})();
|
||||
|
||||
var order = [];
|
||||
var to = setTimeout(function () {
|
||||
assert.fail("Didn't get to the end");
|
||||
}, 1000);
|
||||
|
||||
var times = {};
|
||||
|
||||
ch
|
||||
.do(function (vars) {
|
||||
vars.x = 'y';
|
||||
order.push(1);
|
||||
|
||||
this
|
||||
.do(function (vs) {
|
||||
order.push(2);
|
||||
vs.x = 'x';
|
||||
times.x = Date.now();
|
||||
})
|
||||
.wait(50)
|
||||
.do(function (vs) {
|
||||
order.push(3);
|
||||
vs.z = 'z';
|
||||
|
||||
times.z = Date.now();
|
||||
var dt = times.z - times.x;
|
||||
assert.ok(dt >= 50 && dt < 75);
|
||||
})
|
||||
;
|
||||
})
|
||||
.do(function (vars) {
|
||||
vars.y = 'y';
|
||||
order.push(4);
|
||||
|
||||
times.y = Date.now();
|
||||
})
|
||||
.wait(100)
|
||||
.do(function (vars) {
|
||||
assert.eql(order, [1,2,3,4]);
|
||||
assert.eql(vars, { x : 'x', y : 'y', z : 'z' });
|
||||
clearTimeout(to);
|
||||
|
||||
times.end = Date.now();
|
||||
var dt = times.end - times.y;
|
||||
assert.ok(dt >= 100 && dt < 125)
|
||||
})
|
||||
;
|
||||
};
|
||||
|
||||
exports.nestNext = function () {
|
||||
var ch = (function () {
|
||||
var vars = {};
|
||||
return Chainsaw(function (saw) {
|
||||
this.do = function (cb) {
|
||||
saw.nest(false, function () {
|
||||
var args = [].slice.call(arguments);
|
||||
args.push(saw.next);
|
||||
cb.apply(this, args);
|
||||
}, vars);
|
||||
};
|
||||
});
|
||||
})();
|
||||
|
||||
var order = [];
|
||||
var to = setTimeout(function () {
|
||||
assert.fail("Didn't get to the end");
|
||||
}, 500);
|
||||
|
||||
var times = [];
|
||||
|
||||
ch
|
||||
.do(function (vars, next_) {
|
||||
vars.x = 'y';
|
||||
order.push(1);
|
||||
|
||||
this
|
||||
.do(function (vs, next) {
|
||||
order.push(2);
|
||||
vs.x = 'x';
|
||||
setTimeout(next, 30);
|
||||
})
|
||||
.do(function (vs, next) {
|
||||
order.push(3);
|
||||
vs.z = 'z';
|
||||
setTimeout(next, 10);
|
||||
})
|
||||
.do(function () {
|
||||
setTimeout(next_, 20);
|
||||
})
|
||||
;
|
||||
})
|
||||
.do(function (vars, next) {
|
||||
vars.y = 'y';
|
||||
order.push(4);
|
||||
setTimeout(next, 5);
|
||||
})
|
||||
.do(function (vars) {
|
||||
assert.eql(order, [1,2,3,4]);
|
||||
assert.eql(vars, { x : 'x', y : 'y', z : 'z' });
|
||||
|
||||
clearTimeout(to);
|
||||
})
|
||||
;
|
||||
};
|
||||
|
||||
exports.builder = function () {
|
||||
var cx = Chainsaw(function (saw) {
|
||||
this.x = function () {};
|
||||
});
|
||||
assert.ok(cx.x);
|
||||
|
||||
var cy = Chainsaw(function (saw) {
|
||||
return { y : function () {} };
|
||||
});
|
||||
assert.ok(cy.y);
|
||||
|
||||
var cz = Chainsaw(function (saw) {
|
||||
return { z : function (cb) { saw.nest(cb) } };
|
||||
});
|
||||
assert.ok(cz.z);
|
||||
|
||||
var to = setTimeout(function () {
|
||||
assert.fail("Nested z didn't run");
|
||||
}, 50);
|
||||
|
||||
cz.z(function () {
|
||||
clearTimeout(to);
|
||||
assert.ok(this.z);
|
||||
});
|
||||
};
|
||||
|
||||
this.attr = function () {
|
||||
var to = setTimeout(function () {
|
||||
assert.fail("attr chain didn't finish");
|
||||
}, 50);
|
||||
|
||||
var xy = [];
|
||||
var ch = Chainsaw(function (saw) {
|
||||
this.h = {
|
||||
x : function () {
|
||||
xy.push('x');
|
||||
saw.next();
|
||||
},
|
||||
y : function () {
|
||||
xy.push('y');
|
||||
saw.next();
|
||||
assert.eql(xy, ['x','y']);
|
||||
clearTimeout(to);
|
||||
}
|
||||
};
|
||||
});
|
||||
assert.ok(ch.h);
|
||||
assert.ok(ch.h.x);
|
||||
assert.ok(ch.h.y);
|
||||
|
||||
ch.h.x().h.y();
|
||||
};
|
||||
|
||||
exports.down = function () {
|
||||
var error = null;
|
||||
var s;
|
||||
var ch = Chainsaw(function (saw) {
|
||||
s = saw;
|
||||
this.raise = function (err) {
|
||||
error = err;
|
||||
saw.down('catch');
|
||||
};
|
||||
|
||||
this.do = function (cb) {
|
||||
cb.call(this);
|
||||
};
|
||||
|
||||
this.catch = function (cb) {
|
||||
if (error) {
|
||||
saw.nest(cb, error);
|
||||
error = null;
|
||||
}
|
||||
else saw.next();
|
||||
};
|
||||
});
|
||||
|
||||
var to = setTimeout(function () {
|
||||
assert.fail(".do() after .catch() didn't fire");
|
||||
}, 50);
|
||||
|
||||
ch
|
||||
.do(function () {
|
||||
this.raise('pow');
|
||||
})
|
||||
.do(function () {
|
||||
assert.fail("raise didn't skip over this do block");
|
||||
})
|
||||
.catch(function (err) {
|
||||
assert.equal(err, 'pow');
|
||||
})
|
||||
.do(function () {
|
||||
clearTimeout(to);
|
||||
})
|
||||
;
|
||||
};
|
||||
|
||||
exports.trap = function () {
|
||||
var error = null;
|
||||
var ch = Chainsaw(function (saw) {
|
||||
var pars = 0;
|
||||
var stack = [];
|
||||
var i = 0;
|
||||
|
||||
this.par = function (cb) {
|
||||
pars ++;
|
||||
var j = i ++;
|
||||
cb.call(function () {
|
||||
pars --;
|
||||
stack[j] = [].slice.call(arguments);
|
||||
saw.down('result');
|
||||
});
|
||||
saw.next();
|
||||
};
|
||||
|
||||
this.join = function (cb) {
|
||||
saw.trap('result', function () {
|
||||
if (pars == 0) {
|
||||
cb.apply(this, stack);
|
||||
saw.next();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
this.raise = function (err) {
|
||||
error = err;
|
||||
saw.down('catch');
|
||||
};
|
||||
|
||||
this.do = function (cb) {
|
||||
cb.call(this);
|
||||
};
|
||||
|
||||
this.catch = function (cb) {
|
||||
if (error) {
|
||||
saw.nest(cb, error);
|
||||
error = null;
|
||||
}
|
||||
else saw.next();
|
||||
};
|
||||
});
|
||||
|
||||
var to = setTimeout(function () {
|
||||
assert.fail(".do() after .join() didn't fire");
|
||||
}, 100);
|
||||
var tj = setTimeout(function () {
|
||||
assert.fail('.join() never fired');
|
||||
}, 100);
|
||||
|
||||
var joined = false;
|
||||
ch
|
||||
.par(function () {
|
||||
setTimeout(this.bind(null, 1), 50);
|
||||
})
|
||||
.par(function () {
|
||||
setTimeout(this.bind(null, 2), 25);
|
||||
})
|
||||
.join(function (x, y) {
|
||||
assert.equal(x[0], 1);
|
||||
assert.equal(y[0], 2);
|
||||
clearTimeout(tj);
|
||||
joined = true;
|
||||
})
|
||||
.do(function () {
|
||||
clearTimeout(to);
|
||||
assert.ok(joined);
|
||||
})
|
||||
;
|
||||
};
|
||||
|
||||
exports.jump = function () {
|
||||
var to = setTimeout(function () {
|
||||
assert.fail('builder never fired');
|
||||
}, 50);
|
||||
|
||||
var xs = [ 4, 5, 6, -4, 8, 9, -1, 8 ];
|
||||
var xs_ = [];
|
||||
|
||||
var ch = Chainsaw(function (saw) {
|
||||
this.x = function (i) {
|
||||
xs_.push(i);
|
||||
saw.next();
|
||||
};
|
||||
|
||||
this.y = function (step) {
|
||||
var x = xs.shift();
|
||||
if (x > 0) saw.jump(step);
|
||||
else saw.next();
|
||||
};
|
||||
|
||||
saw.on('end', function () {
|
||||
clearTimeout(to);
|
||||
assert.eql(xs, [ 8 ]);
|
||||
assert.eql(xs_, [ 1, 1, 1, 1, 2, 3, 2, 3, 2, 3 ]);
|
||||
});
|
||||
});
|
||||
|
||||
ch
|
||||
.x(1)
|
||||
.y(0)
|
||||
.x(2)
|
||||
.x(3)
|
||||
.y(2)
|
||||
;
|
||||
};
|
||||
-4
@@ -1,4 +0,0 @@
|
||||
language: node_js
|
||||
node_js:
|
||||
- 0.4
|
||||
- 0.6
|
||||
-18
@@ -1,18 +0,0 @@
|
||||
This software is released under the MIT license:
|
||||
|
||||
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.
|
||||
-62
@@ -1,62 +0,0 @@
|
||||
concat-map
|
||||
==========
|
||||
|
||||
Concatenative mapdashery.
|
||||
|
||||
[](http://ci.testling.com/substack/node-concat-map)
|
||||
|
||||
[](http://travis-ci.org/substack/node-concat-map)
|
||||
|
||||
example
|
||||
=======
|
||||
|
||||
``` js
|
||||
var concatMap = require('concat-map');
|
||||
var xs = [ 1, 2, 3, 4, 5, 6 ];
|
||||
var ys = concatMap(xs, function (x) {
|
||||
return x % 2 ? [ x - 0.1, x, x + 0.1 ] : [];
|
||||
});
|
||||
console.dir(ys);
|
||||
```
|
||||
|
||||
***
|
||||
|
||||
```
|
||||
[ 0.9, 1, 1.1, 2.9, 3, 3.1, 4.9, 5, 5.1 ]
|
||||
```
|
||||
|
||||
methods
|
||||
=======
|
||||
|
||||
``` js
|
||||
var concatMap = require('concat-map')
|
||||
```
|
||||
|
||||
concatMap(xs, fn)
|
||||
-----------------
|
||||
|
||||
Return an array of concatenated elements by calling `fn(x, i)` for each element
|
||||
`x` and each index `i` in the array `xs`.
|
||||
|
||||
When `fn(x, i)` returns an array, its result will be concatenated with the
|
||||
result array. If `fn(x, i)` returns anything else, that value will be pushed
|
||||
onto the end of the result array.
|
||||
|
||||
install
|
||||
=======
|
||||
|
||||
With [npm](http://npmjs.org) do:
|
||||
|
||||
```
|
||||
npm install concat-map
|
||||
```
|
||||
|
||||
license
|
||||
=======
|
||||
|
||||
MIT
|
||||
|
||||
notes
|
||||
=====
|
||||
|
||||
This module was written while sitting high above the ground in a tree.
|
||||
-6
@@ -1,6 +0,0 @@
|
||||
var concatMap = require('../');
|
||||
var xs = [ 1, 2, 3, 4, 5, 6 ];
|
||||
var ys = concatMap(xs, function (x) {
|
||||
return x % 2 ? [ x - 0.1, x, x + 0.1 ] : [];
|
||||
});
|
||||
console.dir(ys);
|
||||
-13
@@ -1,13 +0,0 @@
|
||||
module.exports = function (xs, fn) {
|
||||
var res = [];
|
||||
for (var i = 0; i < xs.length; i++) {
|
||||
var x = fn(xs[i], i);
|
||||
if (isArray(x)) res.push.apply(res, x);
|
||||
else res.push(x);
|
||||
}
|
||||
return res;
|
||||
};
|
||||
|
||||
var isArray = Array.isArray || function (xs) {
|
||||
return Object.prototype.toString.call(xs) === '[object Array]';
|
||||
};
|
||||
-43
@@ -1,43 +0,0 @@
|
||||
{
|
||||
"name" : "concat-map",
|
||||
"description" : "concatenative mapdashery",
|
||||
"version" : "0.0.1",
|
||||
"repository" : {
|
||||
"type" : "git",
|
||||
"url" : "git://github.com/substack/node-concat-map.git"
|
||||
},
|
||||
"main" : "index.js",
|
||||
"keywords" : [
|
||||
"concat",
|
||||
"concatMap",
|
||||
"map",
|
||||
"functional",
|
||||
"higher-order"
|
||||
],
|
||||
"directories" : {
|
||||
"example" : "example",
|
||||
"test" : "test"
|
||||
},
|
||||
"scripts" : {
|
||||
"test" : "tape test/*.js"
|
||||
},
|
||||
"devDependencies" : {
|
||||
"tape" : "~2.4.0"
|
||||
},
|
||||
"license" : "MIT",
|
||||
"author" : {
|
||||
"name" : "James Halliday",
|
||||
"email" : "mail@substack.net",
|
||||
"url" : "http://substack.net"
|
||||
},
|
||||
"testling" : {
|
||||
"files" : "test/*.js",
|
||||
"browsers" : {
|
||||
"ie" : [ 6, 7, 8, 9 ],
|
||||
"ff" : [ 3.5, 10, 15.0 ],
|
||||
"chrome" : [ 10, 22 ],
|
||||
"safari" : [ 5.1 ],
|
||||
"opera" : [ 12 ]
|
||||
}
|
||||
}
|
||||
}
|
||||
-39
@@ -1,39 +0,0 @@
|
||||
var concatMap = require('../');
|
||||
var test = require('tape');
|
||||
|
||||
test('empty or not', function (t) {
|
||||
var xs = [ 1, 2, 3, 4, 5, 6 ];
|
||||
var ixes = [];
|
||||
var ys = concatMap(xs, function (x, ix) {
|
||||
ixes.push(ix);
|
||||
return x % 2 ? [ x - 0.1, x, x + 0.1 ] : [];
|
||||
});
|
||||
t.same(ys, [ 0.9, 1, 1.1, 2.9, 3, 3.1, 4.9, 5, 5.1 ]);
|
||||
t.same(ixes, [ 0, 1, 2, 3, 4, 5 ]);
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('always something', function (t) {
|
||||
var xs = [ 'a', 'b', 'c', 'd' ];
|
||||
var ys = concatMap(xs, function (x) {
|
||||
return x === 'b' ? [ 'B', 'B', 'B' ] : [ x ];
|
||||
});
|
||||
t.same(ys, [ 'a', 'B', 'B', 'B', 'c', 'd' ]);
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('scalars', function (t) {
|
||||
var xs = [ 'a', 'b', 'c', 'd' ];
|
||||
var ys = concatMap(xs, function (x) {
|
||||
return x === 'b' ? [ 'B', 'B', 'B' ] : x;
|
||||
});
|
||||
t.same(ys, [ 'a', 'B', 'B', 'B', 'c', 'd' ]);
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('undefs', function (t) {
|
||||
var xs = [ 'a', 'b', 'c', 'd' ];
|
||||
var ys = concatMap(xs, function () {});
|
||||
t.same(ys, [ undefined, undefined, undefined, undefined ]);
|
||||
t.end();
|
||||
});
|
||||
-19
@@ -1,19 +0,0 @@
|
||||
Copyright Node.js contributors. All rights reserved.
|
||||
|
||||
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.
|
||||
-3
@@ -1,3 +0,0 @@
|
||||
# core-util-is
|
||||
|
||||
The `util.is*` functions introduced in Node v0.12.
|
||||
-107
@@ -1,107 +0,0 @@
|
||||
// Copyright Joyent, Inc. and other Node contributors.
|
||||
//
|
||||
// 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.
|
||||
|
||||
// NOTE: These type checking functions intentionally don't use `instanceof`
|
||||
// because it is fragile and can be easily faked with `Object.create()`.
|
||||
|
||||
function isArray(arg) {
|
||||
if (Array.isArray) {
|
||||
return Array.isArray(arg);
|
||||
}
|
||||
return objectToString(arg) === '[object Array]';
|
||||
}
|
||||
exports.isArray = isArray;
|
||||
|
||||
function isBoolean(arg) {
|
||||
return typeof arg === 'boolean';
|
||||
}
|
||||
exports.isBoolean = isBoolean;
|
||||
|
||||
function isNull(arg) {
|
||||
return arg === null;
|
||||
}
|
||||
exports.isNull = isNull;
|
||||
|
||||
function isNullOrUndefined(arg) {
|
||||
return arg == null;
|
||||
}
|
||||
exports.isNullOrUndefined = isNullOrUndefined;
|
||||
|
||||
function isNumber(arg) {
|
||||
return typeof arg === 'number';
|
||||
}
|
||||
exports.isNumber = isNumber;
|
||||
|
||||
function isString(arg) {
|
||||
return typeof arg === 'string';
|
||||
}
|
||||
exports.isString = isString;
|
||||
|
||||
function isSymbol(arg) {
|
||||
return typeof arg === 'symbol';
|
||||
}
|
||||
exports.isSymbol = isSymbol;
|
||||
|
||||
function isUndefined(arg) {
|
||||
return arg === void 0;
|
||||
}
|
||||
exports.isUndefined = isUndefined;
|
||||
|
||||
function isRegExp(re) {
|
||||
return objectToString(re) === '[object RegExp]';
|
||||
}
|
||||
exports.isRegExp = isRegExp;
|
||||
|
||||
function isObject(arg) {
|
||||
return typeof arg === 'object' && arg !== null;
|
||||
}
|
||||
exports.isObject = isObject;
|
||||
|
||||
function isDate(d) {
|
||||
return objectToString(d) === '[object Date]';
|
||||
}
|
||||
exports.isDate = isDate;
|
||||
|
||||
function isError(e) {
|
||||
return (objectToString(e) === '[object Error]' || e instanceof Error);
|
||||
}
|
||||
exports.isError = isError;
|
||||
|
||||
function isFunction(arg) {
|
||||
return typeof arg === 'function';
|
||||
}
|
||||
exports.isFunction = isFunction;
|
||||
|
||||
function isPrimitive(arg) {
|
||||
return arg === null ||
|
||||
typeof arg === 'boolean' ||
|
||||
typeof arg === 'number' ||
|
||||
typeof arg === 'string' ||
|
||||
typeof arg === 'symbol' || // ES6 symbol
|
||||
typeof arg === 'undefined';
|
||||
}
|
||||
exports.isPrimitive = isPrimitive;
|
||||
|
||||
exports.isBuffer = require('buffer').Buffer.isBuffer;
|
||||
|
||||
function objectToString(o) {
|
||||
return Object.prototype.toString.call(o);
|
||||
}
|
||||
-38
@@ -1,38 +0,0 @@
|
||||
{
|
||||
"name": "core-util-is",
|
||||
"version": "1.0.3",
|
||||
"description": "The `util.is*` functions introduced in Node v0.12.",
|
||||
"main": "lib/util.js",
|
||||
"files": [
|
||||
"lib"
|
||||
],
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git://github.com/isaacs/core-util-is"
|
||||
},
|
||||
"keywords": [
|
||||
"util",
|
||||
"isBuffer",
|
||||
"isArray",
|
||||
"isNumber",
|
||||
"isString",
|
||||
"isRegExp",
|
||||
"isThis",
|
||||
"isThat",
|
||||
"polyfill"
|
||||
],
|
||||
"author": "Isaac Z. Schlueter <i@izs.me> (http://blog.izs.me/)",
|
||||
"license": "MIT",
|
||||
"bugs": {
|
||||
"url": "https://github.com/isaacs/core-util-is/issues"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "tap test.js",
|
||||
"preversion": "npm test",
|
||||
"postversion": "npm publish",
|
||||
"prepublishOnly": "git push origin --follow-tags"
|
||||
},
|
||||
"devDependencies": {
|
||||
"tap": "^15.0.9"
|
||||
}
|
||||
}
|
||||
-26
@@ -1,26 +0,0 @@
|
||||
Copyright (c) 2013, Deoxxa Development
|
||||
======================================
|
||||
All rights reserved.
|
||||
--------------------
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
1. Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
3. Neither the name of Deoxxa Development nor the names of its contributors
|
||||
may be used to endorse or promote products derived from this software
|
||||
without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY DEOXXA DEVELOPMENT ''AS IS'' AND ANY
|
||||
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL DEOXXA DEVELOPMENT BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
-115
@@ -1,115 +0,0 @@
|
||||
# duplexer2 [](https://travis-ci.org/deoxxa/duplexer2) [](https://coveralls.io/github/deoxxa/duplexer2?branch=master)
|
||||
|
||||
Like [duplexer](https://github.com/Raynos/duplexer) but using Streams3
|
||||
|
||||
```javascript
|
||||
var stream = require("stream");
|
||||
|
||||
var duplexer2 = require("duplexer2");
|
||||
|
||||
var writable = new stream.Writable({objectMode: true}),
|
||||
readable = new stream.Readable({objectMode: true});
|
||||
|
||||
writable._write = function _write(input, encoding, done) {
|
||||
if (readable.push(input)) {
|
||||
return done();
|
||||
} else {
|
||||
readable.once("drain", done);
|
||||
}
|
||||
};
|
||||
|
||||
readable._read = function _read(n) {
|
||||
// no-op
|
||||
};
|
||||
|
||||
// simulate the readable thing closing after a bit
|
||||
writable.once("finish", function() {
|
||||
setTimeout(function() {
|
||||
readable.push(null);
|
||||
}, 500);
|
||||
});
|
||||
|
||||
var duplex = duplexer2(writable, readable);
|
||||
|
||||
duplex.on("data", function(e) {
|
||||
console.log("got data", JSON.stringify(e));
|
||||
});
|
||||
|
||||
duplex.on("finish", function() {
|
||||
console.log("got finish event");
|
||||
});
|
||||
|
||||
duplex.on("end", function() {
|
||||
console.log("got end event");
|
||||
});
|
||||
|
||||
duplex.write("oh, hi there", function() {
|
||||
console.log("finished writing");
|
||||
});
|
||||
|
||||
duplex.end(function() {
|
||||
console.log("finished ending");
|
||||
});
|
||||
```
|
||||
|
||||
```
|
||||
got data "oh, hi there"
|
||||
finished writing
|
||||
got finish event
|
||||
finished ending
|
||||
got end event
|
||||
```
|
||||
|
||||
## Overview
|
||||
|
||||
This is a reimplementation of [duplexer](https://www.npmjs.com/package/duplexer) using the
|
||||
Streams3 API which is standard in Node as of v4. Everything largely
|
||||
works the same.
|
||||
|
||||
|
||||
|
||||
## Installation
|
||||
|
||||
[Available via `npm`](https://docs.npmjs.com/cli/install):
|
||||
|
||||
```
|
||||
$ npm i duplexer2
|
||||
```
|
||||
|
||||
## API
|
||||
|
||||
### duplexer2
|
||||
|
||||
Creates a new `DuplexWrapper` object, which is the actual class that implements
|
||||
most of the fun stuff. All that fun stuff is hidden. DON'T LOOK.
|
||||
|
||||
```javascript
|
||||
duplexer2([options], writable, readable)
|
||||
```
|
||||
|
||||
```javascript
|
||||
const duplex = duplexer2(new stream.Writable(), new stream.Readable());
|
||||
```
|
||||
|
||||
Arguments
|
||||
|
||||
* __options__ - an object specifying the regular `stream.Duplex` options, as
|
||||
well as the properties described below.
|
||||
* __writable__ - a writable stream
|
||||
* __readable__ - a readable stream
|
||||
|
||||
Options
|
||||
|
||||
* __bubbleErrors__ - a boolean value that specifies whether to bubble errors
|
||||
from the underlying readable/writable streams. Default is `true`.
|
||||
|
||||
|
||||
## License
|
||||
|
||||
3-clause BSD. [A copy](./LICENSE) is included with the source.
|
||||
|
||||
## Contact
|
||||
|
||||
* GitHub ([deoxxa](http://github.com/deoxxa))
|
||||
* Twitter ([@deoxxa](http://twitter.com/deoxxa))
|
||||
* Email ([deoxxa@fknsrs.biz](mailto:deoxxa@fknsrs.biz))
|
||||
-76
@@ -1,76 +0,0 @@
|
||||
"use strict";
|
||||
|
||||
var stream = require("readable-stream");
|
||||
|
||||
function DuplexWrapper(options, writable, readable) {
|
||||
if (typeof readable === "undefined") {
|
||||
readable = writable;
|
||||
writable = options;
|
||||
options = null;
|
||||
}
|
||||
|
||||
stream.Duplex.call(this, options);
|
||||
|
||||
if (typeof readable.read !== "function") {
|
||||
readable = (new stream.Readable(options)).wrap(readable);
|
||||
}
|
||||
|
||||
this._writable = writable;
|
||||
this._readable = readable;
|
||||
this._waiting = false;
|
||||
|
||||
var self = this;
|
||||
|
||||
writable.once("finish", function() {
|
||||
self.end();
|
||||
});
|
||||
|
||||
this.once("finish", function() {
|
||||
writable.end();
|
||||
});
|
||||
|
||||
readable.on("readable", function() {
|
||||
if (self._waiting) {
|
||||
self._waiting = false;
|
||||
self._read();
|
||||
}
|
||||
});
|
||||
|
||||
readable.once("end", function() {
|
||||
self.push(null);
|
||||
});
|
||||
|
||||
if (!options || typeof options.bubbleErrors === "undefined" || options.bubbleErrors) {
|
||||
writable.on("error", function(err) {
|
||||
self.emit("error", err);
|
||||
});
|
||||
|
||||
readable.on("error", function(err) {
|
||||
self.emit("error", err);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
DuplexWrapper.prototype = Object.create(stream.Duplex.prototype, {constructor: {value: DuplexWrapper}});
|
||||
|
||||
DuplexWrapper.prototype._write = function _write(input, encoding, done) {
|
||||
this._writable.write(input, encoding, done);
|
||||
};
|
||||
|
||||
DuplexWrapper.prototype._read = function _read() {
|
||||
var buf;
|
||||
var reads = 0;
|
||||
while ((buf = this._readable.read()) !== null) {
|
||||
this.push(buf);
|
||||
reads++;
|
||||
}
|
||||
if (reads === 0) {
|
||||
this._waiting = true;
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = function duplex2(options, writable, readable) {
|
||||
return new DuplexWrapper(options, writable, readable);
|
||||
};
|
||||
|
||||
module.exports.DuplexWrapper = DuplexWrapper;
|
||||
-28
@@ -1,28 +0,0 @@
|
||||
{
|
||||
"name": "duplexer2",
|
||||
"version": "0.1.4",
|
||||
"description": "Like duplexer but using streams3",
|
||||
"files": [
|
||||
"index.js"
|
||||
],
|
||||
"scripts": {
|
||||
"test": "mocha -R tap"
|
||||
},
|
||||
"repository": "deoxxa/duplexer2",
|
||||
"keywords": [
|
||||
"duplex",
|
||||
"duplexer",
|
||||
"stream",
|
||||
"stream3",
|
||||
"join",
|
||||
"combine"
|
||||
],
|
||||
"author": "Conrad Pankoff <deoxxa@fknsrs.biz> (http://www.fknsrs.biz/)",
|
||||
"license": "BSD-3-Clause",
|
||||
"dependencies": {
|
||||
"readable-stream": "^2.0.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"mocha": "^2.2.5"
|
||||
}
|
||||
}
|
||||
-43
@@ -1,43 +0,0 @@
|
||||
The ISC License
|
||||
|
||||
Copyright (c) Isaac Z. Schlueter and Contributors
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
|
||||
IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
----
|
||||
|
||||
This library bundles a version of the `fs.realpath` and `fs.realpathSync`
|
||||
methods from Node.js v0.10 under the terms of the Node.js MIT license.
|
||||
|
||||
Node's license follows, also included at the header of `old.js` which contains
|
||||
the licensed code:
|
||||
|
||||
Copyright Joyent, Inc. and other Node contributors.
|
||||
|
||||
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.
|
||||
-33
@@ -1,33 +0,0 @@
|
||||
# fs.realpath
|
||||
|
||||
A backwards-compatible fs.realpath for Node v6 and above
|
||||
|
||||
In Node v6, the JavaScript implementation of fs.realpath was replaced
|
||||
with a faster (but less resilient) native implementation. That raises
|
||||
new and platform-specific errors and cannot handle long or excessively
|
||||
symlink-looping paths.
|
||||
|
||||
This module handles those cases by detecting the new errors and
|
||||
falling back to the JavaScript implementation. On versions of Node
|
||||
prior to v6, it has no effect.
|
||||
|
||||
## USAGE
|
||||
|
||||
```js
|
||||
var rp = require('fs.realpath')
|
||||
|
||||
// async version
|
||||
rp.realpath(someLongAndLoopingPath, function (er, real) {
|
||||
// the ELOOP was handled, but it was a bit slower
|
||||
})
|
||||
|
||||
// sync version
|
||||
var real = rp.realpathSync(someLongAndLoopingPath)
|
||||
|
||||
// monkeypatch at your own risk!
|
||||
// This replaces the fs.realpath/fs.realpathSync builtins
|
||||
rp.monkeypatch()
|
||||
|
||||
// un-do the monkeypatching
|
||||
rp.unmonkeypatch()
|
||||
```
|
||||
-66
@@ -1,66 +0,0 @@
|
||||
module.exports = realpath
|
||||
realpath.realpath = realpath
|
||||
realpath.sync = realpathSync
|
||||
realpath.realpathSync = realpathSync
|
||||
realpath.monkeypatch = monkeypatch
|
||||
realpath.unmonkeypatch = unmonkeypatch
|
||||
|
||||
var fs = require('fs')
|
||||
var origRealpath = fs.realpath
|
||||
var origRealpathSync = fs.realpathSync
|
||||
|
||||
var version = process.version
|
||||
var ok = /^v[0-5]\./.test(version)
|
||||
var old = require('./old.js')
|
||||
|
||||
function newError (er) {
|
||||
return er && er.syscall === 'realpath' && (
|
||||
er.code === 'ELOOP' ||
|
||||
er.code === 'ENOMEM' ||
|
||||
er.code === 'ENAMETOOLONG'
|
||||
)
|
||||
}
|
||||
|
||||
function realpath (p, cache, cb) {
|
||||
if (ok) {
|
||||
return origRealpath(p, cache, cb)
|
||||
}
|
||||
|
||||
if (typeof cache === 'function') {
|
||||
cb = cache
|
||||
cache = null
|
||||
}
|
||||
origRealpath(p, cache, function (er, result) {
|
||||
if (newError(er)) {
|
||||
old.realpath(p, cache, cb)
|
||||
} else {
|
||||
cb(er, result)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function realpathSync (p, cache) {
|
||||
if (ok) {
|
||||
return origRealpathSync(p, cache)
|
||||
}
|
||||
|
||||
try {
|
||||
return origRealpathSync(p, cache)
|
||||
} catch (er) {
|
||||
if (newError(er)) {
|
||||
return old.realpathSync(p, cache)
|
||||
} else {
|
||||
throw er
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function monkeypatch () {
|
||||
fs.realpath = realpath
|
||||
fs.realpathSync = realpathSync
|
||||
}
|
||||
|
||||
function unmonkeypatch () {
|
||||
fs.realpath = origRealpath
|
||||
fs.realpathSync = origRealpathSync
|
||||
}
|
||||
-303
@@ -1,303 +0,0 @@
|
||||
// Copyright Joyent, Inc. and other Node contributors.
|
||||
//
|
||||
// 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.
|
||||
|
||||
var pathModule = require('path');
|
||||
var isWindows = process.platform === 'win32';
|
||||
var fs = require('fs');
|
||||
|
||||
// JavaScript implementation of realpath, ported from node pre-v6
|
||||
|
||||
var DEBUG = process.env.NODE_DEBUG && /fs/.test(process.env.NODE_DEBUG);
|
||||
|
||||
function rethrow() {
|
||||
// Only enable in debug mode. A backtrace uses ~1000 bytes of heap space and
|
||||
// is fairly slow to generate.
|
||||
var callback;
|
||||
if (DEBUG) {
|
||||
var backtrace = new Error;
|
||||
callback = debugCallback;
|
||||
} else
|
||||
callback = missingCallback;
|
||||
|
||||
return callback;
|
||||
|
||||
function debugCallback(err) {
|
||||
if (err) {
|
||||
backtrace.message = err.message;
|
||||
err = backtrace;
|
||||
missingCallback(err);
|
||||
}
|
||||
}
|
||||
|
||||
function missingCallback(err) {
|
||||
if (err) {
|
||||
if (process.throwDeprecation)
|
||||
throw err; // Forgot a callback but don't know where? Use NODE_DEBUG=fs
|
||||
else if (!process.noDeprecation) {
|
||||
var msg = 'fs: missing callback ' + (err.stack || err.message);
|
||||
if (process.traceDeprecation)
|
||||
console.trace(msg);
|
||||
else
|
||||
console.error(msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function maybeCallback(cb) {
|
||||
return typeof cb === 'function' ? cb : rethrow();
|
||||
}
|
||||
|
||||
var normalize = pathModule.normalize;
|
||||
|
||||
// Regexp that finds the next partion of a (partial) path
|
||||
// result is [base_with_slash, base], e.g. ['somedir/', 'somedir']
|
||||
if (isWindows) {
|
||||
var nextPartRe = /(.*?)(?:[\/\\]+|$)/g;
|
||||
} else {
|
||||
var nextPartRe = /(.*?)(?:[\/]+|$)/g;
|
||||
}
|
||||
|
||||
// Regex to find the device root, including trailing slash. E.g. 'c:\\'.
|
||||
if (isWindows) {
|
||||
var splitRootRe = /^(?:[a-zA-Z]:|[\\\/]{2}[^\\\/]+[\\\/][^\\\/]+)?[\\\/]*/;
|
||||
} else {
|
||||
var splitRootRe = /^[\/]*/;
|
||||
}
|
||||
|
||||
exports.realpathSync = function realpathSync(p, cache) {
|
||||
// make p is absolute
|
||||
p = pathModule.resolve(p);
|
||||
|
||||
if (cache && Object.prototype.hasOwnProperty.call(cache, p)) {
|
||||
return cache[p];
|
||||
}
|
||||
|
||||
var original = p,
|
||||
seenLinks = {},
|
||||
knownHard = {};
|
||||
|
||||
// current character position in p
|
||||
var pos;
|
||||
// the partial path so far, including a trailing slash if any
|
||||
var current;
|
||||
// the partial path without a trailing slash (except when pointing at a root)
|
||||
var base;
|
||||
// the partial path scanned in the previous round, with slash
|
||||
var previous;
|
||||
|
||||
start();
|
||||
|
||||
function start() {
|
||||
// Skip over roots
|
||||
var m = splitRootRe.exec(p);
|
||||
pos = m[0].length;
|
||||
current = m[0];
|
||||
base = m[0];
|
||||
previous = '';
|
||||
|
||||
// On windows, check that the root exists. On unix there is no need.
|
||||
if (isWindows && !knownHard[base]) {
|
||||
fs.lstatSync(base);
|
||||
knownHard[base] = true;
|
||||
}
|
||||
}
|
||||
|
||||
// walk down the path, swapping out linked pathparts for their real
|
||||
// values
|
||||
// NB: p.length changes.
|
||||
while (pos < p.length) {
|
||||
// find the next part
|
||||
nextPartRe.lastIndex = pos;
|
||||
var result = nextPartRe.exec(p);
|
||||
previous = current;
|
||||
current += result[0];
|
||||
base = previous + result[1];
|
||||
pos = nextPartRe.lastIndex;
|
||||
|
||||
// continue if not a symlink
|
||||
if (knownHard[base] || (cache && cache[base] === base)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
var resolvedLink;
|
||||
if (cache && Object.prototype.hasOwnProperty.call(cache, base)) {
|
||||
// some known symbolic link. no need to stat again.
|
||||
resolvedLink = cache[base];
|
||||
} else {
|
||||
var stat = fs.lstatSync(base);
|
||||
if (!stat.isSymbolicLink()) {
|
||||
knownHard[base] = true;
|
||||
if (cache) cache[base] = base;
|
||||
continue;
|
||||
}
|
||||
|
||||
// read the link if it wasn't read before
|
||||
// dev/ino always return 0 on windows, so skip the check.
|
||||
var linkTarget = null;
|
||||
if (!isWindows) {
|
||||
var id = stat.dev.toString(32) + ':' + stat.ino.toString(32);
|
||||
if (seenLinks.hasOwnProperty(id)) {
|
||||
linkTarget = seenLinks[id];
|
||||
}
|
||||
}
|
||||
if (linkTarget === null) {
|
||||
fs.statSync(base);
|
||||
linkTarget = fs.readlinkSync(base);
|
||||
}
|
||||
resolvedLink = pathModule.resolve(previous, linkTarget);
|
||||
// track this, if given a cache.
|
||||
if (cache) cache[base] = resolvedLink;
|
||||
if (!isWindows) seenLinks[id] = linkTarget;
|
||||
}
|
||||
|
||||
// resolve the link, then start over
|
||||
p = pathModule.resolve(resolvedLink, p.slice(pos));
|
||||
start();
|
||||
}
|
||||
|
||||
if (cache) cache[original] = p;
|
||||
|
||||
return p;
|
||||
};
|
||||
|
||||
|
||||
exports.realpath = function realpath(p, cache, cb) {
|
||||
if (typeof cb !== 'function') {
|
||||
cb = maybeCallback(cache);
|
||||
cache = null;
|
||||
}
|
||||
|
||||
// make p is absolute
|
||||
p = pathModule.resolve(p);
|
||||
|
||||
if (cache && Object.prototype.hasOwnProperty.call(cache, p)) {
|
||||
return process.nextTick(cb.bind(null, null, cache[p]));
|
||||
}
|
||||
|
||||
var original = p,
|
||||
seenLinks = {},
|
||||
knownHard = {};
|
||||
|
||||
// current character position in p
|
||||
var pos;
|
||||
// the partial path so far, including a trailing slash if any
|
||||
var current;
|
||||
// the partial path without a trailing slash (except when pointing at a root)
|
||||
var base;
|
||||
// the partial path scanned in the previous round, with slash
|
||||
var previous;
|
||||
|
||||
start();
|
||||
|
||||
function start() {
|
||||
// Skip over roots
|
||||
var m = splitRootRe.exec(p);
|
||||
pos = m[0].length;
|
||||
current = m[0];
|
||||
base = m[0];
|
||||
previous = '';
|
||||
|
||||
// On windows, check that the root exists. On unix there is no need.
|
||||
if (isWindows && !knownHard[base]) {
|
||||
fs.lstat(base, function(err) {
|
||||
if (err) return cb(err);
|
||||
knownHard[base] = true;
|
||||
LOOP();
|
||||
});
|
||||
} else {
|
||||
process.nextTick(LOOP);
|
||||
}
|
||||
}
|
||||
|
||||
// walk down the path, swapping out linked pathparts for their real
|
||||
// values
|
||||
function LOOP() {
|
||||
// stop if scanned past end of path
|
||||
if (pos >= p.length) {
|
||||
if (cache) cache[original] = p;
|
||||
return cb(null, p);
|
||||
}
|
||||
|
||||
// find the next part
|
||||
nextPartRe.lastIndex = pos;
|
||||
var result = nextPartRe.exec(p);
|
||||
previous = current;
|
||||
current += result[0];
|
||||
base = previous + result[1];
|
||||
pos = nextPartRe.lastIndex;
|
||||
|
||||
// continue if not a symlink
|
||||
if (knownHard[base] || (cache && cache[base] === base)) {
|
||||
return process.nextTick(LOOP);
|
||||
}
|
||||
|
||||
if (cache && Object.prototype.hasOwnProperty.call(cache, base)) {
|
||||
// known symbolic link. no need to stat again.
|
||||
return gotResolvedLink(cache[base]);
|
||||
}
|
||||
|
||||
return fs.lstat(base, gotStat);
|
||||
}
|
||||
|
||||
function gotStat(err, stat) {
|
||||
if (err) return cb(err);
|
||||
|
||||
// if not a symlink, skip to the next path part
|
||||
if (!stat.isSymbolicLink()) {
|
||||
knownHard[base] = true;
|
||||
if (cache) cache[base] = base;
|
||||
return process.nextTick(LOOP);
|
||||
}
|
||||
|
||||
// stat & read the link if not read before
|
||||
// call gotTarget as soon as the link target is known
|
||||
// dev/ino always return 0 on windows, so skip the check.
|
||||
if (!isWindows) {
|
||||
var id = stat.dev.toString(32) + ':' + stat.ino.toString(32);
|
||||
if (seenLinks.hasOwnProperty(id)) {
|
||||
return gotTarget(null, seenLinks[id], base);
|
||||
}
|
||||
}
|
||||
fs.stat(base, function(err) {
|
||||
if (err) return cb(err);
|
||||
|
||||
fs.readlink(base, function(err, target) {
|
||||
if (!isWindows) seenLinks[id] = target;
|
||||
gotTarget(err, target);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function gotTarget(err, target, base) {
|
||||
if (err) return cb(err);
|
||||
|
||||
var resolvedLink = pathModule.resolve(previous, target);
|
||||
if (cache) cache[base] = resolvedLink;
|
||||
gotResolvedLink(resolvedLink);
|
||||
}
|
||||
|
||||
function gotResolvedLink(resolvedLink) {
|
||||
// resolve the link, then start over
|
||||
p = pathModule.resolve(resolvedLink, p.slice(pos));
|
||||
start();
|
||||
}
|
||||
};
|
||||
-26
@@ -1,26 +0,0 @@
|
||||
{
|
||||
"name": "fs.realpath",
|
||||
"version": "1.0.0",
|
||||
"description": "Use node's fs.realpath, but fall back to the JS implementation if the native one fails",
|
||||
"main": "index.js",
|
||||
"dependencies": {},
|
||||
"devDependencies": {},
|
||||
"scripts": {
|
||||
"test": "tap test/*.js --cov"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/isaacs/fs.realpath.git"
|
||||
},
|
||||
"keywords": [
|
||||
"realpath",
|
||||
"fs",
|
||||
"polyfill"
|
||||
],
|
||||
"author": "Isaac Z. Schlueter <i@izs.me> (http://blog.izs.me/)",
|
||||
"license": "ISC",
|
||||
"files": [
|
||||
"old.js",
|
||||
"index.js"
|
||||
]
|
||||
}
|
||||
-9
@@ -1,9 +0,0 @@
|
||||
language: node_js
|
||||
node_js:
|
||||
- "6"
|
||||
- "4"
|
||||
- "0.10"
|
||||
- "0.12"
|
||||
before_install:
|
||||
- "npm config set spin false"
|
||||
- "npm install -g npm/npm"
|
||||
-15
@@ -1,15 +0,0 @@
|
||||
The ISC License
|
||||
|
||||
Copyright (c) Isaac Z. Schlueter and Contributors
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
|
||||
IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
-76
@@ -1,76 +0,0 @@
|
||||
Like FS streams, but with stat on them, and supporting directories and
|
||||
symbolic links, as well as normal files. Also, you can use this to set
|
||||
the stats on a file, even if you don't change its contents, or to create
|
||||
a symlink, etc.
|
||||
|
||||
So, for example, you can "write" a directory, and it'll call `mkdir`. You
|
||||
can specify a uid and gid, and it'll call `chown`. You can specify a
|
||||
`mtime` and `atime`, and it'll call `utimes`. You can call it a symlink
|
||||
and provide a `linkpath` and it'll call `symlink`.
|
||||
|
||||
Note that it won't automatically resolve symbolic links. So, if you
|
||||
call `fstream.Reader('/some/symlink')` then you'll get an object
|
||||
that stats and then ends immediately (since it has no data). To follow
|
||||
symbolic links, do this: `fstream.Reader({path:'/some/symlink', follow:
|
||||
true })`.
|
||||
|
||||
There are various checks to make sure that the bytes emitted are the
|
||||
same as the intended size, if the size is set.
|
||||
|
||||
## Examples
|
||||
|
||||
```javascript
|
||||
fstream
|
||||
.Writer({ path: "path/to/file"
|
||||
, mode: 0755
|
||||
, size: 6
|
||||
})
|
||||
.write("hello\n")
|
||||
.end()
|
||||
```
|
||||
|
||||
This will create the directories if they're missing, and then write
|
||||
`hello\n` into the file, chmod it to 0755, and assert that 6 bytes have
|
||||
been written when it's done.
|
||||
|
||||
```javascript
|
||||
fstream
|
||||
.Writer({ path: "path/to/file"
|
||||
, mode: 0755
|
||||
, size: 6
|
||||
, flags: "a"
|
||||
})
|
||||
.write("hello\n")
|
||||
.end()
|
||||
```
|
||||
|
||||
You can pass flags in, if you want to append to a file.
|
||||
|
||||
```javascript
|
||||
fstream
|
||||
.Writer({ path: "path/to/symlink"
|
||||
, linkpath: "./file"
|
||||
, SymbolicLink: true
|
||||
, mode: "0755" // octal strings supported
|
||||
})
|
||||
.end()
|
||||
```
|
||||
|
||||
If isSymbolicLink is a function, it'll be called, and if it returns
|
||||
true, then it'll treat it as a symlink. If it's not a function, then
|
||||
any truish value will make a symlink, or you can set `type:
|
||||
'SymbolicLink'`, which does the same thing.
|
||||
|
||||
Note that the linkpath is relative to the symbolic link location, not
|
||||
the parent dir or cwd.
|
||||
|
||||
```javascript
|
||||
fstream
|
||||
.Reader("path/to/dir")
|
||||
.pipe(fstream.Writer("path/to/other/dir"))
|
||||
```
|
||||
|
||||
This will do like `cp -Rp path/to/dir path/to/other/dir`. If the other
|
||||
dir exists and isn't a directory, then it'll emit an error. It'll also
|
||||
set the uid, gid, mode, etc. to be identical. In this way, it's more
|
||||
like `rsync -a` than simply a copy.
|
||||
-134
@@ -1,134 +0,0 @@
|
||||
var fstream = require('../fstream.js')
|
||||
var path = require('path')
|
||||
|
||||
var r = fstream.Reader({
|
||||
path: path.dirname(__dirname),
|
||||
filter: function () {
|
||||
return !this.basename.match(/^\./) &&
|
||||
!this.basename.match(/^node_modules$/) &&
|
||||
!this.basename.match(/^deep-copy$/) &&
|
||||
!this.basename.match(/^filter-copy$/)
|
||||
}
|
||||
})
|
||||
|
||||
// this writer will only write directories
|
||||
var w = fstream.Writer({
|
||||
path: path.resolve(__dirname, 'filter-copy'),
|
||||
type: 'Directory',
|
||||
filter: function () {
|
||||
return this.type === 'Directory'
|
||||
}
|
||||
})
|
||||
|
||||
var indent = ''
|
||||
|
||||
r.on('entry', appears)
|
||||
r.on('ready', function () {
|
||||
console.error('ready to begin!', r.path)
|
||||
})
|
||||
|
||||
function appears (entry) {
|
||||
console.error(indent + 'a %s appears!', entry.type, entry.basename, typeof entry.basename)
|
||||
if (foggy) {
|
||||
console.error('FOGGY!')
|
||||
var p = entry
|
||||
do {
|
||||
console.error(p.depth, p.path, p._paused)
|
||||
p = p.parent
|
||||
} while (p)
|
||||
|
||||
throw new Error('\u001b[mshould not have entries while foggy')
|
||||
}
|
||||
indent += '\t'
|
||||
entry.on('data', missile(entry))
|
||||
entry.on('end', runaway(entry))
|
||||
entry.on('entry', appears)
|
||||
}
|
||||
|
||||
var foggy
|
||||
function missile (entry) {
|
||||
function liftFog (who) {
|
||||
if (!foggy) return
|
||||
if (who) {
|
||||
console.error('%s breaks the spell!', who && who.path)
|
||||
} else {
|
||||
console.error('the spell expires!')
|
||||
}
|
||||
console.error('\u001b[mthe fog lifts!\n')
|
||||
clearTimeout(foggy)
|
||||
foggy = null
|
||||
if (entry._paused) entry.resume()
|
||||
}
|
||||
|
||||
if (entry.type === 'Directory') {
|
||||
var ended = false
|
||||
entry.once('end', function () { ended = true })
|
||||
return function (c) {
|
||||
// throw in some pathological pause()/resume() behavior
|
||||
// just for extra fun.
|
||||
process.nextTick(function () {
|
||||
if (!foggy && !ended) { // && Math.random() < 0.3) {
|
||||
console.error(indent + '%s casts a spell', entry.basename)
|
||||
console.error('\na slowing fog comes over the battlefield...\n\u001b[32m')
|
||||
entry.pause()
|
||||
entry.once('resume', liftFog)
|
||||
foggy = setTimeout(liftFog, 1000)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return function (c) {
|
||||
var e = Math.random() < 0.5
|
||||
console.error(indent + '%s %s for %d damage!',
|
||||
entry.basename,
|
||||
e ? 'is struck' : 'fires a chunk',
|
||||
c.length)
|
||||
}
|
||||
}
|
||||
|
||||
function runaway (entry) {
|
||||
return function () {
|
||||
var e = Math.random() < 0.5
|
||||
console.error(indent + '%s %s',
|
||||
entry.basename,
|
||||
e ? 'turns to flee' : 'is vanquished!')
|
||||
indent = indent.slice(0, -1)
|
||||
}
|
||||
}
|
||||
|
||||
w.on('entry', attacks)
|
||||
// w.on('ready', function () { attacks(w) })
|
||||
function attacks (entry) {
|
||||
console.error(indent + '%s %s!', entry.basename,
|
||||
entry.type === 'Directory' ? 'calls for backup' : 'attacks')
|
||||
entry.on('entry', attacks)
|
||||
}
|
||||
|
||||
var ended = false
|
||||
var i = 1
|
||||
r.on('end', function () {
|
||||
if (foggy) clearTimeout(foggy)
|
||||
console.error("\u001b[mIT'S OVER!!")
|
||||
console.error('A WINNAR IS YOU!')
|
||||
|
||||
console.log('ok ' + (i++) + ' A WINNAR IS YOU')
|
||||
ended = true
|
||||
// now go through and verify that everything in there is a dir.
|
||||
var p = path.resolve(__dirname, 'filter-copy')
|
||||
var checker = fstream.Reader({ path: p })
|
||||
checker.checker = true
|
||||
checker.on('child', function (e) {
|
||||
var ok = e.type === 'Directory'
|
||||
console.log((ok ? '' : 'not ') + 'ok ' + (i++) +
|
||||
' should be a dir: ' +
|
||||
e.path.substr(checker.path.length + 1))
|
||||
})
|
||||
})
|
||||
|
||||
process.on('exit', function () {
|
||||
console.log((ended ? '' : 'not ') + 'ok ' + (i) + ' ended')
|
||||
console.log('1..' + i)
|
||||
})
|
||||
|
||||
r.pipe(w)
|
||||
-118
@@ -1,118 +0,0 @@
|
||||
var fstream = require('../fstream.js')
|
||||
var path = require('path')
|
||||
|
||||
var r = fstream.Reader({
|
||||
path: path.dirname(__dirname),
|
||||
filter: function () {
|
||||
return !this.basename.match(/^\./) &&
|
||||
!this.basename.match(/^node_modules$/) &&
|
||||
!this.basename.match(/^deep-copy$/)
|
||||
}
|
||||
})
|
||||
|
||||
var w = fstream.Writer({
|
||||
path: path.resolve(__dirname, 'deep-copy'),
|
||||
type: 'Directory'
|
||||
})
|
||||
|
||||
var indent = ''
|
||||
|
||||
r.on('entry', appears)
|
||||
r.on('ready', function () {
|
||||
console.error('ready to begin!', r.path)
|
||||
})
|
||||
|
||||
function appears (entry) {
|
||||
console.error(indent + 'a %s appears!', entry.type, entry.basename, typeof entry.basename, entry)
|
||||
if (foggy) {
|
||||
console.error('FOGGY!')
|
||||
var p = entry
|
||||
do {
|
||||
console.error(p.depth, p.path, p._paused)
|
||||
p = p.parent
|
||||
} while (p)
|
||||
|
||||
throw new Error('\u001b[mshould not have entries while foggy')
|
||||
}
|
||||
indent += '\t'
|
||||
entry.on('data', missile(entry))
|
||||
entry.on('end', runaway(entry))
|
||||
entry.on('entry', appears)
|
||||
}
|
||||
|
||||
var foggy
|
||||
function missile (entry) {
|
||||
function liftFog (who) {
|
||||
if (!foggy) return
|
||||
if (who) {
|
||||
console.error('%s breaks the spell!', who && who.path)
|
||||
} else {
|
||||
console.error('the spell expires!')
|
||||
}
|
||||
console.error('\u001b[mthe fog lifts!\n')
|
||||
clearTimeout(foggy)
|
||||
foggy = null
|
||||
if (entry._paused) entry.resume()
|
||||
}
|
||||
|
||||
if (entry.type === 'Directory') {
|
||||
var ended = false
|
||||
entry.once('end', function () { ended = true })
|
||||
return function (c) {
|
||||
// throw in some pathological pause()/resume() behavior
|
||||
// just for extra fun.
|
||||
process.nextTick(function () {
|
||||
if (!foggy && !ended) { // && Math.random() < 0.3) {
|
||||
console.error(indent + '%s casts a spell', entry.basename)
|
||||
console.error('\na slowing fog comes over the battlefield...\n\u001b[32m')
|
||||
entry.pause()
|
||||
entry.once('resume', liftFog)
|
||||
foggy = setTimeout(liftFog, 10)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return function (c) {
|
||||
var e = Math.random() < 0.5
|
||||
console.error(indent + '%s %s for %d damage!',
|
||||
entry.basename,
|
||||
e ? 'is struck' : 'fires a chunk',
|
||||
c.length)
|
||||
}
|
||||
}
|
||||
|
||||
function runaway (entry) {
|
||||
return function () {
|
||||
var e = Math.random() < 0.5
|
||||
console.error(indent + '%s %s',
|
||||
entry.basename,
|
||||
e ? 'turns to flee' : 'is vanquished!')
|
||||
indent = indent.slice(0, -1)
|
||||
}
|
||||
}
|
||||
|
||||
w.on('entry', attacks)
|
||||
// w.on('ready', function () { attacks(w) })
|
||||
function attacks (entry) {
|
||||
console.error(indent + '%s %s!', entry.basename,
|
||||
entry.type === 'Directory' ? 'calls for backup' : 'attacks')
|
||||
entry.on('entry', attacks)
|
||||
}
|
||||
|
||||
var ended = false
|
||||
r.on('end', function () {
|
||||
if (foggy) clearTimeout(foggy)
|
||||
console.error("\u001b[mIT'S OVER!!")
|
||||
console.error('A WINNAR IS YOU!')
|
||||
|
||||
console.log('ok 1 A WINNAR IS YOU')
|
||||
ended = true
|
||||
})
|
||||
|
||||
process.on('exit', function () {
|
||||
console.log((ended ? '' : 'not ') + 'ok 2 ended')
|
||||
console.log('1..2')
|
||||
})
|
||||
|
||||
r.pipe(w)
|
||||
-68
@@ -1,68 +0,0 @@
|
||||
var fstream = require('../fstream.js')
|
||||
var tap = require('tap')
|
||||
var fs = require('fs')
|
||||
var path = require('path')
|
||||
var dir = path.dirname(__dirname)
|
||||
|
||||
tap.test('reader test', function (t) {
|
||||
var children = -1
|
||||
var gotReady = false
|
||||
var ended = false
|
||||
|
||||
var r = fstream.Reader({
|
||||
path: dir,
|
||||
filter: function () {
|
||||
// return this.parent === r
|
||||
return this.parent === r || this === r
|
||||
}
|
||||
})
|
||||
|
||||
r.on('ready', function () {
|
||||
gotReady = true
|
||||
children = fs.readdirSync(dir).length
|
||||
console.error('Setting expected children to ' + children)
|
||||
t.equal(r.type, 'Directory', 'should be a directory')
|
||||
})
|
||||
|
||||
r.on('entry', function (entry) {
|
||||
children--
|
||||
if (!gotReady) {
|
||||
t.fail('children before ready!')
|
||||
}
|
||||
t.equal(entry.dirname, r.path, 'basename is parent dir')
|
||||
})
|
||||
|
||||
r.on('error', function (er) {
|
||||
t.fail(er)
|
||||
t.end()
|
||||
process.exit(1)
|
||||
})
|
||||
|
||||
r.on('end', function () {
|
||||
t.equal(children, 0, 'should have seen all children')
|
||||
ended = true
|
||||
})
|
||||
|
||||
var closed = false
|
||||
r.on('close', function () {
|
||||
t.ok(ended, 'saw end before close')
|
||||
t.notOk(closed, 'close should only happen once')
|
||||
closed = true
|
||||
t.end()
|
||||
})
|
||||
})
|
||||
|
||||
tap.test('reader error test', function (t) {
|
||||
// assumes non-root on a *nix system
|
||||
var r = fstream.Reader({ path: '/etc/shadow' })
|
||||
|
||||
r.once('error', function (er) {
|
||||
t.ok(true)
|
||||
t.end()
|
||||
})
|
||||
|
||||
r.on('end', function () {
|
||||
t.fail('reader ended without error')
|
||||
t.end()
|
||||
})
|
||||
})
|
||||
-27
@@ -1,27 +0,0 @@
|
||||
var fstream = require('../fstream.js')
|
||||
var notOpen = false
|
||||
process.chdir(__dirname)
|
||||
|
||||
fstream
|
||||
.Writer({
|
||||
path: 'path/to/symlink',
|
||||
linkpath: './file',
|
||||
isSymbolicLink: true,
|
||||
mode: '0755' // octal strings supported
|
||||
})
|
||||
.on('close', function () {
|
||||
notOpen = true
|
||||
var fs = require('fs')
|
||||
var s = fs.lstatSync('path/to/symlink')
|
||||
var isSym = s.isSymbolicLink()
|
||||
console.log((isSym ? '' : 'not ') + 'ok 1 should be symlink')
|
||||
var t = fs.readlinkSync('path/to/symlink')
|
||||
var isTarget = t === './file'
|
||||
console.log((isTarget ? '' : 'not ') + 'ok 2 should link to ./file')
|
||||
})
|
||||
.end()
|
||||
|
||||
process.on('exit', function () {
|
||||
console.log((notOpen ? '' : 'not ') + 'ok 3 should be closed')
|
||||
console.log('1..3')
|
||||
})
|
||||
-35
@@ -1,35 +0,0 @@
|
||||
exports.Abstract = require('./lib/abstract.js')
|
||||
exports.Reader = require('./lib/reader.js')
|
||||
exports.Writer = require('./lib/writer.js')
|
||||
|
||||
exports.File = {
|
||||
Reader: require('./lib/file-reader.js'),
|
||||
Writer: require('./lib/file-writer.js')
|
||||
}
|
||||
|
||||
exports.Dir = {
|
||||
Reader: require('./lib/dir-reader.js'),
|
||||
Writer: require('./lib/dir-writer.js')
|
||||
}
|
||||
|
||||
exports.Link = {
|
||||
Reader: require('./lib/link-reader.js'),
|
||||
Writer: require('./lib/link-writer.js')
|
||||
}
|
||||
|
||||
exports.Proxy = {
|
||||
Reader: require('./lib/proxy-reader.js'),
|
||||
Writer: require('./lib/proxy-writer.js')
|
||||
}
|
||||
|
||||
exports.Reader.Dir = exports.DirReader = exports.Dir.Reader
|
||||
exports.Reader.File = exports.FileReader = exports.File.Reader
|
||||
exports.Reader.Link = exports.LinkReader = exports.Link.Reader
|
||||
exports.Reader.Proxy = exports.ProxyReader = exports.Proxy.Reader
|
||||
|
||||
exports.Writer.Dir = exports.DirWriter = exports.Dir.Writer
|
||||
exports.Writer.File = exports.FileWriter = exports.File.Writer
|
||||
exports.Writer.Link = exports.LinkWriter = exports.Link.Writer
|
||||
exports.Writer.Proxy = exports.ProxyWriter = exports.Proxy.Writer
|
||||
|
||||
exports.collect = require('./lib/collect.js')
|
||||
-85
@@ -1,85 +0,0 @@
|
||||
// the parent class for all fstreams.
|
||||
|
||||
module.exports = Abstract
|
||||
|
||||
var Stream = require('stream').Stream
|
||||
var inherits = require('inherits')
|
||||
|
||||
function Abstract () {
|
||||
Stream.call(this)
|
||||
}
|
||||
|
||||
inherits(Abstract, Stream)
|
||||
|
||||
Abstract.prototype.on = function (ev, fn) {
|
||||
if (ev === 'ready' && this.ready) {
|
||||
process.nextTick(fn.bind(this))
|
||||
} else {
|
||||
Stream.prototype.on.call(this, ev, fn)
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
Abstract.prototype.abort = function () {
|
||||
this._aborted = true
|
||||
this.emit('abort')
|
||||
}
|
||||
|
||||
Abstract.prototype.destroy = function () {}
|
||||
|
||||
Abstract.prototype.warn = function (msg, code) {
|
||||
var self = this
|
||||
var er = decorate(msg, code, self)
|
||||
if (!self.listeners('warn')) {
|
||||
console.error('%s %s\n' +
|
||||
'path = %s\n' +
|
||||
'syscall = %s\n' +
|
||||
'fstream_type = %s\n' +
|
||||
'fstream_path = %s\n' +
|
||||
'fstream_unc_path = %s\n' +
|
||||
'fstream_class = %s\n' +
|
||||
'fstream_stack =\n%s\n',
|
||||
code || 'UNKNOWN',
|
||||
er.stack,
|
||||
er.path,
|
||||
er.syscall,
|
||||
er.fstream_type,
|
||||
er.fstream_path,
|
||||
er.fstream_unc_path,
|
||||
er.fstream_class,
|
||||
er.fstream_stack.join('\n'))
|
||||
} else {
|
||||
self.emit('warn', er)
|
||||
}
|
||||
}
|
||||
|
||||
Abstract.prototype.info = function (msg, code) {
|
||||
this.emit('info', msg, code)
|
||||
}
|
||||
|
||||
Abstract.prototype.error = function (msg, code, th) {
|
||||
var er = decorate(msg, code, this)
|
||||
if (th) throw er
|
||||
else this.emit('error', er)
|
||||
}
|
||||
|
||||
function decorate (er, code, self) {
|
||||
if (!(er instanceof Error)) er = new Error(er)
|
||||
er.code = er.code || code
|
||||
er.path = er.path || self.path
|
||||
er.fstream_type = er.fstream_type || self.type
|
||||
er.fstream_path = er.fstream_path || self.path
|
||||
if (self._path !== self.path) {
|
||||
er.fstream_unc_path = er.fstream_unc_path || self._path
|
||||
}
|
||||
if (self.linkpath) {
|
||||
er.fstream_linkpath = er.fstream_linkpath || self.linkpath
|
||||
}
|
||||
er.fstream_class = er.fstream_class || self.constructor.name
|
||||
er.fstream_stack = er.fstream_stack ||
|
||||
new Error().stack.split(/\n/).slice(3).map(function (s) {
|
||||
return s.replace(/^ {4}at /, '')
|
||||
})
|
||||
|
||||
return er
|
||||
}
|
||||
-70
@@ -1,70 +0,0 @@
|
||||
module.exports = collect
|
||||
|
||||
function collect (stream) {
|
||||
if (stream._collected) return
|
||||
|
||||
if (stream._paused) return stream.on('resume', collect.bind(null, stream))
|
||||
|
||||
stream._collected = true
|
||||
stream.pause()
|
||||
|
||||
stream.on('data', save)
|
||||
stream.on('end', save)
|
||||
var buf = []
|
||||
function save (b) {
|
||||
if (typeof b === 'string') b = new Buffer(b)
|
||||
if (Buffer.isBuffer(b) && !b.length) return
|
||||
buf.push(b)
|
||||
}
|
||||
|
||||
stream.on('entry', saveEntry)
|
||||
var entryBuffer = []
|
||||
function saveEntry (e) {
|
||||
collect(e)
|
||||
entryBuffer.push(e)
|
||||
}
|
||||
|
||||
stream.on('proxy', proxyPause)
|
||||
function proxyPause (p) {
|
||||
p.pause()
|
||||
}
|
||||
|
||||
// replace the pipe method with a new version that will
|
||||
// unlock the buffered stuff. if you just call .pipe()
|
||||
// without a destination, then it'll re-play the events.
|
||||
stream.pipe = (function (orig) {
|
||||
return function (dest) {
|
||||
// console.error(' === open the pipes', dest && dest.path)
|
||||
|
||||
// let the entries flow through one at a time.
|
||||
// Once they're all done, then we can resume completely.
|
||||
var e = 0
|
||||
;(function unblockEntry () {
|
||||
var entry = entryBuffer[e++]
|
||||
// console.error(" ==== unblock entry", entry && entry.path)
|
||||
if (!entry) return resume()
|
||||
entry.on('end', unblockEntry)
|
||||
if (dest) dest.add(entry)
|
||||
else stream.emit('entry', entry)
|
||||
})()
|
||||
|
||||
function resume () {
|
||||
stream.removeListener('entry', saveEntry)
|
||||
stream.removeListener('data', save)
|
||||
stream.removeListener('end', save)
|
||||
|
||||
stream.pipe = orig
|
||||
if (dest) stream.pipe(dest)
|
||||
|
||||
buf.forEach(function (b) {
|
||||
if (b) stream.emit('data', b)
|
||||
else stream.emit('end')
|
||||
})
|
||||
|
||||
stream.resume()
|
||||
}
|
||||
|
||||
return dest
|
||||
}
|
||||
})(stream.pipe)
|
||||
}
|
||||
-252
@@ -1,252 +0,0 @@
|
||||
// A thing that emits "entry" events with Reader objects
|
||||
// Pausing it causes it to stop emitting entry events, and also
|
||||
// pauses the current entry if there is one.
|
||||
|
||||
module.exports = DirReader
|
||||
|
||||
var fs = require('graceful-fs')
|
||||
var inherits = require('inherits')
|
||||
var path = require('path')
|
||||
var Reader = require('./reader.js')
|
||||
var assert = require('assert').ok
|
||||
|
||||
inherits(DirReader, Reader)
|
||||
|
||||
function DirReader (props) {
|
||||
var self = this
|
||||
if (!(self instanceof DirReader)) {
|
||||
throw new Error('DirReader must be called as constructor.')
|
||||
}
|
||||
|
||||
// should already be established as a Directory type
|
||||
if (props.type !== 'Directory' || !props.Directory) {
|
||||
throw new Error('Non-directory type ' + props.type)
|
||||
}
|
||||
|
||||
self.entries = null
|
||||
self._index = -1
|
||||
self._paused = false
|
||||
self._length = -1
|
||||
|
||||
if (props.sort) {
|
||||
this.sort = props.sort
|
||||
}
|
||||
|
||||
Reader.call(this, props)
|
||||
}
|
||||
|
||||
DirReader.prototype._getEntries = function () {
|
||||
var self = this
|
||||
|
||||
// race condition. might pause() before calling _getEntries,
|
||||
// and then resume, and try to get them a second time.
|
||||
if (self._gotEntries) return
|
||||
self._gotEntries = true
|
||||
|
||||
fs.readdir(self._path, function (er, entries) {
|
||||
if (er) return self.error(er)
|
||||
|
||||
self.entries = entries
|
||||
|
||||
self.emit('entries', entries)
|
||||
if (self._paused) self.once('resume', processEntries)
|
||||
else processEntries()
|
||||
|
||||
function processEntries () {
|
||||
self._length = self.entries.length
|
||||
if (typeof self.sort === 'function') {
|
||||
self.entries = self.entries.sort(self.sort.bind(self))
|
||||
}
|
||||
self._read()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// start walking the dir, and emit an "entry" event for each one.
|
||||
DirReader.prototype._read = function () {
|
||||
var self = this
|
||||
|
||||
if (!self.entries) return self._getEntries()
|
||||
|
||||
if (self._paused || self._currentEntry || self._aborted) {
|
||||
// console.error('DR paused=%j, current=%j, aborted=%j', self._paused, !!self._currentEntry, self._aborted)
|
||||
return
|
||||
}
|
||||
|
||||
self._index++
|
||||
if (self._index >= self.entries.length) {
|
||||
if (!self._ended) {
|
||||
self._ended = true
|
||||
self.emit('end')
|
||||
self.emit('close')
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// ok, handle this one, then.
|
||||
|
||||
// save creating a proxy, by stat'ing the thing now.
|
||||
var p = path.resolve(self._path, self.entries[self._index])
|
||||
assert(p !== self._path)
|
||||
assert(self.entries[self._index])
|
||||
|
||||
// set this to prevent trying to _read() again in the stat time.
|
||||
self._currentEntry = p
|
||||
fs[ self.props.follow ? 'stat' : 'lstat' ](p, function (er, stat) {
|
||||
if (er) return self.error(er)
|
||||
|
||||
var who = self._proxy || self
|
||||
|
||||
stat.path = p
|
||||
stat.basename = path.basename(p)
|
||||
stat.dirname = path.dirname(p)
|
||||
var childProps = self.getChildProps.call(who, stat)
|
||||
childProps.path = p
|
||||
childProps.basename = path.basename(p)
|
||||
childProps.dirname = path.dirname(p)
|
||||
|
||||
var entry = Reader(childProps, stat)
|
||||
|
||||
// console.error("DR Entry", p, stat.size)
|
||||
|
||||
self._currentEntry = entry
|
||||
|
||||
// "entry" events are for direct entries in a specific dir.
|
||||
// "child" events are for any and all children at all levels.
|
||||
// This nomenclature is not completely final.
|
||||
|
||||
entry.on('pause', function (who) {
|
||||
if (!self._paused && !entry._disowned) {
|
||||
self.pause(who)
|
||||
}
|
||||
})
|
||||
|
||||
entry.on('resume', function (who) {
|
||||
if (self._paused && !entry._disowned) {
|
||||
self.resume(who)
|
||||
}
|
||||
})
|
||||
|
||||
entry.on('stat', function (props) {
|
||||
self.emit('_entryStat', entry, props)
|
||||
if (entry._aborted) return
|
||||
if (entry._paused) {
|
||||
entry.once('resume', function () {
|
||||
self.emit('entryStat', entry, props)
|
||||
})
|
||||
} else self.emit('entryStat', entry, props)
|
||||
})
|
||||
|
||||
entry.on('ready', function EMITCHILD () {
|
||||
// console.error("DR emit child", entry._path)
|
||||
if (self._paused) {
|
||||
// console.error(" DR emit child - try again later")
|
||||
// pause the child, and emit the "entry" event once we drain.
|
||||
// console.error("DR pausing child entry")
|
||||
entry.pause(self)
|
||||
return self.once('resume', EMITCHILD)
|
||||
}
|
||||
|
||||
// skip over sockets. they can't be piped around properly,
|
||||
// so there's really no sense even acknowledging them.
|
||||
// if someone really wants to see them, they can listen to
|
||||
// the "socket" events.
|
||||
if (entry.type === 'Socket') {
|
||||
self.emit('socket', entry)
|
||||
} else {
|
||||
self.emitEntry(entry)
|
||||
}
|
||||
})
|
||||
|
||||
var ended = false
|
||||
entry.on('close', onend)
|
||||
entry.on('disown', onend)
|
||||
function onend () {
|
||||
if (ended) return
|
||||
ended = true
|
||||
self.emit('childEnd', entry)
|
||||
self.emit('entryEnd', entry)
|
||||
self._currentEntry = null
|
||||
if (!self._paused) {
|
||||
self._read()
|
||||
}
|
||||
}
|
||||
|
||||
// XXX Remove this. Works in node as of 0.6.2 or so.
|
||||
// Long filenames should not break stuff.
|
||||
entry.on('error', function (er) {
|
||||
if (entry._swallowErrors) {
|
||||
self.warn(er)
|
||||
entry.emit('end')
|
||||
entry.emit('close')
|
||||
} else {
|
||||
self.emit('error', er)
|
||||
}
|
||||
})
|
||||
|
||||
// proxy up some events.
|
||||
;[
|
||||
'child',
|
||||
'childEnd',
|
||||
'warn'
|
||||
].forEach(function (ev) {
|
||||
entry.on(ev, self.emit.bind(self, ev))
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
DirReader.prototype.disown = function (entry) {
|
||||
entry.emit('beforeDisown')
|
||||
entry._disowned = true
|
||||
entry.parent = entry.root = null
|
||||
if (entry === this._currentEntry) {
|
||||
this._currentEntry = null
|
||||
}
|
||||
entry.emit('disown')
|
||||
}
|
||||
|
||||
DirReader.prototype.getChildProps = function () {
|
||||
return {
|
||||
depth: this.depth + 1,
|
||||
root: this.root || this,
|
||||
parent: this,
|
||||
follow: this.follow,
|
||||
filter: this.filter,
|
||||
sort: this.props.sort,
|
||||
hardlinks: this.props.hardlinks
|
||||
}
|
||||
}
|
||||
|
||||
DirReader.prototype.pause = function (who) {
|
||||
var self = this
|
||||
if (self._paused) return
|
||||
who = who || self
|
||||
self._paused = true
|
||||
if (self._currentEntry && self._currentEntry.pause) {
|
||||
self._currentEntry.pause(who)
|
||||
}
|
||||
self.emit('pause', who)
|
||||
}
|
||||
|
||||
DirReader.prototype.resume = function (who) {
|
||||
var self = this
|
||||
if (!self._paused) return
|
||||
who = who || self
|
||||
|
||||
self._paused = false
|
||||
// console.error('DR Emit Resume', self._path)
|
||||
self.emit('resume', who)
|
||||
if (self._paused) {
|
||||
// console.error('DR Re-paused', self._path)
|
||||
return
|
||||
}
|
||||
|
||||
if (self._currentEntry) {
|
||||
if (self._currentEntry.resume) self._currentEntry.resume(who)
|
||||
} else self._read()
|
||||
}
|
||||
|
||||
DirReader.prototype.emitEntry = function (entry) {
|
||||
this.emit('entry', entry)
|
||||
this.emit('child', entry)
|
||||
}
|
||||
-174
@@ -1,174 +0,0 @@
|
||||
// It is expected that, when .add() returns false, the consumer
|
||||
// of the DirWriter will pause until a "drain" event occurs. Note
|
||||
// that this is *almost always going to be the case*, unless the
|
||||
// thing being written is some sort of unsupported type, and thus
|
||||
// skipped over.
|
||||
|
||||
module.exports = DirWriter
|
||||
|
||||
var Writer = require('./writer.js')
|
||||
var inherits = require('inherits')
|
||||
var mkdir = require('mkdirp')
|
||||
var path = require('path')
|
||||
var collect = require('./collect.js')
|
||||
|
||||
inherits(DirWriter, Writer)
|
||||
|
||||
function DirWriter (props) {
|
||||
var self = this
|
||||
if (!(self instanceof DirWriter)) {
|
||||
self.error('DirWriter must be called as constructor.', null, true)
|
||||
}
|
||||
|
||||
// should already be established as a Directory type
|
||||
if (props.type !== 'Directory' || !props.Directory) {
|
||||
self.error('Non-directory type ' + props.type + ' ' +
|
||||
JSON.stringify(props), null, true)
|
||||
}
|
||||
|
||||
Writer.call(this, props)
|
||||
}
|
||||
|
||||
DirWriter.prototype._create = function () {
|
||||
var self = this
|
||||
mkdir(self._path, Writer.dirmode, function (er) {
|
||||
if (er) return self.error(er)
|
||||
// ready to start getting entries!
|
||||
self.ready = true
|
||||
self.emit('ready')
|
||||
self._process()
|
||||
})
|
||||
}
|
||||
|
||||
// a DirWriter has an add(entry) method, but its .write() doesn't
|
||||
// do anything. Why a no-op rather than a throw? Because this
|
||||
// leaves open the door for writing directory metadata for
|
||||
// gnu/solaris style dumpdirs.
|
||||
DirWriter.prototype.write = function () {
|
||||
return true
|
||||
}
|
||||
|
||||
DirWriter.prototype.end = function () {
|
||||
this._ended = true
|
||||
this._process()
|
||||
}
|
||||
|
||||
DirWriter.prototype.add = function (entry) {
|
||||
var self = this
|
||||
|
||||
// console.error('\tadd', entry._path, '->', self._path)
|
||||
collect(entry)
|
||||
if (!self.ready || self._currentEntry) {
|
||||
self._buffer.push(entry)
|
||||
return false
|
||||
}
|
||||
|
||||
// create a new writer, and pipe the incoming entry into it.
|
||||
if (self._ended) {
|
||||
return self.error('add after end')
|
||||
}
|
||||
|
||||
self._buffer.push(entry)
|
||||
self._process()
|
||||
|
||||
return this._buffer.length === 0
|
||||
}
|
||||
|
||||
DirWriter.prototype._process = function () {
|
||||
var self = this
|
||||
|
||||
// console.error('DW Process p=%j', self._processing, self.basename)
|
||||
|
||||
if (self._processing) return
|
||||
|
||||
var entry = self._buffer.shift()
|
||||
if (!entry) {
|
||||
// console.error("DW Drain")
|
||||
self.emit('drain')
|
||||
if (self._ended) self._finish()
|
||||
return
|
||||
}
|
||||
|
||||
self._processing = true
|
||||
// console.error("DW Entry", entry._path)
|
||||
|
||||
self.emit('entry', entry)
|
||||
|
||||
// ok, add this entry
|
||||
//
|
||||
// don't allow recursive copying
|
||||
var p = entry
|
||||
var pp
|
||||
do {
|
||||
pp = p._path || p.path
|
||||
if (pp === self.root._path || pp === self._path ||
|
||||
(pp && pp.indexOf(self._path) === 0)) {
|
||||
// console.error('DW Exit (recursive)', entry.basename, self._path)
|
||||
self._processing = false
|
||||
if (entry._collected) entry.pipe()
|
||||
return self._process()
|
||||
}
|
||||
p = p.parent
|
||||
} while (p)
|
||||
|
||||
// console.error("DW not recursive")
|
||||
|
||||
// chop off the entry's root dir, replace with ours
|
||||
var props = {
|
||||
parent: self,
|
||||
root: self.root || self,
|
||||
type: entry.type,
|
||||
depth: self.depth + 1
|
||||
}
|
||||
|
||||
pp = entry._path || entry.path || entry.props.path
|
||||
if (entry.parent) {
|
||||
pp = pp.substr(entry.parent._path.length + 1)
|
||||
}
|
||||
// get rid of any ../../ shenanigans
|
||||
props.path = path.join(self.path, path.join('/', pp))
|
||||
|
||||
// if i have a filter, the child should inherit it.
|
||||
props.filter = self.filter
|
||||
|
||||
// all the rest of the stuff, copy over from the source.
|
||||
Object.keys(entry.props).forEach(function (k) {
|
||||
if (!props.hasOwnProperty(k)) {
|
||||
props[k] = entry.props[k]
|
||||
}
|
||||
})
|
||||
|
||||
// not sure at this point what kind of writer this is.
|
||||
var child = self._currentChild = new Writer(props)
|
||||
child.on('ready', function () {
|
||||
// console.error("DW Child Ready", child.type, child._path)
|
||||
// console.error(" resuming", entry._path)
|
||||
entry.pipe(child)
|
||||
entry.resume()
|
||||
})
|
||||
|
||||
// XXX Make this work in node.
|
||||
// Long filenames should not break stuff.
|
||||
child.on('error', function (er) {
|
||||
if (child._swallowErrors) {
|
||||
self.warn(er)
|
||||
child.emit('end')
|
||||
child.emit('close')
|
||||
} else {
|
||||
self.emit('error', er)
|
||||
}
|
||||
})
|
||||
|
||||
// we fire _end internally *after* end, so that we don't move on
|
||||
// until any "end" listeners have had their chance to do stuff.
|
||||
child.on('close', onend)
|
||||
var ended = false
|
||||
function onend () {
|
||||
if (ended) return
|
||||
ended = true
|
||||
// console.error("* DW Child end", child.basename)
|
||||
self._currentChild = null
|
||||
self._processing = false
|
||||
self._process()
|
||||
}
|
||||
}
|
||||
-150
@@ -1,150 +0,0 @@
|
||||
// Basically just a wrapper around an fs.ReadStream
|
||||
|
||||
module.exports = FileReader
|
||||
|
||||
var fs = require('graceful-fs')
|
||||
var inherits = require('inherits')
|
||||
var Reader = require('./reader.js')
|
||||
var EOF = {EOF: true}
|
||||
var CLOSE = {CLOSE: true}
|
||||
|
||||
inherits(FileReader, Reader)
|
||||
|
||||
function FileReader (props) {
|
||||
// console.error(" FR create", props.path, props.size, new Error().stack)
|
||||
var self = this
|
||||
if (!(self instanceof FileReader)) {
|
||||
throw new Error('FileReader must be called as constructor.')
|
||||
}
|
||||
|
||||
// should already be established as a File type
|
||||
// XXX Todo: preserve hardlinks by tracking dev+inode+nlink,
|
||||
// with a HardLinkReader class.
|
||||
if (!((props.type === 'Link' && props.Link) ||
|
||||
(props.type === 'File' && props.File))) {
|
||||
throw new Error('Non-file type ' + props.type)
|
||||
}
|
||||
|
||||
self._buffer = []
|
||||
self._bytesEmitted = 0
|
||||
Reader.call(self, props)
|
||||
}
|
||||
|
||||
FileReader.prototype._getStream = function () {
|
||||
var self = this
|
||||
var stream = self._stream = fs.createReadStream(self._path, self.props)
|
||||
|
||||
if (self.props.blksize) {
|
||||
stream.bufferSize = self.props.blksize
|
||||
}
|
||||
|
||||
stream.on('open', self.emit.bind(self, 'open'))
|
||||
|
||||
stream.on('data', function (c) {
|
||||
// console.error('\t\t%d %s', c.length, self.basename)
|
||||
self._bytesEmitted += c.length
|
||||
// no point saving empty chunks
|
||||
if (!c.length) {
|
||||
return
|
||||
} else if (self._paused || self._buffer.length) {
|
||||
self._buffer.push(c)
|
||||
self._read()
|
||||
} else self.emit('data', c)
|
||||
})
|
||||
|
||||
stream.on('end', function () {
|
||||
if (self._paused || self._buffer.length) {
|
||||
// console.error('FR Buffering End', self._path)
|
||||
self._buffer.push(EOF)
|
||||
self._read()
|
||||
} else {
|
||||
self.emit('end')
|
||||
}
|
||||
|
||||
if (self._bytesEmitted !== self.props.size) {
|
||||
self.error("Didn't get expected byte count\n" +
|
||||
'expect: ' + self.props.size + '\n' +
|
||||
'actual: ' + self._bytesEmitted)
|
||||
}
|
||||
})
|
||||
|
||||
stream.on('close', function () {
|
||||
if (self._paused || self._buffer.length) {
|
||||
// console.error('FR Buffering Close', self._path)
|
||||
self._buffer.push(CLOSE)
|
||||
self._read()
|
||||
} else {
|
||||
// console.error('FR close 1', self._path)
|
||||
self.emit('close')
|
||||
}
|
||||
})
|
||||
|
||||
stream.on('error', function (e) {
|
||||
self.emit('error', e)
|
||||
})
|
||||
|
||||
self._read()
|
||||
}
|
||||
|
||||
FileReader.prototype._read = function () {
|
||||
var self = this
|
||||
// console.error('FR _read', self._path)
|
||||
if (self._paused) {
|
||||
// console.error('FR _read paused', self._path)
|
||||
return
|
||||
}
|
||||
|
||||
if (!self._stream) {
|
||||
// console.error('FR _getStream calling', self._path)
|
||||
return self._getStream()
|
||||
}
|
||||
|
||||
// clear out the buffer, if there is one.
|
||||
if (self._buffer.length) {
|
||||
// console.error('FR _read has buffer', self._buffer.length, self._path)
|
||||
var buf = self._buffer
|
||||
for (var i = 0, l = buf.length; i < l; i++) {
|
||||
var c = buf[i]
|
||||
if (c === EOF) {
|
||||
// console.error('FR Read emitting buffered end', self._path)
|
||||
self.emit('end')
|
||||
} else if (c === CLOSE) {
|
||||
// console.error('FR Read emitting buffered close', self._path)
|
||||
self.emit('close')
|
||||
} else {
|
||||
// console.error('FR Read emitting buffered data', self._path)
|
||||
self.emit('data', c)
|
||||
}
|
||||
|
||||
if (self._paused) {
|
||||
// console.error('FR Read Re-pausing at '+i, self._path)
|
||||
self._buffer = buf.slice(i)
|
||||
return
|
||||
}
|
||||
}
|
||||
self._buffer.length = 0
|
||||
}
|
||||
// console.error("FR _read done")
|
||||
// that's about all there is to it.
|
||||
}
|
||||
|
||||
FileReader.prototype.pause = function (who) {
|
||||
var self = this
|
||||
// console.error('FR Pause', self._path)
|
||||
if (self._paused) return
|
||||
who = who || self
|
||||
self._paused = true
|
||||
if (self._stream) self._stream.pause()
|
||||
self.emit('pause', who)
|
||||
}
|
||||
|
||||
FileReader.prototype.resume = function (who) {
|
||||
var self = this
|
||||
// console.error('FR Resume', self._path)
|
||||
if (!self._paused) return
|
||||
who = who || self
|
||||
self.emit('resume', who)
|
||||
self._paused = false
|
||||
if (self._stream) self._stream.resume()
|
||||
self._read()
|
||||
}
|
||||
-107
@@ -1,107 +0,0 @@
|
||||
module.exports = FileWriter
|
||||
|
||||
var fs = require('graceful-fs')
|
||||
var Writer = require('./writer.js')
|
||||
var inherits = require('inherits')
|
||||
var EOF = {}
|
||||
|
||||
inherits(FileWriter, Writer)
|
||||
|
||||
function FileWriter (props) {
|
||||
var self = this
|
||||
if (!(self instanceof FileWriter)) {
|
||||
throw new Error('FileWriter must be called as constructor.')
|
||||
}
|
||||
|
||||
// should already be established as a File type
|
||||
if (props.type !== 'File' || !props.File) {
|
||||
throw new Error('Non-file type ' + props.type)
|
||||
}
|
||||
|
||||
self._buffer = []
|
||||
self._bytesWritten = 0
|
||||
|
||||
Writer.call(this, props)
|
||||
}
|
||||
|
||||
FileWriter.prototype._create = function () {
|
||||
var self = this
|
||||
if (self._stream) return
|
||||
|
||||
var so = {}
|
||||
if (self.props.flags) so.flags = self.props.flags
|
||||
so.mode = Writer.filemode
|
||||
if (self._old && self._old.blksize) so.bufferSize = self._old.blksize
|
||||
|
||||
self._stream = fs.createWriteStream(self._path, so)
|
||||
|
||||
self._stream.on('open', function () {
|
||||
// console.error("FW open", self._buffer, self._path)
|
||||
self.ready = true
|
||||
self._buffer.forEach(function (c) {
|
||||
if (c === EOF) self._stream.end()
|
||||
else self._stream.write(c)
|
||||
})
|
||||
self.emit('ready')
|
||||
// give this a kick just in case it needs it.
|
||||
self.emit('drain')
|
||||
})
|
||||
|
||||
self._stream.on('error', function (er) { self.emit('error', er) })
|
||||
|
||||
self._stream.on('drain', function () { self.emit('drain') })
|
||||
|
||||
self._stream.on('close', function () {
|
||||
// console.error('\n\nFW Stream Close', self._path, self.size)
|
||||
self._finish()
|
||||
})
|
||||
}
|
||||
|
||||
FileWriter.prototype.write = function (c) {
|
||||
var self = this
|
||||
|
||||
self._bytesWritten += c.length
|
||||
|
||||
if (!self.ready) {
|
||||
if (!Buffer.isBuffer(c) && typeof c !== 'string') {
|
||||
throw new Error('invalid write data')
|
||||
}
|
||||
self._buffer.push(c)
|
||||
return false
|
||||
}
|
||||
|
||||
var ret = self._stream.write(c)
|
||||
// console.error('\t-- fw wrote, _stream says', ret, self._stream._queue.length)
|
||||
|
||||
// allow 2 buffered writes, because otherwise there's just too
|
||||
// much stop and go bs.
|
||||
if (ret === false && self._stream._queue) {
|
||||
return self._stream._queue.length <= 2
|
||||
} else {
|
||||
return ret
|
||||
}
|
||||
}
|
||||
|
||||
FileWriter.prototype.end = function (c) {
|
||||
var self = this
|
||||
|
||||
if (c) self.write(c)
|
||||
|
||||
if (!self.ready) {
|
||||
self._buffer.push(EOF)
|
||||
return false
|
||||
}
|
||||
|
||||
return self._stream.end()
|
||||
}
|
||||
|
||||
FileWriter.prototype._finish = function () {
|
||||
var self = this
|
||||
if (typeof self.size === 'number' && self._bytesWritten !== self.size) {
|
||||
self.error(
|
||||
'Did not get expected byte count.\n' +
|
||||
'expect: ' + self.size + '\n' +
|
||||
'actual: ' + self._bytesWritten)
|
||||
}
|
||||
Writer.prototype._finish.call(self)
|
||||
}
|
||||
-33
@@ -1,33 +0,0 @@
|
||||
module.exports = getType
|
||||
|
||||
function getType (st) {
|
||||
var types = [
|
||||
'Directory',
|
||||
'File',
|
||||
'SymbolicLink',
|
||||
'Link', // special for hardlinks from tarballs
|
||||
'BlockDevice',
|
||||
'CharacterDevice',
|
||||
'FIFO',
|
||||
'Socket'
|
||||
]
|
||||
var type
|
||||
|
||||
if (st.type && types.indexOf(st.type) !== -1) {
|
||||
st[st.type] = true
|
||||
return st.type
|
||||
}
|
||||
|
||||
for (var i = 0, l = types.length; i < l; i++) {
|
||||
type = types[i]
|
||||
var is = st[type] || st['is' + type]
|
||||
if (typeof is === 'function') is = is.call(st)
|
||||
if (is) {
|
||||
st[type] = true
|
||||
st.type = type
|
||||
return type
|
||||
}
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
-53
@@ -1,53 +0,0 @@
|
||||
// Basically just a wrapper around an fs.readlink
|
||||
//
|
||||
// XXX: Enhance this to support the Link type, by keeping
|
||||
// a lookup table of {<dev+inode>:<path>}, so that hardlinks
|
||||
// can be preserved in tarballs.
|
||||
|
||||
module.exports = LinkReader
|
||||
|
||||
var fs = require('graceful-fs')
|
||||
var inherits = require('inherits')
|
||||
var Reader = require('./reader.js')
|
||||
|
||||
inherits(LinkReader, Reader)
|
||||
|
||||
function LinkReader (props) {
|
||||
var self = this
|
||||
if (!(self instanceof LinkReader)) {
|
||||
throw new Error('LinkReader must be called as constructor.')
|
||||
}
|
||||
|
||||
if (!((props.type === 'Link' && props.Link) ||
|
||||
(props.type === 'SymbolicLink' && props.SymbolicLink))) {
|
||||
throw new Error('Non-link type ' + props.type)
|
||||
}
|
||||
|
||||
Reader.call(self, props)
|
||||
}
|
||||
|
||||
// When piping a LinkReader into a LinkWriter, we have to
|
||||
// already have the linkpath property set, so that has to
|
||||
// happen *before* the "ready" event, which means we need to
|
||||
// override the _stat method.
|
||||
LinkReader.prototype._stat = function (currentStat) {
|
||||
var self = this
|
||||
fs.readlink(self._path, function (er, linkpath) {
|
||||
if (er) return self.error(er)
|
||||
self.linkpath = self.props.linkpath = linkpath
|
||||
self.emit('linkpath', linkpath)
|
||||
Reader.prototype._stat.call(self, currentStat)
|
||||
})
|
||||
}
|
||||
|
||||
LinkReader.prototype._read = function () {
|
||||
var self = this
|
||||
if (self._paused) return
|
||||
// basically just a no-op, since we got all the info we need
|
||||
// from the _stat method
|
||||
if (!self._ended) {
|
||||
self.emit('end')
|
||||
self.emit('close')
|
||||
self._ended = true
|
||||
}
|
||||
}
|
||||
-95
@@ -1,95 +0,0 @@
|
||||
module.exports = LinkWriter
|
||||
|
||||
var fs = require('graceful-fs')
|
||||
var Writer = require('./writer.js')
|
||||
var inherits = require('inherits')
|
||||
var path = require('path')
|
||||
var rimraf = require('rimraf')
|
||||
|
||||
inherits(LinkWriter, Writer)
|
||||
|
||||
function LinkWriter (props) {
|
||||
var self = this
|
||||
if (!(self instanceof LinkWriter)) {
|
||||
throw new Error('LinkWriter must be called as constructor.')
|
||||
}
|
||||
|
||||
// should already be established as a Link type
|
||||
if (!((props.type === 'Link' && props.Link) ||
|
||||
(props.type === 'SymbolicLink' && props.SymbolicLink))) {
|
||||
throw new Error('Non-link type ' + props.type)
|
||||
}
|
||||
|
||||
if (props.linkpath === '') props.linkpath = '.'
|
||||
if (!props.linkpath) {
|
||||
self.error('Need linkpath property to create ' + props.type)
|
||||
}
|
||||
|
||||
Writer.call(this, props)
|
||||
}
|
||||
|
||||
LinkWriter.prototype._create = function () {
|
||||
// console.error(" LW _create")
|
||||
var self = this
|
||||
var hard = self.type === 'Link' || process.platform === 'win32'
|
||||
var link = hard ? 'link' : 'symlink'
|
||||
var lp = hard ? path.resolve(self.dirname, self.linkpath) : self.linkpath
|
||||
|
||||
// can only change the link path by clobbering
|
||||
// For hard links, let's just assume that's always the case, since
|
||||
// there's no good way to read them if we don't already know.
|
||||
if (hard) return clobber(self, lp, link)
|
||||
|
||||
fs.readlink(self._path, function (er, p) {
|
||||
// only skip creation if it's exactly the same link
|
||||
if (p && p === lp) return finish(self)
|
||||
clobber(self, lp, link)
|
||||
})
|
||||
}
|
||||
|
||||
function clobber (self, lp, link) {
|
||||
rimraf(self._path, function (er) {
|
||||
if (er) return self.error(er)
|
||||
create(self, lp, link)
|
||||
})
|
||||
}
|
||||
|
||||
function create (self, lp, link) {
|
||||
fs[link](lp, self._path, function (er) {
|
||||
// if this is a hard link, and we're in the process of writing out a
|
||||
// directory, it's very possible that the thing we're linking to
|
||||
// doesn't exist yet (especially if it was intended as a symlink),
|
||||
// so swallow ENOENT errors here and just soldier in.
|
||||
// Additionally, an EPERM or EACCES can happen on win32 if it's trying
|
||||
// to make a link to a directory. Again, just skip it.
|
||||
// A better solution would be to have fs.symlink be supported on
|
||||
// windows in some nice fashion.
|
||||
if (er) {
|
||||
if ((er.code === 'ENOENT' ||
|
||||
er.code === 'EACCES' ||
|
||||
er.code === 'EPERM') && process.platform === 'win32') {
|
||||
self.ready = true
|
||||
self.emit('ready')
|
||||
self.emit('end')
|
||||
self.emit('close')
|
||||
self.end = self._finish = function () {}
|
||||
} else return self.error(er)
|
||||
}
|
||||
finish(self)
|
||||
})
|
||||
}
|
||||
|
||||
function finish (self) {
|
||||
self.ready = true
|
||||
self.emit('ready')
|
||||
if (self._ended && !self._finished) self._finish()
|
||||
}
|
||||
|
||||
LinkWriter.prototype.end = function () {
|
||||
// console.error("LW finish in end")
|
||||
this._ended = true
|
||||
if (this.ready) {
|
||||
this._finished = true
|
||||
this._finish()
|
||||
}
|
||||
}
|
||||
-95
@@ -1,95 +0,0 @@
|
||||
// A reader for when we don't yet know what kind of thing
|
||||
// the thing is.
|
||||
|
||||
module.exports = ProxyReader
|
||||
|
||||
var Reader = require('./reader.js')
|
||||
var getType = require('./get-type.js')
|
||||
var inherits = require('inherits')
|
||||
var fs = require('graceful-fs')
|
||||
|
||||
inherits(ProxyReader, Reader)
|
||||
|
||||
function ProxyReader (props) {
|
||||
var self = this
|
||||
if (!(self instanceof ProxyReader)) {
|
||||
throw new Error('ProxyReader must be called as constructor.')
|
||||
}
|
||||
|
||||
self.props = props
|
||||
self._buffer = []
|
||||
self.ready = false
|
||||
|
||||
Reader.call(self, props)
|
||||
}
|
||||
|
||||
ProxyReader.prototype._stat = function () {
|
||||
var self = this
|
||||
var props = self.props
|
||||
// stat the thing to see what the proxy should be.
|
||||
var stat = props.follow ? 'stat' : 'lstat'
|
||||
|
||||
fs[stat](props.path, function (er, current) {
|
||||
var type
|
||||
if (er || !current) {
|
||||
type = 'File'
|
||||
} else {
|
||||
type = getType(current)
|
||||
}
|
||||
|
||||
props[type] = true
|
||||
props.type = self.type = type
|
||||
|
||||
self._old = current
|
||||
self._addProxy(Reader(props, current))
|
||||
})
|
||||
}
|
||||
|
||||
ProxyReader.prototype._addProxy = function (proxy) {
|
||||
var self = this
|
||||
if (self._proxyTarget) {
|
||||
return self.error('proxy already set')
|
||||
}
|
||||
|
||||
self._proxyTarget = proxy
|
||||
proxy._proxy = self
|
||||
|
||||
;[
|
||||
'error',
|
||||
'data',
|
||||
'end',
|
||||
'close',
|
||||
'linkpath',
|
||||
'entry',
|
||||
'entryEnd',
|
||||
'child',
|
||||
'childEnd',
|
||||
'warn',
|
||||
'stat'
|
||||
].forEach(function (ev) {
|
||||
// console.error('~~ proxy event', ev, self.path)
|
||||
proxy.on(ev, self.emit.bind(self, ev))
|
||||
})
|
||||
|
||||
self.emit('proxy', proxy)
|
||||
|
||||
proxy.on('ready', function () {
|
||||
// console.error("~~ proxy is ready!", self.path)
|
||||
self.ready = true
|
||||
self.emit('ready')
|
||||
})
|
||||
|
||||
var calls = self._buffer
|
||||
self._buffer.length = 0
|
||||
calls.forEach(function (c) {
|
||||
proxy[c[0]].apply(proxy, c[1])
|
||||
})
|
||||
}
|
||||
|
||||
ProxyReader.prototype.pause = function () {
|
||||
return this._proxyTarget ? this._proxyTarget.pause() : false
|
||||
}
|
||||
|
||||
ProxyReader.prototype.resume = function () {
|
||||
return this._proxyTarget ? this._proxyTarget.resume() : false
|
||||
}
|
||||
-111
@@ -1,111 +0,0 @@
|
||||
// A writer for when we don't know what kind of thing
|
||||
// the thing is. That is, it's not explicitly set,
|
||||
// so we're going to make it whatever the thing already
|
||||
// is, or "File"
|
||||
//
|
||||
// Until then, collect all events.
|
||||
|
||||
module.exports = ProxyWriter
|
||||
|
||||
var Writer = require('./writer.js')
|
||||
var getType = require('./get-type.js')
|
||||
var inherits = require('inherits')
|
||||
var collect = require('./collect.js')
|
||||
var fs = require('fs')
|
||||
|
||||
inherits(ProxyWriter, Writer)
|
||||
|
||||
function ProxyWriter (props) {
|
||||
var self = this
|
||||
if (!(self instanceof ProxyWriter)) {
|
||||
throw new Error('ProxyWriter must be called as constructor.')
|
||||
}
|
||||
|
||||
self.props = props
|
||||
self._needDrain = false
|
||||
|
||||
Writer.call(self, props)
|
||||
}
|
||||
|
||||
ProxyWriter.prototype._stat = function () {
|
||||
var self = this
|
||||
var props = self.props
|
||||
// stat the thing to see what the proxy should be.
|
||||
var stat = props.follow ? 'stat' : 'lstat'
|
||||
|
||||
fs[stat](props.path, function (er, current) {
|
||||
var type
|
||||
if (er || !current) {
|
||||
type = 'File'
|
||||
} else {
|
||||
type = getType(current)
|
||||
}
|
||||
|
||||
props[type] = true
|
||||
props.type = self.type = type
|
||||
|
||||
self._old = current
|
||||
self._addProxy(Writer(props, current))
|
||||
})
|
||||
}
|
||||
|
||||
ProxyWriter.prototype._addProxy = function (proxy) {
|
||||
// console.error("~~ set proxy", this.path)
|
||||
var self = this
|
||||
if (self._proxy) {
|
||||
return self.error('proxy already set')
|
||||
}
|
||||
|
||||
self._proxy = proxy
|
||||
;[
|
||||
'ready',
|
||||
'error',
|
||||
'close',
|
||||
'pipe',
|
||||
'drain',
|
||||
'warn'
|
||||
].forEach(function (ev) {
|
||||
proxy.on(ev, self.emit.bind(self, ev))
|
||||
})
|
||||
|
||||
self.emit('proxy', proxy)
|
||||
|
||||
var calls = self._buffer
|
||||
calls.forEach(function (c) {
|
||||
// console.error("~~ ~~ proxy buffered call", c[0], c[1])
|
||||
proxy[c[0]].apply(proxy, c[1])
|
||||
})
|
||||
self._buffer.length = 0
|
||||
if (self._needsDrain) self.emit('drain')
|
||||
}
|
||||
|
||||
ProxyWriter.prototype.add = function (entry) {
|
||||
// console.error("~~ proxy add")
|
||||
collect(entry)
|
||||
|
||||
if (!this._proxy) {
|
||||
this._buffer.push(['add', [entry]])
|
||||
this._needDrain = true
|
||||
return false
|
||||
}
|
||||
return this._proxy.add(entry)
|
||||
}
|
||||
|
||||
ProxyWriter.prototype.write = function (c) {
|
||||
// console.error('~~ proxy write')
|
||||
if (!this._proxy) {
|
||||
this._buffer.push(['write', [c]])
|
||||
this._needDrain = true
|
||||
return false
|
||||
}
|
||||
return this._proxy.write(c)
|
||||
}
|
||||
|
||||
ProxyWriter.prototype.end = function (c) {
|
||||
// console.error('~~ proxy end')
|
||||
if (!this._proxy) {
|
||||
this._buffer.push(['end', [c]])
|
||||
return false
|
||||
}
|
||||
return this._proxy.end(c)
|
||||
}
|
||||
-255
@@ -1,255 +0,0 @@
|
||||
module.exports = Reader
|
||||
|
||||
var fs = require('graceful-fs')
|
||||
var Stream = require('stream').Stream
|
||||
var inherits = require('inherits')
|
||||
var path = require('path')
|
||||
var getType = require('./get-type.js')
|
||||
var hardLinks = Reader.hardLinks = {}
|
||||
var Abstract = require('./abstract.js')
|
||||
|
||||
// Must do this *before* loading the child classes
|
||||
inherits(Reader, Abstract)
|
||||
|
||||
var LinkReader = require('./link-reader.js')
|
||||
|
||||
function Reader (props, currentStat) {
|
||||
var self = this
|
||||
if (!(self instanceof Reader)) return new Reader(props, currentStat)
|
||||
|
||||
if (typeof props === 'string') {
|
||||
props = { path: props }
|
||||
}
|
||||
|
||||
// polymorphism.
|
||||
// call fstream.Reader(dir) to get a DirReader object, etc.
|
||||
// Note that, unlike in the Writer case, ProxyReader is going
|
||||
// to be the *normal* state of affairs, since we rarely know
|
||||
// the type of a file prior to reading it.
|
||||
|
||||
var type
|
||||
var ClassType
|
||||
|
||||
if (props.type && typeof props.type === 'function') {
|
||||
type = props.type
|
||||
ClassType = type
|
||||
} else {
|
||||
type = getType(props)
|
||||
ClassType = Reader
|
||||
}
|
||||
|
||||
if (currentStat && !type) {
|
||||
type = getType(currentStat)
|
||||
props[type] = true
|
||||
props.type = type
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case 'Directory':
|
||||
ClassType = require('./dir-reader.js')
|
||||
break
|
||||
|
||||
case 'Link':
|
||||
// XXX hard links are just files.
|
||||
// However, it would be good to keep track of files' dev+inode
|
||||
// and nlink values, and create a HardLinkReader that emits
|
||||
// a linkpath value of the original copy, so that the tar
|
||||
// writer can preserve them.
|
||||
// ClassType = HardLinkReader
|
||||
// break
|
||||
|
||||
case 'File':
|
||||
ClassType = require('./file-reader.js')
|
||||
break
|
||||
|
||||
case 'SymbolicLink':
|
||||
ClassType = LinkReader
|
||||
break
|
||||
|
||||
case 'Socket':
|
||||
ClassType = require('./socket-reader.js')
|
||||
break
|
||||
|
||||
case null:
|
||||
ClassType = require('./proxy-reader.js')
|
||||
break
|
||||
}
|
||||
|
||||
if (!(self instanceof ClassType)) {
|
||||
return new ClassType(props)
|
||||
}
|
||||
|
||||
Abstract.call(self)
|
||||
|
||||
if (!props.path) {
|
||||
self.error('Must provide a path', null, true)
|
||||
}
|
||||
|
||||
self.readable = true
|
||||
self.writable = false
|
||||
|
||||
self.type = type
|
||||
self.props = props
|
||||
self.depth = props.depth = props.depth || 0
|
||||
self.parent = props.parent || null
|
||||
self.root = props.root || (props.parent && props.parent.root) || self
|
||||
|
||||
self._path = self.path = path.resolve(props.path)
|
||||
if (process.platform === 'win32') {
|
||||
self.path = self._path = self.path.replace(/\?/g, '_')
|
||||
if (self._path.length >= 260) {
|
||||
// how DOES one create files on the moon?
|
||||
// if the path has spaces in it, then UNC will fail.
|
||||
self._swallowErrors = true
|
||||
// if (self._path.indexOf(" ") === -1) {
|
||||
self._path = '\\\\?\\' + self.path.replace(/\//g, '\\')
|
||||
// }
|
||||
}
|
||||
}
|
||||
self.basename = props.basename = path.basename(self.path)
|
||||
self.dirname = props.dirname = path.dirname(self.path)
|
||||
|
||||
// these have served their purpose, and are now just noisy clutter
|
||||
props.parent = props.root = null
|
||||
|
||||
// console.error("\n\n\n%s setting size to", props.path, props.size)
|
||||
self.size = props.size
|
||||
self.filter = typeof props.filter === 'function' ? props.filter : null
|
||||
if (props.sort === 'alpha') props.sort = alphasort
|
||||
|
||||
// start the ball rolling.
|
||||
// this will stat the thing, and then call self._read()
|
||||
// to start reading whatever it is.
|
||||
// console.error("calling stat", props.path, currentStat)
|
||||
self._stat(currentStat)
|
||||
}
|
||||
|
||||
function alphasort (a, b) {
|
||||
return a === b ? 0
|
||||
: a.toLowerCase() > b.toLowerCase() ? 1
|
||||
: a.toLowerCase() < b.toLowerCase() ? -1
|
||||
: a > b ? 1
|
||||
: -1
|
||||
}
|
||||
|
||||
Reader.prototype._stat = function (currentStat) {
|
||||
var self = this
|
||||
var props = self.props
|
||||
var stat = props.follow ? 'stat' : 'lstat'
|
||||
// console.error("Reader._stat", self._path, currentStat)
|
||||
if (currentStat) process.nextTick(statCb.bind(null, null, currentStat))
|
||||
else fs[stat](self._path, statCb)
|
||||
|
||||
function statCb (er, props_) {
|
||||
// console.error("Reader._stat, statCb", self._path, props_, props_.nlink)
|
||||
if (er) return self.error(er)
|
||||
|
||||
Object.keys(props_).forEach(function (k) {
|
||||
props[k] = props_[k]
|
||||
})
|
||||
|
||||
// if it's not the expected size, then abort here.
|
||||
if (undefined !== self.size && props.size !== self.size) {
|
||||
return self.error('incorrect size')
|
||||
}
|
||||
self.size = props.size
|
||||
|
||||
var type = getType(props)
|
||||
var handleHardlinks = props.hardlinks !== false
|
||||
|
||||
// special little thing for handling hardlinks.
|
||||
if (handleHardlinks && type !== 'Directory' && props.nlink && props.nlink > 1) {
|
||||
var k = props.dev + ':' + props.ino
|
||||
// console.error("Reader has nlink", self._path, k)
|
||||
if (hardLinks[k] === self._path || !hardLinks[k]) {
|
||||
hardLinks[k] = self._path
|
||||
} else {
|
||||
// switch into hardlink mode.
|
||||
type = self.type = self.props.type = 'Link'
|
||||
self.Link = self.props.Link = true
|
||||
self.linkpath = self.props.linkpath = hardLinks[k]
|
||||
// console.error("Hardlink detected, switching mode", self._path, self.linkpath)
|
||||
// Setting __proto__ would arguably be the "correct"
|
||||
// approach here, but that just seems too wrong.
|
||||
self._stat = self._read = LinkReader.prototype._read
|
||||
}
|
||||
}
|
||||
|
||||
if (self.type && self.type !== type) {
|
||||
self.error('Unexpected type: ' + type)
|
||||
}
|
||||
|
||||
// if the filter doesn't pass, then just skip over this one.
|
||||
// still have to emit end so that dir-walking can move on.
|
||||
if (self.filter) {
|
||||
var who = self._proxy || self
|
||||
// special handling for ProxyReaders
|
||||
if (!self.filter.call(who, who, props)) {
|
||||
if (!self._disowned) {
|
||||
self.abort()
|
||||
self.emit('end')
|
||||
self.emit('close')
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// last chance to abort or disown before the flow starts!
|
||||
var events = ['_stat', 'stat', 'ready']
|
||||
var e = 0
|
||||
;(function go () {
|
||||
if (self._aborted) {
|
||||
self.emit('end')
|
||||
self.emit('close')
|
||||
return
|
||||
}
|
||||
|
||||
if (self._paused && self.type !== 'Directory') {
|
||||
self.once('resume', go)
|
||||
return
|
||||
}
|
||||
|
||||
var ev = events[e++]
|
||||
if (!ev) {
|
||||
return self._read()
|
||||
}
|
||||
self.emit(ev, props)
|
||||
go()
|
||||
})()
|
||||
}
|
||||
}
|
||||
|
||||
Reader.prototype.pipe = function (dest) {
|
||||
var self = this
|
||||
if (typeof dest.add === 'function') {
|
||||
// piping to a multi-compatible, and we've got directory entries.
|
||||
self.on('entry', function (entry) {
|
||||
var ret = dest.add(entry)
|
||||
if (ret === false) {
|
||||
self.pause()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// console.error("R Pipe apply Stream Pipe")
|
||||
return Stream.prototype.pipe.apply(this, arguments)
|
||||
}
|
||||
|
||||
Reader.prototype.pause = function (who) {
|
||||
this._paused = true
|
||||
who = who || this
|
||||
this.emit('pause', who)
|
||||
if (this._stream) this._stream.pause(who)
|
||||
}
|
||||
|
||||
Reader.prototype.resume = function (who) {
|
||||
this._paused = false
|
||||
who = who || this
|
||||
this.emit('resume', who)
|
||||
if (this._stream) this._stream.resume(who)
|
||||
this._read()
|
||||
}
|
||||
|
||||
Reader.prototype._read = function () {
|
||||
this.error('Cannot read unknown type: ' + this.type)
|
||||
}
|
||||
-36
@@ -1,36 +0,0 @@
|
||||
// Just get the stats, and then don't do anything.
|
||||
// You can't really "read" from a socket. You "connect" to it.
|
||||
// Mostly, this is here so that reading a dir with a socket in it
|
||||
// doesn't blow up.
|
||||
|
||||
module.exports = SocketReader
|
||||
|
||||
var inherits = require('inherits')
|
||||
var Reader = require('./reader.js')
|
||||
|
||||
inherits(SocketReader, Reader)
|
||||
|
||||
function SocketReader (props) {
|
||||
var self = this
|
||||
if (!(self instanceof SocketReader)) {
|
||||
throw new Error('SocketReader must be called as constructor.')
|
||||
}
|
||||
|
||||
if (!(props.type === 'Socket' && props.Socket)) {
|
||||
throw new Error('Non-socket type ' + props.type)
|
||||
}
|
||||
|
||||
Reader.call(self, props)
|
||||
}
|
||||
|
||||
SocketReader.prototype._read = function () {
|
||||
var self = this
|
||||
if (self._paused) return
|
||||
// basically just a no-op, since we got all the info we have
|
||||
// from the _stat method
|
||||
if (!self._ended) {
|
||||
self.emit('end')
|
||||
self.emit('close')
|
||||
self._ended = true
|
||||
}
|
||||
}
|
||||
-390
@@ -1,390 +0,0 @@
|
||||
module.exports = Writer
|
||||
|
||||
var fs = require('graceful-fs')
|
||||
var inherits = require('inherits')
|
||||
var rimraf = require('rimraf')
|
||||
var mkdir = require('mkdirp')
|
||||
var path = require('path')
|
||||
var umask = process.platform === 'win32' ? 0 : process.umask()
|
||||
var getType = require('./get-type.js')
|
||||
var Abstract = require('./abstract.js')
|
||||
|
||||
// Must do this *before* loading the child classes
|
||||
inherits(Writer, Abstract)
|
||||
|
||||
Writer.dirmode = parseInt('0777', 8) & (~umask)
|
||||
Writer.filemode = parseInt('0666', 8) & (~umask)
|
||||
|
||||
var DirWriter = require('./dir-writer.js')
|
||||
var LinkWriter = require('./link-writer.js')
|
||||
var FileWriter = require('./file-writer.js')
|
||||
var ProxyWriter = require('./proxy-writer.js')
|
||||
|
||||
// props is the desired state. current is optionally the current stat,
|
||||
// provided here so that subclasses can avoid statting the target
|
||||
// more than necessary.
|
||||
function Writer (props, current) {
|
||||
var self = this
|
||||
|
||||
if (typeof props === 'string') {
|
||||
props = { path: props }
|
||||
}
|
||||
|
||||
// polymorphism.
|
||||
// call fstream.Writer(dir) to get a DirWriter object, etc.
|
||||
var type = getType(props)
|
||||
var ClassType = Writer
|
||||
|
||||
switch (type) {
|
||||
case 'Directory':
|
||||
ClassType = DirWriter
|
||||
break
|
||||
case 'File':
|
||||
ClassType = FileWriter
|
||||
break
|
||||
case 'Link':
|
||||
case 'SymbolicLink':
|
||||
ClassType = LinkWriter
|
||||
break
|
||||
case null:
|
||||
default:
|
||||
// Don't know yet what type to create, so we wrap in a proxy.
|
||||
ClassType = ProxyWriter
|
||||
break
|
||||
}
|
||||
|
||||
if (!(self instanceof ClassType)) return new ClassType(props)
|
||||
|
||||
// now get down to business.
|
||||
|
||||
Abstract.call(self)
|
||||
|
||||
if (!props.path) self.error('Must provide a path', null, true)
|
||||
|
||||
// props is what we want to set.
|
||||
// set some convenience properties as well.
|
||||
self.type = props.type
|
||||
self.props = props
|
||||
self.depth = props.depth || 0
|
||||
self.clobber = props.clobber === false ? props.clobber : true
|
||||
self.parent = props.parent || null
|
||||
self.root = props.root || (props.parent && props.parent.root) || self
|
||||
|
||||
self._path = self.path = path.resolve(props.path)
|
||||
if (process.platform === 'win32') {
|
||||
self.path = self._path = self.path.replace(/\?/g, '_')
|
||||
if (self._path.length >= 260) {
|
||||
self._swallowErrors = true
|
||||
self._path = '\\\\?\\' + self.path.replace(/\//g, '\\')
|
||||
}
|
||||
}
|
||||
self.basename = path.basename(props.path)
|
||||
self.dirname = path.dirname(props.path)
|
||||
self.linkpath = props.linkpath || null
|
||||
|
||||
props.parent = props.root = null
|
||||
|
||||
// console.error("\n\n\n%s setting size to", props.path, props.size)
|
||||
self.size = props.size
|
||||
|
||||
if (typeof props.mode === 'string') {
|
||||
props.mode = parseInt(props.mode, 8)
|
||||
}
|
||||
|
||||
self.readable = false
|
||||
self.writable = true
|
||||
|
||||
// buffer until ready, or while handling another entry
|
||||
self._buffer = []
|
||||
self.ready = false
|
||||
|
||||
self.filter = typeof props.filter === 'function' ? props.filter : null
|
||||
|
||||
// start the ball rolling.
|
||||
// this checks what's there already, and then calls
|
||||
// self._create() to call the impl-specific creation stuff.
|
||||
self._stat(current)
|
||||
}
|
||||
|
||||
// Calling this means that it's something we can't create.
|
||||
// Just assert that it's already there, otherwise raise a warning.
|
||||
Writer.prototype._create = function () {
|
||||
var self = this
|
||||
fs[self.props.follow ? 'stat' : 'lstat'](self._path, function (er) {
|
||||
if (er) {
|
||||
return self.warn('Cannot create ' + self._path + '\n' +
|
||||
'Unsupported type: ' + self.type, 'ENOTSUP')
|
||||
}
|
||||
self._finish()
|
||||
})
|
||||
}
|
||||
|
||||
Writer.prototype._stat = function (current) {
|
||||
var self = this
|
||||
var props = self.props
|
||||
var stat = props.follow ? 'stat' : 'lstat'
|
||||
var who = self._proxy || self
|
||||
|
||||
if (current) statCb(null, current)
|
||||
else fs[stat](self._path, statCb)
|
||||
|
||||
function statCb (er, current) {
|
||||
if (self.filter && !self.filter.call(who, who, current)) {
|
||||
self._aborted = true
|
||||
self.emit('end')
|
||||
self.emit('close')
|
||||
return
|
||||
}
|
||||
|
||||
// if it's not there, great. We'll just create it.
|
||||
// if it is there, then we'll need to change whatever differs
|
||||
if (er || !current) {
|
||||
return create(self)
|
||||
}
|
||||
|
||||
self._old = current
|
||||
var currentType = getType(current)
|
||||
|
||||
// if it's a type change, then we need to clobber or error.
|
||||
// if it's not a type change, then let the impl take care of it.
|
||||
if (currentType !== self.type || self.type === 'File' && current.nlink > 1) {
|
||||
return rimraf(self._path, function (er) {
|
||||
if (er) return self.error(er)
|
||||
self._old = null
|
||||
create(self)
|
||||
})
|
||||
}
|
||||
|
||||
// otherwise, just handle in the app-specific way
|
||||
// this creates a fs.WriteStream, or mkdir's, or whatever
|
||||
create(self)
|
||||
}
|
||||
}
|
||||
|
||||
function create (self) {
|
||||
// console.error("W create", self._path, Writer.dirmode)
|
||||
|
||||
// XXX Need to clobber non-dirs that are in the way,
|
||||
// unless { clobber: false } in the props.
|
||||
mkdir(path.dirname(self._path), Writer.dirmode, function (er, made) {
|
||||
// console.error("W created", path.dirname(self._path), er)
|
||||
if (er) return self.error(er)
|
||||
|
||||
// later on, we have to set the mode and owner for these
|
||||
self._madeDir = made
|
||||
return self._create()
|
||||
})
|
||||
}
|
||||
|
||||
function endChmod (self, want, current, path, cb) {
|
||||
var wantMode = want.mode
|
||||
var chmod = want.follow || self.type !== 'SymbolicLink'
|
||||
? 'chmod' : 'lchmod'
|
||||
|
||||
if (!fs[chmod]) return cb()
|
||||
if (typeof wantMode !== 'number') return cb()
|
||||
|
||||
var curMode = current.mode & parseInt('0777', 8)
|
||||
wantMode = wantMode & parseInt('0777', 8)
|
||||
if (wantMode === curMode) return cb()
|
||||
|
||||
fs[chmod](path, wantMode, cb)
|
||||
}
|
||||
|
||||
function endChown (self, want, current, path, cb) {
|
||||
// Don't even try it unless root. Too easy to EPERM.
|
||||
if (process.platform === 'win32') return cb()
|
||||
if (!process.getuid || process.getuid() !== 0) return cb()
|
||||
if (typeof want.uid !== 'number' &&
|
||||
typeof want.gid !== 'number') return cb()
|
||||
|
||||
if (current.uid === want.uid &&
|
||||
current.gid === want.gid) return cb()
|
||||
|
||||
var chown = (self.props.follow || self.type !== 'SymbolicLink')
|
||||
? 'chown' : 'lchown'
|
||||
if (!fs[chown]) return cb()
|
||||
|
||||
if (typeof want.uid !== 'number') want.uid = current.uid
|
||||
if (typeof want.gid !== 'number') want.gid = current.gid
|
||||
|
||||
fs[chown](path, want.uid, want.gid, cb)
|
||||
}
|
||||
|
||||
function endUtimes (self, want, current, path, cb) {
|
||||
if (!fs.utimes || process.platform === 'win32') return cb()
|
||||
|
||||
var utimes = (want.follow || self.type !== 'SymbolicLink')
|
||||
? 'utimes' : 'lutimes'
|
||||
|
||||
if (utimes === 'lutimes' && !fs[utimes]) {
|
||||
utimes = 'utimes'
|
||||
}
|
||||
|
||||
if (!fs[utimes]) return cb()
|
||||
|
||||
var curA = current.atime
|
||||
var curM = current.mtime
|
||||
var meA = want.atime
|
||||
var meM = want.mtime
|
||||
|
||||
if (meA === undefined) meA = curA
|
||||
if (meM === undefined) meM = curM
|
||||
|
||||
if (!isDate(meA)) meA = new Date(meA)
|
||||
if (!isDate(meM)) meA = new Date(meM)
|
||||
|
||||
if (meA.getTime() === curA.getTime() &&
|
||||
meM.getTime() === curM.getTime()) return cb()
|
||||
|
||||
fs[utimes](path, meA, meM, cb)
|
||||
}
|
||||
|
||||
// XXX This function is beastly. Break it up!
|
||||
Writer.prototype._finish = function () {
|
||||
var self = this
|
||||
|
||||
if (self._finishing) return
|
||||
self._finishing = true
|
||||
|
||||
// console.error(" W Finish", self._path, self.size)
|
||||
|
||||
// set up all the things.
|
||||
// At this point, we're already done writing whatever we've gotta write,
|
||||
// adding files to the dir, etc.
|
||||
var todo = 0
|
||||
var errState = null
|
||||
var done = false
|
||||
|
||||
if (self._old) {
|
||||
// the times will almost *certainly* have changed.
|
||||
// adds the utimes syscall, but remove another stat.
|
||||
self._old.atime = new Date(0)
|
||||
self._old.mtime = new Date(0)
|
||||
// console.error(" W Finish Stale Stat", self._path, self.size)
|
||||
setProps(self._old)
|
||||
} else {
|
||||
var stat = self.props.follow ? 'stat' : 'lstat'
|
||||
// console.error(" W Finish Stating", self._path, self.size)
|
||||
fs[stat](self._path, function (er, current) {
|
||||
// console.error(" W Finish Stated", self._path, self.size, current)
|
||||
if (er) {
|
||||
// if we're in the process of writing out a
|
||||
// directory, it's very possible that the thing we're linking to
|
||||
// doesn't exist yet (especially if it was intended as a symlink),
|
||||
// so swallow ENOENT errors here and just soldier on.
|
||||
if (er.code === 'ENOENT' &&
|
||||
(self.type === 'Link' || self.type === 'SymbolicLink') &&
|
||||
process.platform === 'win32') {
|
||||
self.ready = true
|
||||
self.emit('ready')
|
||||
self.emit('end')
|
||||
self.emit('close')
|
||||
self.end = self._finish = function () {}
|
||||
return
|
||||
} else return self.error(er)
|
||||
}
|
||||
setProps(self._old = current)
|
||||
})
|
||||
}
|
||||
|
||||
return
|
||||
|
||||
function setProps (current) {
|
||||
todo += 3
|
||||
endChmod(self, self.props, current, self._path, next('chmod'))
|
||||
endChown(self, self.props, current, self._path, next('chown'))
|
||||
endUtimes(self, self.props, current, self._path, next('utimes'))
|
||||
}
|
||||
|
||||
function next (what) {
|
||||
return function (er) {
|
||||
// console.error(" W Finish", what, todo)
|
||||
if (errState) return
|
||||
if (er) {
|
||||
er.fstream_finish_call = what
|
||||
return self.error(errState = er)
|
||||
}
|
||||
if (--todo > 0) return
|
||||
if (done) return
|
||||
done = true
|
||||
|
||||
// we may still need to set the mode/etc. on some parent dirs
|
||||
// that were created previously. delay end/close until then.
|
||||
if (!self._madeDir) return end()
|
||||
else endMadeDir(self, self._path, end)
|
||||
|
||||
function end (er) {
|
||||
if (er) {
|
||||
er.fstream_finish_call = 'setupMadeDir'
|
||||
return self.error(er)
|
||||
}
|
||||
// all the props have been set, so we're completely done.
|
||||
self.emit('end')
|
||||
self.emit('close')
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function endMadeDir (self, p, cb) {
|
||||
var made = self._madeDir
|
||||
// everything *between* made and path.dirname(self._path)
|
||||
// needs to be set up. Note that this may just be one dir.
|
||||
var d = path.dirname(p)
|
||||
|
||||
endMadeDir_(self, d, function (er) {
|
||||
if (er) return cb(er)
|
||||
if (d === made) {
|
||||
return cb()
|
||||
}
|
||||
endMadeDir(self, d, cb)
|
||||
})
|
||||
}
|
||||
|
||||
function endMadeDir_ (self, p, cb) {
|
||||
var dirProps = {}
|
||||
Object.keys(self.props).forEach(function (k) {
|
||||
dirProps[k] = self.props[k]
|
||||
|
||||
// only make non-readable dirs if explicitly requested.
|
||||
if (k === 'mode' && self.type !== 'Directory') {
|
||||
dirProps[k] = dirProps[k] | parseInt('0111', 8)
|
||||
}
|
||||
})
|
||||
|
||||
var todo = 3
|
||||
var errState = null
|
||||
fs.stat(p, function (er, current) {
|
||||
if (er) return cb(errState = er)
|
||||
endChmod(self, dirProps, current, p, next)
|
||||
endChown(self, dirProps, current, p, next)
|
||||
endUtimes(self, dirProps, current, p, next)
|
||||
})
|
||||
|
||||
function next (er) {
|
||||
if (errState) return
|
||||
if (er) return cb(errState = er)
|
||||
if (--todo === 0) return cb()
|
||||
}
|
||||
}
|
||||
|
||||
Writer.prototype.pipe = function () {
|
||||
this.error("Can't pipe from writable stream")
|
||||
}
|
||||
|
||||
Writer.prototype.add = function () {
|
||||
this.error("Can't add to non-Directory type")
|
||||
}
|
||||
|
||||
Writer.prototype.write = function () {
|
||||
return true
|
||||
}
|
||||
|
||||
function objectToString (d) {
|
||||
return Object.prototype.toString.call(d)
|
||||
}
|
||||
|
||||
function isDate (d) {
|
||||
return typeof d === 'object' && objectToString(d) === '[object Date]'
|
||||
}
|
||||
-28
@@ -1,28 +0,0 @@
|
||||
{
|
||||
"author": "Isaac Z. Schlueter <i@izs.me> (http://blog.izs.me/)",
|
||||
"name": "fstream",
|
||||
"description": "Advanced file system stream things",
|
||||
"version": "1.0.12",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/npm/fstream.git"
|
||||
},
|
||||
"main": "fstream.js",
|
||||
"engines": {
|
||||
"node": ">=0.6"
|
||||
},
|
||||
"dependencies": {
|
||||
"graceful-fs": "^4.1.2",
|
||||
"inherits": "~2.0.0",
|
||||
"mkdirp": ">=0.5 0",
|
||||
"rimraf": "2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"standard": "^4.0.0",
|
||||
"tap": "^1.2.0"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "standard && tap examples/*.js"
|
||||
},
|
||||
"license": "ISC"
|
||||
}
|
||||
-21
@@ -1,21 +0,0 @@
|
||||
The ISC License
|
||||
|
||||
Copyright (c) Isaac Z. Schlueter and Contributors
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
|
||||
IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
## Glob Logo
|
||||
|
||||
Glob's logo created by Tanya Brassie <http://tanyabrassie.com/>, licensed
|
||||
under a Creative Commons Attribution-ShareAlike 4.0 International License
|
||||
https://creativecommons.org/licenses/by-sa/4.0/
|
||||
-378
@@ -1,378 +0,0 @@
|
||||
# Glob
|
||||
|
||||
Match files using the patterns the shell uses, like stars and stuff.
|
||||
|
||||
[](https://travis-ci.org/isaacs/node-glob/) [](https://ci.appveyor.com/project/isaacs/node-glob) [](https://coveralls.io/github/isaacs/node-glob?branch=master)
|
||||
|
||||
This is a glob implementation in JavaScript. It uses the `minimatch`
|
||||
library to do its matching.
|
||||
|
||||

|
||||
|
||||
## Usage
|
||||
|
||||
Install with npm
|
||||
|
||||
```
|
||||
npm i glob
|
||||
```
|
||||
|
||||
```javascript
|
||||
var glob = require("glob")
|
||||
|
||||
// options is optional
|
||||
glob("**/*.js", options, function (er, files) {
|
||||
// files is an array of filenames.
|
||||
// If the `nonull` option is set, and nothing
|
||||
// was found, then files is ["**/*.js"]
|
||||
// er is an error object or null.
|
||||
})
|
||||
```
|
||||
|
||||
## Glob Primer
|
||||
|
||||
"Globs" are the patterns you type when you do stuff like `ls *.js` on
|
||||
the command line, or put `build/*` in a `.gitignore` file.
|
||||
|
||||
Before parsing the path part patterns, braced sections are expanded
|
||||
into a set. Braced sections start with `{` and end with `}`, with any
|
||||
number of comma-delimited sections within. Braced sections may contain
|
||||
slash characters, so `a{/b/c,bcd}` would expand into `a/b/c` and `abcd`.
|
||||
|
||||
The following characters have special magic meaning when used in a
|
||||
path portion:
|
||||
|
||||
* `*` Matches 0 or more characters in a single path portion
|
||||
* `?` Matches 1 character
|
||||
* `[...]` Matches a range of characters, similar to a RegExp range.
|
||||
If the first character of the range is `!` or `^` then it matches
|
||||
any character not in the range.
|
||||
* `!(pattern|pattern|pattern)` Matches anything that does not match
|
||||
any of the patterns provided.
|
||||
* `?(pattern|pattern|pattern)` Matches zero or one occurrence of the
|
||||
patterns provided.
|
||||
* `+(pattern|pattern|pattern)` Matches one or more occurrences of the
|
||||
patterns provided.
|
||||
* `*(a|b|c)` Matches zero or more occurrences of the patterns provided
|
||||
* `@(pattern|pat*|pat?erN)` Matches exactly one of the patterns
|
||||
provided
|
||||
* `**` If a "globstar" is alone in a path portion, then it matches
|
||||
zero or more directories and subdirectories searching for matches.
|
||||
It does not crawl symlinked directories.
|
||||
|
||||
### Dots
|
||||
|
||||
If a file or directory path portion has a `.` as the first character,
|
||||
then it will not match any glob pattern unless that pattern's
|
||||
corresponding path part also has a `.` as its first character.
|
||||
|
||||
For example, the pattern `a/.*/c` would match the file at `a/.b/c`.
|
||||
However the pattern `a/*/c` would not, because `*` does not start with
|
||||
a dot character.
|
||||
|
||||
You can make glob treat dots as normal characters by setting
|
||||
`dot:true` in the options.
|
||||
|
||||
### Basename Matching
|
||||
|
||||
If you set `matchBase:true` in the options, and the pattern has no
|
||||
slashes in it, then it will seek for any file anywhere in the tree
|
||||
with a matching basename. For example, `*.js` would match
|
||||
`test/simple/basic.js`.
|
||||
|
||||
### Empty Sets
|
||||
|
||||
If no matching files are found, then an empty array is returned. This
|
||||
differs from the shell, where the pattern itself is returned. For
|
||||
example:
|
||||
|
||||
$ echo a*s*d*f
|
||||
a*s*d*f
|
||||
|
||||
To get the bash-style behavior, set the `nonull:true` in the options.
|
||||
|
||||
### See Also:
|
||||
|
||||
* `man sh`
|
||||
* `man bash` (Search for "Pattern Matching")
|
||||
* `man 3 fnmatch`
|
||||
* `man 5 gitignore`
|
||||
* [minimatch documentation](https://github.com/isaacs/minimatch)
|
||||
|
||||
## glob.hasMagic(pattern, [options])
|
||||
|
||||
Returns `true` if there are any special characters in the pattern, and
|
||||
`false` otherwise.
|
||||
|
||||
Note that the options affect the results. If `noext:true` is set in
|
||||
the options object, then `+(a|b)` will not be considered a magic
|
||||
pattern. If the pattern has a brace expansion, like `a/{b/c,x/y}`
|
||||
then that is considered magical, unless `nobrace:true` is set in the
|
||||
options.
|
||||
|
||||
## glob(pattern, [options], cb)
|
||||
|
||||
* `pattern` `{String}` Pattern to be matched
|
||||
* `options` `{Object}`
|
||||
* `cb` `{Function}`
|
||||
* `err` `{Error | null}`
|
||||
* `matches` `{Array<String>}` filenames found matching the pattern
|
||||
|
||||
Perform an asynchronous glob search.
|
||||
|
||||
## glob.sync(pattern, [options])
|
||||
|
||||
* `pattern` `{String}` Pattern to be matched
|
||||
* `options` `{Object}`
|
||||
* return: `{Array<String>}` filenames found matching the pattern
|
||||
|
||||
Perform a synchronous glob search.
|
||||
|
||||
## Class: glob.Glob
|
||||
|
||||
Create a Glob object by instantiating the `glob.Glob` class.
|
||||
|
||||
```javascript
|
||||
var Glob = require("glob").Glob
|
||||
var mg = new Glob(pattern, options, cb)
|
||||
```
|
||||
|
||||
It's an EventEmitter, and starts walking the filesystem to find matches
|
||||
immediately.
|
||||
|
||||
### new glob.Glob(pattern, [options], [cb])
|
||||
|
||||
* `pattern` `{String}` pattern to search for
|
||||
* `options` `{Object}`
|
||||
* `cb` `{Function}` Called when an error occurs, or matches are found
|
||||
* `err` `{Error | null}`
|
||||
* `matches` `{Array<String>}` filenames found matching the pattern
|
||||
|
||||
Note that if the `sync` flag is set in the options, then matches will
|
||||
be immediately available on the `g.found` member.
|
||||
|
||||
### Properties
|
||||
|
||||
* `minimatch` The minimatch object that the glob uses.
|
||||
* `options` The options object passed in.
|
||||
* `aborted` Boolean which is set to true when calling `abort()`. There
|
||||
is no way at this time to continue a glob search after aborting, but
|
||||
you can re-use the statCache to avoid having to duplicate syscalls.
|
||||
* `cache` Convenience object. Each field has the following possible
|
||||
values:
|
||||
* `false` - Path does not exist
|
||||
* `true` - Path exists
|
||||
* `'FILE'` - Path exists, and is not a directory
|
||||
* `'DIR'` - Path exists, and is a directory
|
||||
* `[file, entries, ...]` - Path exists, is a directory, and the
|
||||
array value is the results of `fs.readdir`
|
||||
* `statCache` Cache of `fs.stat` results, to prevent statting the same
|
||||
path multiple times.
|
||||
* `symlinks` A record of which paths are symbolic links, which is
|
||||
relevant in resolving `**` patterns.
|
||||
* `realpathCache` An optional object which is passed to `fs.realpath`
|
||||
to minimize unnecessary syscalls. It is stored on the instantiated
|
||||
Glob object, and may be re-used.
|
||||
|
||||
### Events
|
||||
|
||||
* `end` When the matching is finished, this is emitted with all the
|
||||
matches found. If the `nonull` option is set, and no match was found,
|
||||
then the `matches` list contains the original pattern. The matches
|
||||
are sorted, unless the `nosort` flag is set.
|
||||
* `match` Every time a match is found, this is emitted with the specific
|
||||
thing that matched. It is not deduplicated or resolved to a realpath.
|
||||
* `error` Emitted when an unexpected error is encountered, or whenever
|
||||
any fs error occurs if `options.strict` is set.
|
||||
* `abort` When `abort()` is called, this event is raised.
|
||||
|
||||
### Methods
|
||||
|
||||
* `pause` Temporarily stop the search
|
||||
* `resume` Resume the search
|
||||
* `abort` Stop the search forever
|
||||
|
||||
### Options
|
||||
|
||||
All the options that can be passed to Minimatch can also be passed to
|
||||
Glob to change pattern matching behavior. Also, some have been added,
|
||||
or have glob-specific ramifications.
|
||||
|
||||
All options are false by default, unless otherwise noted.
|
||||
|
||||
All options are added to the Glob object, as well.
|
||||
|
||||
If you are running many `glob` operations, you can pass a Glob object
|
||||
as the `options` argument to a subsequent operation to shortcut some
|
||||
`stat` and `readdir` calls. At the very least, you may pass in shared
|
||||
`symlinks`, `statCache`, `realpathCache`, and `cache` options, so that
|
||||
parallel glob operations will be sped up by sharing information about
|
||||
the filesystem.
|
||||
|
||||
* `cwd` The current working directory in which to search. Defaults
|
||||
to `process.cwd()`.
|
||||
* `root` The place where patterns starting with `/` will be mounted
|
||||
onto. Defaults to `path.resolve(options.cwd, "/")` (`/` on Unix
|
||||
systems, and `C:\` or some such on Windows.)
|
||||
* `dot` Include `.dot` files in normal matches and `globstar` matches.
|
||||
Note that an explicit dot in a portion of the pattern will always
|
||||
match dot files.
|
||||
* `nomount` By default, a pattern starting with a forward-slash will be
|
||||
"mounted" onto the root setting, so that a valid filesystem path is
|
||||
returned. Set this flag to disable that behavior.
|
||||
* `mark` Add a `/` character to directory matches. Note that this
|
||||
requires additional stat calls.
|
||||
* `nosort` Don't sort the results.
|
||||
* `stat` Set to true to stat *all* results. This reduces performance
|
||||
somewhat, and is completely unnecessary, unless `readdir` is presumed
|
||||
to be an untrustworthy indicator of file existence.
|
||||
* `silent` When an unusual error is encountered when attempting to
|
||||
read a directory, a warning will be printed to stderr. Set the
|
||||
`silent` option to true to suppress these warnings.
|
||||
* `strict` When an unusual error is encountered when attempting to
|
||||
read a directory, the process will just continue on in search of
|
||||
other matches. Set the `strict` option to raise an error in these
|
||||
cases.
|
||||
* `cache` See `cache` property above. Pass in a previously generated
|
||||
cache object to save some fs calls.
|
||||
* `statCache` A cache of results of filesystem information, to prevent
|
||||
unnecessary stat calls. While it should not normally be necessary
|
||||
to set this, you may pass the statCache from one glob() call to the
|
||||
options object of another, if you know that the filesystem will not
|
||||
change between calls. (See "Race Conditions" below.)
|
||||
* `symlinks` A cache of known symbolic links. You may pass in a
|
||||
previously generated `symlinks` object to save `lstat` calls when
|
||||
resolving `**` matches.
|
||||
* `sync` DEPRECATED: use `glob.sync(pattern, opts)` instead.
|
||||
* `nounique` In some cases, brace-expanded patterns can result in the
|
||||
same file showing up multiple times in the result set. By default,
|
||||
this implementation prevents duplicates in the result set. Set this
|
||||
flag to disable that behavior.
|
||||
* `nonull` Set to never return an empty set, instead returning a set
|
||||
containing the pattern itself. This is the default in glob(3).
|
||||
* `debug` Set to enable debug logging in minimatch and glob.
|
||||
* `nobrace` Do not expand `{a,b}` and `{1..3}` brace sets.
|
||||
* `noglobstar` Do not match `**` against multiple filenames. (Ie,
|
||||
treat it as a normal `*` instead.)
|
||||
* `noext` Do not match `+(a|b)` "extglob" patterns.
|
||||
* `nocase` Perform a case-insensitive match. Note: on
|
||||
case-insensitive filesystems, non-magic patterns will match by
|
||||
default, since `stat` and `readdir` will not raise errors.
|
||||
* `matchBase` Perform a basename-only match if the pattern does not
|
||||
contain any slash characters. That is, `*.js` would be treated as
|
||||
equivalent to `**/*.js`, matching all js files in all directories.
|
||||
* `nodir` Do not match directories, only files. (Note: to match
|
||||
*only* directories, simply put a `/` at the end of the pattern.)
|
||||
* `ignore` Add a pattern or an array of glob patterns to exclude matches.
|
||||
Note: `ignore` patterns are *always* in `dot:true` mode, regardless
|
||||
of any other settings.
|
||||
* `follow` Follow symlinked directories when expanding `**` patterns.
|
||||
Note that this can result in a lot of duplicate references in the
|
||||
presence of cyclic links.
|
||||
* `realpath` Set to true to call `fs.realpath` on all of the results.
|
||||
In the case of a symlink that cannot be resolved, the full absolute
|
||||
path to the matched entry is returned (though it will usually be a
|
||||
broken symlink)
|
||||
* `absolute` Set to true to always receive absolute paths for matched
|
||||
files. Unlike `realpath`, this also affects the values returned in
|
||||
the `match` event.
|
||||
* `fs` File-system object with Node's `fs` API. By default, the built-in
|
||||
`fs` module will be used. Set to a volume provided by a library like
|
||||
`memfs` to avoid using the "real" file-system.
|
||||
|
||||
## Comparisons to other fnmatch/glob implementations
|
||||
|
||||
While strict compliance with the existing standards is a worthwhile
|
||||
goal, some discrepancies exist between node-glob and other
|
||||
implementations, and are intentional.
|
||||
|
||||
The double-star character `**` is supported by default, unless the
|
||||
`noglobstar` flag is set. This is supported in the manner of bsdglob
|
||||
and bash 4.3, where `**` only has special significance if it is the only
|
||||
thing in a path part. That is, `a/**/b` will match `a/x/y/b`, but
|
||||
`a/**b` will not.
|
||||
|
||||
Note that symlinked directories are not crawled as part of a `**`,
|
||||
though their contents may match against subsequent portions of the
|
||||
pattern. This prevents infinite loops and duplicates and the like.
|
||||
|
||||
If an escaped pattern has no matches, and the `nonull` flag is set,
|
||||
then glob returns the pattern as-provided, rather than
|
||||
interpreting the character escapes. For example,
|
||||
`glob.match([], "\\*a\\?")` will return `"\\*a\\?"` rather than
|
||||
`"*a?"`. This is akin to setting the `nullglob` option in bash, except
|
||||
that it does not resolve escaped pattern characters.
|
||||
|
||||
If brace expansion is not disabled, then it is performed before any
|
||||
other interpretation of the glob pattern. Thus, a pattern like
|
||||
`+(a|{b),c)}`, which would not be valid in bash or zsh, is expanded
|
||||
**first** into the set of `+(a|b)` and `+(a|c)`, and those patterns are
|
||||
checked for validity. Since those two are valid, matching proceeds.
|
||||
|
||||
### Comments and Negation
|
||||
|
||||
Previously, this module let you mark a pattern as a "comment" if it
|
||||
started with a `#` character, or a "negated" pattern if it started
|
||||
with a `!` character.
|
||||
|
||||
These options were deprecated in version 5, and removed in version 6.
|
||||
|
||||
To specify things that should not match, use the `ignore` option.
|
||||
|
||||
## Windows
|
||||
|
||||
**Please only use forward-slashes in glob expressions.**
|
||||
|
||||
Though windows uses either `/` or `\` as its path separator, only `/`
|
||||
characters are used by this glob implementation. You must use
|
||||
forward-slashes **only** in glob expressions. Back-slashes will always
|
||||
be interpreted as escape characters, not path separators.
|
||||
|
||||
Results from absolute patterns such as `/foo/*` are mounted onto the
|
||||
root setting using `path.join`. On windows, this will by default result
|
||||
in `/foo/*` matching `C:\foo\bar.txt`.
|
||||
|
||||
## Race Conditions
|
||||
|
||||
Glob searching, by its very nature, is susceptible to race conditions,
|
||||
since it relies on directory walking and such.
|
||||
|
||||
As a result, it is possible that a file that exists when glob looks for
|
||||
it may have been deleted or modified by the time it returns the result.
|
||||
|
||||
As part of its internal implementation, this program caches all stat
|
||||
and readdir calls that it makes, in order to cut down on system
|
||||
overhead. However, this also makes it even more susceptible to races,
|
||||
especially if the cache or statCache objects are reused between glob
|
||||
calls.
|
||||
|
||||
Users are thus advised not to use a glob result as a guarantee of
|
||||
filesystem state in the face of rapid changes. For the vast majority
|
||||
of operations, this is never a problem.
|
||||
|
||||
## Glob Logo
|
||||
Glob's logo was created by [Tanya Brassie](http://tanyabrassie.com/). Logo files can be found [here](https://github.com/isaacs/node-glob/tree/master/logo).
|
||||
|
||||
The logo is licensed under a [Creative Commons Attribution-ShareAlike 4.0 International License](https://creativecommons.org/licenses/by-sa/4.0/).
|
||||
|
||||
## Contributing
|
||||
|
||||
Any change to behavior (including bugfixes) must come with a test.
|
||||
|
||||
Patches that fail tests or reduce performance will be rejected.
|
||||
|
||||
```
|
||||
# to run tests
|
||||
npm test
|
||||
|
||||
# to re-generate test fixtures
|
||||
npm run test-regen
|
||||
|
||||
# to benchmark against bash/zsh
|
||||
npm run bench
|
||||
|
||||
# to profile javascript
|
||||
npm run prof
|
||||
```
|
||||
|
||||

|
||||
-236
@@ -1,236 +0,0 @@
|
||||
exports.setopts = setopts
|
||||
exports.ownProp = ownProp
|
||||
exports.makeAbs = makeAbs
|
||||
exports.finish = finish
|
||||
exports.mark = mark
|
||||
exports.isIgnored = isIgnored
|
||||
exports.childrenIgnored = childrenIgnored
|
||||
|
||||
function ownProp (obj, field) {
|
||||
return Object.prototype.hasOwnProperty.call(obj, field)
|
||||
}
|
||||
|
||||
var fs = require("fs")
|
||||
var path = require("path")
|
||||
var minimatch = require("minimatch")
|
||||
var isAbsolute = require("path-is-absolute")
|
||||
var Minimatch = minimatch.Minimatch
|
||||
|
||||
function alphasort (a, b) {
|
||||
return a.localeCompare(b, 'en')
|
||||
}
|
||||
|
||||
function setupIgnores (self, options) {
|
||||
self.ignore = options.ignore || []
|
||||
|
||||
if (!Array.isArray(self.ignore))
|
||||
self.ignore = [self.ignore]
|
||||
|
||||
if (self.ignore.length) {
|
||||
self.ignore = self.ignore.map(ignoreMap)
|
||||
}
|
||||
}
|
||||
|
||||
// ignore patterns are always in dot:true mode.
|
||||
function ignoreMap (pattern) {
|
||||
var gmatcher = null
|
||||
if (pattern.slice(-3) === '/**') {
|
||||
var gpattern = pattern.replace(/(\/\*\*)+$/, '')
|
||||
gmatcher = new Minimatch(gpattern, { dot: true })
|
||||
}
|
||||
|
||||
return {
|
||||
matcher: new Minimatch(pattern, { dot: true }),
|
||||
gmatcher: gmatcher
|
||||
}
|
||||
}
|
||||
|
||||
function setopts (self, pattern, options) {
|
||||
if (!options)
|
||||
options = {}
|
||||
|
||||
// base-matching: just use globstar for that.
|
||||
if (options.matchBase && -1 === pattern.indexOf("/")) {
|
||||
if (options.noglobstar) {
|
||||
throw new Error("base matching requires globstar")
|
||||
}
|
||||
pattern = "**/" + pattern
|
||||
}
|
||||
|
||||
self.silent = !!options.silent
|
||||
self.pattern = pattern
|
||||
self.strict = options.strict !== false
|
||||
self.realpath = !!options.realpath
|
||||
self.realpathCache = options.realpathCache || Object.create(null)
|
||||
self.follow = !!options.follow
|
||||
self.dot = !!options.dot
|
||||
self.mark = !!options.mark
|
||||
self.nodir = !!options.nodir
|
||||
if (self.nodir)
|
||||
self.mark = true
|
||||
self.sync = !!options.sync
|
||||
self.nounique = !!options.nounique
|
||||
self.nonull = !!options.nonull
|
||||
self.nosort = !!options.nosort
|
||||
self.nocase = !!options.nocase
|
||||
self.stat = !!options.stat
|
||||
self.noprocess = !!options.noprocess
|
||||
self.absolute = !!options.absolute
|
||||
self.fs = options.fs || fs
|
||||
|
||||
self.maxLength = options.maxLength || Infinity
|
||||
self.cache = options.cache || Object.create(null)
|
||||
self.statCache = options.statCache || Object.create(null)
|
||||
self.symlinks = options.symlinks || Object.create(null)
|
||||
|
||||
setupIgnores(self, options)
|
||||
|
||||
self.changedCwd = false
|
||||
var cwd = process.cwd()
|
||||
if (!ownProp(options, "cwd"))
|
||||
self.cwd = cwd
|
||||
else {
|
||||
self.cwd = path.resolve(options.cwd)
|
||||
self.changedCwd = self.cwd !== cwd
|
||||
}
|
||||
|
||||
self.root = options.root || path.resolve(self.cwd, "/")
|
||||
self.root = path.resolve(self.root)
|
||||
if (process.platform === "win32")
|
||||
self.root = self.root.replace(/\\/g, "/")
|
||||
|
||||
// TODO: is an absolute `cwd` supposed to be resolved against `root`?
|
||||
// e.g. { cwd: '/test', root: __dirname } === path.join(__dirname, '/test')
|
||||
self.cwdAbs = isAbsolute(self.cwd) ? self.cwd : makeAbs(self, self.cwd)
|
||||
if (process.platform === "win32")
|
||||
self.cwdAbs = self.cwdAbs.replace(/\\/g, "/")
|
||||
self.nomount = !!options.nomount
|
||||
|
||||
// disable comments and negation in Minimatch.
|
||||
// Note that they are not supported in Glob itself anyway.
|
||||
options.nonegate = true
|
||||
options.nocomment = true
|
||||
|
||||
self.minimatch = new Minimatch(pattern, options)
|
||||
self.options = self.minimatch.options
|
||||
}
|
||||
|
||||
function finish (self) {
|
||||
var nou = self.nounique
|
||||
var all = nou ? [] : Object.create(null)
|
||||
|
||||
for (var i = 0, l = self.matches.length; i < l; i ++) {
|
||||
var matches = self.matches[i]
|
||||
if (!matches || Object.keys(matches).length === 0) {
|
||||
if (self.nonull) {
|
||||
// do like the shell, and spit out the literal glob
|
||||
var literal = self.minimatch.globSet[i]
|
||||
if (nou)
|
||||
all.push(literal)
|
||||
else
|
||||
all[literal] = true
|
||||
}
|
||||
} else {
|
||||
// had matches
|
||||
var m = Object.keys(matches)
|
||||
if (nou)
|
||||
all.push.apply(all, m)
|
||||
else
|
||||
m.forEach(function (m) {
|
||||
all[m] = true
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
if (!nou)
|
||||
all = Object.keys(all)
|
||||
|
||||
if (!self.nosort)
|
||||
all = all.sort(alphasort)
|
||||
|
||||
// at *some* point we statted all of these
|
||||
if (self.mark) {
|
||||
for (var i = 0; i < all.length; i++) {
|
||||
all[i] = self._mark(all[i])
|
||||
}
|
||||
if (self.nodir) {
|
||||
all = all.filter(function (e) {
|
||||
var notDir = !(/\/$/.test(e))
|
||||
var c = self.cache[e] || self.cache[makeAbs(self, e)]
|
||||
if (notDir && c)
|
||||
notDir = c !== 'DIR' && !Array.isArray(c)
|
||||
return notDir
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
if (self.ignore.length)
|
||||
all = all.filter(function(m) {
|
||||
return !isIgnored(self, m)
|
||||
})
|
||||
|
||||
self.found = all
|
||||
}
|
||||
|
||||
function mark (self, p) {
|
||||
var abs = makeAbs(self, p)
|
||||
var c = self.cache[abs]
|
||||
var m = p
|
||||
if (c) {
|
||||
var isDir = c === 'DIR' || Array.isArray(c)
|
||||
var slash = p.slice(-1) === '/'
|
||||
|
||||
if (isDir && !slash)
|
||||
m += '/'
|
||||
else if (!isDir && slash)
|
||||
m = m.slice(0, -1)
|
||||
|
||||
if (m !== p) {
|
||||
var mabs = makeAbs(self, m)
|
||||
self.statCache[mabs] = self.statCache[abs]
|
||||
self.cache[mabs] = self.cache[abs]
|
||||
}
|
||||
}
|
||||
|
||||
return m
|
||||
}
|
||||
|
||||
// lotta situps...
|
||||
function makeAbs (self, f) {
|
||||
var abs = f
|
||||
if (f.charAt(0) === '/') {
|
||||
abs = path.join(self.root, f)
|
||||
} else if (isAbsolute(f) || f === '') {
|
||||
abs = f
|
||||
} else if (self.changedCwd) {
|
||||
abs = path.resolve(self.cwd, f)
|
||||
} else {
|
||||
abs = path.resolve(f)
|
||||
}
|
||||
|
||||
if (process.platform === 'win32')
|
||||
abs = abs.replace(/\\/g, '/')
|
||||
|
||||
return abs
|
||||
}
|
||||
|
||||
|
||||
// Return true, if pattern ends with globstar '**', for the accompanying parent directory.
|
||||
// Ex:- If node_modules/** is the pattern, add 'node_modules' to ignore list along with it's contents
|
||||
function isIgnored (self, path) {
|
||||
if (!self.ignore.length)
|
||||
return false
|
||||
|
||||
return self.ignore.some(function(item) {
|
||||
return item.matcher.match(path) || !!(item.gmatcher && item.gmatcher.match(path))
|
||||
})
|
||||
}
|
||||
|
||||
function childrenIgnored (self, path) {
|
||||
if (!self.ignore.length)
|
||||
return false
|
||||
|
||||
return self.ignore.some(function(item) {
|
||||
return !!(item.gmatcher && item.gmatcher.match(path))
|
||||
})
|
||||
}
|
||||
-787
@@ -1,787 +0,0 @@
|
||||
// Approach:
|
||||
//
|
||||
// 1. Get the minimatch set
|
||||
// 2. For each pattern in the set, PROCESS(pattern, false)
|
||||
// 3. Store matches per-set, then uniq them
|
||||
//
|
||||
// PROCESS(pattern, inGlobStar)
|
||||
// Get the first [n] items from pattern that are all strings
|
||||
// Join these together. This is PREFIX.
|
||||
// If there is no more remaining, then stat(PREFIX) and
|
||||
// add to matches if it succeeds. END.
|
||||
//
|
||||
// If inGlobStar and PREFIX is symlink and points to dir
|
||||
// set ENTRIES = []
|
||||
// else readdir(PREFIX) as ENTRIES
|
||||
// If fail, END
|
||||
//
|
||||
// with ENTRIES
|
||||
// If pattern[n] is GLOBSTAR
|
||||
// // handle the case where the globstar match is empty
|
||||
// // by pruning it out, and testing the resulting pattern
|
||||
// PROCESS(pattern[0..n] + pattern[n+1 .. $], false)
|
||||
// // handle other cases.
|
||||
// for ENTRY in ENTRIES (not dotfiles)
|
||||
// // attach globstar + tail onto the entry
|
||||
// // Mark that this entry is a globstar match
|
||||
// PROCESS(pattern[0..n] + ENTRY + pattern[n .. $], true)
|
||||
//
|
||||
// else // not globstar
|
||||
// for ENTRY in ENTRIES (not dotfiles, unless pattern[n] is dot)
|
||||
// Test ENTRY against pattern[n]
|
||||
// If fails, continue
|
||||
// If passes, PROCESS(pattern[0..n] + item + pattern[n+1 .. $])
|
||||
//
|
||||
// Caveat:
|
||||
// Cache all stats and readdirs results to minimize syscall. Since all
|
||||
// we ever care about is existence and directory-ness, we can just keep
|
||||
// `true` for files, and [children,...] for directories, or `false` for
|
||||
// things that don't exist.
|
||||
|
||||
module.exports = glob
|
||||
|
||||
var rp = require('fs.realpath')
|
||||
var minimatch = require('minimatch')
|
||||
var Minimatch = minimatch.Minimatch
|
||||
var inherits = require('inherits')
|
||||
var EE = require('events').EventEmitter
|
||||
var path = require('path')
|
||||
var assert = require('assert')
|
||||
var isAbsolute = require('path-is-absolute')
|
||||
var globSync = require('./sync.js')
|
||||
var common = require('./common.js')
|
||||
var setopts = common.setopts
|
||||
var ownProp = common.ownProp
|
||||
var inflight = require('inflight')
|
||||
var util = require('util')
|
||||
var childrenIgnored = common.childrenIgnored
|
||||
var isIgnored = common.isIgnored
|
||||
|
||||
var once = require('once')
|
||||
|
||||
function glob (pattern, options, cb) {
|
||||
if (typeof options === 'function') cb = options, options = {}
|
||||
if (!options) options = {}
|
||||
|
||||
if (options.sync) {
|
||||
if (cb)
|
||||
throw new TypeError('callback provided to sync glob')
|
||||
return globSync(pattern, options)
|
||||
}
|
||||
|
||||
return new Glob(pattern, options, cb)
|
||||
}
|
||||
|
||||
glob.sync = globSync
|
||||
var GlobSync = glob.GlobSync = globSync.GlobSync
|
||||
|
||||
// old api surface
|
||||
glob.glob = glob
|
||||
|
||||
function extend (origin, add) {
|
||||
if (add === null || typeof add !== 'object') {
|
||||
return origin
|
||||
}
|
||||
|
||||
var keys = Object.keys(add)
|
||||
var i = keys.length
|
||||
while (i--) {
|
||||
origin[keys[i]] = add[keys[i]]
|
||||
}
|
||||
return origin
|
||||
}
|
||||
|
||||
glob.hasMagic = function (pattern, options_) {
|
||||
var options = extend({}, options_)
|
||||
options.noprocess = true
|
||||
|
||||
var g = new Glob(pattern, options)
|
||||
var set = g.minimatch.set
|
||||
|
||||
if (!pattern)
|
||||
return false
|
||||
|
||||
if (set.length > 1)
|
||||
return true
|
||||
|
||||
for (var j = 0; j < set[0].length; j++) {
|
||||
if (typeof set[0][j] !== 'string')
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
glob.Glob = Glob
|
||||
inherits(Glob, EE)
|
||||
function Glob (pattern, options, cb) {
|
||||
if (typeof options === 'function') {
|
||||
cb = options
|
||||
options = null
|
||||
}
|
||||
|
||||
if (options && options.sync) {
|
||||
if (cb)
|
||||
throw new TypeError('callback provided to sync glob')
|
||||
return new GlobSync(pattern, options)
|
||||
}
|
||||
|
||||
if (!(this instanceof Glob))
|
||||
return new Glob(pattern, options, cb)
|
||||
|
||||
setopts(this, pattern, options)
|
||||
this._didRealPath = false
|
||||
|
||||
// process each pattern in the minimatch set
|
||||
var n = this.minimatch.set.length
|
||||
|
||||
// The matches are stored as {<filename>: true,...} so that
|
||||
// duplicates are automagically pruned.
|
||||
// Later, we do an Object.keys() on these.
|
||||
// Keep them as a list so we can fill in when nonull is set.
|
||||
this.matches = new Array(n)
|
||||
|
||||
if (typeof cb === 'function') {
|
||||
cb = once(cb)
|
||||
this.on('error', cb)
|
||||
this.on('end', function (matches) {
|
||||
cb(null, matches)
|
||||
})
|
||||
}
|
||||
|
||||
var self = this
|
||||
this._processing = 0
|
||||
|
||||
this._emitQueue = []
|
||||
this._processQueue = []
|
||||
this.paused = false
|
||||
|
||||
if (this.noprocess)
|
||||
return this
|
||||
|
||||
if (n === 0)
|
||||
return done()
|
||||
|
||||
var sync = true
|
||||
for (var i = 0; i < n; i ++) {
|
||||
this._process(this.minimatch.set[i], i, false, done)
|
||||
}
|
||||
sync = false
|
||||
|
||||
function done () {
|
||||
--self._processing
|
||||
if (self._processing <= 0) {
|
||||
if (sync) {
|
||||
process.nextTick(function () {
|
||||
self._finish()
|
||||
})
|
||||
} else {
|
||||
self._finish()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Glob.prototype._finish = function () {
|
||||
assert(this instanceof Glob)
|
||||
if (this.aborted)
|
||||
return
|
||||
|
||||
if (this.realpath && !this._didRealpath)
|
||||
return this._realpath()
|
||||
|
||||
common.finish(this)
|
||||
this.emit('end', this.found)
|
||||
}
|
||||
|
||||
Glob.prototype._realpath = function () {
|
||||
if (this._didRealpath)
|
||||
return
|
||||
|
||||
this._didRealpath = true
|
||||
|
||||
var n = this.matches.length
|
||||
if (n === 0)
|
||||
return this._finish()
|
||||
|
||||
var self = this
|
||||
for (var i = 0; i < this.matches.length; i++)
|
||||
this._realpathSet(i, next)
|
||||
|
||||
function next () {
|
||||
if (--n === 0)
|
||||
self._finish()
|
||||
}
|
||||
}
|
||||
|
||||
Glob.prototype._realpathSet = function (index, cb) {
|
||||
var matchset = this.matches[index]
|
||||
if (!matchset)
|
||||
return cb()
|
||||
|
||||
var found = Object.keys(matchset)
|
||||
var self = this
|
||||
var n = found.length
|
||||
|
||||
if (n === 0)
|
||||
return cb()
|
||||
|
||||
var set = this.matches[index] = Object.create(null)
|
||||
found.forEach(function (p, i) {
|
||||
// If there's a problem with the stat, then it means that
|
||||
// one or more of the links in the realpath couldn't be
|
||||
// resolved. just return the abs value in that case.
|
||||
p = self._makeAbs(p)
|
||||
rp.realpath(p, self.realpathCache, function (er, real) {
|
||||
if (!er)
|
||||
set[real] = true
|
||||
else if (er.syscall === 'stat')
|
||||
set[p] = true
|
||||
else
|
||||
self.emit('error', er) // srsly wtf right here
|
||||
|
||||
if (--n === 0) {
|
||||
self.matches[index] = set
|
||||
cb()
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
Glob.prototype._mark = function (p) {
|
||||
return common.mark(this, p)
|
||||
}
|
||||
|
||||
Glob.prototype._makeAbs = function (f) {
|
||||
return common.makeAbs(this, f)
|
||||
}
|
||||
|
||||
Glob.prototype.abort = function () {
|
||||
this.aborted = true
|
||||
this.emit('abort')
|
||||
}
|
||||
|
||||
Glob.prototype.pause = function () {
|
||||
if (!this.paused) {
|
||||
this.paused = true
|
||||
this.emit('pause')
|
||||
}
|
||||
}
|
||||
|
||||
Glob.prototype.resume = function () {
|
||||
if (this.paused) {
|
||||
this.emit('resume')
|
||||
this.paused = false
|
||||
if (this._emitQueue.length) {
|
||||
var eq = this._emitQueue.slice(0)
|
||||
this._emitQueue.length = 0
|
||||
for (var i = 0; i < eq.length; i ++) {
|
||||
var e = eq[i]
|
||||
this._emitMatch(e[0], e[1])
|
||||
}
|
||||
}
|
||||
if (this._processQueue.length) {
|
||||
var pq = this._processQueue.slice(0)
|
||||
this._processQueue.length = 0
|
||||
for (var i = 0; i < pq.length; i ++) {
|
||||
var p = pq[i]
|
||||
this._processing--
|
||||
this._process(p[0], p[1], p[2], p[3])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Glob.prototype._process = function (pattern, index, inGlobStar, cb) {
|
||||
assert(this instanceof Glob)
|
||||
assert(typeof cb === 'function')
|
||||
|
||||
if (this.aborted)
|
||||
return
|
||||
|
||||
this._processing++
|
||||
if (this.paused) {
|
||||
this._processQueue.push([pattern, index, inGlobStar, cb])
|
||||
return
|
||||
}
|
||||
|
||||
//console.error('PROCESS %d', this._processing, pattern)
|
||||
|
||||
// Get the first [n] parts of pattern that are all strings.
|
||||
var n = 0
|
||||
while (typeof pattern[n] === 'string') {
|
||||
n ++
|
||||
}
|
||||
// now n is the index of the first one that is *not* a string.
|
||||
|
||||
// see if there's anything else
|
||||
var prefix
|
||||
switch (n) {
|
||||
// if not, then this is rather simple
|
||||
case pattern.length:
|
||||
this._processSimple(pattern.join('/'), index, cb)
|
||||
return
|
||||
|
||||
case 0:
|
||||
// pattern *starts* with some non-trivial item.
|
||||
// going to readdir(cwd), but not include the prefix in matches.
|
||||
prefix = null
|
||||
break
|
||||
|
||||
default:
|
||||
// pattern has some string bits in the front.
|
||||
// whatever it starts with, whether that's 'absolute' like /foo/bar,
|
||||
// or 'relative' like '../baz'
|
||||
prefix = pattern.slice(0, n).join('/')
|
||||
break
|
||||
}
|
||||
|
||||
var remain = pattern.slice(n)
|
||||
|
||||
// get the list of entries.
|
||||
var read
|
||||
if (prefix === null)
|
||||
read = '.'
|
||||
else if (isAbsolute(prefix) || isAbsolute(pattern.join('/'))) {
|
||||
if (!prefix || !isAbsolute(prefix))
|
||||
prefix = '/' + prefix
|
||||
read = prefix
|
||||
} else
|
||||
read = prefix
|
||||
|
||||
var abs = this._makeAbs(read)
|
||||
|
||||
//if ignored, skip _processing
|
||||
if (childrenIgnored(this, read))
|
||||
return cb()
|
||||
|
||||
var isGlobStar = remain[0] === minimatch.GLOBSTAR
|
||||
if (isGlobStar)
|
||||
this._processGlobStar(prefix, read, abs, remain, index, inGlobStar, cb)
|
||||
else
|
||||
this._processReaddir(prefix, read, abs, remain, index, inGlobStar, cb)
|
||||
}
|
||||
|
||||
Glob.prototype._processReaddir = function (prefix, read, abs, remain, index, inGlobStar, cb) {
|
||||
var self = this
|
||||
this._readdir(abs, inGlobStar, function (er, entries) {
|
||||
return self._processReaddir2(prefix, read, abs, remain, index, inGlobStar, entries, cb)
|
||||
})
|
||||
}
|
||||
|
||||
Glob.prototype._processReaddir2 = function (prefix, read, abs, remain, index, inGlobStar, entries, cb) {
|
||||
|
||||
// if the abs isn't a dir, then nothing can match!
|
||||
if (!entries)
|
||||
return cb()
|
||||
|
||||
// It will only match dot entries if it starts with a dot, or if
|
||||
// dot is set. Stuff like @(.foo|.bar) isn't allowed.
|
||||
var pn = remain[0]
|
||||
var negate = !!this.minimatch.negate
|
||||
var rawGlob = pn._glob
|
||||
var dotOk = this.dot || rawGlob.charAt(0) === '.'
|
||||
|
||||
var matchedEntries = []
|
||||
for (var i = 0; i < entries.length; i++) {
|
||||
var e = entries[i]
|
||||
if (e.charAt(0) !== '.' || dotOk) {
|
||||
var m
|
||||
if (negate && !prefix) {
|
||||
m = !e.match(pn)
|
||||
} else {
|
||||
m = e.match(pn)
|
||||
}
|
||||
if (m)
|
||||
matchedEntries.push(e)
|
||||
}
|
||||
}
|
||||
|
||||
//console.error('prd2', prefix, entries, remain[0]._glob, matchedEntries)
|
||||
|
||||
var len = matchedEntries.length
|
||||
// If there are no matched entries, then nothing matches.
|
||||
if (len === 0)
|
||||
return cb()
|
||||
|
||||
// if this is the last remaining pattern bit, then no need for
|
||||
// an additional stat *unless* the user has specified mark or
|
||||
// stat explicitly. We know they exist, since readdir returned
|
||||
// them.
|
||||
|
||||
if (remain.length === 1 && !this.mark && !this.stat) {
|
||||
if (!this.matches[index])
|
||||
this.matches[index] = Object.create(null)
|
||||
|
||||
for (var i = 0; i < len; i ++) {
|
||||
var e = matchedEntries[i]
|
||||
if (prefix) {
|
||||
if (prefix !== '/')
|
||||
e = prefix + '/' + e
|
||||
else
|
||||
e = prefix + e
|
||||
}
|
||||
|
||||
if (e.charAt(0) === '/' && !this.nomount) {
|
||||
e = path.join(this.root, e)
|
||||
}
|
||||
this._emitMatch(index, e)
|
||||
}
|
||||
// This was the last one, and no stats were needed
|
||||
return cb()
|
||||
}
|
||||
|
||||
// now test all matched entries as stand-ins for that part
|
||||
// of the pattern.
|
||||
remain.shift()
|
||||
for (var i = 0; i < len; i ++) {
|
||||
var e = matchedEntries[i]
|
||||
var newPattern
|
||||
if (prefix) {
|
||||
if (prefix !== '/')
|
||||
e = prefix + '/' + e
|
||||
else
|
||||
e = prefix + e
|
||||
}
|
||||
this._process([e].concat(remain), index, inGlobStar, cb)
|
||||
}
|
||||
cb()
|
||||
}
|
||||
|
||||
Glob.prototype._emitMatch = function (index, e) {
|
||||
if (this.aborted)
|
||||
return
|
||||
|
||||
if (isIgnored(this, e))
|
||||
return
|
||||
|
||||
if (this.paused) {
|
||||
this._emitQueue.push([index, e])
|
||||
return
|
||||
}
|
||||
|
||||
var abs = isAbsolute(e) ? e : this._makeAbs(e)
|
||||
|
||||
if (this.mark)
|
||||
e = this._mark(e)
|
||||
|
||||
if (this.absolute)
|
||||
e = abs
|
||||
|
||||
if (this.matches[index][e])
|
||||
return
|
||||
|
||||
if (this.nodir) {
|
||||
var c = this.cache[abs]
|
||||
if (c === 'DIR' || Array.isArray(c))
|
||||
return
|
||||
}
|
||||
|
||||
this.matches[index][e] = true
|
||||
|
||||
var st = this.statCache[abs]
|
||||
if (st)
|
||||
this.emit('stat', e, st)
|
||||
|
||||
this.emit('match', e)
|
||||
}
|
||||
|
||||
Glob.prototype._readdirInGlobStar = function (abs, cb) {
|
||||
if (this.aborted)
|
||||
return
|
||||
|
||||
// follow all symlinked directories forever
|
||||
// just proceed as if this is a non-globstar situation
|
||||
if (this.follow)
|
||||
return this._readdir(abs, false, cb)
|
||||
|
||||
var lstatkey = 'lstat\0' + abs
|
||||
var self = this
|
||||
var lstatcb = inflight(lstatkey, lstatcb_)
|
||||
|
||||
if (lstatcb)
|
||||
self.fs.lstat(abs, lstatcb)
|
||||
|
||||
function lstatcb_ (er, lstat) {
|
||||
if (er && er.code === 'ENOENT')
|
||||
return cb()
|
||||
|
||||
var isSym = lstat && lstat.isSymbolicLink()
|
||||
self.symlinks[abs] = isSym
|
||||
|
||||
// If it's not a symlink or a dir, then it's definitely a regular file.
|
||||
// don't bother doing a readdir in that case.
|
||||
if (!isSym && lstat && !lstat.isDirectory()) {
|
||||
self.cache[abs] = 'FILE'
|
||||
cb()
|
||||
} else
|
||||
self._readdir(abs, false, cb)
|
||||
}
|
||||
}
|
||||
|
||||
Glob.prototype._readdir = function (abs, inGlobStar, cb) {
|
||||
if (this.aborted)
|
||||
return
|
||||
|
||||
cb = inflight('readdir\0'+abs+'\0'+inGlobStar, cb)
|
||||
if (!cb)
|
||||
return
|
||||
|
||||
//console.error('RD %j %j', +inGlobStar, abs)
|
||||
if (inGlobStar && !ownProp(this.symlinks, abs))
|
||||
return this._readdirInGlobStar(abs, cb)
|
||||
|
||||
if (ownProp(this.cache, abs)) {
|
||||
var c = this.cache[abs]
|
||||
if (!c || c === 'FILE')
|
||||
return cb()
|
||||
|
||||
if (Array.isArray(c))
|
||||
return cb(null, c)
|
||||
}
|
||||
|
||||
var self = this
|
||||
self.fs.readdir(abs, readdirCb(this, abs, cb))
|
||||
}
|
||||
|
||||
function readdirCb (self, abs, cb) {
|
||||
return function (er, entries) {
|
||||
if (er)
|
||||
self._readdirError(abs, er, cb)
|
||||
else
|
||||
self._readdirEntries(abs, entries, cb)
|
||||
}
|
||||
}
|
||||
|
||||
Glob.prototype._readdirEntries = function (abs, entries, cb) {
|
||||
if (this.aborted)
|
||||
return
|
||||
|
||||
// if we haven't asked to stat everything, then just
|
||||
// assume that everything in there exists, so we can avoid
|
||||
// having to stat it a second time.
|
||||
if (!this.mark && !this.stat) {
|
||||
for (var i = 0; i < entries.length; i ++) {
|
||||
var e = entries[i]
|
||||
if (abs === '/')
|
||||
e = abs + e
|
||||
else
|
||||
e = abs + '/' + e
|
||||
this.cache[e] = true
|
||||
}
|
||||
}
|
||||
|
||||
this.cache[abs] = entries
|
||||
return cb(null, entries)
|
||||
}
|
||||
|
||||
Glob.prototype._readdirError = function (f, er, cb) {
|
||||
if (this.aborted)
|
||||
return
|
||||
|
||||
// handle errors, and cache the information
|
||||
switch (er.code) {
|
||||
case 'ENOTSUP': // https://github.com/isaacs/node-glob/issues/205
|
||||
case 'ENOTDIR': // totally normal. means it *does* exist.
|
||||
var abs = this._makeAbs(f)
|
||||
this.cache[abs] = 'FILE'
|
||||
if (abs === this.cwdAbs) {
|
||||
var error = new Error(er.code + ' invalid cwd ' + this.cwd)
|
||||
error.path = this.cwd
|
||||
error.code = er.code
|
||||
this.emit('error', error)
|
||||
this.abort()
|
||||
}
|
||||
break
|
||||
|
||||
case 'ENOENT': // not terribly unusual
|
||||
case 'ELOOP':
|
||||
case 'ENAMETOOLONG':
|
||||
case 'UNKNOWN':
|
||||
this.cache[this._makeAbs(f)] = false
|
||||
break
|
||||
|
||||
default: // some unusual error. Treat as failure.
|
||||
this.cache[this._makeAbs(f)] = false
|
||||
if (this.strict) {
|
||||
this.emit('error', er)
|
||||
// If the error is handled, then we abort
|
||||
// if not, we threw out of here
|
||||
this.abort()
|
||||
}
|
||||
if (!this.silent)
|
||||
console.error('glob error', er)
|
||||
break
|
||||
}
|
||||
|
||||
return cb()
|
||||
}
|
||||
|
||||
Glob.prototype._processGlobStar = function (prefix, read, abs, remain, index, inGlobStar, cb) {
|
||||
var self = this
|
||||
this._readdir(abs, inGlobStar, function (er, entries) {
|
||||
self._processGlobStar2(prefix, read, abs, remain, index, inGlobStar, entries, cb)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
Glob.prototype._processGlobStar2 = function (prefix, read, abs, remain, index, inGlobStar, entries, cb) {
|
||||
//console.error('pgs2', prefix, remain[0], entries)
|
||||
|
||||
// no entries means not a dir, so it can never have matches
|
||||
// foo.txt/** doesn't match foo.txt
|
||||
if (!entries)
|
||||
return cb()
|
||||
|
||||
// test without the globstar, and with every child both below
|
||||
// and replacing the globstar.
|
||||
var remainWithoutGlobStar = remain.slice(1)
|
||||
var gspref = prefix ? [ prefix ] : []
|
||||
var noGlobStar = gspref.concat(remainWithoutGlobStar)
|
||||
|
||||
// the noGlobStar pattern exits the inGlobStar state
|
||||
this._process(noGlobStar, index, false, cb)
|
||||
|
||||
var isSym = this.symlinks[abs]
|
||||
var len = entries.length
|
||||
|
||||
// If it's a symlink, and we're in a globstar, then stop
|
||||
if (isSym && inGlobStar)
|
||||
return cb()
|
||||
|
||||
for (var i = 0; i < len; i++) {
|
||||
var e = entries[i]
|
||||
if (e.charAt(0) === '.' && !this.dot)
|
||||
continue
|
||||
|
||||
// these two cases enter the inGlobStar state
|
||||
var instead = gspref.concat(entries[i], remainWithoutGlobStar)
|
||||
this._process(instead, index, true, cb)
|
||||
|
||||
var below = gspref.concat(entries[i], remain)
|
||||
this._process(below, index, true, cb)
|
||||
}
|
||||
|
||||
cb()
|
||||
}
|
||||
|
||||
Glob.prototype._processSimple = function (prefix, index, cb) {
|
||||
// XXX review this. Shouldn't it be doing the mounting etc
|
||||
// before doing stat? kinda weird?
|
||||
var self = this
|
||||
this._stat(prefix, function (er, exists) {
|
||||
self._processSimple2(prefix, index, er, exists, cb)
|
||||
})
|
||||
}
|
||||
Glob.prototype._processSimple2 = function (prefix, index, er, exists, cb) {
|
||||
|
||||
//console.error('ps2', prefix, exists)
|
||||
|
||||
if (!this.matches[index])
|
||||
this.matches[index] = Object.create(null)
|
||||
|
||||
// If it doesn't exist, then just mark the lack of results
|
||||
if (!exists)
|
||||
return cb()
|
||||
|
||||
if (prefix && isAbsolute(prefix) && !this.nomount) {
|
||||
var trail = /[\/\\]$/.test(prefix)
|
||||
if (prefix.charAt(0) === '/') {
|
||||
prefix = path.join(this.root, prefix)
|
||||
} else {
|
||||
prefix = path.resolve(this.root, prefix)
|
||||
if (trail)
|
||||
prefix += '/'
|
||||
}
|
||||
}
|
||||
|
||||
if (process.platform === 'win32')
|
||||
prefix = prefix.replace(/\\/g, '/')
|
||||
|
||||
// Mark this as a match
|
||||
this._emitMatch(index, prefix)
|
||||
cb()
|
||||
}
|
||||
|
||||
// Returns either 'DIR', 'FILE', or false
|
||||
Glob.prototype._stat = function (f, cb) {
|
||||
var abs = this._makeAbs(f)
|
||||
var needDir = f.slice(-1) === '/'
|
||||
|
||||
if (f.length > this.maxLength)
|
||||
return cb()
|
||||
|
||||
if (!this.stat && ownProp(this.cache, abs)) {
|
||||
var c = this.cache[abs]
|
||||
|
||||
if (Array.isArray(c))
|
||||
c = 'DIR'
|
||||
|
||||
// It exists, but maybe not how we need it
|
||||
if (!needDir || c === 'DIR')
|
||||
return cb(null, c)
|
||||
|
||||
if (needDir && c === 'FILE')
|
||||
return cb()
|
||||
|
||||
// otherwise we have to stat, because maybe c=true
|
||||
// if we know it exists, but not what it is.
|
||||
}
|
||||
|
||||
var exists
|
||||
var stat = this.statCache[abs]
|
||||
if (stat !== undefined) {
|
||||
if (stat === false)
|
||||
return cb(null, stat)
|
||||
else {
|
||||
var type = stat.isDirectory() ? 'DIR' : 'FILE'
|
||||
if (needDir && type === 'FILE')
|
||||
return cb()
|
||||
else
|
||||
return cb(null, type, stat)
|
||||
}
|
||||
}
|
||||
|
||||
var self = this
|
||||
var statcb = inflight('stat\0' + abs, lstatcb_)
|
||||
if (statcb)
|
||||
self.fs.lstat(abs, statcb)
|
||||
|
||||
function lstatcb_ (er, lstat) {
|
||||
if (lstat && lstat.isSymbolicLink()) {
|
||||
// If it's a symlink, then treat it as the target, unless
|
||||
// the target does not exist, then treat it as a file.
|
||||
return self.fs.stat(abs, function (er, stat) {
|
||||
if (er)
|
||||
self._stat2(f, abs, null, lstat, cb)
|
||||
else
|
||||
self._stat2(f, abs, er, stat, cb)
|
||||
})
|
||||
} else {
|
||||
self._stat2(f, abs, er, lstat, cb)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Glob.prototype._stat2 = function (f, abs, er, stat, cb) {
|
||||
if (er && (er.code === 'ENOENT' || er.code === 'ENOTDIR')) {
|
||||
this.statCache[abs] = false
|
||||
return cb()
|
||||
}
|
||||
|
||||
var needDir = f.slice(-1) === '/'
|
||||
this.statCache[abs] = stat
|
||||
|
||||
if (abs.slice(-1) === '/' && stat && !stat.isDirectory())
|
||||
return cb(null, false, stat)
|
||||
|
||||
var c = true
|
||||
if (stat)
|
||||
c = stat.isDirectory() ? 'DIR' : 'FILE'
|
||||
this.cache[abs] = this.cache[abs] || c
|
||||
|
||||
if (needDir && c === 'FILE')
|
||||
return cb()
|
||||
|
||||
return cb(null, c, stat)
|
||||
}
|
||||
-52
@@ -1,52 +0,0 @@
|
||||
{
|
||||
"author": "Isaac Z. Schlueter <i@izs.me> (http://blog.izs.me/)",
|
||||
"name": "glob",
|
||||
"description": "a little globber",
|
||||
"version": "7.2.0",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git://github.com/isaacs/node-glob.git"
|
||||
},
|
||||
"main": "glob.js",
|
||||
"files": [
|
||||
"glob.js",
|
||||
"sync.js",
|
||||
"common.js"
|
||||
],
|
||||
"engines": {
|
||||
"node": "*"
|
||||
},
|
||||
"dependencies": {
|
||||
"fs.realpath": "^1.0.0",
|
||||
"inflight": "^1.0.4",
|
||||
"inherits": "2",
|
||||
"minimatch": "^3.0.4",
|
||||
"once": "^1.3.0",
|
||||
"path-is-absolute": "^1.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"memfs": "^3.2.0",
|
||||
"mkdirp": "0",
|
||||
"rimraf": "^2.2.8",
|
||||
"tap": "^15.0.6",
|
||||
"tick": "0.0.6"
|
||||
},
|
||||
"tap": {
|
||||
"before": "test/00-setup.js",
|
||||
"after": "test/zz-cleanup.js",
|
||||
"jobs": 1
|
||||
},
|
||||
"scripts": {
|
||||
"prepublish": "npm run benchclean",
|
||||
"profclean": "rm -f v8.log profile.txt",
|
||||
"test": "tap",
|
||||
"test-regen": "npm run profclean && TEST_REGEN=1 node test/00-setup.js",
|
||||
"bench": "bash benchmark.sh",
|
||||
"prof": "bash prof.sh && cat profile.txt",
|
||||
"benchclean": "node benchclean.js"
|
||||
},
|
||||
"license": "ISC",
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
}
|
||||
-483
@@ -1,483 +0,0 @@
|
||||
module.exports = globSync
|
||||
globSync.GlobSync = GlobSync
|
||||
|
||||
var rp = require('fs.realpath')
|
||||
var minimatch = require('minimatch')
|
||||
var Minimatch = minimatch.Minimatch
|
||||
var Glob = require('./glob.js').Glob
|
||||
var util = require('util')
|
||||
var path = require('path')
|
||||
var assert = require('assert')
|
||||
var isAbsolute = require('path-is-absolute')
|
||||
var common = require('./common.js')
|
||||
var setopts = common.setopts
|
||||
var ownProp = common.ownProp
|
||||
var childrenIgnored = common.childrenIgnored
|
||||
var isIgnored = common.isIgnored
|
||||
|
||||
function globSync (pattern, options) {
|
||||
if (typeof options === 'function' || arguments.length === 3)
|
||||
throw new TypeError('callback provided to sync glob\n'+
|
||||
'See: https://github.com/isaacs/node-glob/issues/167')
|
||||
|
||||
return new GlobSync(pattern, options).found
|
||||
}
|
||||
|
||||
function GlobSync (pattern, options) {
|
||||
if (!pattern)
|
||||
throw new Error('must provide pattern')
|
||||
|
||||
if (typeof options === 'function' || arguments.length === 3)
|
||||
throw new TypeError('callback provided to sync glob\n'+
|
||||
'See: https://github.com/isaacs/node-glob/issues/167')
|
||||
|
||||
if (!(this instanceof GlobSync))
|
||||
return new GlobSync(pattern, options)
|
||||
|
||||
setopts(this, pattern, options)
|
||||
|
||||
if (this.noprocess)
|
||||
return this
|
||||
|
||||
var n = this.minimatch.set.length
|
||||
this.matches = new Array(n)
|
||||
for (var i = 0; i < n; i ++) {
|
||||
this._process(this.minimatch.set[i], i, false)
|
||||
}
|
||||
this._finish()
|
||||
}
|
||||
|
||||
GlobSync.prototype._finish = function () {
|
||||
assert(this instanceof GlobSync)
|
||||
if (this.realpath) {
|
||||
var self = this
|
||||
this.matches.forEach(function (matchset, index) {
|
||||
var set = self.matches[index] = Object.create(null)
|
||||
for (var p in matchset) {
|
||||
try {
|
||||
p = self._makeAbs(p)
|
||||
var real = rp.realpathSync(p, self.realpathCache)
|
||||
set[real] = true
|
||||
} catch (er) {
|
||||
if (er.syscall === 'stat')
|
||||
set[self._makeAbs(p)] = true
|
||||
else
|
||||
throw er
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
common.finish(this)
|
||||
}
|
||||
|
||||
|
||||
GlobSync.prototype._process = function (pattern, index, inGlobStar) {
|
||||
assert(this instanceof GlobSync)
|
||||
|
||||
// Get the first [n] parts of pattern that are all strings.
|
||||
var n = 0
|
||||
while (typeof pattern[n] === 'string') {
|
||||
n ++
|
||||
}
|
||||
// now n is the index of the first one that is *not* a string.
|
||||
|
||||
// See if there's anything else
|
||||
var prefix
|
||||
switch (n) {
|
||||
// if not, then this is rather simple
|
||||
case pattern.length:
|
||||
this._processSimple(pattern.join('/'), index)
|
||||
return
|
||||
|
||||
case 0:
|
||||
// pattern *starts* with some non-trivial item.
|
||||
// going to readdir(cwd), but not include the prefix in matches.
|
||||
prefix = null
|
||||
break
|
||||
|
||||
default:
|
||||
// pattern has some string bits in the front.
|
||||
// whatever it starts with, whether that's 'absolute' like /foo/bar,
|
||||
// or 'relative' like '../baz'
|
||||
prefix = pattern.slice(0, n).join('/')
|
||||
break
|
||||
}
|
||||
|
||||
var remain = pattern.slice(n)
|
||||
|
||||
// get the list of entries.
|
||||
var read
|
||||
if (prefix === null)
|
||||
read = '.'
|
||||
else if (isAbsolute(prefix) || isAbsolute(pattern.join('/'))) {
|
||||
if (!prefix || !isAbsolute(prefix))
|
||||
prefix = '/' + prefix
|
||||
read = prefix
|
||||
} else
|
||||
read = prefix
|
||||
|
||||
var abs = this._makeAbs(read)
|
||||
|
||||
//if ignored, skip processing
|
||||
if (childrenIgnored(this, read))
|
||||
return
|
||||
|
||||
var isGlobStar = remain[0] === minimatch.GLOBSTAR
|
||||
if (isGlobStar)
|
||||
this._processGlobStar(prefix, read, abs, remain, index, inGlobStar)
|
||||
else
|
||||
this._processReaddir(prefix, read, abs, remain, index, inGlobStar)
|
||||
}
|
||||
|
||||
|
||||
GlobSync.prototype._processReaddir = function (prefix, read, abs, remain, index, inGlobStar) {
|
||||
var entries = this._readdir(abs, inGlobStar)
|
||||
|
||||
// if the abs isn't a dir, then nothing can match!
|
||||
if (!entries)
|
||||
return
|
||||
|
||||
// It will only match dot entries if it starts with a dot, or if
|
||||
// dot is set. Stuff like @(.foo|.bar) isn't allowed.
|
||||
var pn = remain[0]
|
||||
var negate = !!this.minimatch.negate
|
||||
var rawGlob = pn._glob
|
||||
var dotOk = this.dot || rawGlob.charAt(0) === '.'
|
||||
|
||||
var matchedEntries = []
|
||||
for (var i = 0; i < entries.length; i++) {
|
||||
var e = entries[i]
|
||||
if (e.charAt(0) !== '.' || dotOk) {
|
||||
var m
|
||||
if (negate && !prefix) {
|
||||
m = !e.match(pn)
|
||||
} else {
|
||||
m = e.match(pn)
|
||||
}
|
||||
if (m)
|
||||
matchedEntries.push(e)
|
||||
}
|
||||
}
|
||||
|
||||
var len = matchedEntries.length
|
||||
// If there are no matched entries, then nothing matches.
|
||||
if (len === 0)
|
||||
return
|
||||
|
||||
// if this is the last remaining pattern bit, then no need for
|
||||
// an additional stat *unless* the user has specified mark or
|
||||
// stat explicitly. We know they exist, since readdir returned
|
||||
// them.
|
||||
|
||||
if (remain.length === 1 && !this.mark && !this.stat) {
|
||||
if (!this.matches[index])
|
||||
this.matches[index] = Object.create(null)
|
||||
|
||||
for (var i = 0; i < len; i ++) {
|
||||
var e = matchedEntries[i]
|
||||
if (prefix) {
|
||||
if (prefix.slice(-1) !== '/')
|
||||
e = prefix + '/' + e
|
||||
else
|
||||
e = prefix + e
|
||||
}
|
||||
|
||||
if (e.charAt(0) === '/' && !this.nomount) {
|
||||
e = path.join(this.root, e)
|
||||
}
|
||||
this._emitMatch(index, e)
|
||||
}
|
||||
// This was the last one, and no stats were needed
|
||||
return
|
||||
}
|
||||
|
||||
// now test all matched entries as stand-ins for that part
|
||||
// of the pattern.
|
||||
remain.shift()
|
||||
for (var i = 0; i < len; i ++) {
|
||||
var e = matchedEntries[i]
|
||||
var newPattern
|
||||
if (prefix)
|
||||
newPattern = [prefix, e]
|
||||
else
|
||||
newPattern = [e]
|
||||
this._process(newPattern.concat(remain), index, inGlobStar)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
GlobSync.prototype._emitMatch = function (index, e) {
|
||||
if (isIgnored(this, e))
|
||||
return
|
||||
|
||||
var abs = this._makeAbs(e)
|
||||
|
||||
if (this.mark)
|
||||
e = this._mark(e)
|
||||
|
||||
if (this.absolute) {
|
||||
e = abs
|
||||
}
|
||||
|
||||
if (this.matches[index][e])
|
||||
return
|
||||
|
||||
if (this.nodir) {
|
||||
var c = this.cache[abs]
|
||||
if (c === 'DIR' || Array.isArray(c))
|
||||
return
|
||||
}
|
||||
|
||||
this.matches[index][e] = true
|
||||
|
||||
if (this.stat)
|
||||
this._stat(e)
|
||||
}
|
||||
|
||||
|
||||
GlobSync.prototype._readdirInGlobStar = function (abs) {
|
||||
// follow all symlinked directories forever
|
||||
// just proceed as if this is a non-globstar situation
|
||||
if (this.follow)
|
||||
return this._readdir(abs, false)
|
||||
|
||||
var entries
|
||||
var lstat
|
||||
var stat
|
||||
try {
|
||||
lstat = this.fs.lstatSync(abs)
|
||||
} catch (er) {
|
||||
if (er.code === 'ENOENT') {
|
||||
// lstat failed, doesn't exist
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
var isSym = lstat && lstat.isSymbolicLink()
|
||||
this.symlinks[abs] = isSym
|
||||
|
||||
// If it's not a symlink or a dir, then it's definitely a regular file.
|
||||
// don't bother doing a readdir in that case.
|
||||
if (!isSym && lstat && !lstat.isDirectory())
|
||||
this.cache[abs] = 'FILE'
|
||||
else
|
||||
entries = this._readdir(abs, false)
|
||||
|
||||
return entries
|
||||
}
|
||||
|
||||
GlobSync.prototype._readdir = function (abs, inGlobStar) {
|
||||
var entries
|
||||
|
||||
if (inGlobStar && !ownProp(this.symlinks, abs))
|
||||
return this._readdirInGlobStar(abs)
|
||||
|
||||
if (ownProp(this.cache, abs)) {
|
||||
var c = this.cache[abs]
|
||||
if (!c || c === 'FILE')
|
||||
return null
|
||||
|
||||
if (Array.isArray(c))
|
||||
return c
|
||||
}
|
||||
|
||||
try {
|
||||
return this._readdirEntries(abs, this.fs.readdirSync(abs))
|
||||
} catch (er) {
|
||||
this._readdirError(abs, er)
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
GlobSync.prototype._readdirEntries = function (abs, entries) {
|
||||
// if we haven't asked to stat everything, then just
|
||||
// assume that everything in there exists, so we can avoid
|
||||
// having to stat it a second time.
|
||||
if (!this.mark && !this.stat) {
|
||||
for (var i = 0; i < entries.length; i ++) {
|
||||
var e = entries[i]
|
||||
if (abs === '/')
|
||||
e = abs + e
|
||||
else
|
||||
e = abs + '/' + e
|
||||
this.cache[e] = true
|
||||
}
|
||||
}
|
||||
|
||||
this.cache[abs] = entries
|
||||
|
||||
// mark and cache dir-ness
|
||||
return entries
|
||||
}
|
||||
|
||||
GlobSync.prototype._readdirError = function (f, er) {
|
||||
// handle errors, and cache the information
|
||||
switch (er.code) {
|
||||
case 'ENOTSUP': // https://github.com/isaacs/node-glob/issues/205
|
||||
case 'ENOTDIR': // totally normal. means it *does* exist.
|
||||
var abs = this._makeAbs(f)
|
||||
this.cache[abs] = 'FILE'
|
||||
if (abs === this.cwdAbs) {
|
||||
var error = new Error(er.code + ' invalid cwd ' + this.cwd)
|
||||
error.path = this.cwd
|
||||
error.code = er.code
|
||||
throw error
|
||||
}
|
||||
break
|
||||
|
||||
case 'ENOENT': // not terribly unusual
|
||||
case 'ELOOP':
|
||||
case 'ENAMETOOLONG':
|
||||
case 'UNKNOWN':
|
||||
this.cache[this._makeAbs(f)] = false
|
||||
break
|
||||
|
||||
default: // some unusual error. Treat as failure.
|
||||
this.cache[this._makeAbs(f)] = false
|
||||
if (this.strict)
|
||||
throw er
|
||||
if (!this.silent)
|
||||
console.error('glob error', er)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
GlobSync.prototype._processGlobStar = function (prefix, read, abs, remain, index, inGlobStar) {
|
||||
|
||||
var entries = this._readdir(abs, inGlobStar)
|
||||
|
||||
// no entries means not a dir, so it can never have matches
|
||||
// foo.txt/** doesn't match foo.txt
|
||||
if (!entries)
|
||||
return
|
||||
|
||||
// test without the globstar, and with every child both below
|
||||
// and replacing the globstar.
|
||||
var remainWithoutGlobStar = remain.slice(1)
|
||||
var gspref = prefix ? [ prefix ] : []
|
||||
var noGlobStar = gspref.concat(remainWithoutGlobStar)
|
||||
|
||||
// the noGlobStar pattern exits the inGlobStar state
|
||||
this._process(noGlobStar, index, false)
|
||||
|
||||
var len = entries.length
|
||||
var isSym = this.symlinks[abs]
|
||||
|
||||
// If it's a symlink, and we're in a globstar, then stop
|
||||
if (isSym && inGlobStar)
|
||||
return
|
||||
|
||||
for (var i = 0; i < len; i++) {
|
||||
var e = entries[i]
|
||||
if (e.charAt(0) === '.' && !this.dot)
|
||||
continue
|
||||
|
||||
// these two cases enter the inGlobStar state
|
||||
var instead = gspref.concat(entries[i], remainWithoutGlobStar)
|
||||
this._process(instead, index, true)
|
||||
|
||||
var below = gspref.concat(entries[i], remain)
|
||||
this._process(below, index, true)
|
||||
}
|
||||
}
|
||||
|
||||
GlobSync.prototype._processSimple = function (prefix, index) {
|
||||
// XXX review this. Shouldn't it be doing the mounting etc
|
||||
// before doing stat? kinda weird?
|
||||
var exists = this._stat(prefix)
|
||||
|
||||
if (!this.matches[index])
|
||||
this.matches[index] = Object.create(null)
|
||||
|
||||
// If it doesn't exist, then just mark the lack of results
|
||||
if (!exists)
|
||||
return
|
||||
|
||||
if (prefix && isAbsolute(prefix) && !this.nomount) {
|
||||
var trail = /[\/\\]$/.test(prefix)
|
||||
if (prefix.charAt(0) === '/') {
|
||||
prefix = path.join(this.root, prefix)
|
||||
} else {
|
||||
prefix = path.resolve(this.root, prefix)
|
||||
if (trail)
|
||||
prefix += '/'
|
||||
}
|
||||
}
|
||||
|
||||
if (process.platform === 'win32')
|
||||
prefix = prefix.replace(/\\/g, '/')
|
||||
|
||||
// Mark this as a match
|
||||
this._emitMatch(index, prefix)
|
||||
}
|
||||
|
||||
// Returns either 'DIR', 'FILE', or false
|
||||
GlobSync.prototype._stat = function (f) {
|
||||
var abs = this._makeAbs(f)
|
||||
var needDir = f.slice(-1) === '/'
|
||||
|
||||
if (f.length > this.maxLength)
|
||||
return false
|
||||
|
||||
if (!this.stat && ownProp(this.cache, abs)) {
|
||||
var c = this.cache[abs]
|
||||
|
||||
if (Array.isArray(c))
|
||||
c = 'DIR'
|
||||
|
||||
// It exists, but maybe not how we need it
|
||||
if (!needDir || c === 'DIR')
|
||||
return c
|
||||
|
||||
if (needDir && c === 'FILE')
|
||||
return false
|
||||
|
||||
// otherwise we have to stat, because maybe c=true
|
||||
// if we know it exists, but not what it is.
|
||||
}
|
||||
|
||||
var exists
|
||||
var stat = this.statCache[abs]
|
||||
if (!stat) {
|
||||
var lstat
|
||||
try {
|
||||
lstat = this.fs.lstatSync(abs)
|
||||
} catch (er) {
|
||||
if (er && (er.code === 'ENOENT' || er.code === 'ENOTDIR')) {
|
||||
this.statCache[abs] = false
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
if (lstat && lstat.isSymbolicLink()) {
|
||||
try {
|
||||
stat = this.fs.statSync(abs)
|
||||
} catch (er) {
|
||||
stat = lstat
|
||||
}
|
||||
} else {
|
||||
stat = lstat
|
||||
}
|
||||
}
|
||||
|
||||
this.statCache[abs] = stat
|
||||
|
||||
var c = true
|
||||
if (stat)
|
||||
c = stat.isDirectory() ? 'DIR' : 'FILE'
|
||||
|
||||
this.cache[abs] = this.cache[abs] || c
|
||||
|
||||
if (needDir && c === 'FILE')
|
||||
return false
|
||||
|
||||
return c
|
||||
}
|
||||
|
||||
GlobSync.prototype._mark = function (p) {
|
||||
return common.mark(this, p)
|
||||
}
|
||||
|
||||
GlobSync.prototype._makeAbs = function (f) {
|
||||
return common.makeAbs(this, f)
|
||||
}
|
||||
-15
@@ -1,15 +0,0 @@
|
||||
The ISC License
|
||||
|
||||
Copyright (c) Isaac Z. Schlueter, Ben Noordhuis, and Contributors
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
|
||||
IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
-143
@@ -1,143 +0,0 @@
|
||||
# graceful-fs
|
||||
|
||||
graceful-fs functions as a drop-in replacement for the fs module,
|
||||
making various improvements.
|
||||
|
||||
The improvements are meant to normalize behavior across different
|
||||
platforms and environments, and to make filesystem access more
|
||||
resilient to errors.
|
||||
|
||||
## Improvements over [fs module](https://nodejs.org/api/fs.html)
|
||||
|
||||
* Queues up `open` and `readdir` calls, and retries them once
|
||||
something closes if there is an EMFILE error from too many file
|
||||
descriptors.
|
||||
* fixes `lchmod` for Node versions prior to 0.6.2.
|
||||
* implements `fs.lutimes` if possible. Otherwise it becomes a noop.
|
||||
* ignores `EINVAL` and `EPERM` errors in `chown`, `fchown` or
|
||||
`lchown` if the user isn't root.
|
||||
* makes `lchmod` and `lchown` become noops, if not available.
|
||||
* retries reading a file if `read` results in EAGAIN error.
|
||||
|
||||
On Windows, it retries renaming a file for up to one second if `EACCESS`
|
||||
or `EPERM` error occurs, likely because antivirus software has locked
|
||||
the directory.
|
||||
|
||||
## USAGE
|
||||
|
||||
```javascript
|
||||
// use just like fs
|
||||
var fs = require('graceful-fs')
|
||||
|
||||
// now go and do stuff with it...
|
||||
fs.readFile('some-file-or-whatever', (err, data) => {
|
||||
// Do stuff here.
|
||||
})
|
||||
```
|
||||
|
||||
## Sync methods
|
||||
|
||||
This module cannot intercept or handle `EMFILE` or `ENFILE` errors from sync
|
||||
methods. If you use sync methods which open file descriptors then you are
|
||||
responsible for dealing with any errors.
|
||||
|
||||
This is a known limitation, not a bug.
|
||||
|
||||
## Global Patching
|
||||
|
||||
If you want to patch the global fs module (or any other fs-like
|
||||
module) you can do this:
|
||||
|
||||
```javascript
|
||||
// Make sure to read the caveat below.
|
||||
var realFs = require('fs')
|
||||
var gracefulFs = require('graceful-fs')
|
||||
gracefulFs.gracefulify(realFs)
|
||||
```
|
||||
|
||||
This should only ever be done at the top-level application layer, in
|
||||
order to delay on EMFILE errors from any fs-using dependencies. You
|
||||
should **not** do this in a library, because it can cause unexpected
|
||||
delays in other parts of the program.
|
||||
|
||||
## Changes
|
||||
|
||||
This module is fairly stable at this point, and used by a lot of
|
||||
things. That being said, because it implements a subtle behavior
|
||||
change in a core part of the node API, even modest changes can be
|
||||
extremely breaking, and the versioning is thus biased towards
|
||||
bumping the major when in doubt.
|
||||
|
||||
The main change between major versions has been switching between
|
||||
providing a fully-patched `fs` module vs monkey-patching the node core
|
||||
builtin, and the approach by which a non-monkey-patched `fs` was
|
||||
created.
|
||||
|
||||
The goal is to trade `EMFILE` errors for slower fs operations. So, if
|
||||
you try to open a zillion files, rather than crashing, `open`
|
||||
operations will be queued up and wait for something else to `close`.
|
||||
|
||||
There are advantages to each approach. Monkey-patching the fs means
|
||||
that no `EMFILE` errors can possibly occur anywhere in your
|
||||
application, because everything is using the same core `fs` module,
|
||||
which is patched. However, it can also obviously cause undesirable
|
||||
side-effects, especially if the module is loaded multiple times.
|
||||
|
||||
Implementing a separate-but-identical patched `fs` module is more
|
||||
surgical (and doesn't run the risk of patching multiple times), but
|
||||
also imposes the challenge of keeping in sync with the core module.
|
||||
|
||||
The current approach loads the `fs` module, and then creates a
|
||||
lookalike object that has all the same methods, except a few that are
|
||||
patched. It is safe to use in all versions of Node from 0.8 through
|
||||
7.0.
|
||||
|
||||
### v4
|
||||
|
||||
* Do not monkey-patch the fs module. This module may now be used as a
|
||||
drop-in dep, and users can opt into monkey-patching the fs builtin
|
||||
if their app requires it.
|
||||
|
||||
### v3
|
||||
|
||||
* Monkey-patch fs, because the eval approach no longer works on recent
|
||||
node.
|
||||
* fixed possible type-error throw if rename fails on windows
|
||||
* verify that we *never* get EMFILE errors
|
||||
* Ignore ENOSYS from chmod/chown
|
||||
* clarify that graceful-fs must be used as a drop-in
|
||||
|
||||
### v2.1.0
|
||||
|
||||
* Use eval rather than monkey-patching fs.
|
||||
* readdir: Always sort the results
|
||||
* win32: requeue a file if error has an OK status
|
||||
|
||||
### v2.0
|
||||
|
||||
* A return to monkey patching
|
||||
* wrap process.cwd
|
||||
|
||||
### v1.1
|
||||
|
||||
* wrap readFile
|
||||
* Wrap fs.writeFile.
|
||||
* readdir protection
|
||||
* Don't clobber the fs builtin
|
||||
* Handle fs.read EAGAIN errors by trying again
|
||||
* Expose the curOpen counter
|
||||
* No-op lchown/lchmod if not implemented
|
||||
* fs.rename patch only for win32
|
||||
* Patch fs.rename to handle AV software on Windows
|
||||
* Close #4 Chown should not fail on einval or eperm if non-root
|
||||
* Fix isaacs/fstream#1 Only wrap fs one time
|
||||
* Fix #3 Start at 1024 max files, then back off on EMFILE
|
||||
* lutimes that doens't blow up on Linux
|
||||
* A full on-rewrite using a queue instead of just swallowing the EMFILE error
|
||||
* Wrap Read/Write streams as well
|
||||
|
||||
### 1.0
|
||||
|
||||
* Update engines for node 0.6
|
||||
* Be lstat-graceful on Windows
|
||||
* first
|
||||
-23
@@ -1,23 +0,0 @@
|
||||
'use strict'
|
||||
|
||||
module.exports = clone
|
||||
|
||||
var getPrototypeOf = Object.getPrototypeOf || function (obj) {
|
||||
return obj.__proto__
|
||||
}
|
||||
|
||||
function clone (obj) {
|
||||
if (obj === null || typeof obj !== 'object')
|
||||
return obj
|
||||
|
||||
if (obj instanceof Object)
|
||||
var copy = { __proto__: getPrototypeOf(obj) }
|
||||
else
|
||||
var copy = Object.create(null)
|
||||
|
||||
Object.getOwnPropertyNames(obj).forEach(function (key) {
|
||||
Object.defineProperty(copy, key, Object.getOwnPropertyDescriptor(obj, key))
|
||||
})
|
||||
|
||||
return copy
|
||||
}
|
||||
-429
@@ -1,429 +0,0 @@
|
||||
var fs = require('fs')
|
||||
var polyfills = require('./polyfills.js')
|
||||
var legacy = require('./legacy-streams.js')
|
||||
var clone = require('./clone.js')
|
||||
|
||||
var util = require('util')
|
||||
|
||||
/* istanbul ignore next - node 0.x polyfill */
|
||||
var gracefulQueue
|
||||
var previousSymbol
|
||||
|
||||
/* istanbul ignore else - node 0.x polyfill */
|
||||
if (typeof Symbol === 'function' && typeof Symbol.for === 'function') {
|
||||
gracefulQueue = Symbol.for('graceful-fs.queue')
|
||||
// This is used in testing by future versions
|
||||
previousSymbol = Symbol.for('graceful-fs.previous')
|
||||
} else {
|
||||
gracefulQueue = '___graceful-fs.queue'
|
||||
previousSymbol = '___graceful-fs.previous'
|
||||
}
|
||||
|
||||
function noop () {}
|
||||
|
||||
function publishQueue(context, queue) {
|
||||
Object.defineProperty(context, gracefulQueue, {
|
||||
get: function() {
|
||||
return queue
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
var debug = noop
|
||||
if (util.debuglog)
|
||||
debug = util.debuglog('gfs4')
|
||||
else if (/\bgfs4\b/i.test(process.env.NODE_DEBUG || ''))
|
||||
debug = function() {
|
||||
var m = util.format.apply(util, arguments)
|
||||
m = 'GFS4: ' + m.split(/\n/).join('\nGFS4: ')
|
||||
console.error(m)
|
||||
}
|
||||
|
||||
// Once time initialization
|
||||
if (!fs[gracefulQueue]) {
|
||||
// This queue can be shared by multiple loaded instances
|
||||
var queue = global[gracefulQueue] || []
|
||||
publishQueue(fs, queue)
|
||||
|
||||
// Patch fs.close/closeSync to shared queue version, because we need
|
||||
// to retry() whenever a close happens *anywhere* in the program.
|
||||
// This is essential when multiple graceful-fs instances are
|
||||
// in play at the same time.
|
||||
fs.close = (function (fs$close) {
|
||||
function close (fd, cb) {
|
||||
return fs$close.call(fs, fd, function (err) {
|
||||
// This function uses the graceful-fs shared queue
|
||||
if (!err) {
|
||||
resetQueue()
|
||||
}
|
||||
|
||||
if (typeof cb === 'function')
|
||||
cb.apply(this, arguments)
|
||||
})
|
||||
}
|
||||
|
||||
Object.defineProperty(close, previousSymbol, {
|
||||
value: fs$close
|
||||
})
|
||||
return close
|
||||
})(fs.close)
|
||||
|
||||
fs.closeSync = (function (fs$closeSync) {
|
||||
function closeSync (fd) {
|
||||
// This function uses the graceful-fs shared queue
|
||||
fs$closeSync.apply(fs, arguments)
|
||||
resetQueue()
|
||||
}
|
||||
|
||||
Object.defineProperty(closeSync, previousSymbol, {
|
||||
value: fs$closeSync
|
||||
})
|
||||
return closeSync
|
||||
})(fs.closeSync)
|
||||
|
||||
if (/\bgfs4\b/i.test(process.env.NODE_DEBUG || '')) {
|
||||
process.on('exit', function() {
|
||||
debug(fs[gracefulQueue])
|
||||
require('assert').equal(fs[gracefulQueue].length, 0)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
if (!global[gracefulQueue]) {
|
||||
publishQueue(global, fs[gracefulQueue]);
|
||||
}
|
||||
|
||||
module.exports = patch(clone(fs))
|
||||
if (process.env.TEST_GRACEFUL_FS_GLOBAL_PATCH && !fs.__patched) {
|
||||
module.exports = patch(fs)
|
||||
fs.__patched = true;
|
||||
}
|
||||
|
||||
function patch (fs) {
|
||||
// Everything that references the open() function needs to be in here
|
||||
polyfills(fs)
|
||||
fs.gracefulify = patch
|
||||
|
||||
fs.createReadStream = createReadStream
|
||||
fs.createWriteStream = createWriteStream
|
||||
var fs$readFile = fs.readFile
|
||||
fs.readFile = readFile
|
||||
function readFile (path, options, cb) {
|
||||
if (typeof options === 'function')
|
||||
cb = options, options = null
|
||||
|
||||
return go$readFile(path, options, cb)
|
||||
|
||||
function go$readFile (path, options, cb, startTime) {
|
||||
return fs$readFile(path, options, function (err) {
|
||||
if (err && (err.code === 'EMFILE' || err.code === 'ENFILE'))
|
||||
enqueue([go$readFile, [path, options, cb], err, startTime || Date.now(), Date.now()])
|
||||
else {
|
||||
if (typeof cb === 'function')
|
||||
cb.apply(this, arguments)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
var fs$writeFile = fs.writeFile
|
||||
fs.writeFile = writeFile
|
||||
function writeFile (path, data, options, cb) {
|
||||
if (typeof options === 'function')
|
||||
cb = options, options = null
|
||||
|
||||
return go$writeFile(path, data, options, cb)
|
||||
|
||||
function go$writeFile (path, data, options, cb, startTime) {
|
||||
return fs$writeFile(path, data, options, function (err) {
|
||||
if (err && (err.code === 'EMFILE' || err.code === 'ENFILE'))
|
||||
enqueue([go$writeFile, [path, data, options, cb], err, startTime || Date.now(), Date.now()])
|
||||
else {
|
||||
if (typeof cb === 'function')
|
||||
cb.apply(this, arguments)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
var fs$appendFile = fs.appendFile
|
||||
if (fs$appendFile)
|
||||
fs.appendFile = appendFile
|
||||
function appendFile (path, data, options, cb) {
|
||||
if (typeof options === 'function')
|
||||
cb = options, options = null
|
||||
|
||||
return go$appendFile(path, data, options, cb)
|
||||
|
||||
function go$appendFile (path, data, options, cb, startTime) {
|
||||
return fs$appendFile(path, data, options, function (err) {
|
||||
if (err && (err.code === 'EMFILE' || err.code === 'ENFILE'))
|
||||
enqueue([go$appendFile, [path, data, options, cb], err, startTime || Date.now(), Date.now()])
|
||||
else {
|
||||
if (typeof cb === 'function')
|
||||
cb.apply(this, arguments)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
var fs$copyFile = fs.copyFile
|
||||
if (fs$copyFile)
|
||||
fs.copyFile = copyFile
|
||||
function copyFile (src, dest, flags, cb) {
|
||||
if (typeof flags === 'function') {
|
||||
cb = flags
|
||||
flags = 0
|
||||
}
|
||||
return go$copyFile(src, dest, flags, cb)
|
||||
|
||||
function go$copyFile (src, dest, flags, cb, startTime) {
|
||||
return fs$copyFile(src, dest, flags, function (err) {
|
||||
if (err && (err.code === 'EMFILE' || err.code === 'ENFILE'))
|
||||
enqueue([go$copyFile, [src, dest, flags, cb], err, startTime || Date.now(), Date.now()])
|
||||
else {
|
||||
if (typeof cb === 'function')
|
||||
cb.apply(this, arguments)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
var fs$readdir = fs.readdir
|
||||
fs.readdir = readdir
|
||||
function readdir (path, options, cb) {
|
||||
if (typeof options === 'function')
|
||||
cb = options, options = null
|
||||
|
||||
return go$readdir(path, options, cb)
|
||||
|
||||
function go$readdir (path, options, cb, startTime) {
|
||||
return fs$readdir(path, options, function (err, files) {
|
||||
if (err && (err.code === 'EMFILE' || err.code === 'ENFILE'))
|
||||
enqueue([go$readdir, [path, options, cb], err, startTime || Date.now(), Date.now()])
|
||||
else {
|
||||
if (files && files.sort)
|
||||
files.sort()
|
||||
|
||||
if (typeof cb === 'function')
|
||||
cb.call(this, err, files)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
if (process.version.substr(0, 4) === 'v0.8') {
|
||||
var legStreams = legacy(fs)
|
||||
ReadStream = legStreams.ReadStream
|
||||
WriteStream = legStreams.WriteStream
|
||||
}
|
||||
|
||||
var fs$ReadStream = fs.ReadStream
|
||||
if (fs$ReadStream) {
|
||||
ReadStream.prototype = Object.create(fs$ReadStream.prototype)
|
||||
ReadStream.prototype.open = ReadStream$open
|
||||
}
|
||||
|
||||
var fs$WriteStream = fs.WriteStream
|
||||
if (fs$WriteStream) {
|
||||
WriteStream.prototype = Object.create(fs$WriteStream.prototype)
|
||||
WriteStream.prototype.open = WriteStream$open
|
||||
}
|
||||
|
||||
Object.defineProperty(fs, 'ReadStream', {
|
||||
get: function () {
|
||||
return ReadStream
|
||||
},
|
||||
set: function (val) {
|
||||
ReadStream = val
|
||||
},
|
||||
enumerable: true,
|
||||
configurable: true
|
||||
})
|
||||
Object.defineProperty(fs, 'WriteStream', {
|
||||
get: function () {
|
||||
return WriteStream
|
||||
},
|
||||
set: function (val) {
|
||||
WriteStream = val
|
||||
},
|
||||
enumerable: true,
|
||||
configurable: true
|
||||
})
|
||||
|
||||
// legacy names
|
||||
var FileReadStream = ReadStream
|
||||
Object.defineProperty(fs, 'FileReadStream', {
|
||||
get: function () {
|
||||
return FileReadStream
|
||||
},
|
||||
set: function (val) {
|
||||
FileReadStream = val
|
||||
},
|
||||
enumerable: true,
|
||||
configurable: true
|
||||
})
|
||||
var FileWriteStream = WriteStream
|
||||
Object.defineProperty(fs, 'FileWriteStream', {
|
||||
get: function () {
|
||||
return FileWriteStream
|
||||
},
|
||||
set: function (val) {
|
||||
FileWriteStream = val
|
||||
},
|
||||
enumerable: true,
|
||||
configurable: true
|
||||
})
|
||||
|
||||
function ReadStream (path, options) {
|
||||
if (this instanceof ReadStream)
|
||||
return fs$ReadStream.apply(this, arguments), this
|
||||
else
|
||||
return ReadStream.apply(Object.create(ReadStream.prototype), arguments)
|
||||
}
|
||||
|
||||
function ReadStream$open () {
|
||||
var that = this
|
||||
open(that.path, that.flags, that.mode, function (err, fd) {
|
||||
if (err) {
|
||||
if (that.autoClose)
|
||||
that.destroy()
|
||||
|
||||
that.emit('error', err)
|
||||
} else {
|
||||
that.fd = fd
|
||||
that.emit('open', fd)
|
||||
that.read()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function WriteStream (path, options) {
|
||||
if (this instanceof WriteStream)
|
||||
return fs$WriteStream.apply(this, arguments), this
|
||||
else
|
||||
return WriteStream.apply(Object.create(WriteStream.prototype), arguments)
|
||||
}
|
||||
|
||||
function WriteStream$open () {
|
||||
var that = this
|
||||
open(that.path, that.flags, that.mode, function (err, fd) {
|
||||
if (err) {
|
||||
that.destroy()
|
||||
that.emit('error', err)
|
||||
} else {
|
||||
that.fd = fd
|
||||
that.emit('open', fd)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function createReadStream (path, options) {
|
||||
return new fs.ReadStream(path, options)
|
||||
}
|
||||
|
||||
function createWriteStream (path, options) {
|
||||
return new fs.WriteStream(path, options)
|
||||
}
|
||||
|
||||
var fs$open = fs.open
|
||||
fs.open = open
|
||||
function open (path, flags, mode, cb) {
|
||||
if (typeof mode === 'function')
|
||||
cb = mode, mode = null
|
||||
|
||||
return go$open(path, flags, mode, cb)
|
||||
|
||||
function go$open (path, flags, mode, cb, startTime) {
|
||||
return fs$open(path, flags, mode, function (err, fd) {
|
||||
if (err && (err.code === 'EMFILE' || err.code === 'ENFILE'))
|
||||
enqueue([go$open, [path, flags, mode, cb], err, startTime || Date.now(), Date.now()])
|
||||
else {
|
||||
if (typeof cb === 'function')
|
||||
cb.apply(this, arguments)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return fs
|
||||
}
|
||||
|
||||
function enqueue (elem) {
|
||||
debug('ENQUEUE', elem[0].name, elem[1])
|
||||
fs[gracefulQueue].push(elem)
|
||||
retry()
|
||||
}
|
||||
|
||||
// keep track of the timeout between retry() calls
|
||||
var retryTimer
|
||||
|
||||
// reset the startTime and lastTime to now
|
||||
// this resets the start of the 60 second overall timeout as well as the
|
||||
// delay between attempts so that we'll retry these jobs sooner
|
||||
function resetQueue () {
|
||||
var now = Date.now()
|
||||
for (var i = 0; i < fs[gracefulQueue].length; ++i) {
|
||||
// entries that are only a length of 2 are from an older version, don't
|
||||
// bother modifying those since they'll be retried anyway.
|
||||
if (fs[gracefulQueue][i].length > 2) {
|
||||
fs[gracefulQueue][i][3] = now // startTime
|
||||
fs[gracefulQueue][i][4] = now // lastTime
|
||||
}
|
||||
}
|
||||
// call retry to make sure we're actively processing the queue
|
||||
retry()
|
||||
}
|
||||
|
||||
function retry () {
|
||||
// clear the timer and remove it to help prevent unintended concurrency
|
||||
clearTimeout(retryTimer)
|
||||
retryTimer = undefined
|
||||
|
||||
if (fs[gracefulQueue].length === 0)
|
||||
return
|
||||
|
||||
var elem = fs[gracefulQueue].shift()
|
||||
var fn = elem[0]
|
||||
var args = elem[1]
|
||||
// these items may be unset if they were added by an older graceful-fs
|
||||
var err = elem[2]
|
||||
var startTime = elem[3]
|
||||
var lastTime = elem[4]
|
||||
|
||||
// if we don't have a startTime we have no way of knowing if we've waited
|
||||
// long enough, so go ahead and retry this item now
|
||||
if (startTime === undefined) {
|
||||
debug('RETRY', fn.name, args)
|
||||
fn.apply(null, args)
|
||||
} else if (Date.now() - startTime >= 60000) {
|
||||
// it's been more than 60 seconds total, bail now
|
||||
debug('TIMEOUT', fn.name, args)
|
||||
var cb = args.pop()
|
||||
if (typeof cb === 'function')
|
||||
cb.call(null, err)
|
||||
} else {
|
||||
// the amount of time between the last attempt and right now
|
||||
var sinceAttempt = Date.now() - lastTime
|
||||
// the amount of time between when we first tried, and when we last tried
|
||||
// rounded up to at least 1
|
||||
var sinceStart = Math.max(lastTime - startTime, 1)
|
||||
// backoff. wait longer than the total time we've been retrying, but only
|
||||
// up to a maximum of 100ms
|
||||
var desiredDelay = Math.min(sinceStart * 1.2, 100)
|
||||
// it's been long enough since the last retry, do it again
|
||||
if (sinceAttempt >= desiredDelay) {
|
||||
debug('RETRY', fn.name, args)
|
||||
fn.apply(null, args.concat([startTime]))
|
||||
} else {
|
||||
// if we can't do this job yet, push it to the end of the queue
|
||||
// and let the next iteration check again
|
||||
fs[gracefulQueue].push(elem)
|
||||
}
|
||||
}
|
||||
|
||||
// schedule our next run if one isn't already scheduled
|
||||
if (retryTimer === undefined) {
|
||||
retryTimer = setTimeout(retry, 0)
|
||||
}
|
||||
}
|
||||
-118
@@ -1,118 +0,0 @@
|
||||
var Stream = require('stream').Stream
|
||||
|
||||
module.exports = legacy
|
||||
|
||||
function legacy (fs) {
|
||||
return {
|
||||
ReadStream: ReadStream,
|
||||
WriteStream: WriteStream
|
||||
}
|
||||
|
||||
function ReadStream (path, options) {
|
||||
if (!(this instanceof ReadStream)) return new ReadStream(path, options);
|
||||
|
||||
Stream.call(this);
|
||||
|
||||
var self = this;
|
||||
|
||||
this.path = path;
|
||||
this.fd = null;
|
||||
this.readable = true;
|
||||
this.paused = false;
|
||||
|
||||
this.flags = 'r';
|
||||
this.mode = 438; /*=0666*/
|
||||
this.bufferSize = 64 * 1024;
|
||||
|
||||
options = options || {};
|
||||
|
||||
// Mixin options into this
|
||||
var keys = Object.keys(options);
|
||||
for (var index = 0, length = keys.length; index < length; index++) {
|
||||
var key = keys[index];
|
||||
this[key] = options[key];
|
||||
}
|
||||
|
||||
if (this.encoding) this.setEncoding(this.encoding);
|
||||
|
||||
if (this.start !== undefined) {
|
||||
if ('number' !== typeof this.start) {
|
||||
throw TypeError('start must be a Number');
|
||||
}
|
||||
if (this.end === undefined) {
|
||||
this.end = Infinity;
|
||||
} else if ('number' !== typeof this.end) {
|
||||
throw TypeError('end must be a Number');
|
||||
}
|
||||
|
||||
if (this.start > this.end) {
|
||||
throw new Error('start must be <= end');
|
||||
}
|
||||
|
||||
this.pos = this.start;
|
||||
}
|
||||
|
||||
if (this.fd !== null) {
|
||||
process.nextTick(function() {
|
||||
self._read();
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
fs.open(this.path, this.flags, this.mode, function (err, fd) {
|
||||
if (err) {
|
||||
self.emit('error', err);
|
||||
self.readable = false;
|
||||
return;
|
||||
}
|
||||
|
||||
self.fd = fd;
|
||||
self.emit('open', fd);
|
||||
self._read();
|
||||
})
|
||||
}
|
||||
|
||||
function WriteStream (path, options) {
|
||||
if (!(this instanceof WriteStream)) return new WriteStream(path, options);
|
||||
|
||||
Stream.call(this);
|
||||
|
||||
this.path = path;
|
||||
this.fd = null;
|
||||
this.writable = true;
|
||||
|
||||
this.flags = 'w';
|
||||
this.encoding = 'binary';
|
||||
this.mode = 438; /*=0666*/
|
||||
this.bytesWritten = 0;
|
||||
|
||||
options = options || {};
|
||||
|
||||
// Mixin options into this
|
||||
var keys = Object.keys(options);
|
||||
for (var index = 0, length = keys.length; index < length; index++) {
|
||||
var key = keys[index];
|
||||
this[key] = options[key];
|
||||
}
|
||||
|
||||
if (this.start !== undefined) {
|
||||
if ('number' !== typeof this.start) {
|
||||
throw TypeError('start must be a Number');
|
||||
}
|
||||
if (this.start < 0) {
|
||||
throw new Error('start must be >= zero');
|
||||
}
|
||||
|
||||
this.pos = this.start;
|
||||
}
|
||||
|
||||
this.busy = false;
|
||||
this._queue = [];
|
||||
|
||||
if (this.fd === null) {
|
||||
this._open = fs.open;
|
||||
this._queue.push([this._open, this.path, this.flags, this.mode, undefined]);
|
||||
this.flush();
|
||||
}
|
||||
}
|
||||
}
|
||||
-50
@@ -1,50 +0,0 @@
|
||||
{
|
||||
"name": "graceful-fs",
|
||||
"description": "A drop-in replacement for fs, making various improvements.",
|
||||
"version": "4.2.8",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/isaacs/node-graceful-fs"
|
||||
},
|
||||
"main": "graceful-fs.js",
|
||||
"directories": {
|
||||
"test": "test"
|
||||
},
|
||||
"scripts": {
|
||||
"preversion": "npm test",
|
||||
"postversion": "npm publish",
|
||||
"postpublish": "git push origin --follow-tags",
|
||||
"test": "nyc --silent node test.js | tap -c -",
|
||||
"posttest": "nyc report"
|
||||
},
|
||||
"keywords": [
|
||||
"fs",
|
||||
"module",
|
||||
"reading",
|
||||
"retry",
|
||||
"retries",
|
||||
"queue",
|
||||
"error",
|
||||
"errors",
|
||||
"handling",
|
||||
"EMFILE",
|
||||
"EAGAIN",
|
||||
"EINVAL",
|
||||
"EPERM",
|
||||
"EACCESS"
|
||||
],
|
||||
"license": "ISC",
|
||||
"devDependencies": {
|
||||
"import-fresh": "^2.0.0",
|
||||
"mkdirp": "^0.5.0",
|
||||
"rimraf": "^2.2.8",
|
||||
"tap": "^12.7.0"
|
||||
},
|
||||
"files": [
|
||||
"fs.js",
|
||||
"graceful-fs.js",
|
||||
"legacy-streams.js",
|
||||
"polyfills.js",
|
||||
"clone.js"
|
||||
]
|
||||
}
|
||||
-346
@@ -1,346 +0,0 @@
|
||||
var constants = require('constants')
|
||||
|
||||
var origCwd = process.cwd
|
||||
var cwd = null
|
||||
|
||||
var platform = process.env.GRACEFUL_FS_PLATFORM || process.platform
|
||||
|
||||
process.cwd = function() {
|
||||
if (!cwd)
|
||||
cwd = origCwd.call(process)
|
||||
return cwd
|
||||
}
|
||||
try {
|
||||
process.cwd()
|
||||
} catch (er) {}
|
||||
|
||||
// This check is needed until node.js 12 is required
|
||||
if (typeof process.chdir === 'function') {
|
||||
var chdir = process.chdir
|
||||
process.chdir = function (d) {
|
||||
cwd = null
|
||||
chdir.call(process, d)
|
||||
}
|
||||
if (Object.setPrototypeOf) Object.setPrototypeOf(process.chdir, chdir)
|
||||
}
|
||||
|
||||
module.exports = patch
|
||||
|
||||
function patch (fs) {
|
||||
// (re-)implement some things that are known busted or missing.
|
||||
|
||||
// lchmod, broken prior to 0.6.2
|
||||
// back-port the fix here.
|
||||
if (constants.hasOwnProperty('O_SYMLINK') &&
|
||||
process.version.match(/^v0\.6\.[0-2]|^v0\.5\./)) {
|
||||
patchLchmod(fs)
|
||||
}
|
||||
|
||||
// lutimes implementation, or no-op
|
||||
if (!fs.lutimes) {
|
||||
patchLutimes(fs)
|
||||
}
|
||||
|
||||
// https://github.com/isaacs/node-graceful-fs/issues/4
|
||||
// Chown should not fail on einval or eperm if non-root.
|
||||
// It should not fail on enosys ever, as this just indicates
|
||||
// that a fs doesn't support the intended operation.
|
||||
|
||||
fs.chown = chownFix(fs.chown)
|
||||
fs.fchown = chownFix(fs.fchown)
|
||||
fs.lchown = chownFix(fs.lchown)
|
||||
|
||||
fs.chmod = chmodFix(fs.chmod)
|
||||
fs.fchmod = chmodFix(fs.fchmod)
|
||||
fs.lchmod = chmodFix(fs.lchmod)
|
||||
|
||||
fs.chownSync = chownFixSync(fs.chownSync)
|
||||
fs.fchownSync = chownFixSync(fs.fchownSync)
|
||||
fs.lchownSync = chownFixSync(fs.lchownSync)
|
||||
|
||||
fs.chmodSync = chmodFixSync(fs.chmodSync)
|
||||
fs.fchmodSync = chmodFixSync(fs.fchmodSync)
|
||||
fs.lchmodSync = chmodFixSync(fs.lchmodSync)
|
||||
|
||||
fs.stat = statFix(fs.stat)
|
||||
fs.fstat = statFix(fs.fstat)
|
||||
fs.lstat = statFix(fs.lstat)
|
||||
|
||||
fs.statSync = statFixSync(fs.statSync)
|
||||
fs.fstatSync = statFixSync(fs.fstatSync)
|
||||
fs.lstatSync = statFixSync(fs.lstatSync)
|
||||
|
||||
// if lchmod/lchown do not exist, then make them no-ops
|
||||
if (!fs.lchmod) {
|
||||
fs.lchmod = function (path, mode, cb) {
|
||||
if (cb) process.nextTick(cb)
|
||||
}
|
||||
fs.lchmodSync = function () {}
|
||||
}
|
||||
if (!fs.lchown) {
|
||||
fs.lchown = function (path, uid, gid, cb) {
|
||||
if (cb) process.nextTick(cb)
|
||||
}
|
||||
fs.lchownSync = function () {}
|
||||
}
|
||||
|
||||
// on Windows, A/V software can lock the directory, causing this
|
||||
// to fail with an EACCES or EPERM if the directory contains newly
|
||||
// created files. Try again on failure, for up to 60 seconds.
|
||||
|
||||
// Set the timeout this long because some Windows Anti-Virus, such as Parity
|
||||
// bit9, may lock files for up to a minute, causing npm package install
|
||||
// failures. Also, take care to yield the scheduler. Windows scheduling gives
|
||||
// CPU to a busy looping process, which can cause the program causing the lock
|
||||
// contention to be starved of CPU by node, so the contention doesn't resolve.
|
||||
if (platform === "win32") {
|
||||
fs.rename = (function (fs$rename) { return function (from, to, cb) {
|
||||
var start = Date.now()
|
||||
var backoff = 0;
|
||||
fs$rename(from, to, function CB (er) {
|
||||
if (er
|
||||
&& (er.code === "EACCES" || er.code === "EPERM")
|
||||
&& Date.now() - start < 60000) {
|
||||
setTimeout(function() {
|
||||
fs.stat(to, function (stater, st) {
|
||||
if (stater && stater.code === "ENOENT")
|
||||
fs$rename(from, to, CB);
|
||||
else
|
||||
cb(er)
|
||||
})
|
||||
}, backoff)
|
||||
if (backoff < 100)
|
||||
backoff += 10;
|
||||
return;
|
||||
}
|
||||
if (cb) cb(er)
|
||||
})
|
||||
}})(fs.rename)
|
||||
}
|
||||
|
||||
// if read() returns EAGAIN, then just try it again.
|
||||
fs.read = (function (fs$read) {
|
||||
function read (fd, buffer, offset, length, position, callback_) {
|
||||
var callback
|
||||
if (callback_ && typeof callback_ === 'function') {
|
||||
var eagCounter = 0
|
||||
callback = function (er, _, __) {
|
||||
if (er && er.code === 'EAGAIN' && eagCounter < 10) {
|
||||
eagCounter ++
|
||||
return fs$read.call(fs, fd, buffer, offset, length, position, callback)
|
||||
}
|
||||
callback_.apply(this, arguments)
|
||||
}
|
||||
}
|
||||
return fs$read.call(fs, fd, buffer, offset, length, position, callback)
|
||||
}
|
||||
|
||||
// This ensures `util.promisify` works as it does for native `fs.read`.
|
||||
if (Object.setPrototypeOf) Object.setPrototypeOf(read, fs$read)
|
||||
return read
|
||||
})(fs.read)
|
||||
|
||||
fs.readSync = (function (fs$readSync) { return function (fd, buffer, offset, length, position) {
|
||||
var eagCounter = 0
|
||||
while (true) {
|
||||
try {
|
||||
return fs$readSync.call(fs, fd, buffer, offset, length, position)
|
||||
} catch (er) {
|
||||
if (er.code === 'EAGAIN' && eagCounter < 10) {
|
||||
eagCounter ++
|
||||
continue
|
||||
}
|
||||
throw er
|
||||
}
|
||||
}
|
||||
}})(fs.readSync)
|
||||
|
||||
function patchLchmod (fs) {
|
||||
fs.lchmod = function (path, mode, callback) {
|
||||
fs.open( path
|
||||
, constants.O_WRONLY | constants.O_SYMLINK
|
||||
, mode
|
||||
, function (err, fd) {
|
||||
if (err) {
|
||||
if (callback) callback(err)
|
||||
return
|
||||
}
|
||||
// prefer to return the chmod error, if one occurs,
|
||||
// but still try to close, and report closing errors if they occur.
|
||||
fs.fchmod(fd, mode, function (err) {
|
||||
fs.close(fd, function(err2) {
|
||||
if (callback) callback(err || err2)
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
fs.lchmodSync = function (path, mode) {
|
||||
var fd = fs.openSync(path, constants.O_WRONLY | constants.O_SYMLINK, mode)
|
||||
|
||||
// prefer to return the chmod error, if one occurs,
|
||||
// but still try to close, and report closing errors if they occur.
|
||||
var threw = true
|
||||
var ret
|
||||
try {
|
||||
ret = fs.fchmodSync(fd, mode)
|
||||
threw = false
|
||||
} finally {
|
||||
if (threw) {
|
||||
try {
|
||||
fs.closeSync(fd)
|
||||
} catch (er) {}
|
||||
} else {
|
||||
fs.closeSync(fd)
|
||||
}
|
||||
}
|
||||
return ret
|
||||
}
|
||||
}
|
||||
|
||||
function patchLutimes (fs) {
|
||||
if (constants.hasOwnProperty("O_SYMLINK")) {
|
||||
fs.lutimes = function (path, at, mt, cb) {
|
||||
fs.open(path, constants.O_SYMLINK, function (er, fd) {
|
||||
if (er) {
|
||||
if (cb) cb(er)
|
||||
return
|
||||
}
|
||||
fs.futimes(fd, at, mt, function (er) {
|
||||
fs.close(fd, function (er2) {
|
||||
if (cb) cb(er || er2)
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
fs.lutimesSync = function (path, at, mt) {
|
||||
var fd = fs.openSync(path, constants.O_SYMLINK)
|
||||
var ret
|
||||
var threw = true
|
||||
try {
|
||||
ret = fs.futimesSync(fd, at, mt)
|
||||
threw = false
|
||||
} finally {
|
||||
if (threw) {
|
||||
try {
|
||||
fs.closeSync(fd)
|
||||
} catch (er) {}
|
||||
} else {
|
||||
fs.closeSync(fd)
|
||||
}
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
} else {
|
||||
fs.lutimes = function (_a, _b, _c, cb) { if (cb) process.nextTick(cb) }
|
||||
fs.lutimesSync = function () {}
|
||||
}
|
||||
}
|
||||
|
||||
function chmodFix (orig) {
|
||||
if (!orig) return orig
|
||||
return function (target, mode, cb) {
|
||||
return orig.call(fs, target, mode, function (er) {
|
||||
if (chownErOk(er)) er = null
|
||||
if (cb) cb.apply(this, arguments)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
function chmodFixSync (orig) {
|
||||
if (!orig) return orig
|
||||
return function (target, mode) {
|
||||
try {
|
||||
return orig.call(fs, target, mode)
|
||||
} catch (er) {
|
||||
if (!chownErOk(er)) throw er
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function chownFix (orig) {
|
||||
if (!orig) return orig
|
||||
return function (target, uid, gid, cb) {
|
||||
return orig.call(fs, target, uid, gid, function (er) {
|
||||
if (chownErOk(er)) er = null
|
||||
if (cb) cb.apply(this, arguments)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
function chownFixSync (orig) {
|
||||
if (!orig) return orig
|
||||
return function (target, uid, gid) {
|
||||
try {
|
||||
return orig.call(fs, target, uid, gid)
|
||||
} catch (er) {
|
||||
if (!chownErOk(er)) throw er
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function statFix (orig) {
|
||||
if (!orig) return orig
|
||||
// Older versions of Node erroneously returned signed integers for
|
||||
// uid + gid.
|
||||
return function (target, options, cb) {
|
||||
if (typeof options === 'function') {
|
||||
cb = options
|
||||
options = null
|
||||
}
|
||||
function callback (er, stats) {
|
||||
if (stats) {
|
||||
if (stats.uid < 0) stats.uid += 0x100000000
|
||||
if (stats.gid < 0) stats.gid += 0x100000000
|
||||
}
|
||||
if (cb) cb.apply(this, arguments)
|
||||
}
|
||||
return options ? orig.call(fs, target, options, callback)
|
||||
: orig.call(fs, target, callback)
|
||||
}
|
||||
}
|
||||
|
||||
function statFixSync (orig) {
|
||||
if (!orig) return orig
|
||||
// Older versions of Node erroneously returned signed integers for
|
||||
// uid + gid.
|
||||
return function (target, options) {
|
||||
var stats = options ? orig.call(fs, target, options)
|
||||
: orig.call(fs, target)
|
||||
if (stats.uid < 0) stats.uid += 0x100000000
|
||||
if (stats.gid < 0) stats.gid += 0x100000000
|
||||
return stats;
|
||||
}
|
||||
}
|
||||
|
||||
// ENOSYS means that the fs doesn't support the op. Just ignore
|
||||
// that, because it doesn't matter.
|
||||
//
|
||||
// if there's no getuid, or if getuid() is something other
|
||||
// than 0, and the error is EINVAL or EPERM, then just ignore
|
||||
// it.
|
||||
//
|
||||
// This specific case is a silent failure in cp, install, tar,
|
||||
// and most other unix tools that manage permissions.
|
||||
//
|
||||
// When running as root, or if other types of errors are
|
||||
// encountered, then it's strict.
|
||||
function chownErOk (er) {
|
||||
if (!er)
|
||||
return true
|
||||
|
||||
if (er.code === "ENOSYS")
|
||||
return true
|
||||
|
||||
var nonroot = !process.getuid || process.getuid() !== 0
|
||||
if (nonroot) {
|
||||
if (er.code === "EINVAL" || er.code === "EPERM")
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
}
|
||||
-15
@@ -1,15 +0,0 @@
|
||||
The ISC License
|
||||
|
||||
Copyright (c) Isaac Z. Schlueter
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
|
||||
IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
-37
@@ -1,37 +0,0 @@
|
||||
# inflight
|
||||
|
||||
Add callbacks to requests in flight to avoid async duplication
|
||||
|
||||
## USAGE
|
||||
|
||||
```javascript
|
||||
var inflight = require('inflight')
|
||||
|
||||
// some request that does some stuff
|
||||
function req(key, callback) {
|
||||
// key is any random string. like a url or filename or whatever.
|
||||
//
|
||||
// will return either a falsey value, indicating that the
|
||||
// request for this key is already in flight, or a new callback
|
||||
// which when called will call all callbacks passed to inflightk
|
||||
// with the same key
|
||||
callback = inflight(key, callback)
|
||||
|
||||
// If we got a falsey value back, then there's already a req going
|
||||
if (!callback) return
|
||||
|
||||
// this is where you'd fetch the url or whatever
|
||||
// callback is also once()-ified, so it can safely be assigned
|
||||
// to multiple events etc. First call wins.
|
||||
setTimeout(function() {
|
||||
callback(null, key)
|
||||
}, 100)
|
||||
}
|
||||
|
||||
// only assigns a single setTimeout
|
||||
// when it dings, all cbs get called
|
||||
req('foo', cb1)
|
||||
req('foo', cb2)
|
||||
req('foo', cb3)
|
||||
req('foo', cb4)
|
||||
```
|
||||
-54
@@ -1,54 +0,0 @@
|
||||
var wrappy = require('wrappy')
|
||||
var reqs = Object.create(null)
|
||||
var once = require('once')
|
||||
|
||||
module.exports = wrappy(inflight)
|
||||
|
||||
function inflight (key, cb) {
|
||||
if (reqs[key]) {
|
||||
reqs[key].push(cb)
|
||||
return null
|
||||
} else {
|
||||
reqs[key] = [cb]
|
||||
return makeres(key)
|
||||
}
|
||||
}
|
||||
|
||||
function makeres (key) {
|
||||
return once(function RES () {
|
||||
var cbs = reqs[key]
|
||||
var len = cbs.length
|
||||
var args = slice(arguments)
|
||||
|
||||
// XXX It's somewhat ambiguous whether a new callback added in this
|
||||
// pass should be queued for later execution if something in the
|
||||
// list of callbacks throws, or if it should just be discarded.
|
||||
// However, it's such an edge case that it hardly matters, and either
|
||||
// choice is likely as surprising as the other.
|
||||
// As it happens, we do go ahead and schedule it for later execution.
|
||||
try {
|
||||
for (var i = 0; i < len; i++) {
|
||||
cbs[i].apply(null, args)
|
||||
}
|
||||
} finally {
|
||||
if (cbs.length > len) {
|
||||
// added more in the interim.
|
||||
// de-zalgo, just in case, but don't call again.
|
||||
cbs.splice(0, len)
|
||||
process.nextTick(function () {
|
||||
RES.apply(null, args)
|
||||
})
|
||||
} else {
|
||||
delete reqs[key]
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function slice (args) {
|
||||
var length = args.length
|
||||
var array = []
|
||||
|
||||
for (var i = 0; i < length; i++) array[i] = args[i]
|
||||
return array
|
||||
}
|
||||
-29
@@ -1,29 +0,0 @@
|
||||
{
|
||||
"name": "inflight",
|
||||
"version": "1.0.6",
|
||||
"description": "Add callbacks to requests in flight to avoid async duplication",
|
||||
"main": "inflight.js",
|
||||
"files": [
|
||||
"inflight.js"
|
||||
],
|
||||
"dependencies": {
|
||||
"once": "^1.3.0",
|
||||
"wrappy": "1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"tap": "^7.1.2"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "tap test.js --100"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/npm/inflight.git"
|
||||
},
|
||||
"author": "Isaac Z. Schlueter <i@izs.me> (http://blog.izs.me/)",
|
||||
"bugs": {
|
||||
"url": "https://github.com/isaacs/inflight/issues"
|
||||
},
|
||||
"homepage": "https://github.com/isaacs/inflight",
|
||||
"license": "ISC"
|
||||
}
|
||||
Reference in New Issue
Block a user